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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3370    init_test(cx, |_| {});
 3371
 3372    let mut cx = EditorTestContext::new(cx).await;
 3373
 3374    let diff_base = r#"
 3375        Line 0
 3376        Line 1
 3377        Line 2
 3378        Line 3
 3379        "#
 3380    .unindent();
 3381
 3382    cx.set_state(
 3383        &r#"
 3384        ˇLine 0
 3385        Line 1
 3386        Line 2
 3387        Line 3
 3388        "#
 3389        .unindent(),
 3390    );
 3391
 3392    cx.set_diff_base(&diff_base);
 3393    executor.run_until_parked();
 3394
 3395    // Join lines
 3396    cx.update_editor(|editor, window, cx| {
 3397        editor.join_lines(&JoinLines, window, cx);
 3398    });
 3399    executor.run_until_parked();
 3400
 3401    cx.assert_editor_state(
 3402        &r#"
 3403        Line 0ˇ Line 1
 3404        Line 2
 3405        Line 3
 3406        "#
 3407        .unindent(),
 3408    );
 3409    // Join again
 3410    cx.update_editor(|editor, window, cx| {
 3411        editor.join_lines(&JoinLines, window, cx);
 3412    });
 3413    executor.run_until_parked();
 3414
 3415    cx.assert_editor_state(
 3416        &r#"
 3417        Line 0 Line 1ˇ Line 2
 3418        Line 3
 3419        "#
 3420        .unindent(),
 3421    );
 3422}
 3423
 3424#[gpui::test]
 3425async fn test_custom_newlines_cause_no_false_positive_diffs(
 3426    executor: BackgroundExecutor,
 3427    cx: &mut TestAppContext,
 3428) {
 3429    init_test(cx, |_| {});
 3430    let mut cx = EditorTestContext::new(cx).await;
 3431    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3432    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3433    executor.run_until_parked();
 3434
 3435    cx.update_editor(|editor, window, cx| {
 3436        let snapshot = editor.snapshot(window, cx);
 3437        assert_eq!(
 3438            snapshot
 3439                .buffer_snapshot
 3440                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3441                .collect::<Vec<_>>(),
 3442            Vec::new(),
 3443            "Should not have any diffs for files with custom newlines"
 3444        );
 3445    });
 3446}
 3447
 3448#[gpui::test]
 3449async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3450    init_test(cx, |_| {});
 3451
 3452    let mut cx = EditorTestContext::new(cx).await;
 3453
 3454    // Test sort_lines_case_insensitive()
 3455    cx.set_state(indoc! {"
 3456        «z
 3457        y
 3458        x
 3459        Z
 3460        Y
 3461        Xˇ»
 3462    "});
 3463    cx.update_editor(|e, window, cx| {
 3464        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3465    });
 3466    cx.assert_editor_state(indoc! {"
 3467        «x
 3468        X
 3469        y
 3470        Y
 3471        z
 3472        Zˇ»
 3473    "});
 3474
 3475    // Test reverse_lines()
 3476    cx.set_state(indoc! {"
 3477        «5
 3478        4
 3479        3
 3480        2
 3481        1ˇ»
 3482    "});
 3483    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3484    cx.assert_editor_state(indoc! {"
 3485        «1
 3486        2
 3487        3
 3488        4
 3489        5ˇ»
 3490    "});
 3491
 3492    // Skip testing shuffle_line()
 3493
 3494    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3495    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3496
 3497    // Don't manipulate when cursor is on single line, but expand the selection
 3498    cx.set_state(indoc! {"
 3499        ddˇdd
 3500        ccc
 3501        bb
 3502        a
 3503    "});
 3504    cx.update_editor(|e, window, cx| {
 3505        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3506    });
 3507    cx.assert_editor_state(indoc! {"
 3508        «ddddˇ»
 3509        ccc
 3510        bb
 3511        a
 3512    "});
 3513
 3514    // Basic manipulate case
 3515    // Start selection moves to column 0
 3516    // End of selection shrinks to fit shorter line
 3517    cx.set_state(indoc! {"
 3518        dd«d
 3519        ccc
 3520        bb
 3521        aaaaaˇ»
 3522    "});
 3523    cx.update_editor(|e, window, cx| {
 3524        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3525    });
 3526    cx.assert_editor_state(indoc! {"
 3527        «aaaaa
 3528        bb
 3529        ccc
 3530        dddˇ»
 3531    "});
 3532
 3533    // Manipulate case with newlines
 3534    cx.set_state(indoc! {"
 3535        dd«d
 3536        ccc
 3537
 3538        bb
 3539        aaaaa
 3540
 3541        ˇ»
 3542    "});
 3543    cx.update_editor(|e, window, cx| {
 3544        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3545    });
 3546    cx.assert_editor_state(indoc! {"
 3547        «
 3548
 3549        aaaaa
 3550        bb
 3551        ccc
 3552        dddˇ»
 3553
 3554    "});
 3555
 3556    // Adding new line
 3557    cx.set_state(indoc! {"
 3558        aa«a
 3559        bbˇ»b
 3560    "});
 3561    cx.update_editor(|e, window, cx| {
 3562        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3563    });
 3564    cx.assert_editor_state(indoc! {"
 3565        «aaa
 3566        bbb
 3567        added_lineˇ»
 3568    "});
 3569
 3570    // Removing line
 3571    cx.set_state(indoc! {"
 3572        aa«a
 3573        bbbˇ»
 3574    "});
 3575    cx.update_editor(|e, window, cx| {
 3576        e.manipulate_lines(window, cx, |lines| {
 3577            lines.pop();
 3578        })
 3579    });
 3580    cx.assert_editor_state(indoc! {"
 3581        «aaaˇ»
 3582    "});
 3583
 3584    // Removing all lines
 3585    cx.set_state(indoc! {"
 3586        aa«a
 3587        bbbˇ»
 3588    "});
 3589    cx.update_editor(|e, window, cx| {
 3590        e.manipulate_lines(window, cx, |lines| {
 3591            lines.drain(..);
 3592        })
 3593    });
 3594    cx.assert_editor_state(indoc! {"
 3595        ˇ
 3596    "});
 3597}
 3598
 3599#[gpui::test]
 3600async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3601    init_test(cx, |_| {});
 3602
 3603    let mut cx = EditorTestContext::new(cx).await;
 3604
 3605    // Consider continuous selection as single selection
 3606    cx.set_state(indoc! {"
 3607        Aaa«aa
 3608        cˇ»c«c
 3609        bb
 3610        aaaˇ»aa
 3611    "});
 3612    cx.update_editor(|e, window, cx| {
 3613        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3614    });
 3615    cx.assert_editor_state(indoc! {"
 3616        «Aaaaa
 3617        ccc
 3618        bb
 3619        aaaaaˇ»
 3620    "});
 3621
 3622    cx.set_state(indoc! {"
 3623        Aaa«aa
 3624        cˇ»c«c
 3625        bb
 3626        aaaˇ»aa
 3627    "});
 3628    cx.update_editor(|e, window, cx| {
 3629        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3630    });
 3631    cx.assert_editor_state(indoc! {"
 3632        «Aaaaa
 3633        ccc
 3634        bbˇ»
 3635    "});
 3636
 3637    // Consider non continuous selection as distinct dedup operations
 3638    cx.set_state(indoc! {"
 3639        «aaaaa
 3640        bb
 3641        aaaaa
 3642        aaaaaˇ»
 3643
 3644        aaa«aaˇ»
 3645    "});
 3646    cx.update_editor(|e, window, cx| {
 3647        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3648    });
 3649    cx.assert_editor_state(indoc! {"
 3650        «aaaaa
 3651        bbˇ»
 3652
 3653        «aaaaaˇ»
 3654    "});
 3655}
 3656
 3657#[gpui::test]
 3658async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3659    init_test(cx, |_| {});
 3660
 3661    let mut cx = EditorTestContext::new(cx).await;
 3662
 3663    cx.set_state(indoc! {"
 3664        «Aaa
 3665        aAa
 3666        Aaaˇ»
 3667    "});
 3668    cx.update_editor(|e, window, cx| {
 3669        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3670    });
 3671    cx.assert_editor_state(indoc! {"
 3672        «Aaa
 3673        aAaˇ»
 3674    "});
 3675
 3676    cx.set_state(indoc! {"
 3677        «Aaa
 3678        aAa
 3679        aaAˇ»
 3680    "});
 3681    cx.update_editor(|e, window, cx| {
 3682        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3683    });
 3684    cx.assert_editor_state(indoc! {"
 3685        «Aaaˇ»
 3686    "});
 3687}
 3688
 3689#[gpui::test]
 3690async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3691    init_test(cx, |_| {});
 3692
 3693    let mut cx = EditorTestContext::new(cx).await;
 3694
 3695    // Manipulate with multiple selections on a single line
 3696    cx.set_state(indoc! {"
 3697        dd«dd
 3698        cˇ»c«c
 3699        bb
 3700        aaaˇ»aa
 3701    "});
 3702    cx.update_editor(|e, window, cx| {
 3703        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3704    });
 3705    cx.assert_editor_state(indoc! {"
 3706        «aaaaa
 3707        bb
 3708        ccc
 3709        ddddˇ»
 3710    "});
 3711
 3712    // Manipulate with multiple disjoin selections
 3713    cx.set_state(indoc! {"
 3714 3715        4
 3716        3
 3717        2
 3718        1ˇ»
 3719
 3720        dd«dd
 3721        ccc
 3722        bb
 3723        aaaˇ»aa
 3724    "});
 3725    cx.update_editor(|e, window, cx| {
 3726        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3727    });
 3728    cx.assert_editor_state(indoc! {"
 3729        «1
 3730        2
 3731        3
 3732        4
 3733        5ˇ»
 3734
 3735        «aaaaa
 3736        bb
 3737        ccc
 3738        ddddˇ»
 3739    "});
 3740
 3741    // Adding lines on each selection
 3742    cx.set_state(indoc! {"
 3743 3744        1ˇ»
 3745
 3746        bb«bb
 3747        aaaˇ»aa
 3748    "});
 3749    cx.update_editor(|e, window, cx| {
 3750        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3751    });
 3752    cx.assert_editor_state(indoc! {"
 3753        «2
 3754        1
 3755        added lineˇ»
 3756
 3757        «bbbb
 3758        aaaaa
 3759        added lineˇ»
 3760    "});
 3761
 3762    // Removing lines on each selection
 3763    cx.set_state(indoc! {"
 3764 3765        1ˇ»
 3766
 3767        bb«bb
 3768        aaaˇ»aa
 3769    "});
 3770    cx.update_editor(|e, window, cx| {
 3771        e.manipulate_lines(window, cx, |lines| {
 3772            lines.pop();
 3773        })
 3774    });
 3775    cx.assert_editor_state(indoc! {"
 3776        «2ˇ»
 3777
 3778        «bbbbˇ»
 3779    "});
 3780}
 3781
 3782#[gpui::test]
 3783async fn test_manipulate_text(cx: &mut TestAppContext) {
 3784    init_test(cx, |_| {});
 3785
 3786    let mut cx = EditorTestContext::new(cx).await;
 3787
 3788    // Test convert_to_upper_case()
 3789    cx.set_state(indoc! {"
 3790        «hello worldˇ»
 3791    "});
 3792    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3793    cx.assert_editor_state(indoc! {"
 3794        «HELLO WORLDˇ»
 3795    "});
 3796
 3797    // Test convert_to_lower_case()
 3798    cx.set_state(indoc! {"
 3799        «HELLO WORLDˇ»
 3800    "});
 3801    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3802    cx.assert_editor_state(indoc! {"
 3803        «hello worldˇ»
 3804    "});
 3805
 3806    // Test multiple line, single selection case
 3807    cx.set_state(indoc! {"
 3808        «The quick brown
 3809        fox jumps over
 3810        the lazy dogˇ»
 3811    "});
 3812    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3813    cx.assert_editor_state(indoc! {"
 3814        «The Quick Brown
 3815        Fox Jumps Over
 3816        The Lazy Dogˇ»
 3817    "});
 3818
 3819    // Test multiple line, single selection case
 3820    cx.set_state(indoc! {"
 3821        «The quick brown
 3822        fox jumps over
 3823        the lazy dogˇ»
 3824    "});
 3825    cx.update_editor(|e, window, cx| {
 3826        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3827    });
 3828    cx.assert_editor_state(indoc! {"
 3829        «TheQuickBrown
 3830        FoxJumpsOver
 3831        TheLazyDogˇ»
 3832    "});
 3833
 3834    // From here on out, test more complex cases of manipulate_text()
 3835
 3836    // Test no selection case - should affect words cursors are in
 3837    // Cursor at beginning, middle, and end of word
 3838    cx.set_state(indoc! {"
 3839        ˇhello big beauˇtiful worldˇ
 3840    "});
 3841    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3842    cx.assert_editor_state(indoc! {"
 3843        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3844    "});
 3845
 3846    // Test multiple selections on a single line and across multiple lines
 3847    cx.set_state(indoc! {"
 3848        «Theˇ» quick «brown
 3849        foxˇ» jumps «overˇ»
 3850        the «lazyˇ» dog
 3851    "});
 3852    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3853    cx.assert_editor_state(indoc! {"
 3854        «THEˇ» quick «BROWN
 3855        FOXˇ» jumps «OVERˇ»
 3856        the «LAZYˇ» dog
 3857    "});
 3858
 3859    // Test case where text length grows
 3860    cx.set_state(indoc! {"
 3861        «tschüߡ»
 3862    "});
 3863    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3864    cx.assert_editor_state(indoc! {"
 3865        «TSCHÜSSˇ»
 3866    "});
 3867
 3868    // Test to make sure we don't crash when text shrinks
 3869    cx.set_state(indoc! {"
 3870        aaa_bbbˇ
 3871    "});
 3872    cx.update_editor(|e, window, cx| {
 3873        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3874    });
 3875    cx.assert_editor_state(indoc! {"
 3876        «aaaBbbˇ»
 3877    "});
 3878
 3879    // Test to make sure we all aware of the fact that each word can grow and shrink
 3880    // Final selections should be aware of this fact
 3881    cx.set_state(indoc! {"
 3882        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3883    "});
 3884    cx.update_editor(|e, window, cx| {
 3885        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3886    });
 3887    cx.assert_editor_state(indoc! {"
 3888        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3889    "});
 3890
 3891    cx.set_state(indoc! {"
 3892        «hElLo, WoRld!ˇ»
 3893    "});
 3894    cx.update_editor(|e, window, cx| {
 3895        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3896    });
 3897    cx.assert_editor_state(indoc! {"
 3898        «HeLlO, wOrLD!ˇ»
 3899    "});
 3900}
 3901
 3902#[gpui::test]
 3903fn test_duplicate_line(cx: &mut TestAppContext) {
 3904    init_test(cx, |_| {});
 3905
 3906    let editor = cx.add_window(|window, cx| {
 3907        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3908        build_editor(buffer, window, cx)
 3909    });
 3910    _ = editor.update(cx, |editor, window, cx| {
 3911        editor.change_selections(None, window, cx, |s| {
 3912            s.select_display_ranges([
 3913                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3914                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3915                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3916                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3917            ])
 3918        });
 3919        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3920        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3921        assert_eq!(
 3922            editor.selections.display_ranges(cx),
 3923            vec![
 3924                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3925                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3926                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3927                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3928            ]
 3929        );
 3930    });
 3931
 3932    let editor = cx.add_window(|window, cx| {
 3933        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3934        build_editor(buffer, window, cx)
 3935    });
 3936    _ = editor.update(cx, |editor, window, cx| {
 3937        editor.change_selections(None, window, cx, |s| {
 3938            s.select_display_ranges([
 3939                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3940                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3941            ])
 3942        });
 3943        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3944        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3945        assert_eq!(
 3946            editor.selections.display_ranges(cx),
 3947            vec![
 3948                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3949                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3950            ]
 3951        );
 3952    });
 3953
 3954    // With `move_upwards` the selections stay in place, except for
 3955    // the lines inserted above them
 3956    let editor = cx.add_window(|window, cx| {
 3957        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3958        build_editor(buffer, window, cx)
 3959    });
 3960    _ = editor.update(cx, |editor, window, cx| {
 3961        editor.change_selections(None, window, cx, |s| {
 3962            s.select_display_ranges([
 3963                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3964                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3965                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3966                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3967            ])
 3968        });
 3969        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3970        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3971        assert_eq!(
 3972            editor.selections.display_ranges(cx),
 3973            vec![
 3974                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3975                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3976                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3977                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3978            ]
 3979        );
 3980    });
 3981
 3982    let editor = cx.add_window(|window, cx| {
 3983        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3984        build_editor(buffer, window, cx)
 3985    });
 3986    _ = editor.update(cx, |editor, window, cx| {
 3987        editor.change_selections(None, window, cx, |s| {
 3988            s.select_display_ranges([
 3989                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3990                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3991            ])
 3992        });
 3993        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3994        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3995        assert_eq!(
 3996            editor.selections.display_ranges(cx),
 3997            vec![
 3998                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3999                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4000            ]
 4001        );
 4002    });
 4003
 4004    let editor = cx.add_window(|window, cx| {
 4005        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4006        build_editor(buffer, window, cx)
 4007    });
 4008    _ = editor.update(cx, |editor, window, cx| {
 4009        editor.change_selections(None, window, cx, |s| {
 4010            s.select_display_ranges([
 4011                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4012                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4013            ])
 4014        });
 4015        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4016        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4017        assert_eq!(
 4018            editor.selections.display_ranges(cx),
 4019            vec![
 4020                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4021                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4022            ]
 4023        );
 4024    });
 4025}
 4026
 4027#[gpui::test]
 4028fn test_move_line_up_down(cx: &mut TestAppContext) {
 4029    init_test(cx, |_| {});
 4030
 4031    let editor = cx.add_window(|window, cx| {
 4032        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4033        build_editor(buffer, window, cx)
 4034    });
 4035    _ = editor.update(cx, |editor, window, cx| {
 4036        editor.fold_creases(
 4037            vec![
 4038                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4039                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4040                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4041            ],
 4042            true,
 4043            window,
 4044            cx,
 4045        );
 4046        editor.change_selections(None, window, cx, |s| {
 4047            s.select_display_ranges([
 4048                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4049                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4050                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4051                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4052            ])
 4053        });
 4054        assert_eq!(
 4055            editor.display_text(cx),
 4056            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4057        );
 4058
 4059        editor.move_line_up(&MoveLineUp, window, cx);
 4060        assert_eq!(
 4061            editor.display_text(cx),
 4062            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4063        );
 4064        assert_eq!(
 4065            editor.selections.display_ranges(cx),
 4066            vec![
 4067                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4068                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4069                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4070                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4071            ]
 4072        );
 4073    });
 4074
 4075    _ = editor.update(cx, |editor, window, cx| {
 4076        editor.move_line_down(&MoveLineDown, window, cx);
 4077        assert_eq!(
 4078            editor.display_text(cx),
 4079            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4080        );
 4081        assert_eq!(
 4082            editor.selections.display_ranges(cx),
 4083            vec![
 4084                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4085                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4086                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4087                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4088            ]
 4089        );
 4090    });
 4091
 4092    _ = editor.update(cx, |editor, window, cx| {
 4093        editor.move_line_down(&MoveLineDown, window, cx);
 4094        assert_eq!(
 4095            editor.display_text(cx),
 4096            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4097        );
 4098        assert_eq!(
 4099            editor.selections.display_ranges(cx),
 4100            vec![
 4101                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4102                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4103                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4104                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4105            ]
 4106        );
 4107    });
 4108
 4109    _ = editor.update(cx, |editor, window, cx| {
 4110        editor.move_line_up(&MoveLineUp, window, cx);
 4111        assert_eq!(
 4112            editor.display_text(cx),
 4113            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4114        );
 4115        assert_eq!(
 4116            editor.selections.display_ranges(cx),
 4117            vec![
 4118                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4119                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4120                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4121                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4122            ]
 4123        );
 4124    });
 4125}
 4126
 4127#[gpui::test]
 4128fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4129    init_test(cx, |_| {});
 4130
 4131    let editor = cx.add_window(|window, cx| {
 4132        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4133        build_editor(buffer, window, cx)
 4134    });
 4135    _ = editor.update(cx, |editor, window, cx| {
 4136        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4137        editor.insert_blocks(
 4138            [BlockProperties {
 4139                style: BlockStyle::Fixed,
 4140                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4141                height: 1,
 4142                render: Arc::new(|_| div().into_any()),
 4143                priority: 0,
 4144            }],
 4145            Some(Autoscroll::fit()),
 4146            cx,
 4147        );
 4148        editor.change_selections(None, window, cx, |s| {
 4149            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4150        });
 4151        editor.move_line_down(&MoveLineDown, window, cx);
 4152    });
 4153}
 4154
 4155#[gpui::test]
 4156async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4157    init_test(cx, |_| {});
 4158
 4159    let mut cx = EditorTestContext::new(cx).await;
 4160    cx.set_state(
 4161        &"
 4162            ˇzero
 4163            one
 4164            two
 4165            three
 4166            four
 4167            five
 4168        "
 4169        .unindent(),
 4170    );
 4171
 4172    // Create a four-line block that replaces three lines of text.
 4173    cx.update_editor(|editor, window, cx| {
 4174        let snapshot = editor.snapshot(window, cx);
 4175        let snapshot = &snapshot.buffer_snapshot;
 4176        let placement = BlockPlacement::Replace(
 4177            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4178        );
 4179        editor.insert_blocks(
 4180            [BlockProperties {
 4181                placement,
 4182                height: 4,
 4183                style: BlockStyle::Sticky,
 4184                render: Arc::new(|_| gpui::div().into_any_element()),
 4185                priority: 0,
 4186            }],
 4187            None,
 4188            cx,
 4189        );
 4190    });
 4191
 4192    // Move down so that the cursor touches the block.
 4193    cx.update_editor(|editor, window, cx| {
 4194        editor.move_down(&Default::default(), window, cx);
 4195    });
 4196    cx.assert_editor_state(
 4197        &"
 4198            zero
 4199            «one
 4200            two
 4201            threeˇ»
 4202            four
 4203            five
 4204        "
 4205        .unindent(),
 4206    );
 4207
 4208    // Move down past the block.
 4209    cx.update_editor(|editor, window, cx| {
 4210        editor.move_down(&Default::default(), window, cx);
 4211    });
 4212    cx.assert_editor_state(
 4213        &"
 4214            zero
 4215            one
 4216            two
 4217            three
 4218            ˇfour
 4219            five
 4220        "
 4221        .unindent(),
 4222    );
 4223}
 4224
 4225#[gpui::test]
 4226fn test_transpose(cx: &mut TestAppContext) {
 4227    init_test(cx, |_| {});
 4228
 4229    _ = cx.add_window(|window, cx| {
 4230        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4231        editor.set_style(EditorStyle::default(), window, cx);
 4232        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4233        editor.transpose(&Default::default(), window, cx);
 4234        assert_eq!(editor.text(cx), "bac");
 4235        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4236
 4237        editor.transpose(&Default::default(), window, cx);
 4238        assert_eq!(editor.text(cx), "bca");
 4239        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4240
 4241        editor.transpose(&Default::default(), window, cx);
 4242        assert_eq!(editor.text(cx), "bac");
 4243        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4244
 4245        editor
 4246    });
 4247
 4248    _ = cx.add_window(|window, cx| {
 4249        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4250        editor.set_style(EditorStyle::default(), window, cx);
 4251        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4252        editor.transpose(&Default::default(), window, cx);
 4253        assert_eq!(editor.text(cx), "acb\nde");
 4254        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4255
 4256        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4257        editor.transpose(&Default::default(), window, cx);
 4258        assert_eq!(editor.text(cx), "acbd\ne");
 4259        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4260
 4261        editor.transpose(&Default::default(), window, cx);
 4262        assert_eq!(editor.text(cx), "acbde\n");
 4263        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4264
 4265        editor.transpose(&Default::default(), window, cx);
 4266        assert_eq!(editor.text(cx), "acbd\ne");
 4267        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4268
 4269        editor
 4270    });
 4271
 4272    _ = cx.add_window(|window, cx| {
 4273        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4274        editor.set_style(EditorStyle::default(), window, cx);
 4275        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4276        editor.transpose(&Default::default(), window, cx);
 4277        assert_eq!(editor.text(cx), "bacd\ne");
 4278        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4279
 4280        editor.transpose(&Default::default(), window, cx);
 4281        assert_eq!(editor.text(cx), "bcade\n");
 4282        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4283
 4284        editor.transpose(&Default::default(), window, cx);
 4285        assert_eq!(editor.text(cx), "bcda\ne");
 4286        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4287
 4288        editor.transpose(&Default::default(), window, cx);
 4289        assert_eq!(editor.text(cx), "bcade\n");
 4290        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4291
 4292        editor.transpose(&Default::default(), window, cx);
 4293        assert_eq!(editor.text(cx), "bcaed\n");
 4294        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4295
 4296        editor
 4297    });
 4298
 4299    _ = cx.add_window(|window, cx| {
 4300        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4301        editor.set_style(EditorStyle::default(), window, cx);
 4302        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4303        editor.transpose(&Default::default(), window, cx);
 4304        assert_eq!(editor.text(cx), "🏀🍐✋");
 4305        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4306
 4307        editor.transpose(&Default::default(), window, cx);
 4308        assert_eq!(editor.text(cx), "🏀✋🍐");
 4309        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4310
 4311        editor.transpose(&Default::default(), window, cx);
 4312        assert_eq!(editor.text(cx), "🏀🍐✋");
 4313        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4314
 4315        editor
 4316    });
 4317}
 4318
 4319#[gpui::test]
 4320async fn test_rewrap(cx: &mut TestAppContext) {
 4321    init_test(cx, |settings| {
 4322        settings.languages.extend([
 4323            (
 4324                "Markdown".into(),
 4325                LanguageSettingsContent {
 4326                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4327                    ..Default::default()
 4328                },
 4329            ),
 4330            (
 4331                "Plain Text".into(),
 4332                LanguageSettingsContent {
 4333                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4334                    ..Default::default()
 4335                },
 4336            ),
 4337        ])
 4338    });
 4339
 4340    let mut cx = EditorTestContext::new(cx).await;
 4341
 4342    let language_with_c_comments = Arc::new(Language::new(
 4343        LanguageConfig {
 4344            line_comments: vec!["// ".into()],
 4345            ..LanguageConfig::default()
 4346        },
 4347        None,
 4348    ));
 4349    let language_with_pound_comments = Arc::new(Language::new(
 4350        LanguageConfig {
 4351            line_comments: vec!["# ".into()],
 4352            ..LanguageConfig::default()
 4353        },
 4354        None,
 4355    ));
 4356    let markdown_language = Arc::new(Language::new(
 4357        LanguageConfig {
 4358            name: "Markdown".into(),
 4359            ..LanguageConfig::default()
 4360        },
 4361        None,
 4362    ));
 4363    let language_with_doc_comments = Arc::new(Language::new(
 4364        LanguageConfig {
 4365            line_comments: vec!["// ".into(), "/// ".into()],
 4366            ..LanguageConfig::default()
 4367        },
 4368        Some(tree_sitter_rust::LANGUAGE.into()),
 4369    ));
 4370
 4371    let plaintext_language = Arc::new(Language::new(
 4372        LanguageConfig {
 4373            name: "Plain Text".into(),
 4374            ..LanguageConfig::default()
 4375        },
 4376        None,
 4377    ));
 4378
 4379    assert_rewrap(
 4380        indoc! {"
 4381            // ˇ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.
 4382        "},
 4383        indoc! {"
 4384            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4385            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4386            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4387            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4388            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4389            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4390            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4391            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4392            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4393            // porttitor id. Aliquam id accumsan eros.
 4394        "},
 4395        language_with_c_comments.clone(),
 4396        &mut cx,
 4397    );
 4398
 4399    // Test that rewrapping works inside of a selection
 4400    assert_rewrap(
 4401        indoc! {"
 4402            «// 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.ˇ»
 4403        "},
 4404        indoc! {"
 4405            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4406            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4407            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4408            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4409            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4410            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4411            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4412            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4413            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4414            // porttitor id. Aliquam id accumsan eros.ˇ»
 4415        "},
 4416        language_with_c_comments.clone(),
 4417        &mut cx,
 4418    );
 4419
 4420    // Test that cursors that expand to the same region are collapsed.
 4421    assert_rewrap(
 4422        indoc! {"
 4423            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4424            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4425            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4426            // ˇ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.
 4427        "},
 4428        indoc! {"
 4429            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4430            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4431            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4432            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4433            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4434            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4435            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4436            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4437            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4438            // porttitor id. Aliquam id accumsan eros.
 4439        "},
 4440        language_with_c_comments.clone(),
 4441        &mut cx,
 4442    );
 4443
 4444    // Test that non-contiguous selections are treated separately.
 4445    assert_rewrap(
 4446        indoc! {"
 4447            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4448            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4449            //
 4450            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4451            // ˇ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.
 4452        "},
 4453        indoc! {"
 4454            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4455            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4456            // auctor, eu lacinia sapien scelerisque.
 4457            //
 4458            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4459            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4460            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4461            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4462            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4463            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4464            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4465        "},
 4466        language_with_c_comments.clone(),
 4467        &mut cx,
 4468    );
 4469
 4470    // Test that different comment prefixes are supported.
 4471    assert_rewrap(
 4472        indoc! {"
 4473            # ˇ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.
 4474        "},
 4475        indoc! {"
 4476            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4477            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4478            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4479            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4480            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4481            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4482            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4483            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4484            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4485            # accumsan eros.
 4486        "},
 4487        language_with_pound_comments.clone(),
 4488        &mut cx,
 4489    );
 4490
 4491    // Test that rewrapping is ignored outside of comments in most languages.
 4492    assert_rewrap(
 4493        indoc! {"
 4494            /// Adds two numbers.
 4495            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4496            fn add(a: u32, b: u32) -> u32 {
 4497                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ˇ
 4498            }
 4499        "},
 4500        indoc! {"
 4501            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4502            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4503            fn add(a: u32, b: u32) -> u32 {
 4504                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ˇ
 4505            }
 4506        "},
 4507        language_with_doc_comments.clone(),
 4508        &mut cx,
 4509    );
 4510
 4511    // Test that rewrapping works in Markdown and Plain Text languages.
 4512    assert_rewrap(
 4513        indoc! {"
 4514            # Hello
 4515
 4516            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.
 4517        "},
 4518        indoc! {"
 4519            # Hello
 4520
 4521            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4522            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4523            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4524            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4525            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4526            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4527            Integer sit amet scelerisque nisi.
 4528        "},
 4529        markdown_language,
 4530        &mut cx,
 4531    );
 4532
 4533    assert_rewrap(
 4534        indoc! {"
 4535            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.
 4536        "},
 4537        indoc! {"
 4538            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4539            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4540            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4541            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4542            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4543            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4544            Integer sit amet scelerisque nisi.
 4545        "},
 4546        plaintext_language,
 4547        &mut cx,
 4548    );
 4549
 4550    // Test rewrapping unaligned comments in a selection.
 4551    assert_rewrap(
 4552        indoc! {"
 4553            fn foo() {
 4554                if true {
 4555            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4556            // Praesent semper egestas tellus id dignissim.ˇ»
 4557                    do_something();
 4558                } else {
 4559                    //
 4560                }
 4561            }
 4562        "},
 4563        indoc! {"
 4564            fn foo() {
 4565                if true {
 4566            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4567                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4568                    // egestas tellus id dignissim.ˇ»
 4569                    do_something();
 4570                } else {
 4571                    //
 4572                }
 4573            }
 4574        "},
 4575        language_with_doc_comments.clone(),
 4576        &mut cx,
 4577    );
 4578
 4579    assert_rewrap(
 4580        indoc! {"
 4581            fn foo() {
 4582                if true {
 4583            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4584            // Praesent semper egestas tellus id dignissim.»
 4585                    do_something();
 4586                } else {
 4587                    //
 4588                }
 4589
 4590            }
 4591        "},
 4592        indoc! {"
 4593            fn foo() {
 4594                if true {
 4595            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4596                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4597                    // egestas tellus id dignissim.»
 4598                    do_something();
 4599                } else {
 4600                    //
 4601                }
 4602
 4603            }
 4604        "},
 4605        language_with_doc_comments.clone(),
 4606        &mut cx,
 4607    );
 4608
 4609    #[track_caller]
 4610    fn assert_rewrap(
 4611        unwrapped_text: &str,
 4612        wrapped_text: &str,
 4613        language: Arc<Language>,
 4614        cx: &mut EditorTestContext,
 4615    ) {
 4616        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4617        cx.set_state(unwrapped_text);
 4618        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4619        cx.assert_editor_state(wrapped_text);
 4620    }
 4621}
 4622
 4623#[gpui::test]
 4624async fn test_clipboard(cx: &mut TestAppContext) {
 4625    init_test(cx, |_| {});
 4626
 4627    let mut cx = EditorTestContext::new(cx).await;
 4628
 4629    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4630    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4631    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4632
 4633    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4634    cx.set_state("two ˇfour ˇsix ˇ");
 4635    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4636    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4637
 4638    // Paste again but with only two cursors. Since the number of cursors doesn't
 4639    // match the number of slices in the clipboard, the entire clipboard text
 4640    // is pasted at each cursor.
 4641    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4642    cx.update_editor(|e, window, cx| {
 4643        e.handle_input("( ", window, cx);
 4644        e.paste(&Paste, window, cx);
 4645        e.handle_input(") ", window, cx);
 4646    });
 4647    cx.assert_editor_state(
 4648        &([
 4649            "( one✅ ",
 4650            "three ",
 4651            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4652            "three ",
 4653            "five ) ˇ",
 4654        ]
 4655        .join("\n")),
 4656    );
 4657
 4658    // Cut with three selections, one of which is full-line.
 4659    cx.set_state(indoc! {"
 4660        1«2ˇ»3
 4661        4ˇ567
 4662        «8ˇ»9"});
 4663    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        1ˇ3
 4666        ˇ9"});
 4667
 4668    // Paste with three selections, noticing how the copied selection that was full-line
 4669    // gets inserted before the second cursor.
 4670    cx.set_state(indoc! {"
 4671        1ˇ3
 4672 4673        «oˇ»ne"});
 4674    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4675    cx.assert_editor_state(indoc! {"
 4676        12ˇ3
 4677        4567
 4678 4679        8ˇne"});
 4680
 4681    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4682    cx.set_state(indoc! {"
 4683        The quick brown
 4684        fox juˇmps over
 4685        the lazy dog"});
 4686    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4687    assert_eq!(
 4688        cx.read_from_clipboard()
 4689            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4690        Some("fox jumps over\n".to_string())
 4691    );
 4692
 4693    // Paste with three selections, noticing how the copied full-line selection is inserted
 4694    // before the empty selections but replaces the selection that is non-empty.
 4695    cx.set_state(indoc! {"
 4696        Tˇhe quick brown
 4697        «foˇ»x jumps over
 4698        tˇhe lazy dog"});
 4699    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4700    cx.assert_editor_state(indoc! {"
 4701        fox jumps over
 4702        Tˇhe quick brown
 4703        fox jumps over
 4704        ˇx jumps over
 4705        fox jumps over
 4706        tˇhe lazy dog"});
 4707}
 4708
 4709#[gpui::test]
 4710async fn test_paste_multiline(cx: &mut TestAppContext) {
 4711    init_test(cx, |_| {});
 4712
 4713    let mut cx = EditorTestContext::new(cx).await;
 4714    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4715
 4716    // Cut an indented block, without the leading whitespace.
 4717    cx.set_state(indoc! {"
 4718        const a: B = (
 4719            c(),
 4720            «d(
 4721                e,
 4722                f
 4723            )ˇ»
 4724        );
 4725    "});
 4726    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4727    cx.assert_editor_state(indoc! {"
 4728        const a: B = (
 4729            c(),
 4730            ˇ
 4731        );
 4732    "});
 4733
 4734    // Paste it at the same position.
 4735    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4736    cx.assert_editor_state(indoc! {"
 4737        const a: B = (
 4738            c(),
 4739            d(
 4740                e,
 4741                f
 4742 4743        );
 4744    "});
 4745
 4746    // Paste it at a line with a lower indent level.
 4747    cx.set_state(indoc! {"
 4748        ˇ
 4749        const a: B = (
 4750            c(),
 4751        );
 4752    "});
 4753    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4754    cx.assert_editor_state(indoc! {"
 4755        d(
 4756            e,
 4757            f
 4758 4759        const a: B = (
 4760            c(),
 4761        );
 4762    "});
 4763
 4764    // Cut an indented block, with the leading whitespace.
 4765    cx.set_state(indoc! {"
 4766        const a: B = (
 4767            c(),
 4768        «    d(
 4769                e,
 4770                f
 4771            )
 4772        ˇ»);
 4773    "});
 4774    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4775    cx.assert_editor_state(indoc! {"
 4776        const a: B = (
 4777            c(),
 4778        ˇ);
 4779    "});
 4780
 4781    // Paste it at the same position.
 4782    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4783    cx.assert_editor_state(indoc! {"
 4784        const a: B = (
 4785            c(),
 4786            d(
 4787                e,
 4788                f
 4789            )
 4790        ˇ);
 4791    "});
 4792
 4793    // Paste it at a line with a higher indent level.
 4794    cx.set_state(indoc! {"
 4795        const a: B = (
 4796            c(),
 4797            d(
 4798                e,
 4799 4800            )
 4801        );
 4802    "});
 4803    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4804    cx.assert_editor_state(indoc! {"
 4805        const a: B = (
 4806            c(),
 4807            d(
 4808                e,
 4809                f    d(
 4810                    e,
 4811                    f
 4812                )
 4813        ˇ
 4814            )
 4815        );
 4816    "});
 4817}
 4818
 4819#[gpui::test]
 4820async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4821    init_test(cx, |_| {});
 4822
 4823    cx.write_to_clipboard(ClipboardItem::new_string(
 4824        "    d(\n        e\n    );\n".into(),
 4825    ));
 4826
 4827    let mut cx = EditorTestContext::new(cx).await;
 4828    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4829
 4830    cx.set_state(indoc! {"
 4831        fn a() {
 4832            b();
 4833            if c() {
 4834                ˇ
 4835            }
 4836        }
 4837    "});
 4838
 4839    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4840    cx.assert_editor_state(indoc! {"
 4841        fn a() {
 4842            b();
 4843            if c() {
 4844                d(
 4845                    e
 4846                );
 4847        ˇ
 4848            }
 4849        }
 4850    "});
 4851
 4852    cx.set_state(indoc! {"
 4853        fn a() {
 4854            b();
 4855            ˇ
 4856        }
 4857    "});
 4858
 4859    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4860    cx.assert_editor_state(indoc! {"
 4861        fn a() {
 4862            b();
 4863            d(
 4864                e
 4865            );
 4866        ˇ
 4867        }
 4868    "});
 4869}
 4870
 4871#[gpui::test]
 4872fn test_select_all(cx: &mut TestAppContext) {
 4873    init_test(cx, |_| {});
 4874
 4875    let editor = cx.add_window(|window, cx| {
 4876        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4877        build_editor(buffer, window, cx)
 4878    });
 4879    _ = editor.update(cx, |editor, window, cx| {
 4880        editor.select_all(&SelectAll, window, cx);
 4881        assert_eq!(
 4882            editor.selections.display_ranges(cx),
 4883            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4884        );
 4885    });
 4886}
 4887
 4888#[gpui::test]
 4889fn test_select_line(cx: &mut TestAppContext) {
 4890    init_test(cx, |_| {});
 4891
 4892    let editor = cx.add_window(|window, cx| {
 4893        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4894        build_editor(buffer, window, cx)
 4895    });
 4896    _ = editor.update(cx, |editor, window, cx| {
 4897        editor.change_selections(None, window, cx, |s| {
 4898            s.select_display_ranges([
 4899                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4900                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4901                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4902                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4903            ])
 4904        });
 4905        editor.select_line(&SelectLine, window, cx);
 4906        assert_eq!(
 4907            editor.selections.display_ranges(cx),
 4908            vec![
 4909                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4910                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4911            ]
 4912        );
 4913    });
 4914
 4915    _ = editor.update(cx, |editor, window, cx| {
 4916        editor.select_line(&SelectLine, window, cx);
 4917        assert_eq!(
 4918            editor.selections.display_ranges(cx),
 4919            vec![
 4920                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4921                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4922            ]
 4923        );
 4924    });
 4925
 4926    _ = editor.update(cx, |editor, window, cx| {
 4927        editor.select_line(&SelectLine, window, cx);
 4928        assert_eq!(
 4929            editor.selections.display_ranges(cx),
 4930            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4931        );
 4932    });
 4933}
 4934
 4935#[gpui::test]
 4936async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4937    init_test(cx, |_| {});
 4938    let mut cx = EditorTestContext::new(cx).await;
 4939
 4940    #[track_caller]
 4941    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4942        cx.set_state(initial_state);
 4943        cx.update_editor(|e, window, cx| {
 4944            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4945        });
 4946        cx.assert_editor_state(expected_state);
 4947    }
 4948
 4949    // Selection starts and ends at the middle of lines, left-to-right
 4950    test(
 4951        &mut cx,
 4952        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4953        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4954    );
 4955    // Same thing, right-to-left
 4956    test(
 4957        &mut cx,
 4958        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4959        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4960    );
 4961
 4962    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4963    test(
 4964        &mut cx,
 4965        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4966        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4967    );
 4968    // Same thing, right-to-left
 4969    test(
 4970        &mut cx,
 4971        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4972        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4973    );
 4974
 4975    // Whole buffer, left-to-right, last line ends with newline
 4976    test(
 4977        &mut cx,
 4978        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4979        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4980    );
 4981    // Same thing, right-to-left
 4982    test(
 4983        &mut cx,
 4984        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4985        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4986    );
 4987
 4988    // Starts at the end of a line, ends at the start of another
 4989    test(
 4990        &mut cx,
 4991        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4992        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4993    );
 4994}
 4995
 4996#[gpui::test]
 4997async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 4998    init_test(cx, |_| {});
 4999
 5000    let editor = cx.add_window(|window, cx| {
 5001        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5002        build_editor(buffer, window, cx)
 5003    });
 5004
 5005    // setup
 5006    _ = editor.update(cx, |editor, window, cx| {
 5007        editor.fold_creases(
 5008            vec![
 5009                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5010                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5011                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5012            ],
 5013            true,
 5014            window,
 5015            cx,
 5016        );
 5017        assert_eq!(
 5018            editor.display_text(cx),
 5019            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5020        );
 5021    });
 5022
 5023    _ = editor.update(cx, |editor, window, cx| {
 5024        editor.change_selections(None, window, cx, |s| {
 5025            s.select_display_ranges([
 5026                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5027                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5028                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5029                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5030            ])
 5031        });
 5032        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5033        assert_eq!(
 5034            editor.display_text(cx),
 5035            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5036        );
 5037    });
 5038    EditorTestContext::for_editor(editor, cx)
 5039        .await
 5040        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5041
 5042    _ = editor.update(cx, |editor, window, cx| {
 5043        editor.change_selections(None, window, cx, |s| {
 5044            s.select_display_ranges([
 5045                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5046            ])
 5047        });
 5048        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5049        assert_eq!(
 5050            editor.display_text(cx),
 5051            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5052        );
 5053        assert_eq!(
 5054            editor.selections.display_ranges(cx),
 5055            [
 5056                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5057                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5058                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5059                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5060                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5061                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5062                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5063            ]
 5064        );
 5065    });
 5066    EditorTestContext::for_editor(editor, cx)
 5067        .await
 5068        .assert_editor_state(
 5069            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5070        );
 5071}
 5072
 5073#[gpui::test]
 5074async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5075    init_test(cx, |_| {});
 5076
 5077    let mut cx = EditorTestContext::new(cx).await;
 5078
 5079    cx.set_state(indoc!(
 5080        r#"abc
 5081           defˇghi
 5082
 5083           jk
 5084           nlmo
 5085           "#
 5086    ));
 5087
 5088    cx.update_editor(|editor, window, cx| {
 5089        editor.add_selection_above(&Default::default(), window, cx);
 5090    });
 5091
 5092    cx.assert_editor_state(indoc!(
 5093        r#"abcˇ
 5094           defˇghi
 5095
 5096           jk
 5097           nlmo
 5098           "#
 5099    ));
 5100
 5101    cx.update_editor(|editor, window, cx| {
 5102        editor.add_selection_above(&Default::default(), window, cx);
 5103    });
 5104
 5105    cx.assert_editor_state(indoc!(
 5106        r#"abcˇ
 5107            defˇghi
 5108
 5109            jk
 5110            nlmo
 5111            "#
 5112    ));
 5113
 5114    cx.update_editor(|editor, window, cx| {
 5115        editor.add_selection_below(&Default::default(), window, cx);
 5116    });
 5117
 5118    cx.assert_editor_state(indoc!(
 5119        r#"abc
 5120           defˇghi
 5121
 5122           jk
 5123           nlmo
 5124           "#
 5125    ));
 5126
 5127    cx.update_editor(|editor, window, cx| {
 5128        editor.undo_selection(&Default::default(), window, cx);
 5129    });
 5130
 5131    cx.assert_editor_state(indoc!(
 5132        r#"abcˇ
 5133           defˇghi
 5134
 5135           jk
 5136           nlmo
 5137           "#
 5138    ));
 5139
 5140    cx.update_editor(|editor, window, cx| {
 5141        editor.redo_selection(&Default::default(), window, cx);
 5142    });
 5143
 5144    cx.assert_editor_state(indoc!(
 5145        r#"abc
 5146           defˇghi
 5147
 5148           jk
 5149           nlmo
 5150           "#
 5151    ));
 5152
 5153    cx.update_editor(|editor, window, cx| {
 5154        editor.add_selection_below(&Default::default(), window, cx);
 5155    });
 5156
 5157    cx.assert_editor_state(indoc!(
 5158        r#"abc
 5159           defˇghi
 5160
 5161           jk
 5162           nlmˇo
 5163           "#
 5164    ));
 5165
 5166    cx.update_editor(|editor, window, cx| {
 5167        editor.add_selection_below(&Default::default(), window, cx);
 5168    });
 5169
 5170    cx.assert_editor_state(indoc!(
 5171        r#"abc
 5172           defˇghi
 5173
 5174           jk
 5175           nlmˇo
 5176           "#
 5177    ));
 5178
 5179    // change selections
 5180    cx.set_state(indoc!(
 5181        r#"abc
 5182           def«ˇg»hi
 5183
 5184           jk
 5185           nlmo
 5186           "#
 5187    ));
 5188
 5189    cx.update_editor(|editor, window, cx| {
 5190        editor.add_selection_below(&Default::default(), window, cx);
 5191    });
 5192
 5193    cx.assert_editor_state(indoc!(
 5194        r#"abc
 5195           def«ˇg»hi
 5196
 5197           jk
 5198           nlm«ˇo»
 5199           "#
 5200    ));
 5201
 5202    cx.update_editor(|editor, window, cx| {
 5203        editor.add_selection_below(&Default::default(), window, cx);
 5204    });
 5205
 5206    cx.assert_editor_state(indoc!(
 5207        r#"abc
 5208           def«ˇg»hi
 5209
 5210           jk
 5211           nlm«ˇo»
 5212           "#
 5213    ));
 5214
 5215    cx.update_editor(|editor, window, cx| {
 5216        editor.add_selection_above(&Default::default(), window, cx);
 5217    });
 5218
 5219    cx.assert_editor_state(indoc!(
 5220        r#"abc
 5221           def«ˇg»hi
 5222
 5223           jk
 5224           nlmo
 5225           "#
 5226    ));
 5227
 5228    cx.update_editor(|editor, window, cx| {
 5229        editor.add_selection_above(&Default::default(), window, cx);
 5230    });
 5231
 5232    cx.assert_editor_state(indoc!(
 5233        r#"abc
 5234           def«ˇg»hi
 5235
 5236           jk
 5237           nlmo
 5238           "#
 5239    ));
 5240
 5241    // Change selections again
 5242    cx.set_state(indoc!(
 5243        r#"a«bc
 5244           defgˇ»hi
 5245
 5246           jk
 5247           nlmo
 5248           "#
 5249    ));
 5250
 5251    cx.update_editor(|editor, window, cx| {
 5252        editor.add_selection_below(&Default::default(), window, cx);
 5253    });
 5254
 5255    cx.assert_editor_state(indoc!(
 5256        r#"a«bcˇ»
 5257           d«efgˇ»hi
 5258
 5259           j«kˇ»
 5260           nlmo
 5261           "#
 5262    ));
 5263
 5264    cx.update_editor(|editor, window, cx| {
 5265        editor.add_selection_below(&Default::default(), window, cx);
 5266    });
 5267    cx.assert_editor_state(indoc!(
 5268        r#"a«bcˇ»
 5269           d«efgˇ»hi
 5270
 5271           j«kˇ»
 5272           n«lmoˇ»
 5273           "#
 5274    ));
 5275    cx.update_editor(|editor, window, cx| {
 5276        editor.add_selection_above(&Default::default(), window, cx);
 5277    });
 5278
 5279    cx.assert_editor_state(indoc!(
 5280        r#"a«bcˇ»
 5281           d«efgˇ»hi
 5282
 5283           j«kˇ»
 5284           nlmo
 5285           "#
 5286    ));
 5287
 5288    // Change selections again
 5289    cx.set_state(indoc!(
 5290        r#"abc
 5291           d«ˇefghi
 5292
 5293           jk
 5294           nlm»o
 5295           "#
 5296    ));
 5297
 5298    cx.update_editor(|editor, window, cx| {
 5299        editor.add_selection_above(&Default::default(), window, cx);
 5300    });
 5301
 5302    cx.assert_editor_state(indoc!(
 5303        r#"a«ˇbc»
 5304           d«ˇef»ghi
 5305
 5306           j«ˇk»
 5307           n«ˇlm»o
 5308           "#
 5309    ));
 5310
 5311    cx.update_editor(|editor, window, cx| {
 5312        editor.add_selection_below(&Default::default(), window, cx);
 5313    });
 5314
 5315    cx.assert_editor_state(indoc!(
 5316        r#"abc
 5317           d«ˇef»ghi
 5318
 5319           j«ˇk»
 5320           n«ˇlm»o
 5321           "#
 5322    ));
 5323}
 5324
 5325#[gpui::test]
 5326async fn test_select_next(cx: &mut TestAppContext) {
 5327    init_test(cx, |_| {});
 5328
 5329    let mut cx = EditorTestContext::new(cx).await;
 5330    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5331
 5332    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5333        .unwrap();
 5334    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5335
 5336    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5337        .unwrap();
 5338    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5339
 5340    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5341    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5342
 5343    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5344    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5345
 5346    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5347        .unwrap();
 5348    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5349
 5350    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5351        .unwrap();
 5352    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5353}
 5354
 5355#[gpui::test]
 5356async fn test_select_all_matches(cx: &mut TestAppContext) {
 5357    init_test(cx, |_| {});
 5358
 5359    let mut cx = EditorTestContext::new(cx).await;
 5360
 5361    // Test caret-only selections
 5362    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5363    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5364        .unwrap();
 5365    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5366
 5367    // Test left-to-right selections
 5368    cx.set_state("abc\n«abcˇ»\nabc");
 5369    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5370        .unwrap();
 5371    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5372
 5373    // Test right-to-left selections
 5374    cx.set_state("abc\n«ˇabc»\nabc");
 5375    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5376        .unwrap();
 5377    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5378
 5379    // Test selecting whitespace with caret selection
 5380    cx.set_state("abc\nˇ   abc\nabc");
 5381    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5382        .unwrap();
 5383    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5384
 5385    // Test selecting whitespace with left-to-right selection
 5386    cx.set_state("abc\n«ˇ  »abc\nabc");
 5387    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5388        .unwrap();
 5389    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5390
 5391    // Test no matches with right-to-left selection
 5392    cx.set_state("abc\n«  ˇ»abc\nabc");
 5393    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5394        .unwrap();
 5395    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5396}
 5397
 5398#[gpui::test]
 5399async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5400    init_test(cx, |_| {});
 5401
 5402    let mut cx = EditorTestContext::new(cx).await;
 5403    cx.set_state(
 5404        r#"let foo = 2;
 5405lˇet foo = 2;
 5406let fooˇ = 2;
 5407let foo = 2;
 5408let foo = ˇ2;"#,
 5409    );
 5410
 5411    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5412        .unwrap();
 5413    cx.assert_editor_state(
 5414        r#"let foo = 2;
 5415«letˇ» foo = 2;
 5416let «fooˇ» = 2;
 5417let foo = 2;
 5418let foo = «2ˇ»;"#,
 5419    );
 5420
 5421    // noop for multiple selections with different contents
 5422    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5423        .unwrap();
 5424    cx.assert_editor_state(
 5425        r#"let foo = 2;
 5426«letˇ» foo = 2;
 5427let «fooˇ» = 2;
 5428let foo = 2;
 5429let foo = «2ˇ»;"#,
 5430    );
 5431}
 5432
 5433#[gpui::test]
 5434async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5435    init_test(cx, |_| {});
 5436
 5437    let mut cx =
 5438        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5439
 5440    cx.assert_editor_state(indoc! {"
 5441        ˇbbb
 5442        ccc
 5443
 5444        bbb
 5445        ccc
 5446        "});
 5447    cx.dispatch_action(SelectPrevious::default());
 5448    cx.assert_editor_state(indoc! {"
 5449                «bbbˇ»
 5450                ccc
 5451
 5452                bbb
 5453                ccc
 5454                "});
 5455    cx.dispatch_action(SelectPrevious::default());
 5456    cx.assert_editor_state(indoc! {"
 5457                «bbbˇ»
 5458                ccc
 5459
 5460                «bbbˇ»
 5461                ccc
 5462                "});
 5463}
 5464
 5465#[gpui::test]
 5466async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5467    init_test(cx, |_| {});
 5468
 5469    let mut cx = EditorTestContext::new(cx).await;
 5470    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5471
 5472    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5473        .unwrap();
 5474    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5475
 5476    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5477        .unwrap();
 5478    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5479
 5480    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5481    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5482
 5483    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5484    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5485
 5486    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5487        .unwrap();
 5488    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5489
 5490    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5491        .unwrap();
 5492    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5493
 5494    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5495        .unwrap();
 5496    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5497}
 5498
 5499#[gpui::test]
 5500async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5501    init_test(cx, |_| {});
 5502
 5503    let mut cx = EditorTestContext::new(cx).await;
 5504    cx.set_state("");
 5505
 5506    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5507        .unwrap();
 5508    cx.assert_editor_state("«aˇ»");
 5509    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5510        .unwrap();
 5511    cx.assert_editor_state("«aˇ»");
 5512}
 5513
 5514#[gpui::test]
 5515async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5516    init_test(cx, |_| {});
 5517
 5518    let mut cx = EditorTestContext::new(cx).await;
 5519    cx.set_state(
 5520        r#"let foo = 2;
 5521lˇet foo = 2;
 5522let fooˇ = 2;
 5523let foo = 2;
 5524let foo = ˇ2;"#,
 5525    );
 5526
 5527    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5528        .unwrap();
 5529    cx.assert_editor_state(
 5530        r#"let foo = 2;
 5531«letˇ» foo = 2;
 5532let «fooˇ» = 2;
 5533let foo = 2;
 5534let foo = «2ˇ»;"#,
 5535    );
 5536
 5537    // noop for multiple selections with different contents
 5538    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5539        .unwrap();
 5540    cx.assert_editor_state(
 5541        r#"let foo = 2;
 5542«letˇ» foo = 2;
 5543let «fooˇ» = 2;
 5544let foo = 2;
 5545let foo = «2ˇ»;"#,
 5546    );
 5547}
 5548
 5549#[gpui::test]
 5550async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5551    init_test(cx, |_| {});
 5552
 5553    let mut cx = EditorTestContext::new(cx).await;
 5554    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5555
 5556    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5557        .unwrap();
 5558    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5559
 5560    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5561        .unwrap();
 5562    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5563
 5564    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5565    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5566
 5567    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5568    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5569
 5570    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5571        .unwrap();
 5572    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5573
 5574    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5575        .unwrap();
 5576    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5577}
 5578
 5579#[gpui::test]
 5580async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5581    init_test(cx, |_| {});
 5582
 5583    let language = Arc::new(Language::new(
 5584        LanguageConfig::default(),
 5585        Some(tree_sitter_rust::LANGUAGE.into()),
 5586    ));
 5587
 5588    let text = r#"
 5589        use mod1::mod2::{mod3, mod4};
 5590
 5591        fn fn_1(param1: bool, param2: &str) {
 5592            let var1 = "text";
 5593        }
 5594    "#
 5595    .unindent();
 5596
 5597    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5598    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5599    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5600
 5601    editor
 5602        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5603        .await;
 5604
 5605    editor.update_in(cx, |editor, window, cx| {
 5606        editor.change_selections(None, window, cx, |s| {
 5607            s.select_display_ranges([
 5608                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5609                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5610                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5611            ]);
 5612        });
 5613        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5614    });
 5615    editor.update(cx, |editor, cx| {
 5616        assert_text_with_selections(
 5617            editor,
 5618            indoc! {r#"
 5619                use mod1::mod2::{mod3, «mod4ˇ»};
 5620
 5621                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5622                    let var1 = "«textˇ»";
 5623                }
 5624            "#},
 5625            cx,
 5626        );
 5627    });
 5628
 5629    editor.update_in(cx, |editor, window, cx| {
 5630        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5631    });
 5632    editor.update(cx, |editor, cx| {
 5633        assert_text_with_selections(
 5634            editor,
 5635            indoc! {r#"
 5636                use mod1::mod2::«{mod3, mod4}ˇ»;
 5637
 5638                «ˇfn fn_1(param1: bool, param2: &str) {
 5639                    let var1 = "text";
 5640 5641            "#},
 5642            cx,
 5643        );
 5644    });
 5645
 5646    editor.update_in(cx, |editor, window, cx| {
 5647        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5648    });
 5649    assert_eq!(
 5650        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5651        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5652    );
 5653
 5654    // Trying to expand the selected syntax node one more time has no effect.
 5655    editor.update_in(cx, |editor, window, cx| {
 5656        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5657    });
 5658    assert_eq!(
 5659        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5660        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5661    );
 5662
 5663    editor.update_in(cx, |editor, window, cx| {
 5664        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5665    });
 5666    editor.update(cx, |editor, cx| {
 5667        assert_text_with_selections(
 5668            editor,
 5669            indoc! {r#"
 5670                use mod1::mod2::«{mod3, mod4}ˇ»;
 5671
 5672                «ˇfn fn_1(param1: bool, param2: &str) {
 5673                    let var1 = "text";
 5674 5675            "#},
 5676            cx,
 5677        );
 5678    });
 5679
 5680    editor.update_in(cx, |editor, window, cx| {
 5681        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5682    });
 5683    editor.update(cx, |editor, cx| {
 5684        assert_text_with_selections(
 5685            editor,
 5686            indoc! {r#"
 5687                use mod1::mod2::{mod3, «mod4ˇ»};
 5688
 5689                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5690                    let var1 = "«textˇ»";
 5691                }
 5692            "#},
 5693            cx,
 5694        );
 5695    });
 5696
 5697    editor.update_in(cx, |editor, window, cx| {
 5698        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5699    });
 5700    editor.update(cx, |editor, cx| {
 5701        assert_text_with_selections(
 5702            editor,
 5703            indoc! {r#"
 5704                use mod1::mod2::{mod3, mo«ˇ»d4};
 5705
 5706                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5707                    let var1 = "te«ˇ»xt";
 5708                }
 5709            "#},
 5710            cx,
 5711        );
 5712    });
 5713
 5714    // Trying to shrink the selected syntax node one more time has no effect.
 5715    editor.update_in(cx, |editor, window, cx| {
 5716        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5717    });
 5718    editor.update_in(cx, |editor, _, cx| {
 5719        assert_text_with_selections(
 5720            editor,
 5721            indoc! {r#"
 5722                use mod1::mod2::{mod3, mo«ˇ»d4};
 5723
 5724                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5725                    let var1 = "te«ˇ»xt";
 5726                }
 5727            "#},
 5728            cx,
 5729        );
 5730    });
 5731
 5732    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5733    // a fold.
 5734    editor.update_in(cx, |editor, window, cx| {
 5735        editor.fold_creases(
 5736            vec![
 5737                Crease::simple(
 5738                    Point::new(0, 21)..Point::new(0, 24),
 5739                    FoldPlaceholder::test(),
 5740                ),
 5741                Crease::simple(
 5742                    Point::new(3, 20)..Point::new(3, 22),
 5743                    FoldPlaceholder::test(),
 5744                ),
 5745            ],
 5746            true,
 5747            window,
 5748            cx,
 5749        );
 5750        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5751    });
 5752    editor.update(cx, |editor, cx| {
 5753        assert_text_with_selections(
 5754            editor,
 5755            indoc! {r#"
 5756                use mod1::mod2::«{mod3, mod4}ˇ»;
 5757
 5758                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5759                    «let var1 = "text";ˇ»
 5760                }
 5761            "#},
 5762            cx,
 5763        );
 5764    });
 5765}
 5766
 5767#[gpui::test]
 5768async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5769    init_test(cx, |_| {});
 5770
 5771    let base_text = r#"
 5772        impl A {
 5773            // this is an uncommitted comment
 5774
 5775            fn b() {
 5776                c();
 5777            }
 5778
 5779            // this is another uncommitted comment
 5780
 5781            fn d() {
 5782                // e
 5783                // f
 5784            }
 5785        }
 5786
 5787        fn g() {
 5788            // h
 5789        }
 5790    "#
 5791    .unindent();
 5792
 5793    let text = r#"
 5794        ˇimpl A {
 5795
 5796            fn b() {
 5797                c();
 5798            }
 5799
 5800            fn d() {
 5801                // e
 5802                // f
 5803            }
 5804        }
 5805
 5806        fn g() {
 5807            // h
 5808        }
 5809    "#
 5810    .unindent();
 5811
 5812    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5813    cx.set_state(&text);
 5814    cx.set_diff_base(&base_text);
 5815    cx.update_editor(|editor, window, cx| {
 5816        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5817    });
 5818
 5819    cx.assert_state_with_diff(
 5820        "
 5821        ˇimpl A {
 5822      -     // this is an uncommitted comment
 5823
 5824            fn b() {
 5825                c();
 5826            }
 5827
 5828      -     // this is another uncommitted comment
 5829      -
 5830            fn d() {
 5831                // e
 5832                // f
 5833            }
 5834        }
 5835
 5836        fn g() {
 5837            // h
 5838        }
 5839    "
 5840        .unindent(),
 5841    );
 5842
 5843    let expected_display_text = "
 5844        impl A {
 5845            // this is an uncommitted comment
 5846
 5847            fn b() {
 5848 5849            }
 5850
 5851            // this is another uncommitted comment
 5852
 5853            fn d() {
 5854 5855            }
 5856        }
 5857
 5858        fn g() {
 5859 5860        }
 5861        "
 5862    .unindent();
 5863
 5864    cx.update_editor(|editor, window, cx| {
 5865        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5866        assert_eq!(editor.display_text(cx), expected_display_text);
 5867    });
 5868}
 5869
 5870#[gpui::test]
 5871async fn test_autoindent(cx: &mut TestAppContext) {
 5872    init_test(cx, |_| {});
 5873
 5874    let language = Arc::new(
 5875        Language::new(
 5876            LanguageConfig {
 5877                brackets: BracketPairConfig {
 5878                    pairs: vec![
 5879                        BracketPair {
 5880                            start: "{".to_string(),
 5881                            end: "}".to_string(),
 5882                            close: false,
 5883                            surround: false,
 5884                            newline: true,
 5885                        },
 5886                        BracketPair {
 5887                            start: "(".to_string(),
 5888                            end: ")".to_string(),
 5889                            close: false,
 5890                            surround: false,
 5891                            newline: true,
 5892                        },
 5893                    ],
 5894                    ..Default::default()
 5895                },
 5896                ..Default::default()
 5897            },
 5898            Some(tree_sitter_rust::LANGUAGE.into()),
 5899        )
 5900        .with_indents_query(
 5901            r#"
 5902                (_ "(" ")" @end) @indent
 5903                (_ "{" "}" @end) @indent
 5904            "#,
 5905        )
 5906        .unwrap(),
 5907    );
 5908
 5909    let text = "fn a() {}";
 5910
 5911    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5912    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5913    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5914    editor
 5915        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5916        .await;
 5917
 5918    editor.update_in(cx, |editor, window, cx| {
 5919        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5920        editor.newline(&Newline, window, cx);
 5921        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5922        assert_eq!(
 5923            editor.selections.ranges(cx),
 5924            &[
 5925                Point::new(1, 4)..Point::new(1, 4),
 5926                Point::new(3, 4)..Point::new(3, 4),
 5927                Point::new(5, 0)..Point::new(5, 0)
 5928            ]
 5929        );
 5930    });
 5931}
 5932
 5933#[gpui::test]
 5934async fn test_autoindent_selections(cx: &mut TestAppContext) {
 5935    init_test(cx, |_| {});
 5936
 5937    {
 5938        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5939        cx.set_state(indoc! {"
 5940            impl A {
 5941
 5942                fn b() {}
 5943
 5944            «fn c() {
 5945
 5946            }ˇ»
 5947            }
 5948        "});
 5949
 5950        cx.update_editor(|editor, window, cx| {
 5951            editor.autoindent(&Default::default(), window, cx);
 5952        });
 5953
 5954        cx.assert_editor_state(indoc! {"
 5955            impl A {
 5956
 5957                fn b() {}
 5958
 5959                «fn c() {
 5960
 5961                }ˇ»
 5962            }
 5963        "});
 5964    }
 5965
 5966    {
 5967        let mut cx = EditorTestContext::new_multibuffer(
 5968            cx,
 5969            [indoc! { "
 5970                impl A {
 5971                «
 5972                // a
 5973                fn b(){}
 5974                »
 5975                «
 5976                    }
 5977                    fn c(){}
 5978                »
 5979            "}],
 5980        );
 5981
 5982        let buffer = cx.update_editor(|editor, _, cx| {
 5983            let buffer = editor.buffer().update(cx, |buffer, _| {
 5984                buffer.all_buffers().iter().next().unwrap().clone()
 5985            });
 5986            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5987            buffer
 5988        });
 5989
 5990        cx.run_until_parked();
 5991        cx.update_editor(|editor, window, cx| {
 5992            editor.select_all(&Default::default(), window, cx);
 5993            editor.autoindent(&Default::default(), window, cx)
 5994        });
 5995        cx.run_until_parked();
 5996
 5997        cx.update(|_, cx| {
 5998            pretty_assertions::assert_eq!(
 5999                buffer.read(cx).text(),
 6000                indoc! { "
 6001                    impl A {
 6002
 6003                        // a
 6004                        fn b(){}
 6005
 6006
 6007                    }
 6008                    fn c(){}
 6009
 6010                " }
 6011            )
 6012        });
 6013    }
 6014}
 6015
 6016#[gpui::test]
 6017async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6018    init_test(cx, |_| {});
 6019
 6020    let mut cx = EditorTestContext::new(cx).await;
 6021
 6022    let language = Arc::new(Language::new(
 6023        LanguageConfig {
 6024            brackets: BracketPairConfig {
 6025                pairs: vec![
 6026                    BracketPair {
 6027                        start: "{".to_string(),
 6028                        end: "}".to_string(),
 6029                        close: true,
 6030                        surround: true,
 6031                        newline: true,
 6032                    },
 6033                    BracketPair {
 6034                        start: "(".to_string(),
 6035                        end: ")".to_string(),
 6036                        close: true,
 6037                        surround: true,
 6038                        newline: true,
 6039                    },
 6040                    BracketPair {
 6041                        start: "/*".to_string(),
 6042                        end: " */".to_string(),
 6043                        close: true,
 6044                        surround: true,
 6045                        newline: true,
 6046                    },
 6047                    BracketPair {
 6048                        start: "[".to_string(),
 6049                        end: "]".to_string(),
 6050                        close: false,
 6051                        surround: false,
 6052                        newline: true,
 6053                    },
 6054                    BracketPair {
 6055                        start: "\"".to_string(),
 6056                        end: "\"".to_string(),
 6057                        close: true,
 6058                        surround: true,
 6059                        newline: false,
 6060                    },
 6061                    BracketPair {
 6062                        start: "<".to_string(),
 6063                        end: ">".to_string(),
 6064                        close: false,
 6065                        surround: true,
 6066                        newline: true,
 6067                    },
 6068                ],
 6069                ..Default::default()
 6070            },
 6071            autoclose_before: "})]".to_string(),
 6072            ..Default::default()
 6073        },
 6074        Some(tree_sitter_rust::LANGUAGE.into()),
 6075    ));
 6076
 6077    cx.language_registry().add(language.clone());
 6078    cx.update_buffer(|buffer, cx| {
 6079        buffer.set_language(Some(language), cx);
 6080    });
 6081
 6082    cx.set_state(
 6083        &r#"
 6084            🏀ˇ
 6085            εˇ
 6086            ❤️ˇ
 6087        "#
 6088        .unindent(),
 6089    );
 6090
 6091    // autoclose multiple nested brackets at multiple cursors
 6092    cx.update_editor(|editor, window, cx| {
 6093        editor.handle_input("{", window, cx);
 6094        editor.handle_input("{", window, cx);
 6095        editor.handle_input("{", window, cx);
 6096    });
 6097    cx.assert_editor_state(
 6098        &"
 6099            🏀{{{ˇ}}}
 6100            ε{{{ˇ}}}
 6101            ❤️{{{ˇ}}}
 6102        "
 6103        .unindent(),
 6104    );
 6105
 6106    // insert a different closing bracket
 6107    cx.update_editor(|editor, window, cx| {
 6108        editor.handle_input(")", window, cx);
 6109    });
 6110    cx.assert_editor_state(
 6111        &"
 6112            🏀{{{)ˇ}}}
 6113            ε{{{)ˇ}}}
 6114            ❤️{{{)ˇ}}}
 6115        "
 6116        .unindent(),
 6117    );
 6118
 6119    // skip over the auto-closed brackets when typing a closing bracket
 6120    cx.update_editor(|editor, window, cx| {
 6121        editor.move_right(&MoveRight, window, cx);
 6122        editor.handle_input("}", window, cx);
 6123        editor.handle_input("}", window, cx);
 6124        editor.handle_input("}", window, cx);
 6125    });
 6126    cx.assert_editor_state(
 6127        &"
 6128            🏀{{{)}}}}ˇ
 6129            ε{{{)}}}}ˇ
 6130            ❤️{{{)}}}}ˇ
 6131        "
 6132        .unindent(),
 6133    );
 6134
 6135    // autoclose multi-character pairs
 6136    cx.set_state(
 6137        &"
 6138            ˇ
 6139            ˇ
 6140        "
 6141        .unindent(),
 6142    );
 6143    cx.update_editor(|editor, window, cx| {
 6144        editor.handle_input("/", window, cx);
 6145        editor.handle_input("*", window, cx);
 6146    });
 6147    cx.assert_editor_state(
 6148        &"
 6149            /*ˇ */
 6150            /*ˇ */
 6151        "
 6152        .unindent(),
 6153    );
 6154
 6155    // one cursor autocloses a multi-character pair, one cursor
 6156    // does not autoclose.
 6157    cx.set_state(
 6158        &"
 6159 6160            ˇ
 6161        "
 6162        .unindent(),
 6163    );
 6164    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6165    cx.assert_editor_state(
 6166        &"
 6167            /*ˇ */
 6168 6169        "
 6170        .unindent(),
 6171    );
 6172
 6173    // Don't autoclose if the next character isn't whitespace and isn't
 6174    // listed in the language's "autoclose_before" section.
 6175    cx.set_state("ˇa b");
 6176    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6177    cx.assert_editor_state("{ˇa b");
 6178
 6179    // Don't autoclose if `close` is false for the bracket pair
 6180    cx.set_state("ˇ");
 6181    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6182    cx.assert_editor_state("");
 6183
 6184    // Surround with brackets if text is selected
 6185    cx.set_state("«aˇ» b");
 6186    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6187    cx.assert_editor_state("{«aˇ»} b");
 6188
 6189    // Autclose pair where the start and end characters are the same
 6190    cx.set_state("");
 6191    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6192    cx.assert_editor_state("a\"ˇ\"");
 6193    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6194    cx.assert_editor_state("a\"\"ˇ");
 6195
 6196    // Don't autoclose pair if autoclose is disabled
 6197    cx.set_state("ˇ");
 6198    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6199    cx.assert_editor_state("");
 6200
 6201    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6202    cx.set_state("«aˇ» b");
 6203    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6204    cx.assert_editor_state("<«aˇ»> b");
 6205}
 6206
 6207#[gpui::test]
 6208async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6209    init_test(cx, |settings| {
 6210        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6211    });
 6212
 6213    let mut cx = EditorTestContext::new(cx).await;
 6214
 6215    let language = Arc::new(Language::new(
 6216        LanguageConfig {
 6217            brackets: BracketPairConfig {
 6218                pairs: vec![
 6219                    BracketPair {
 6220                        start: "{".to_string(),
 6221                        end: "}".to_string(),
 6222                        close: true,
 6223                        surround: true,
 6224                        newline: true,
 6225                    },
 6226                    BracketPair {
 6227                        start: "(".to_string(),
 6228                        end: ")".to_string(),
 6229                        close: true,
 6230                        surround: true,
 6231                        newline: true,
 6232                    },
 6233                    BracketPair {
 6234                        start: "[".to_string(),
 6235                        end: "]".to_string(),
 6236                        close: false,
 6237                        surround: false,
 6238                        newline: true,
 6239                    },
 6240                ],
 6241                ..Default::default()
 6242            },
 6243            autoclose_before: "})]".to_string(),
 6244            ..Default::default()
 6245        },
 6246        Some(tree_sitter_rust::LANGUAGE.into()),
 6247    ));
 6248
 6249    cx.language_registry().add(language.clone());
 6250    cx.update_buffer(|buffer, cx| {
 6251        buffer.set_language(Some(language), cx);
 6252    });
 6253
 6254    cx.set_state(
 6255        &"
 6256            ˇ
 6257            ˇ
 6258            ˇ
 6259        "
 6260        .unindent(),
 6261    );
 6262
 6263    // ensure only matching closing brackets are skipped over
 6264    cx.update_editor(|editor, window, cx| {
 6265        editor.handle_input("}", window, cx);
 6266        editor.move_left(&MoveLeft, window, cx);
 6267        editor.handle_input(")", window, cx);
 6268        editor.move_left(&MoveLeft, window, cx);
 6269    });
 6270    cx.assert_editor_state(
 6271        &"
 6272            ˇ)}
 6273            ˇ)}
 6274            ˇ)}
 6275        "
 6276        .unindent(),
 6277    );
 6278
 6279    // skip-over closing brackets at multiple cursors
 6280    cx.update_editor(|editor, window, cx| {
 6281        editor.handle_input(")", window, cx);
 6282        editor.handle_input("}", window, cx);
 6283    });
 6284    cx.assert_editor_state(
 6285        &"
 6286            )}ˇ
 6287            )}ˇ
 6288            )}ˇ
 6289        "
 6290        .unindent(),
 6291    );
 6292
 6293    // ignore non-close brackets
 6294    cx.update_editor(|editor, window, cx| {
 6295        editor.handle_input("]", window, cx);
 6296        editor.move_left(&MoveLeft, window, cx);
 6297        editor.handle_input("]", window, cx);
 6298    });
 6299    cx.assert_editor_state(
 6300        &"
 6301            )}]ˇ]
 6302            )}]ˇ]
 6303            )}]ˇ]
 6304        "
 6305        .unindent(),
 6306    );
 6307}
 6308
 6309#[gpui::test]
 6310async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6311    init_test(cx, |_| {});
 6312
 6313    let mut cx = EditorTestContext::new(cx).await;
 6314
 6315    let html_language = Arc::new(
 6316        Language::new(
 6317            LanguageConfig {
 6318                name: "HTML".into(),
 6319                brackets: BracketPairConfig {
 6320                    pairs: vec![
 6321                        BracketPair {
 6322                            start: "<".into(),
 6323                            end: ">".into(),
 6324                            close: true,
 6325                            ..Default::default()
 6326                        },
 6327                        BracketPair {
 6328                            start: "{".into(),
 6329                            end: "}".into(),
 6330                            close: true,
 6331                            ..Default::default()
 6332                        },
 6333                        BracketPair {
 6334                            start: "(".into(),
 6335                            end: ")".into(),
 6336                            close: true,
 6337                            ..Default::default()
 6338                        },
 6339                    ],
 6340                    ..Default::default()
 6341                },
 6342                autoclose_before: "})]>".into(),
 6343                ..Default::default()
 6344            },
 6345            Some(tree_sitter_html::LANGUAGE.into()),
 6346        )
 6347        .with_injection_query(
 6348            r#"
 6349            (script_element
 6350                (raw_text) @injection.content
 6351                (#set! injection.language "javascript"))
 6352            "#,
 6353        )
 6354        .unwrap(),
 6355    );
 6356
 6357    let javascript_language = Arc::new(Language::new(
 6358        LanguageConfig {
 6359            name: "JavaScript".into(),
 6360            brackets: BracketPairConfig {
 6361                pairs: vec![
 6362                    BracketPair {
 6363                        start: "/*".into(),
 6364                        end: " */".into(),
 6365                        close: true,
 6366                        ..Default::default()
 6367                    },
 6368                    BracketPair {
 6369                        start: "{".into(),
 6370                        end: "}".into(),
 6371                        close: true,
 6372                        ..Default::default()
 6373                    },
 6374                    BracketPair {
 6375                        start: "(".into(),
 6376                        end: ")".into(),
 6377                        close: true,
 6378                        ..Default::default()
 6379                    },
 6380                ],
 6381                ..Default::default()
 6382            },
 6383            autoclose_before: "})]>".into(),
 6384            ..Default::default()
 6385        },
 6386        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6387    ));
 6388
 6389    cx.language_registry().add(html_language.clone());
 6390    cx.language_registry().add(javascript_language.clone());
 6391
 6392    cx.update_buffer(|buffer, cx| {
 6393        buffer.set_language(Some(html_language), cx);
 6394    });
 6395
 6396    cx.set_state(
 6397        &r#"
 6398            <body>ˇ
 6399                <script>
 6400                    var x = 1;ˇ
 6401                </script>
 6402            </body>ˇ
 6403        "#
 6404        .unindent(),
 6405    );
 6406
 6407    // Precondition: different languages are active at different locations.
 6408    cx.update_editor(|editor, window, cx| {
 6409        let snapshot = editor.snapshot(window, cx);
 6410        let cursors = editor.selections.ranges::<usize>(cx);
 6411        let languages = cursors
 6412            .iter()
 6413            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6414            .collect::<Vec<_>>();
 6415        assert_eq!(
 6416            languages,
 6417            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6418        );
 6419    });
 6420
 6421    // Angle brackets autoclose in HTML, but not JavaScript.
 6422    cx.update_editor(|editor, window, cx| {
 6423        editor.handle_input("<", window, cx);
 6424        editor.handle_input("a", window, cx);
 6425    });
 6426    cx.assert_editor_state(
 6427        &r#"
 6428            <body><aˇ>
 6429                <script>
 6430                    var x = 1;<aˇ
 6431                </script>
 6432            </body><aˇ>
 6433        "#
 6434        .unindent(),
 6435    );
 6436
 6437    // Curly braces and parens autoclose in both HTML and JavaScript.
 6438    cx.update_editor(|editor, window, cx| {
 6439        editor.handle_input(" b=", window, cx);
 6440        editor.handle_input("{", window, cx);
 6441        editor.handle_input("c", window, cx);
 6442        editor.handle_input("(", window, cx);
 6443    });
 6444    cx.assert_editor_state(
 6445        &r#"
 6446            <body><a b={c(ˇ)}>
 6447                <script>
 6448                    var x = 1;<a b={c(ˇ)}
 6449                </script>
 6450            </body><a b={c(ˇ)}>
 6451        "#
 6452        .unindent(),
 6453    );
 6454
 6455    // Brackets that were already autoclosed are skipped.
 6456    cx.update_editor(|editor, window, cx| {
 6457        editor.handle_input(")", window, cx);
 6458        editor.handle_input("d", window, cx);
 6459        editor.handle_input("}", window, cx);
 6460    });
 6461    cx.assert_editor_state(
 6462        &r#"
 6463            <body><a b={c()d}ˇ>
 6464                <script>
 6465                    var x = 1;<a b={c()d}ˇ
 6466                </script>
 6467            </body><a b={c()d}ˇ>
 6468        "#
 6469        .unindent(),
 6470    );
 6471    cx.update_editor(|editor, window, cx| {
 6472        editor.handle_input(">", window, cx);
 6473    });
 6474    cx.assert_editor_state(
 6475        &r#"
 6476            <body><a b={c()d}>ˇ
 6477                <script>
 6478                    var x = 1;<a b={c()d}>ˇ
 6479                </script>
 6480            </body><a b={c()d}>ˇ
 6481        "#
 6482        .unindent(),
 6483    );
 6484
 6485    // Reset
 6486    cx.set_state(
 6487        &r#"
 6488            <body>ˇ
 6489                <script>
 6490                    var x = 1;ˇ
 6491                </script>
 6492            </body>ˇ
 6493        "#
 6494        .unindent(),
 6495    );
 6496
 6497    cx.update_editor(|editor, window, cx| {
 6498        editor.handle_input("<", window, cx);
 6499    });
 6500    cx.assert_editor_state(
 6501        &r#"
 6502            <body><ˇ>
 6503                <script>
 6504                    var x = 1;<ˇ
 6505                </script>
 6506            </body><ˇ>
 6507        "#
 6508        .unindent(),
 6509    );
 6510
 6511    // When backspacing, the closing angle brackets are removed.
 6512    cx.update_editor(|editor, window, cx| {
 6513        editor.backspace(&Backspace, window, cx);
 6514    });
 6515    cx.assert_editor_state(
 6516        &r#"
 6517            <body>ˇ
 6518                <script>
 6519                    var x = 1;ˇ
 6520                </script>
 6521            </body>ˇ
 6522        "#
 6523        .unindent(),
 6524    );
 6525
 6526    // Block comments autoclose in JavaScript, but not HTML.
 6527    cx.update_editor(|editor, window, cx| {
 6528        editor.handle_input("/", window, cx);
 6529        editor.handle_input("*", window, cx);
 6530    });
 6531    cx.assert_editor_state(
 6532        &r#"
 6533            <body>/*ˇ
 6534                <script>
 6535                    var x = 1;/*ˇ */
 6536                </script>
 6537            </body>/*ˇ
 6538        "#
 6539        .unindent(),
 6540    );
 6541}
 6542
 6543#[gpui::test]
 6544async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6545    init_test(cx, |_| {});
 6546
 6547    let mut cx = EditorTestContext::new(cx).await;
 6548
 6549    let rust_language = Arc::new(
 6550        Language::new(
 6551            LanguageConfig {
 6552                name: "Rust".into(),
 6553                brackets: serde_json::from_value(json!([
 6554                    { "start": "{", "end": "}", "close": true, "newline": true },
 6555                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6556                ]))
 6557                .unwrap(),
 6558                autoclose_before: "})]>".into(),
 6559                ..Default::default()
 6560            },
 6561            Some(tree_sitter_rust::LANGUAGE.into()),
 6562        )
 6563        .with_override_query("(string_literal) @string")
 6564        .unwrap(),
 6565    );
 6566
 6567    cx.language_registry().add(rust_language.clone());
 6568    cx.update_buffer(|buffer, cx| {
 6569        buffer.set_language(Some(rust_language), cx);
 6570    });
 6571
 6572    cx.set_state(
 6573        &r#"
 6574            let x = ˇ
 6575        "#
 6576        .unindent(),
 6577    );
 6578
 6579    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6580    cx.update_editor(|editor, window, cx| {
 6581        editor.handle_input("\"", window, cx);
 6582    });
 6583    cx.assert_editor_state(
 6584        &r#"
 6585            let x = "ˇ"
 6586        "#
 6587        .unindent(),
 6588    );
 6589
 6590    // Inserting another quotation mark. The cursor moves across the existing
 6591    // automatically-inserted quotation mark.
 6592    cx.update_editor(|editor, window, cx| {
 6593        editor.handle_input("\"", window, cx);
 6594    });
 6595    cx.assert_editor_state(
 6596        &r#"
 6597            let x = ""ˇ
 6598        "#
 6599        .unindent(),
 6600    );
 6601
 6602    // Reset
 6603    cx.set_state(
 6604        &r#"
 6605            let x = ˇ
 6606        "#
 6607        .unindent(),
 6608    );
 6609
 6610    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6611    cx.update_editor(|editor, window, cx| {
 6612        editor.handle_input("\"", window, cx);
 6613        editor.handle_input(" ", window, cx);
 6614        editor.move_left(&Default::default(), window, cx);
 6615        editor.handle_input("\\", window, cx);
 6616        editor.handle_input("\"", window, cx);
 6617    });
 6618    cx.assert_editor_state(
 6619        &r#"
 6620            let x = "\"ˇ "
 6621        "#
 6622        .unindent(),
 6623    );
 6624
 6625    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6626    // mark. Nothing is inserted.
 6627    cx.update_editor(|editor, window, cx| {
 6628        editor.move_right(&Default::default(), window, cx);
 6629        editor.handle_input("\"", window, cx);
 6630    });
 6631    cx.assert_editor_state(
 6632        &r#"
 6633            let x = "\" "ˇ
 6634        "#
 6635        .unindent(),
 6636    );
 6637}
 6638
 6639#[gpui::test]
 6640async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6641    init_test(cx, |_| {});
 6642
 6643    let language = Arc::new(Language::new(
 6644        LanguageConfig {
 6645            brackets: BracketPairConfig {
 6646                pairs: vec![
 6647                    BracketPair {
 6648                        start: "{".to_string(),
 6649                        end: "}".to_string(),
 6650                        close: true,
 6651                        surround: true,
 6652                        newline: true,
 6653                    },
 6654                    BracketPair {
 6655                        start: "/* ".to_string(),
 6656                        end: "*/".to_string(),
 6657                        close: true,
 6658                        surround: true,
 6659                        ..Default::default()
 6660                    },
 6661                ],
 6662                ..Default::default()
 6663            },
 6664            ..Default::default()
 6665        },
 6666        Some(tree_sitter_rust::LANGUAGE.into()),
 6667    ));
 6668
 6669    let text = r#"
 6670        a
 6671        b
 6672        c
 6673    "#
 6674    .unindent();
 6675
 6676    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6677    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6678    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6679    editor
 6680        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6681        .await;
 6682
 6683    editor.update_in(cx, |editor, window, cx| {
 6684        editor.change_selections(None, window, cx, |s| {
 6685            s.select_display_ranges([
 6686                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6687                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6688                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6689            ])
 6690        });
 6691
 6692        editor.handle_input("{", window, cx);
 6693        editor.handle_input("{", window, cx);
 6694        editor.handle_input("{", window, cx);
 6695        assert_eq!(
 6696            editor.text(cx),
 6697            "
 6698                {{{a}}}
 6699                {{{b}}}
 6700                {{{c}}}
 6701            "
 6702            .unindent()
 6703        );
 6704        assert_eq!(
 6705            editor.selections.display_ranges(cx),
 6706            [
 6707                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6708                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6709                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6710            ]
 6711        );
 6712
 6713        editor.undo(&Undo, window, cx);
 6714        editor.undo(&Undo, window, cx);
 6715        editor.undo(&Undo, window, cx);
 6716        assert_eq!(
 6717            editor.text(cx),
 6718            "
 6719                a
 6720                b
 6721                c
 6722            "
 6723            .unindent()
 6724        );
 6725        assert_eq!(
 6726            editor.selections.display_ranges(cx),
 6727            [
 6728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6729                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6730                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6731            ]
 6732        );
 6733
 6734        // Ensure inserting the first character of a multi-byte bracket pair
 6735        // doesn't surround the selections with the bracket.
 6736        editor.handle_input("/", window, cx);
 6737        assert_eq!(
 6738            editor.text(cx),
 6739            "
 6740                /
 6741                /
 6742                /
 6743            "
 6744            .unindent()
 6745        );
 6746        assert_eq!(
 6747            editor.selections.display_ranges(cx),
 6748            [
 6749                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6750                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6751                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6752            ]
 6753        );
 6754
 6755        editor.undo(&Undo, window, cx);
 6756        assert_eq!(
 6757            editor.text(cx),
 6758            "
 6759                a
 6760                b
 6761                c
 6762            "
 6763            .unindent()
 6764        );
 6765        assert_eq!(
 6766            editor.selections.display_ranges(cx),
 6767            [
 6768                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6769                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6770                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6771            ]
 6772        );
 6773
 6774        // Ensure inserting the last character of a multi-byte bracket pair
 6775        // doesn't surround the selections with the bracket.
 6776        editor.handle_input("*", window, cx);
 6777        assert_eq!(
 6778            editor.text(cx),
 6779            "
 6780                *
 6781                *
 6782                *
 6783            "
 6784            .unindent()
 6785        );
 6786        assert_eq!(
 6787            editor.selections.display_ranges(cx),
 6788            [
 6789                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6790                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6791                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6792            ]
 6793        );
 6794    });
 6795}
 6796
 6797#[gpui::test]
 6798async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6799    init_test(cx, |_| {});
 6800
 6801    let language = Arc::new(Language::new(
 6802        LanguageConfig {
 6803            brackets: BracketPairConfig {
 6804                pairs: vec![BracketPair {
 6805                    start: "{".to_string(),
 6806                    end: "}".to_string(),
 6807                    close: true,
 6808                    surround: true,
 6809                    newline: true,
 6810                }],
 6811                ..Default::default()
 6812            },
 6813            autoclose_before: "}".to_string(),
 6814            ..Default::default()
 6815        },
 6816        Some(tree_sitter_rust::LANGUAGE.into()),
 6817    ));
 6818
 6819    let text = r#"
 6820        a
 6821        b
 6822        c
 6823    "#
 6824    .unindent();
 6825
 6826    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6827    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6828    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6829    editor
 6830        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6831        .await;
 6832
 6833    editor.update_in(cx, |editor, window, cx| {
 6834        editor.change_selections(None, window, cx, |s| {
 6835            s.select_ranges([
 6836                Point::new(0, 1)..Point::new(0, 1),
 6837                Point::new(1, 1)..Point::new(1, 1),
 6838                Point::new(2, 1)..Point::new(2, 1),
 6839            ])
 6840        });
 6841
 6842        editor.handle_input("{", window, cx);
 6843        editor.handle_input("{", window, cx);
 6844        editor.handle_input("_", window, cx);
 6845        assert_eq!(
 6846            editor.text(cx),
 6847            "
 6848                a{{_}}
 6849                b{{_}}
 6850                c{{_}}
 6851            "
 6852            .unindent()
 6853        );
 6854        assert_eq!(
 6855            editor.selections.ranges::<Point>(cx),
 6856            [
 6857                Point::new(0, 4)..Point::new(0, 4),
 6858                Point::new(1, 4)..Point::new(1, 4),
 6859                Point::new(2, 4)..Point::new(2, 4)
 6860            ]
 6861        );
 6862
 6863        editor.backspace(&Default::default(), window, cx);
 6864        editor.backspace(&Default::default(), window, cx);
 6865        assert_eq!(
 6866            editor.text(cx),
 6867            "
 6868                a{}
 6869                b{}
 6870                c{}
 6871            "
 6872            .unindent()
 6873        );
 6874        assert_eq!(
 6875            editor.selections.ranges::<Point>(cx),
 6876            [
 6877                Point::new(0, 2)..Point::new(0, 2),
 6878                Point::new(1, 2)..Point::new(1, 2),
 6879                Point::new(2, 2)..Point::new(2, 2)
 6880            ]
 6881        );
 6882
 6883        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6884        assert_eq!(
 6885            editor.text(cx),
 6886            "
 6887                a
 6888                b
 6889                c
 6890            "
 6891            .unindent()
 6892        );
 6893        assert_eq!(
 6894            editor.selections.ranges::<Point>(cx),
 6895            [
 6896                Point::new(0, 1)..Point::new(0, 1),
 6897                Point::new(1, 1)..Point::new(1, 1),
 6898                Point::new(2, 1)..Point::new(2, 1)
 6899            ]
 6900        );
 6901    });
 6902}
 6903
 6904#[gpui::test]
 6905async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 6906    init_test(cx, |settings| {
 6907        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6908    });
 6909
 6910    let mut cx = EditorTestContext::new(cx).await;
 6911
 6912    let language = Arc::new(Language::new(
 6913        LanguageConfig {
 6914            brackets: BracketPairConfig {
 6915                pairs: vec![
 6916                    BracketPair {
 6917                        start: "{".to_string(),
 6918                        end: "}".to_string(),
 6919                        close: true,
 6920                        surround: true,
 6921                        newline: true,
 6922                    },
 6923                    BracketPair {
 6924                        start: "(".to_string(),
 6925                        end: ")".to_string(),
 6926                        close: true,
 6927                        surround: true,
 6928                        newline: true,
 6929                    },
 6930                    BracketPair {
 6931                        start: "[".to_string(),
 6932                        end: "]".to_string(),
 6933                        close: false,
 6934                        surround: true,
 6935                        newline: true,
 6936                    },
 6937                ],
 6938                ..Default::default()
 6939            },
 6940            autoclose_before: "})]".to_string(),
 6941            ..Default::default()
 6942        },
 6943        Some(tree_sitter_rust::LANGUAGE.into()),
 6944    ));
 6945
 6946    cx.language_registry().add(language.clone());
 6947    cx.update_buffer(|buffer, cx| {
 6948        buffer.set_language(Some(language), cx);
 6949    });
 6950
 6951    cx.set_state(
 6952        &"
 6953            {(ˇ)}
 6954            [[ˇ]]
 6955            {(ˇ)}
 6956        "
 6957        .unindent(),
 6958    );
 6959
 6960    cx.update_editor(|editor, window, cx| {
 6961        editor.backspace(&Default::default(), window, cx);
 6962        editor.backspace(&Default::default(), window, cx);
 6963    });
 6964
 6965    cx.assert_editor_state(
 6966        &"
 6967            ˇ
 6968            ˇ]]
 6969            ˇ
 6970        "
 6971        .unindent(),
 6972    );
 6973
 6974    cx.update_editor(|editor, window, cx| {
 6975        editor.handle_input("{", window, cx);
 6976        editor.handle_input("{", window, cx);
 6977        editor.move_right(&MoveRight, window, cx);
 6978        editor.move_right(&MoveRight, window, cx);
 6979        editor.move_left(&MoveLeft, window, cx);
 6980        editor.move_left(&MoveLeft, window, cx);
 6981        editor.backspace(&Default::default(), window, cx);
 6982    });
 6983
 6984    cx.assert_editor_state(
 6985        &"
 6986            {ˇ}
 6987            {ˇ}]]
 6988            {ˇ}
 6989        "
 6990        .unindent(),
 6991    );
 6992
 6993    cx.update_editor(|editor, window, cx| {
 6994        editor.backspace(&Default::default(), window, cx);
 6995    });
 6996
 6997    cx.assert_editor_state(
 6998        &"
 6999            ˇ
 7000            ˇ]]
 7001            ˇ
 7002        "
 7003        .unindent(),
 7004    );
 7005}
 7006
 7007#[gpui::test]
 7008async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7009    init_test(cx, |_| {});
 7010
 7011    let language = Arc::new(Language::new(
 7012        LanguageConfig::default(),
 7013        Some(tree_sitter_rust::LANGUAGE.into()),
 7014    ));
 7015
 7016    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7017    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7018    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7019    editor
 7020        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7021        .await;
 7022
 7023    editor.update_in(cx, |editor, window, cx| {
 7024        editor.set_auto_replace_emoji_shortcode(true);
 7025
 7026        editor.handle_input("Hello ", window, cx);
 7027        editor.handle_input(":wave", window, cx);
 7028        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7029
 7030        editor.handle_input(":", window, cx);
 7031        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7032
 7033        editor.handle_input(" :smile", window, cx);
 7034        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7035
 7036        editor.handle_input(":", window, cx);
 7037        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7038
 7039        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7040        editor.handle_input(":wave", window, cx);
 7041        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7042
 7043        editor.handle_input(":", window, cx);
 7044        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7045
 7046        editor.handle_input(":1", window, cx);
 7047        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7048
 7049        editor.handle_input(":", window, cx);
 7050        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7051
 7052        // Ensure shortcode does not get replaced when it is part of a word
 7053        editor.handle_input(" Test:wave", window, cx);
 7054        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7055
 7056        editor.handle_input(":", window, cx);
 7057        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7058
 7059        editor.set_auto_replace_emoji_shortcode(false);
 7060
 7061        // Ensure shortcode does not get replaced when auto replace is off
 7062        editor.handle_input(" :wave", window, cx);
 7063        assert_eq!(
 7064            editor.text(cx),
 7065            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7066        );
 7067
 7068        editor.handle_input(":", window, cx);
 7069        assert_eq!(
 7070            editor.text(cx),
 7071            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7072        );
 7073    });
 7074}
 7075
 7076#[gpui::test]
 7077async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7078    init_test(cx, |_| {});
 7079
 7080    let (text, insertion_ranges) = marked_text_ranges(
 7081        indoc! {"
 7082            ˇ
 7083        "},
 7084        false,
 7085    );
 7086
 7087    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7088    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7089
 7090    _ = editor.update_in(cx, |editor, window, cx| {
 7091        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7092
 7093        editor
 7094            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7095            .unwrap();
 7096
 7097        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7098            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7099            assert_eq!(editor.text(cx), expected_text);
 7100            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7101        }
 7102
 7103        assert(
 7104            editor,
 7105            cx,
 7106            indoc! {"
 7107            type «» =•
 7108            "},
 7109        );
 7110
 7111        assert!(editor.context_menu_visible(), "There should be a matches");
 7112    });
 7113}
 7114
 7115#[gpui::test]
 7116async fn test_snippets(cx: &mut TestAppContext) {
 7117    init_test(cx, |_| {});
 7118
 7119    let (text, insertion_ranges) = marked_text_ranges(
 7120        indoc! {"
 7121            a.ˇ b
 7122            a.ˇ b
 7123            a.ˇ b
 7124        "},
 7125        false,
 7126    );
 7127
 7128    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7129    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7130
 7131    editor.update_in(cx, |editor, window, cx| {
 7132        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7133
 7134        editor
 7135            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7136            .unwrap();
 7137
 7138        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7139            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7140            assert_eq!(editor.text(cx), expected_text);
 7141            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7142        }
 7143
 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
 7154        // Can't move earlier than the first tab stop
 7155        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7156        assert(
 7157            editor,
 7158            cx,
 7159            indoc! {"
 7160                a.f(«one», two, «three») b
 7161                a.f(«one», two, «three») b
 7162                a.f(«one», two, «three») b
 7163            "},
 7164        );
 7165
 7166        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7167        assert(
 7168            editor,
 7169            cx,
 7170            indoc! {"
 7171                a.f(one, «two», three) b
 7172                a.f(one, «two», three) b
 7173                a.f(one, «two», three) b
 7174            "},
 7175        );
 7176
 7177        editor.move_to_prev_snippet_tabstop(window, cx);
 7178        assert(
 7179            editor,
 7180            cx,
 7181            indoc! {"
 7182                a.f(«one», two, «three») b
 7183                a.f(«one», two, «three») b
 7184                a.f(«one», two, «three») b
 7185            "},
 7186        );
 7187
 7188        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7189        assert(
 7190            editor,
 7191            cx,
 7192            indoc! {"
 7193                a.f(one, «two», three) b
 7194                a.f(one, «two», three) b
 7195                a.f(one, «two», three) b
 7196            "},
 7197        );
 7198        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7199        assert(
 7200            editor,
 7201            cx,
 7202            indoc! {"
 7203                a.f(one, two, three)ˇ b
 7204                a.f(one, two, three)ˇ b
 7205                a.f(one, two, three)ˇ b
 7206            "},
 7207        );
 7208
 7209        // As soon as the last tab stop is reached, snippet state is gone
 7210        editor.move_to_prev_snippet_tabstop(window, cx);
 7211        assert(
 7212            editor,
 7213            cx,
 7214            indoc! {"
 7215                a.f(one, two, three)ˇ b
 7216                a.f(one, two, three)ˇ b
 7217                a.f(one, two, three)ˇ b
 7218            "},
 7219        );
 7220    });
 7221}
 7222
 7223#[gpui::test]
 7224async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7225    init_test(cx, |_| {});
 7226
 7227    let fs = FakeFs::new(cx.executor());
 7228    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7229
 7230    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7231
 7232    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7233    language_registry.add(rust_lang());
 7234    let mut fake_servers = language_registry.register_fake_lsp(
 7235        "Rust",
 7236        FakeLspAdapter {
 7237            capabilities: lsp::ServerCapabilities {
 7238                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7239                ..Default::default()
 7240            },
 7241            ..Default::default()
 7242        },
 7243    );
 7244
 7245    let buffer = project
 7246        .update(cx, |project, cx| {
 7247            project.open_local_buffer(path!("/file.rs"), cx)
 7248        })
 7249        .await
 7250        .unwrap();
 7251
 7252    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7253    let (editor, cx) = cx.add_window_view(|window, cx| {
 7254        build_editor_with_project(project.clone(), buffer, window, cx)
 7255    });
 7256    editor.update_in(cx, |editor, window, cx| {
 7257        editor.set_text("one\ntwo\nthree\n", window, cx)
 7258    });
 7259    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7260
 7261    cx.executor().start_waiting();
 7262    let fake_server = fake_servers.next().await.unwrap();
 7263
 7264    let save = editor
 7265        .update_in(cx, |editor, window, cx| {
 7266            editor.save(true, project.clone(), window, cx)
 7267        })
 7268        .unwrap();
 7269    fake_server
 7270        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7271            assert_eq!(
 7272                params.text_document.uri,
 7273                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7274            );
 7275            assert_eq!(params.options.tab_size, 4);
 7276            Ok(Some(vec![lsp::TextEdit::new(
 7277                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7278                ", ".to_string(),
 7279            )]))
 7280        })
 7281        .next()
 7282        .await;
 7283    cx.executor().start_waiting();
 7284    save.await;
 7285
 7286    assert_eq!(
 7287        editor.update(cx, |editor, cx| editor.text(cx)),
 7288        "one, two\nthree\n"
 7289    );
 7290    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7291
 7292    editor.update_in(cx, |editor, window, cx| {
 7293        editor.set_text("one\ntwo\nthree\n", window, cx)
 7294    });
 7295    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7296
 7297    // Ensure we can still save even if formatting hangs.
 7298    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7299        assert_eq!(
 7300            params.text_document.uri,
 7301            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7302        );
 7303        futures::future::pending::<()>().await;
 7304        unreachable!()
 7305    });
 7306    let save = editor
 7307        .update_in(cx, |editor, window, cx| {
 7308            editor.save(true, project.clone(), window, cx)
 7309        })
 7310        .unwrap();
 7311    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7312    cx.executor().start_waiting();
 7313    save.await;
 7314    assert_eq!(
 7315        editor.update(cx, |editor, cx| editor.text(cx)),
 7316        "one\ntwo\nthree\n"
 7317    );
 7318    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7319
 7320    // For non-dirty buffer, no formatting request should be sent
 7321    let save = editor
 7322        .update_in(cx, |editor, window, cx| {
 7323            editor.save(true, project.clone(), window, cx)
 7324        })
 7325        .unwrap();
 7326    let _pending_format_request = fake_server
 7327        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7328            panic!("Should not be invoked on non-dirty buffer");
 7329        })
 7330        .next();
 7331    cx.executor().start_waiting();
 7332    save.await;
 7333
 7334    // Set rust language override and assert overridden tabsize is sent to language server
 7335    update_test_language_settings(cx, |settings| {
 7336        settings.languages.insert(
 7337            "Rust".into(),
 7338            LanguageSettingsContent {
 7339                tab_size: NonZeroU32::new(8),
 7340                ..Default::default()
 7341            },
 7342        );
 7343    });
 7344
 7345    editor.update_in(cx, |editor, window, cx| {
 7346        editor.set_text("somehting_new\n", window, cx)
 7347    });
 7348    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7349    let save = editor
 7350        .update_in(cx, |editor, window, cx| {
 7351            editor.save(true, project.clone(), window, cx)
 7352        })
 7353        .unwrap();
 7354    fake_server
 7355        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7356            assert_eq!(
 7357                params.text_document.uri,
 7358                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7359            );
 7360            assert_eq!(params.options.tab_size, 8);
 7361            Ok(Some(vec![]))
 7362        })
 7363        .next()
 7364        .await;
 7365    cx.executor().start_waiting();
 7366    save.await;
 7367}
 7368
 7369#[gpui::test]
 7370async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7371    init_test(cx, |_| {});
 7372
 7373    let cols = 4;
 7374    let rows = 10;
 7375    let sample_text_1 = sample_text(rows, cols, 'a');
 7376    assert_eq!(
 7377        sample_text_1,
 7378        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7379    );
 7380    let sample_text_2 = sample_text(rows, cols, 'l');
 7381    assert_eq!(
 7382        sample_text_2,
 7383        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7384    );
 7385    let sample_text_3 = sample_text(rows, cols, 'v');
 7386    assert_eq!(
 7387        sample_text_3,
 7388        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7389    );
 7390
 7391    let fs = FakeFs::new(cx.executor());
 7392    fs.insert_tree(
 7393        path!("/a"),
 7394        json!({
 7395            "main.rs": sample_text_1,
 7396            "other.rs": sample_text_2,
 7397            "lib.rs": sample_text_3,
 7398        }),
 7399    )
 7400    .await;
 7401
 7402    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7403    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7404    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7405
 7406    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7407    language_registry.add(rust_lang());
 7408    let mut fake_servers = language_registry.register_fake_lsp(
 7409        "Rust",
 7410        FakeLspAdapter {
 7411            capabilities: lsp::ServerCapabilities {
 7412                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7413                ..Default::default()
 7414            },
 7415            ..Default::default()
 7416        },
 7417    );
 7418
 7419    let worktree = project.update(cx, |project, cx| {
 7420        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7421        assert_eq!(worktrees.len(), 1);
 7422        worktrees.pop().unwrap()
 7423    });
 7424    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7425
 7426    let buffer_1 = project
 7427        .update(cx, |project, cx| {
 7428            project.open_buffer((worktree_id, "main.rs"), cx)
 7429        })
 7430        .await
 7431        .unwrap();
 7432    let buffer_2 = project
 7433        .update(cx, |project, cx| {
 7434            project.open_buffer((worktree_id, "other.rs"), cx)
 7435        })
 7436        .await
 7437        .unwrap();
 7438    let buffer_3 = project
 7439        .update(cx, |project, cx| {
 7440            project.open_buffer((worktree_id, "lib.rs"), cx)
 7441        })
 7442        .await
 7443        .unwrap();
 7444
 7445    let multi_buffer = cx.new(|cx| {
 7446        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7447        multi_buffer.push_excerpts(
 7448            buffer_1.clone(),
 7449            [
 7450                ExcerptRange {
 7451                    context: Point::new(0, 0)..Point::new(3, 0),
 7452                    primary: None,
 7453                },
 7454                ExcerptRange {
 7455                    context: Point::new(5, 0)..Point::new(7, 0),
 7456                    primary: None,
 7457                },
 7458                ExcerptRange {
 7459                    context: Point::new(9, 0)..Point::new(10, 4),
 7460                    primary: None,
 7461                },
 7462            ],
 7463            cx,
 7464        );
 7465        multi_buffer.push_excerpts(
 7466            buffer_2.clone(),
 7467            [
 7468                ExcerptRange {
 7469                    context: Point::new(0, 0)..Point::new(3, 0),
 7470                    primary: None,
 7471                },
 7472                ExcerptRange {
 7473                    context: Point::new(5, 0)..Point::new(7, 0),
 7474                    primary: None,
 7475                },
 7476                ExcerptRange {
 7477                    context: Point::new(9, 0)..Point::new(10, 4),
 7478                    primary: None,
 7479                },
 7480            ],
 7481            cx,
 7482        );
 7483        multi_buffer.push_excerpts(
 7484            buffer_3.clone(),
 7485            [
 7486                ExcerptRange {
 7487                    context: Point::new(0, 0)..Point::new(3, 0),
 7488                    primary: None,
 7489                },
 7490                ExcerptRange {
 7491                    context: Point::new(5, 0)..Point::new(7, 0),
 7492                    primary: None,
 7493                },
 7494                ExcerptRange {
 7495                    context: Point::new(9, 0)..Point::new(10, 4),
 7496                    primary: None,
 7497                },
 7498            ],
 7499            cx,
 7500        );
 7501        multi_buffer
 7502    });
 7503    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7504        Editor::new(
 7505            EditorMode::Full,
 7506            multi_buffer,
 7507            Some(project.clone()),
 7508            true,
 7509            window,
 7510            cx,
 7511        )
 7512    });
 7513
 7514    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7515        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7516            s.select_ranges(Some(1..2))
 7517        });
 7518        editor.insert("|one|two|three|", window, cx);
 7519    });
 7520    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7521    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7522        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7523            s.select_ranges(Some(60..70))
 7524        });
 7525        editor.insert("|four|five|six|", window, cx);
 7526    });
 7527    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7528
 7529    // First two buffers should be edited, but not the third one.
 7530    assert_eq!(
 7531        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7532        "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}",
 7533    );
 7534    buffer_1.update(cx, |buffer, _| {
 7535        assert!(buffer.is_dirty());
 7536        assert_eq!(
 7537            buffer.text(),
 7538            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7539        )
 7540    });
 7541    buffer_2.update(cx, |buffer, _| {
 7542        assert!(buffer.is_dirty());
 7543        assert_eq!(
 7544            buffer.text(),
 7545            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7546        )
 7547    });
 7548    buffer_3.update(cx, |buffer, _| {
 7549        assert!(!buffer.is_dirty());
 7550        assert_eq!(buffer.text(), sample_text_3,)
 7551    });
 7552    cx.executor().run_until_parked();
 7553
 7554    cx.executor().start_waiting();
 7555    let save = multi_buffer_editor
 7556        .update_in(cx, |editor, window, cx| {
 7557            editor.save(true, project.clone(), window, cx)
 7558        })
 7559        .unwrap();
 7560
 7561    let fake_server = fake_servers.next().await.unwrap();
 7562    fake_server
 7563        .server
 7564        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7565            Ok(Some(vec![lsp::TextEdit::new(
 7566                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7567                format!("[{} formatted]", params.text_document.uri),
 7568            )]))
 7569        })
 7570        .detach();
 7571    save.await;
 7572
 7573    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7574    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7575    assert_eq!(
 7576        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7577        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}"),
 7578    );
 7579    buffer_1.update(cx, |buffer, _| {
 7580        assert!(!buffer.is_dirty());
 7581        assert_eq!(
 7582            buffer.text(),
 7583            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7584        )
 7585    });
 7586    buffer_2.update(cx, |buffer, _| {
 7587        assert!(!buffer.is_dirty());
 7588        assert_eq!(
 7589            buffer.text(),
 7590            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7591        )
 7592    });
 7593    buffer_3.update(cx, |buffer, _| {
 7594        assert!(!buffer.is_dirty());
 7595        assert_eq!(buffer.text(), sample_text_3,)
 7596    });
 7597}
 7598
 7599#[gpui::test]
 7600async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7601    init_test(cx, |_| {});
 7602
 7603    let fs = FakeFs::new(cx.executor());
 7604    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7605
 7606    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7607
 7608    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7609    language_registry.add(rust_lang());
 7610    let mut fake_servers = language_registry.register_fake_lsp(
 7611        "Rust",
 7612        FakeLspAdapter {
 7613            capabilities: lsp::ServerCapabilities {
 7614                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7615                ..Default::default()
 7616            },
 7617            ..Default::default()
 7618        },
 7619    );
 7620
 7621    let buffer = project
 7622        .update(cx, |project, cx| {
 7623            project.open_local_buffer(path!("/file.rs"), cx)
 7624        })
 7625        .await
 7626        .unwrap();
 7627
 7628    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7629    let (editor, cx) = cx.add_window_view(|window, cx| {
 7630        build_editor_with_project(project.clone(), buffer, window, cx)
 7631    });
 7632    editor.update_in(cx, |editor, window, cx| {
 7633        editor.set_text("one\ntwo\nthree\n", window, cx)
 7634    });
 7635    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7636
 7637    cx.executor().start_waiting();
 7638    let fake_server = fake_servers.next().await.unwrap();
 7639
 7640    let save = editor
 7641        .update_in(cx, |editor, window, cx| {
 7642            editor.save(true, project.clone(), window, cx)
 7643        })
 7644        .unwrap();
 7645    fake_server
 7646        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7647            assert_eq!(
 7648                params.text_document.uri,
 7649                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7650            );
 7651            assert_eq!(params.options.tab_size, 4);
 7652            Ok(Some(vec![lsp::TextEdit::new(
 7653                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7654                ", ".to_string(),
 7655            )]))
 7656        })
 7657        .next()
 7658        .await;
 7659    cx.executor().start_waiting();
 7660    save.await;
 7661    assert_eq!(
 7662        editor.update(cx, |editor, cx| editor.text(cx)),
 7663        "one, two\nthree\n"
 7664    );
 7665    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7666
 7667    editor.update_in(cx, |editor, window, cx| {
 7668        editor.set_text("one\ntwo\nthree\n", window, cx)
 7669    });
 7670    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7671
 7672    // Ensure we can still save even if formatting hangs.
 7673    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7674        move |params, _| async move {
 7675            assert_eq!(
 7676                params.text_document.uri,
 7677                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7678            );
 7679            futures::future::pending::<()>().await;
 7680            unreachable!()
 7681        },
 7682    );
 7683    let save = editor
 7684        .update_in(cx, |editor, window, cx| {
 7685            editor.save(true, project.clone(), window, cx)
 7686        })
 7687        .unwrap();
 7688    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7689    cx.executor().start_waiting();
 7690    save.await;
 7691    assert_eq!(
 7692        editor.update(cx, |editor, cx| editor.text(cx)),
 7693        "one\ntwo\nthree\n"
 7694    );
 7695    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7696
 7697    // For non-dirty buffer, no formatting request should be sent
 7698    let save = editor
 7699        .update_in(cx, |editor, window, cx| {
 7700            editor.save(true, project.clone(), window, cx)
 7701        })
 7702        .unwrap();
 7703    let _pending_format_request = fake_server
 7704        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7705            panic!("Should not be invoked on non-dirty buffer");
 7706        })
 7707        .next();
 7708    cx.executor().start_waiting();
 7709    save.await;
 7710
 7711    // Set Rust language override and assert overridden tabsize is sent to language server
 7712    update_test_language_settings(cx, |settings| {
 7713        settings.languages.insert(
 7714            "Rust".into(),
 7715            LanguageSettingsContent {
 7716                tab_size: NonZeroU32::new(8),
 7717                ..Default::default()
 7718            },
 7719        );
 7720    });
 7721
 7722    editor.update_in(cx, |editor, window, cx| {
 7723        editor.set_text("somehting_new\n", window, cx)
 7724    });
 7725    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7726    let save = editor
 7727        .update_in(cx, |editor, window, cx| {
 7728            editor.save(true, project.clone(), window, cx)
 7729        })
 7730        .unwrap();
 7731    fake_server
 7732        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7733            assert_eq!(
 7734                params.text_document.uri,
 7735                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7736            );
 7737            assert_eq!(params.options.tab_size, 8);
 7738            Ok(Some(vec![]))
 7739        })
 7740        .next()
 7741        .await;
 7742    cx.executor().start_waiting();
 7743    save.await;
 7744}
 7745
 7746#[gpui::test]
 7747async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7748    init_test(cx, |settings| {
 7749        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7750            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7751        ))
 7752    });
 7753
 7754    let fs = FakeFs::new(cx.executor());
 7755    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7756
 7757    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7758
 7759    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7760    language_registry.add(Arc::new(Language::new(
 7761        LanguageConfig {
 7762            name: "Rust".into(),
 7763            matcher: LanguageMatcher {
 7764                path_suffixes: vec!["rs".to_string()],
 7765                ..Default::default()
 7766            },
 7767            ..LanguageConfig::default()
 7768        },
 7769        Some(tree_sitter_rust::LANGUAGE.into()),
 7770    )));
 7771    update_test_language_settings(cx, |settings| {
 7772        // Enable Prettier formatting for the same buffer, and ensure
 7773        // LSP is called instead of Prettier.
 7774        settings.defaults.prettier = Some(PrettierSettings {
 7775            allowed: true,
 7776            ..PrettierSettings::default()
 7777        });
 7778    });
 7779    let mut fake_servers = language_registry.register_fake_lsp(
 7780        "Rust",
 7781        FakeLspAdapter {
 7782            capabilities: lsp::ServerCapabilities {
 7783                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7784                ..Default::default()
 7785            },
 7786            ..Default::default()
 7787        },
 7788    );
 7789
 7790    let buffer = project
 7791        .update(cx, |project, cx| {
 7792            project.open_local_buffer(path!("/file.rs"), cx)
 7793        })
 7794        .await
 7795        .unwrap();
 7796
 7797    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7798    let (editor, cx) = cx.add_window_view(|window, cx| {
 7799        build_editor_with_project(project.clone(), buffer, window, cx)
 7800    });
 7801    editor.update_in(cx, |editor, window, cx| {
 7802        editor.set_text("one\ntwo\nthree\n", window, cx)
 7803    });
 7804
 7805    cx.executor().start_waiting();
 7806    let fake_server = fake_servers.next().await.unwrap();
 7807
 7808    let format = editor
 7809        .update_in(cx, |editor, window, cx| {
 7810            editor.perform_format(
 7811                project.clone(),
 7812                FormatTrigger::Manual,
 7813                FormatTarget::Buffers,
 7814                window,
 7815                cx,
 7816            )
 7817        })
 7818        .unwrap();
 7819    fake_server
 7820        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7821            assert_eq!(
 7822                params.text_document.uri,
 7823                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7824            );
 7825            assert_eq!(params.options.tab_size, 4);
 7826            Ok(Some(vec![lsp::TextEdit::new(
 7827                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7828                ", ".to_string(),
 7829            )]))
 7830        })
 7831        .next()
 7832        .await;
 7833    cx.executor().start_waiting();
 7834    format.await;
 7835    assert_eq!(
 7836        editor.update(cx, |editor, cx| editor.text(cx)),
 7837        "one, two\nthree\n"
 7838    );
 7839
 7840    editor.update_in(cx, |editor, window, cx| {
 7841        editor.set_text("one\ntwo\nthree\n", window, cx)
 7842    });
 7843    // Ensure we don't lock if formatting hangs.
 7844    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7845        assert_eq!(
 7846            params.text_document.uri,
 7847            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7848        );
 7849        futures::future::pending::<()>().await;
 7850        unreachable!()
 7851    });
 7852    let format = editor
 7853        .update_in(cx, |editor, window, cx| {
 7854            editor.perform_format(
 7855                project,
 7856                FormatTrigger::Manual,
 7857                FormatTarget::Buffers,
 7858                window,
 7859                cx,
 7860            )
 7861        })
 7862        .unwrap();
 7863    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7864    cx.executor().start_waiting();
 7865    format.await;
 7866    assert_eq!(
 7867        editor.update(cx, |editor, cx| editor.text(cx)),
 7868        "one\ntwo\nthree\n"
 7869    );
 7870}
 7871
 7872#[gpui::test]
 7873async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 7874    init_test(cx, |_| {});
 7875
 7876    let mut cx = EditorLspTestContext::new_rust(
 7877        lsp::ServerCapabilities {
 7878            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7879            ..Default::default()
 7880        },
 7881        cx,
 7882    )
 7883    .await;
 7884
 7885    cx.set_state(indoc! {"
 7886        one.twoˇ
 7887    "});
 7888
 7889    // The format request takes a long time. When it completes, it inserts
 7890    // a newline and an indent before the `.`
 7891    cx.lsp
 7892        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7893            let executor = cx.background_executor().clone();
 7894            async move {
 7895                executor.timer(Duration::from_millis(100)).await;
 7896                Ok(Some(vec![lsp::TextEdit {
 7897                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7898                    new_text: "\n    ".into(),
 7899                }]))
 7900            }
 7901        });
 7902
 7903    // Submit a format request.
 7904    let format_1 = cx
 7905        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7906        .unwrap();
 7907    cx.executor().run_until_parked();
 7908
 7909    // Submit a second format request.
 7910    let format_2 = cx
 7911        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7912        .unwrap();
 7913    cx.executor().run_until_parked();
 7914
 7915    // Wait for both format requests to complete
 7916    cx.executor().advance_clock(Duration::from_millis(200));
 7917    cx.executor().start_waiting();
 7918    format_1.await.unwrap();
 7919    cx.executor().start_waiting();
 7920    format_2.await.unwrap();
 7921
 7922    // The formatting edits only happens once.
 7923    cx.assert_editor_state(indoc! {"
 7924        one
 7925            .twoˇ
 7926    "});
 7927}
 7928
 7929#[gpui::test]
 7930async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 7931    init_test(cx, |settings| {
 7932        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7933    });
 7934
 7935    let mut cx = EditorLspTestContext::new_rust(
 7936        lsp::ServerCapabilities {
 7937            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7938            ..Default::default()
 7939        },
 7940        cx,
 7941    )
 7942    .await;
 7943
 7944    // Set up a buffer white some trailing whitespace and no trailing newline.
 7945    cx.set_state(
 7946        &[
 7947            "one ",   //
 7948            "twoˇ",   //
 7949            "three ", //
 7950            "four",   //
 7951        ]
 7952        .join("\n"),
 7953    );
 7954
 7955    // Submit a format request.
 7956    let format = cx
 7957        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7958        .unwrap();
 7959
 7960    // Record which buffer changes have been sent to the language server
 7961    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7962    cx.lsp
 7963        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7964            let buffer_changes = buffer_changes.clone();
 7965            move |params, _| {
 7966                buffer_changes.lock().extend(
 7967                    params
 7968                        .content_changes
 7969                        .into_iter()
 7970                        .map(|e| (e.range.unwrap(), e.text)),
 7971                );
 7972            }
 7973        });
 7974
 7975    // Handle formatting requests to the language server.
 7976    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7977        let buffer_changes = buffer_changes.clone();
 7978        move |_, _| {
 7979            // When formatting is requested, trailing whitespace has already been stripped,
 7980            // and the trailing newline has already been added.
 7981            assert_eq!(
 7982                &buffer_changes.lock()[1..],
 7983                &[
 7984                    (
 7985                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7986                        "".into()
 7987                    ),
 7988                    (
 7989                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7990                        "".into()
 7991                    ),
 7992                    (
 7993                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7994                        "\n".into()
 7995                    ),
 7996                ]
 7997            );
 7998
 7999            // Insert blank lines between each line of the buffer.
 8000            async move {
 8001                Ok(Some(vec![
 8002                    lsp::TextEdit {
 8003                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8004                        new_text: "\n".into(),
 8005                    },
 8006                    lsp::TextEdit {
 8007                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8008                        new_text: "\n".into(),
 8009                    },
 8010                ]))
 8011            }
 8012        }
 8013    });
 8014
 8015    // After formatting the buffer, the trailing whitespace is stripped,
 8016    // a newline is appended, and the edits provided by the language server
 8017    // have been applied.
 8018    format.await.unwrap();
 8019    cx.assert_editor_state(
 8020        &[
 8021            "one",   //
 8022            "",      //
 8023            "twoˇ",  //
 8024            "",      //
 8025            "three", //
 8026            "four",  //
 8027            "",      //
 8028        ]
 8029        .join("\n"),
 8030    );
 8031
 8032    // Undoing the formatting undoes the trailing whitespace removal, the
 8033    // trailing newline, and the LSP edits.
 8034    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8035    cx.assert_editor_state(
 8036        &[
 8037            "one ",   //
 8038            "twoˇ",   //
 8039            "three ", //
 8040            "four",   //
 8041        ]
 8042        .join("\n"),
 8043    );
 8044}
 8045
 8046#[gpui::test]
 8047async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8048    cx: &mut TestAppContext,
 8049) {
 8050    init_test(cx, |_| {});
 8051
 8052    cx.update(|cx| {
 8053        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8054            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8055                settings.auto_signature_help = Some(true);
 8056            });
 8057        });
 8058    });
 8059
 8060    let mut cx = EditorLspTestContext::new_rust(
 8061        lsp::ServerCapabilities {
 8062            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8063                ..Default::default()
 8064            }),
 8065            ..Default::default()
 8066        },
 8067        cx,
 8068    )
 8069    .await;
 8070
 8071    let language = Language::new(
 8072        LanguageConfig {
 8073            name: "Rust".into(),
 8074            brackets: BracketPairConfig {
 8075                pairs: vec![
 8076                    BracketPair {
 8077                        start: "{".to_string(),
 8078                        end: "}".to_string(),
 8079                        close: true,
 8080                        surround: true,
 8081                        newline: true,
 8082                    },
 8083                    BracketPair {
 8084                        start: "(".to_string(),
 8085                        end: ")".to_string(),
 8086                        close: true,
 8087                        surround: true,
 8088                        newline: true,
 8089                    },
 8090                    BracketPair {
 8091                        start: "/*".to_string(),
 8092                        end: " */".to_string(),
 8093                        close: true,
 8094                        surround: true,
 8095                        newline: true,
 8096                    },
 8097                    BracketPair {
 8098                        start: "[".to_string(),
 8099                        end: "]".to_string(),
 8100                        close: false,
 8101                        surround: false,
 8102                        newline: true,
 8103                    },
 8104                    BracketPair {
 8105                        start: "\"".to_string(),
 8106                        end: "\"".to_string(),
 8107                        close: true,
 8108                        surround: true,
 8109                        newline: false,
 8110                    },
 8111                    BracketPair {
 8112                        start: "<".to_string(),
 8113                        end: ">".to_string(),
 8114                        close: false,
 8115                        surround: true,
 8116                        newline: true,
 8117                    },
 8118                ],
 8119                ..Default::default()
 8120            },
 8121            autoclose_before: "})]".to_string(),
 8122            ..Default::default()
 8123        },
 8124        Some(tree_sitter_rust::LANGUAGE.into()),
 8125    );
 8126    let language = Arc::new(language);
 8127
 8128    cx.language_registry().add(language.clone());
 8129    cx.update_buffer(|buffer, cx| {
 8130        buffer.set_language(Some(language), cx);
 8131    });
 8132
 8133    cx.set_state(
 8134        &r#"
 8135            fn main() {
 8136                sampleˇ
 8137            }
 8138        "#
 8139        .unindent(),
 8140    );
 8141
 8142    cx.update_editor(|editor, window, cx| {
 8143        editor.handle_input("(", window, cx);
 8144    });
 8145    cx.assert_editor_state(
 8146        &"
 8147            fn main() {
 8148                sample(ˇ)
 8149            }
 8150        "
 8151        .unindent(),
 8152    );
 8153
 8154    let mocked_response = lsp::SignatureHelp {
 8155        signatures: vec![lsp::SignatureInformation {
 8156            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8157            documentation: None,
 8158            parameters: Some(vec![
 8159                lsp::ParameterInformation {
 8160                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8161                    documentation: None,
 8162                },
 8163                lsp::ParameterInformation {
 8164                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8165                    documentation: None,
 8166                },
 8167            ]),
 8168            active_parameter: None,
 8169        }],
 8170        active_signature: Some(0),
 8171        active_parameter: Some(0),
 8172    };
 8173    handle_signature_help_request(&mut cx, mocked_response).await;
 8174
 8175    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8176        .await;
 8177
 8178    cx.editor(|editor, _, _| {
 8179        let signature_help_state = editor.signature_help_state.popover().cloned();
 8180        assert_eq!(
 8181            signature_help_state.unwrap().label,
 8182            "param1: u8, param2: u8"
 8183        );
 8184    });
 8185}
 8186
 8187#[gpui::test]
 8188async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8189    init_test(cx, |_| {});
 8190
 8191    cx.update(|cx| {
 8192        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8193            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8194                settings.auto_signature_help = Some(false);
 8195                settings.show_signature_help_after_edits = Some(false);
 8196            });
 8197        });
 8198    });
 8199
 8200    let mut cx = EditorLspTestContext::new_rust(
 8201        lsp::ServerCapabilities {
 8202            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8203                ..Default::default()
 8204            }),
 8205            ..Default::default()
 8206        },
 8207        cx,
 8208    )
 8209    .await;
 8210
 8211    let language = Language::new(
 8212        LanguageConfig {
 8213            name: "Rust".into(),
 8214            brackets: BracketPairConfig {
 8215                pairs: vec![
 8216                    BracketPair {
 8217                        start: "{".to_string(),
 8218                        end: "}".to_string(),
 8219                        close: true,
 8220                        surround: true,
 8221                        newline: true,
 8222                    },
 8223                    BracketPair {
 8224                        start: "(".to_string(),
 8225                        end: ")".to_string(),
 8226                        close: true,
 8227                        surround: true,
 8228                        newline: true,
 8229                    },
 8230                    BracketPair {
 8231                        start: "/*".to_string(),
 8232                        end: " */".to_string(),
 8233                        close: true,
 8234                        surround: true,
 8235                        newline: true,
 8236                    },
 8237                    BracketPair {
 8238                        start: "[".to_string(),
 8239                        end: "]".to_string(),
 8240                        close: false,
 8241                        surround: false,
 8242                        newline: true,
 8243                    },
 8244                    BracketPair {
 8245                        start: "\"".to_string(),
 8246                        end: "\"".to_string(),
 8247                        close: true,
 8248                        surround: true,
 8249                        newline: false,
 8250                    },
 8251                    BracketPair {
 8252                        start: "<".to_string(),
 8253                        end: ">".to_string(),
 8254                        close: false,
 8255                        surround: true,
 8256                        newline: true,
 8257                    },
 8258                ],
 8259                ..Default::default()
 8260            },
 8261            autoclose_before: "})]".to_string(),
 8262            ..Default::default()
 8263        },
 8264        Some(tree_sitter_rust::LANGUAGE.into()),
 8265    );
 8266    let language = Arc::new(language);
 8267
 8268    cx.language_registry().add(language.clone());
 8269    cx.update_buffer(|buffer, cx| {
 8270        buffer.set_language(Some(language), cx);
 8271    });
 8272
 8273    // Ensure that signature_help is not called when no signature help is enabled.
 8274    cx.set_state(
 8275        &r#"
 8276            fn main() {
 8277                sampleˇ
 8278            }
 8279        "#
 8280        .unindent(),
 8281    );
 8282    cx.update_editor(|editor, window, cx| {
 8283        editor.handle_input("(", window, cx);
 8284    });
 8285    cx.assert_editor_state(
 8286        &"
 8287            fn main() {
 8288                sample(ˇ)
 8289            }
 8290        "
 8291        .unindent(),
 8292    );
 8293    cx.editor(|editor, _, _| {
 8294        assert!(editor.signature_help_state.task().is_none());
 8295    });
 8296
 8297    let mocked_response = lsp::SignatureHelp {
 8298        signatures: vec![lsp::SignatureInformation {
 8299            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8300            documentation: None,
 8301            parameters: Some(vec![
 8302                lsp::ParameterInformation {
 8303                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8304                    documentation: None,
 8305                },
 8306                lsp::ParameterInformation {
 8307                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8308                    documentation: None,
 8309                },
 8310            ]),
 8311            active_parameter: None,
 8312        }],
 8313        active_signature: Some(0),
 8314        active_parameter: Some(0),
 8315    };
 8316
 8317    // Ensure that signature_help is called when enabled afte edits
 8318    cx.update(|_, cx| {
 8319        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8320            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8321                settings.auto_signature_help = Some(false);
 8322                settings.show_signature_help_after_edits = Some(true);
 8323            });
 8324        });
 8325    });
 8326    cx.set_state(
 8327        &r#"
 8328            fn main() {
 8329                sampleˇ
 8330            }
 8331        "#
 8332        .unindent(),
 8333    );
 8334    cx.update_editor(|editor, window, cx| {
 8335        editor.handle_input("(", window, cx);
 8336    });
 8337    cx.assert_editor_state(
 8338        &"
 8339            fn main() {
 8340                sample(ˇ)
 8341            }
 8342        "
 8343        .unindent(),
 8344    );
 8345    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8346    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8347        .await;
 8348    cx.update_editor(|editor, _, _| {
 8349        let signature_help_state = editor.signature_help_state.popover().cloned();
 8350        assert!(signature_help_state.is_some());
 8351        assert_eq!(
 8352            signature_help_state.unwrap().label,
 8353            "param1: u8, param2: u8"
 8354        );
 8355        editor.signature_help_state = SignatureHelpState::default();
 8356    });
 8357
 8358    // Ensure that signature_help is called when auto signature help override is enabled
 8359    cx.update(|_, cx| {
 8360        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8361            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8362                settings.auto_signature_help = Some(true);
 8363                settings.show_signature_help_after_edits = Some(false);
 8364            });
 8365        });
 8366    });
 8367    cx.set_state(
 8368        &r#"
 8369            fn main() {
 8370                sampleˇ
 8371            }
 8372        "#
 8373        .unindent(),
 8374    );
 8375    cx.update_editor(|editor, window, cx| {
 8376        editor.handle_input("(", window, cx);
 8377    });
 8378    cx.assert_editor_state(
 8379        &"
 8380            fn main() {
 8381                sample(ˇ)
 8382            }
 8383        "
 8384        .unindent(),
 8385    );
 8386    handle_signature_help_request(&mut cx, mocked_response).await;
 8387    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8388        .await;
 8389    cx.editor(|editor, _, _| {
 8390        let signature_help_state = editor.signature_help_state.popover().cloned();
 8391        assert!(signature_help_state.is_some());
 8392        assert_eq!(
 8393            signature_help_state.unwrap().label,
 8394            "param1: u8, param2: u8"
 8395        );
 8396    });
 8397}
 8398
 8399#[gpui::test]
 8400async fn test_signature_help(cx: &mut TestAppContext) {
 8401    init_test(cx, |_| {});
 8402    cx.update(|cx| {
 8403        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8404            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8405                settings.auto_signature_help = Some(true);
 8406            });
 8407        });
 8408    });
 8409
 8410    let mut cx = EditorLspTestContext::new_rust(
 8411        lsp::ServerCapabilities {
 8412            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8413                ..Default::default()
 8414            }),
 8415            ..Default::default()
 8416        },
 8417        cx,
 8418    )
 8419    .await;
 8420
 8421    // A test that directly calls `show_signature_help`
 8422    cx.update_editor(|editor, window, cx| {
 8423        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8424    });
 8425
 8426    let mocked_response = lsp::SignatureHelp {
 8427        signatures: vec![lsp::SignatureInformation {
 8428            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8429            documentation: None,
 8430            parameters: Some(vec![
 8431                lsp::ParameterInformation {
 8432                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8433                    documentation: None,
 8434                },
 8435                lsp::ParameterInformation {
 8436                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8437                    documentation: None,
 8438                },
 8439            ]),
 8440            active_parameter: None,
 8441        }],
 8442        active_signature: Some(0),
 8443        active_parameter: Some(0),
 8444    };
 8445    handle_signature_help_request(&mut cx, mocked_response).await;
 8446
 8447    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8448        .await;
 8449
 8450    cx.editor(|editor, _, _| {
 8451        let signature_help_state = editor.signature_help_state.popover().cloned();
 8452        assert!(signature_help_state.is_some());
 8453        assert_eq!(
 8454            signature_help_state.unwrap().label,
 8455            "param1: u8, param2: u8"
 8456        );
 8457    });
 8458
 8459    // When exiting outside from inside the brackets, `signature_help` is closed.
 8460    cx.set_state(indoc! {"
 8461        fn main() {
 8462            sample(ˇ);
 8463        }
 8464
 8465        fn sample(param1: u8, param2: u8) {}
 8466    "});
 8467
 8468    cx.update_editor(|editor, window, cx| {
 8469        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8470    });
 8471
 8472    let mocked_response = lsp::SignatureHelp {
 8473        signatures: Vec::new(),
 8474        active_signature: None,
 8475        active_parameter: None,
 8476    };
 8477    handle_signature_help_request(&mut cx, mocked_response).await;
 8478
 8479    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8480        .await;
 8481
 8482    cx.editor(|editor, _, _| {
 8483        assert!(!editor.signature_help_state.is_shown());
 8484    });
 8485
 8486    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8487    cx.set_state(indoc! {"
 8488        fn main() {
 8489            sample(ˇ);
 8490        }
 8491
 8492        fn sample(param1: u8, param2: u8) {}
 8493    "});
 8494
 8495    let mocked_response = lsp::SignatureHelp {
 8496        signatures: vec![lsp::SignatureInformation {
 8497            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8498            documentation: None,
 8499            parameters: Some(vec![
 8500                lsp::ParameterInformation {
 8501                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8502                    documentation: None,
 8503                },
 8504                lsp::ParameterInformation {
 8505                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8506                    documentation: None,
 8507                },
 8508            ]),
 8509            active_parameter: None,
 8510        }],
 8511        active_signature: Some(0),
 8512        active_parameter: Some(0),
 8513    };
 8514    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8515    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8516        .await;
 8517    cx.editor(|editor, _, _| {
 8518        assert!(editor.signature_help_state.is_shown());
 8519    });
 8520
 8521    // Restore the popover with more parameter input
 8522    cx.set_state(indoc! {"
 8523        fn main() {
 8524            sample(param1, param2ˇ);
 8525        }
 8526
 8527        fn sample(param1: u8, param2: u8) {}
 8528    "});
 8529
 8530    let mocked_response = lsp::SignatureHelp {
 8531        signatures: vec![lsp::SignatureInformation {
 8532            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8533            documentation: None,
 8534            parameters: Some(vec![
 8535                lsp::ParameterInformation {
 8536                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8537                    documentation: None,
 8538                },
 8539                lsp::ParameterInformation {
 8540                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8541                    documentation: None,
 8542                },
 8543            ]),
 8544            active_parameter: None,
 8545        }],
 8546        active_signature: Some(0),
 8547        active_parameter: Some(1),
 8548    };
 8549    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8550    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8551        .await;
 8552
 8553    // When selecting a range, the popover is gone.
 8554    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8555    cx.update_editor(|editor, window, cx| {
 8556        editor.change_selections(None, window, cx, |s| {
 8557            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8558        })
 8559    });
 8560    cx.assert_editor_state(indoc! {"
 8561        fn main() {
 8562            sample(param1, «ˇparam2»);
 8563        }
 8564
 8565        fn sample(param1: u8, param2: u8) {}
 8566    "});
 8567    cx.editor(|editor, _, _| {
 8568        assert!(!editor.signature_help_state.is_shown());
 8569    });
 8570
 8571    // When unselecting again, the popover is back if within the brackets.
 8572    cx.update_editor(|editor, window, cx| {
 8573        editor.change_selections(None, window, cx, |s| {
 8574            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8575        })
 8576    });
 8577    cx.assert_editor_state(indoc! {"
 8578        fn main() {
 8579            sample(param1, ˇparam2);
 8580        }
 8581
 8582        fn sample(param1: u8, param2: u8) {}
 8583    "});
 8584    handle_signature_help_request(&mut cx, mocked_response).await;
 8585    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8586        .await;
 8587    cx.editor(|editor, _, _| {
 8588        assert!(editor.signature_help_state.is_shown());
 8589    });
 8590
 8591    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8592    cx.update_editor(|editor, window, cx| {
 8593        editor.change_selections(None, window, cx, |s| {
 8594            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8595            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8596        })
 8597    });
 8598    cx.assert_editor_state(indoc! {"
 8599        fn main() {
 8600            sample(param1, ˇparam2);
 8601        }
 8602
 8603        fn sample(param1: u8, param2: u8) {}
 8604    "});
 8605
 8606    let mocked_response = lsp::SignatureHelp {
 8607        signatures: vec![lsp::SignatureInformation {
 8608            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8609            documentation: None,
 8610            parameters: Some(vec![
 8611                lsp::ParameterInformation {
 8612                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8613                    documentation: None,
 8614                },
 8615                lsp::ParameterInformation {
 8616                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8617                    documentation: None,
 8618                },
 8619            ]),
 8620            active_parameter: None,
 8621        }],
 8622        active_signature: Some(0),
 8623        active_parameter: Some(1),
 8624    };
 8625    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8626    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8627        .await;
 8628    cx.update_editor(|editor, _, cx| {
 8629        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8630    });
 8631    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8632        .await;
 8633    cx.update_editor(|editor, window, cx| {
 8634        editor.change_selections(None, window, cx, |s| {
 8635            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8636        })
 8637    });
 8638    cx.assert_editor_state(indoc! {"
 8639        fn main() {
 8640            sample(param1, «ˇparam2»);
 8641        }
 8642
 8643        fn sample(param1: u8, param2: u8) {}
 8644    "});
 8645    cx.update_editor(|editor, window, cx| {
 8646        editor.change_selections(None, window, cx, |s| {
 8647            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8648        })
 8649    });
 8650    cx.assert_editor_state(indoc! {"
 8651        fn main() {
 8652            sample(param1, ˇparam2);
 8653        }
 8654
 8655        fn sample(param1: u8, param2: u8) {}
 8656    "});
 8657    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8658        .await;
 8659}
 8660
 8661#[gpui::test]
 8662async fn test_completion(cx: &mut TestAppContext) {
 8663    init_test(cx, |_| {});
 8664
 8665    let mut cx = EditorLspTestContext::new_rust(
 8666        lsp::ServerCapabilities {
 8667            completion_provider: Some(lsp::CompletionOptions {
 8668                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8669                resolve_provider: Some(true),
 8670                ..Default::default()
 8671            }),
 8672            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8673            ..Default::default()
 8674        },
 8675        cx,
 8676    )
 8677    .await;
 8678    let counter = Arc::new(AtomicUsize::new(0));
 8679
 8680    cx.set_state(indoc! {"
 8681        oneˇ
 8682        two
 8683        three
 8684    "});
 8685    cx.simulate_keystroke(".");
 8686    handle_completion_request(
 8687        &mut cx,
 8688        indoc! {"
 8689            one.|<>
 8690            two
 8691            three
 8692        "},
 8693        vec!["first_completion", "second_completion"],
 8694        counter.clone(),
 8695    )
 8696    .await;
 8697    cx.condition(|editor, _| editor.context_menu_visible())
 8698        .await;
 8699    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8700
 8701    let _handler = handle_signature_help_request(
 8702        &mut cx,
 8703        lsp::SignatureHelp {
 8704            signatures: vec![lsp::SignatureInformation {
 8705                label: "test signature".to_string(),
 8706                documentation: None,
 8707                parameters: Some(vec![lsp::ParameterInformation {
 8708                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8709                    documentation: None,
 8710                }]),
 8711                active_parameter: None,
 8712            }],
 8713            active_signature: None,
 8714            active_parameter: None,
 8715        },
 8716    );
 8717    cx.update_editor(|editor, window, cx| {
 8718        assert!(
 8719            !editor.signature_help_state.is_shown(),
 8720            "No signature help was called for"
 8721        );
 8722        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8723    });
 8724    cx.run_until_parked();
 8725    cx.update_editor(|editor, _, _| {
 8726        assert!(
 8727            !editor.signature_help_state.is_shown(),
 8728            "No signature help should be shown when completions menu is open"
 8729        );
 8730    });
 8731
 8732    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8733        editor.context_menu_next(&Default::default(), window, cx);
 8734        editor
 8735            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8736            .unwrap()
 8737    });
 8738    cx.assert_editor_state(indoc! {"
 8739        one.second_completionˇ
 8740        two
 8741        three
 8742    "});
 8743
 8744    handle_resolve_completion_request(
 8745        &mut cx,
 8746        Some(vec![
 8747            (
 8748                //This overlaps with the primary completion edit which is
 8749                //misbehavior from the LSP spec, test that we filter it out
 8750                indoc! {"
 8751                    one.second_ˇcompletion
 8752                    two
 8753                    threeˇ
 8754                "},
 8755                "overlapping additional edit",
 8756            ),
 8757            (
 8758                indoc! {"
 8759                    one.second_completion
 8760                    two
 8761                    threeˇ
 8762                "},
 8763                "\nadditional edit",
 8764            ),
 8765        ]),
 8766    )
 8767    .await;
 8768    apply_additional_edits.await.unwrap();
 8769    cx.assert_editor_state(indoc! {"
 8770        one.second_completionˇ
 8771        two
 8772        three
 8773        additional edit
 8774    "});
 8775
 8776    cx.set_state(indoc! {"
 8777        one.second_completion
 8778        twoˇ
 8779        threeˇ
 8780        additional edit
 8781    "});
 8782    cx.simulate_keystroke(" ");
 8783    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8784    cx.simulate_keystroke("s");
 8785    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8786
 8787    cx.assert_editor_state(indoc! {"
 8788        one.second_completion
 8789        two sˇ
 8790        three sˇ
 8791        additional edit
 8792    "});
 8793    handle_completion_request(
 8794        &mut cx,
 8795        indoc! {"
 8796            one.second_completion
 8797            two s
 8798            three <s|>
 8799            additional edit
 8800        "},
 8801        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8802        counter.clone(),
 8803    )
 8804    .await;
 8805    cx.condition(|editor, _| editor.context_menu_visible())
 8806        .await;
 8807    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8808
 8809    cx.simulate_keystroke("i");
 8810
 8811    handle_completion_request(
 8812        &mut cx,
 8813        indoc! {"
 8814            one.second_completion
 8815            two si
 8816            three <si|>
 8817            additional edit
 8818        "},
 8819        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8820        counter.clone(),
 8821    )
 8822    .await;
 8823    cx.condition(|editor, _| editor.context_menu_visible())
 8824        .await;
 8825    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8826
 8827    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8828        editor
 8829            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8830            .unwrap()
 8831    });
 8832    cx.assert_editor_state(indoc! {"
 8833        one.second_completion
 8834        two sixth_completionˇ
 8835        three sixth_completionˇ
 8836        additional edit
 8837    "});
 8838
 8839    apply_additional_edits.await.unwrap();
 8840
 8841    update_test_language_settings(&mut cx, |settings| {
 8842        settings.defaults.show_completions_on_input = Some(false);
 8843    });
 8844    cx.set_state("editorˇ");
 8845    cx.simulate_keystroke(".");
 8846    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8847    cx.simulate_keystroke("c");
 8848    cx.simulate_keystroke("l");
 8849    cx.simulate_keystroke("o");
 8850    cx.assert_editor_state("editor.cloˇ");
 8851    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8852    cx.update_editor(|editor, window, cx| {
 8853        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8854    });
 8855    handle_completion_request(
 8856        &mut cx,
 8857        "editor.<clo|>",
 8858        vec!["close", "clobber"],
 8859        counter.clone(),
 8860    )
 8861    .await;
 8862    cx.condition(|editor, _| editor.context_menu_visible())
 8863        .await;
 8864    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8865
 8866    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8867        editor
 8868            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8869            .unwrap()
 8870    });
 8871    cx.assert_editor_state("editor.closeˇ");
 8872    handle_resolve_completion_request(&mut cx, None).await;
 8873    apply_additional_edits.await.unwrap();
 8874}
 8875
 8876#[gpui::test]
 8877async fn test_multiline_completion(cx: &mut TestAppContext) {
 8878    init_test(cx, |_| {});
 8879
 8880    let fs = FakeFs::new(cx.executor());
 8881    fs.insert_tree(
 8882        path!("/a"),
 8883        json!({
 8884            "main.ts": "a",
 8885        }),
 8886    )
 8887    .await;
 8888
 8889    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8890    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8891    let typescript_language = Arc::new(Language::new(
 8892        LanguageConfig {
 8893            name: "TypeScript".into(),
 8894            matcher: LanguageMatcher {
 8895                path_suffixes: vec!["ts".to_string()],
 8896                ..LanguageMatcher::default()
 8897            },
 8898            line_comments: vec!["// ".into()],
 8899            ..LanguageConfig::default()
 8900        },
 8901        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8902    ));
 8903    language_registry.add(typescript_language.clone());
 8904    let mut fake_servers = language_registry.register_fake_lsp(
 8905        "TypeScript",
 8906        FakeLspAdapter {
 8907            capabilities: lsp::ServerCapabilities {
 8908                completion_provider: Some(lsp::CompletionOptions {
 8909                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8910                    ..lsp::CompletionOptions::default()
 8911                }),
 8912                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8913                ..lsp::ServerCapabilities::default()
 8914            },
 8915            // Emulate vtsls label generation
 8916            label_for_completion: Some(Box::new(|item, _| {
 8917                let text = if let Some(description) = item
 8918                    .label_details
 8919                    .as_ref()
 8920                    .and_then(|label_details| label_details.description.as_ref())
 8921                {
 8922                    format!("{} {}", item.label, description)
 8923                } else if let Some(detail) = &item.detail {
 8924                    format!("{} {}", item.label, detail)
 8925                } else {
 8926                    item.label.clone()
 8927                };
 8928                let len = text.len();
 8929                Some(language::CodeLabel {
 8930                    text,
 8931                    runs: Vec::new(),
 8932                    filter_range: 0..len,
 8933                })
 8934            })),
 8935            ..FakeLspAdapter::default()
 8936        },
 8937    );
 8938    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8939    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8940    let worktree_id = workspace
 8941        .update(cx, |workspace, _window, cx| {
 8942            workspace.project().update(cx, |project, cx| {
 8943                project.worktrees(cx).next().unwrap().read(cx).id()
 8944            })
 8945        })
 8946        .unwrap();
 8947    let _buffer = project
 8948        .update(cx, |project, cx| {
 8949            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8950        })
 8951        .await
 8952        .unwrap();
 8953    let editor = workspace
 8954        .update(cx, |workspace, window, cx| {
 8955            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8956        })
 8957        .unwrap()
 8958        .await
 8959        .unwrap()
 8960        .downcast::<Editor>()
 8961        .unwrap();
 8962    let fake_server = fake_servers.next().await.unwrap();
 8963
 8964    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8965    let multiline_label_2 = "a\nb\nc\n";
 8966    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8967    let multiline_description = "d\ne\nf\n";
 8968    let multiline_detail_2 = "g\nh\ni\n";
 8969
 8970    let mut completion_handle =
 8971        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8972            Ok(Some(lsp::CompletionResponse::Array(vec![
 8973                lsp::CompletionItem {
 8974                    label: multiline_label.to_string(),
 8975                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8976                        range: lsp::Range {
 8977                            start: lsp::Position {
 8978                                line: params.text_document_position.position.line,
 8979                                character: params.text_document_position.position.character,
 8980                            },
 8981                            end: lsp::Position {
 8982                                line: params.text_document_position.position.line,
 8983                                character: params.text_document_position.position.character,
 8984                            },
 8985                        },
 8986                        new_text: "new_text_1".to_string(),
 8987                    })),
 8988                    ..lsp::CompletionItem::default()
 8989                },
 8990                lsp::CompletionItem {
 8991                    label: "single line label 1".to_string(),
 8992                    detail: Some(multiline_detail.to_string()),
 8993                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8994                        range: lsp::Range {
 8995                            start: lsp::Position {
 8996                                line: params.text_document_position.position.line,
 8997                                character: params.text_document_position.position.character,
 8998                            },
 8999                            end: lsp::Position {
 9000                                line: params.text_document_position.position.line,
 9001                                character: params.text_document_position.position.character,
 9002                            },
 9003                        },
 9004                        new_text: "new_text_2".to_string(),
 9005                    })),
 9006                    ..lsp::CompletionItem::default()
 9007                },
 9008                lsp::CompletionItem {
 9009                    label: "single line label 2".to_string(),
 9010                    label_details: Some(lsp::CompletionItemLabelDetails {
 9011                        description: Some(multiline_description.to_string()),
 9012                        detail: None,
 9013                    }),
 9014                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9015                        range: lsp::Range {
 9016                            start: lsp::Position {
 9017                                line: params.text_document_position.position.line,
 9018                                character: params.text_document_position.position.character,
 9019                            },
 9020                            end: lsp::Position {
 9021                                line: params.text_document_position.position.line,
 9022                                character: params.text_document_position.position.character,
 9023                            },
 9024                        },
 9025                        new_text: "new_text_2".to_string(),
 9026                    })),
 9027                    ..lsp::CompletionItem::default()
 9028                },
 9029                lsp::CompletionItem {
 9030                    label: multiline_label_2.to_string(),
 9031                    detail: Some(multiline_detail_2.to_string()),
 9032                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9033                        range: lsp::Range {
 9034                            start: lsp::Position {
 9035                                line: params.text_document_position.position.line,
 9036                                character: params.text_document_position.position.character,
 9037                            },
 9038                            end: lsp::Position {
 9039                                line: params.text_document_position.position.line,
 9040                                character: params.text_document_position.position.character,
 9041                            },
 9042                        },
 9043                        new_text: "new_text_3".to_string(),
 9044                    })),
 9045                    ..lsp::CompletionItem::default()
 9046                },
 9047                lsp::CompletionItem {
 9048                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9049                    detail: Some(
 9050                        "Details with many     spaces and \t but without newlines".to_string(),
 9051                    ),
 9052                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9053                        range: lsp::Range {
 9054                            start: lsp::Position {
 9055                                line: params.text_document_position.position.line,
 9056                                character: params.text_document_position.position.character,
 9057                            },
 9058                            end: lsp::Position {
 9059                                line: params.text_document_position.position.line,
 9060                                character: params.text_document_position.position.character,
 9061                            },
 9062                        },
 9063                        new_text: "new_text_4".to_string(),
 9064                    })),
 9065                    ..lsp::CompletionItem::default()
 9066                },
 9067            ])))
 9068        });
 9069
 9070    editor.update_in(cx, |editor, window, cx| {
 9071        cx.focus_self(window);
 9072        editor.move_to_end(&MoveToEnd, window, cx);
 9073        editor.handle_input(".", window, cx);
 9074    });
 9075    cx.run_until_parked();
 9076    completion_handle.next().await.unwrap();
 9077
 9078    editor.update(cx, |editor, _| {
 9079        assert!(editor.context_menu_visible());
 9080        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9081        {
 9082            let completion_labels = menu
 9083                .completions
 9084                .borrow()
 9085                .iter()
 9086                .map(|c| c.label.text.clone())
 9087                .collect::<Vec<_>>();
 9088            assert_eq!(
 9089                completion_labels,
 9090                &[
 9091                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9092                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9093                    "single line label 2 d e f ",
 9094                    "a b c g h i ",
 9095                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9096                ],
 9097                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9098            );
 9099
 9100            for completion in menu
 9101                .completions
 9102                .borrow()
 9103                .iter() {
 9104                    assert_eq!(
 9105                        completion.label.filter_range,
 9106                        0..completion.label.text.len(),
 9107                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9108                    );
 9109                }
 9110
 9111        } else {
 9112            panic!("expected completion menu to be open");
 9113        }
 9114    });
 9115}
 9116
 9117#[gpui::test]
 9118async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9119    init_test(cx, |_| {});
 9120    let mut cx = EditorLspTestContext::new_rust(
 9121        lsp::ServerCapabilities {
 9122            completion_provider: Some(lsp::CompletionOptions {
 9123                trigger_characters: Some(vec![".".to_string()]),
 9124                ..Default::default()
 9125            }),
 9126            ..Default::default()
 9127        },
 9128        cx,
 9129    )
 9130    .await;
 9131    cx.lsp
 9132        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9133            Ok(Some(lsp::CompletionResponse::Array(vec![
 9134                lsp::CompletionItem {
 9135                    label: "first".into(),
 9136                    ..Default::default()
 9137                },
 9138                lsp::CompletionItem {
 9139                    label: "last".into(),
 9140                    ..Default::default()
 9141                },
 9142            ])))
 9143        });
 9144    cx.set_state("variableˇ");
 9145    cx.simulate_keystroke(".");
 9146    cx.executor().run_until_parked();
 9147
 9148    cx.update_editor(|editor, _, _| {
 9149        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9150        {
 9151            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9152        } else {
 9153            panic!("expected completion menu to be open");
 9154        }
 9155    });
 9156
 9157    cx.update_editor(|editor, window, cx| {
 9158        editor.move_page_down(&MovePageDown::default(), window, cx);
 9159        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9160        {
 9161            assert!(
 9162                menu.selected_item == 1,
 9163                "expected PageDown to select the last item from the context menu"
 9164            );
 9165        } else {
 9166            panic!("expected completion menu to stay open after PageDown");
 9167        }
 9168    });
 9169
 9170    cx.update_editor(|editor, window, cx| {
 9171        editor.move_page_up(&MovePageUp::default(), window, cx);
 9172        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9173        {
 9174            assert!(
 9175                menu.selected_item == 0,
 9176                "expected PageUp to select the first item from the context menu"
 9177            );
 9178        } else {
 9179            panic!("expected completion menu to stay open after PageUp");
 9180        }
 9181    });
 9182}
 9183
 9184#[gpui::test]
 9185async fn test_completion_sort(cx: &mut TestAppContext) {
 9186    init_test(cx, |_| {});
 9187    let mut cx = EditorLspTestContext::new_rust(
 9188        lsp::ServerCapabilities {
 9189            completion_provider: Some(lsp::CompletionOptions {
 9190                trigger_characters: Some(vec![".".to_string()]),
 9191                ..Default::default()
 9192            }),
 9193            ..Default::default()
 9194        },
 9195        cx,
 9196    )
 9197    .await;
 9198    cx.lsp
 9199        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9200            Ok(Some(lsp::CompletionResponse::Array(vec![
 9201                lsp::CompletionItem {
 9202                    label: "Range".into(),
 9203                    sort_text: Some("a".into()),
 9204                    ..Default::default()
 9205                },
 9206                lsp::CompletionItem {
 9207                    label: "r".into(),
 9208                    sort_text: Some("b".into()),
 9209                    ..Default::default()
 9210                },
 9211                lsp::CompletionItem {
 9212                    label: "ret".into(),
 9213                    sort_text: Some("c".into()),
 9214                    ..Default::default()
 9215                },
 9216                lsp::CompletionItem {
 9217                    label: "return".into(),
 9218                    sort_text: Some("d".into()),
 9219                    ..Default::default()
 9220                },
 9221                lsp::CompletionItem {
 9222                    label: "slice".into(),
 9223                    sort_text: Some("d".into()),
 9224                    ..Default::default()
 9225                },
 9226            ])))
 9227        });
 9228    cx.set_state("");
 9229    cx.executor().run_until_parked();
 9230    cx.update_editor(|editor, window, cx| {
 9231        editor.show_completions(
 9232            &ShowCompletions {
 9233                trigger: Some("r".into()),
 9234            },
 9235            window,
 9236            cx,
 9237        );
 9238    });
 9239    cx.executor().run_until_parked();
 9240
 9241    cx.update_editor(|editor, _, _| {
 9242        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9243        {
 9244            assert_eq!(
 9245                completion_menu_entries(&menu),
 9246                &["r", "ret", "Range", "return"]
 9247            );
 9248        } else {
 9249            panic!("expected completion menu to be open");
 9250        }
 9251    });
 9252}
 9253
 9254#[gpui::test]
 9255async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9256    init_test(cx, |_| {});
 9257
 9258    let mut cx = EditorLspTestContext::new_rust(
 9259        lsp::ServerCapabilities {
 9260            completion_provider: Some(lsp::CompletionOptions {
 9261                trigger_characters: Some(vec![".".to_string()]),
 9262                resolve_provider: Some(true),
 9263                ..Default::default()
 9264            }),
 9265            ..Default::default()
 9266        },
 9267        cx,
 9268    )
 9269    .await;
 9270
 9271    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9272    cx.simulate_keystroke(".");
 9273    let completion_item = lsp::CompletionItem {
 9274        label: "Some".into(),
 9275        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9276        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9277        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9278            kind: lsp::MarkupKind::Markdown,
 9279            value: "```rust\nSome(2)\n```".to_string(),
 9280        })),
 9281        deprecated: Some(false),
 9282        sort_text: Some("Some".to_string()),
 9283        filter_text: Some("Some".to_string()),
 9284        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9285        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9286            range: lsp::Range {
 9287                start: lsp::Position {
 9288                    line: 0,
 9289                    character: 22,
 9290                },
 9291                end: lsp::Position {
 9292                    line: 0,
 9293                    character: 22,
 9294                },
 9295            },
 9296            new_text: "Some(2)".to_string(),
 9297        })),
 9298        additional_text_edits: Some(vec![lsp::TextEdit {
 9299            range: lsp::Range {
 9300                start: lsp::Position {
 9301                    line: 0,
 9302                    character: 20,
 9303                },
 9304                end: lsp::Position {
 9305                    line: 0,
 9306                    character: 22,
 9307                },
 9308            },
 9309            new_text: "".to_string(),
 9310        }]),
 9311        ..Default::default()
 9312    };
 9313
 9314    let closure_completion_item = completion_item.clone();
 9315    let counter = Arc::new(AtomicUsize::new(0));
 9316    let counter_clone = counter.clone();
 9317    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9318        let task_completion_item = closure_completion_item.clone();
 9319        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9320        async move {
 9321            Ok(Some(lsp::CompletionResponse::Array(vec![
 9322                task_completion_item,
 9323            ])))
 9324        }
 9325    });
 9326
 9327    cx.condition(|editor, _| editor.context_menu_visible())
 9328        .await;
 9329    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9330    assert!(request.next().await.is_some());
 9331    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9332
 9333    cx.simulate_keystroke("S");
 9334    cx.simulate_keystroke("o");
 9335    cx.simulate_keystroke("m");
 9336    cx.condition(|editor, _| editor.context_menu_visible())
 9337        .await;
 9338    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9339    assert!(request.next().await.is_some());
 9340    assert!(request.next().await.is_some());
 9341    assert!(request.next().await.is_some());
 9342    request.close();
 9343    assert!(request.next().await.is_none());
 9344    assert_eq!(
 9345        counter.load(atomic::Ordering::Acquire),
 9346        4,
 9347        "With the completions menu open, only one LSP request should happen per input"
 9348    );
 9349}
 9350
 9351#[gpui::test]
 9352async fn test_toggle_comment(cx: &mut TestAppContext) {
 9353    init_test(cx, |_| {});
 9354    let mut cx = EditorTestContext::new(cx).await;
 9355    let language = Arc::new(Language::new(
 9356        LanguageConfig {
 9357            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9358            ..Default::default()
 9359        },
 9360        Some(tree_sitter_rust::LANGUAGE.into()),
 9361    ));
 9362    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9363
 9364    // If multiple selections intersect a line, the line is only toggled once.
 9365    cx.set_state(indoc! {"
 9366        fn a() {
 9367            «//b();
 9368            ˇ»// «c();
 9369            //ˇ»  d();
 9370        }
 9371    "});
 9372
 9373    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9374
 9375    cx.assert_editor_state(indoc! {"
 9376        fn a() {
 9377            «b();
 9378            c();
 9379            ˇ» d();
 9380        }
 9381    "});
 9382
 9383    // The comment prefix is inserted at the same column for every line in a
 9384    // selection.
 9385    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9386
 9387    cx.assert_editor_state(indoc! {"
 9388        fn a() {
 9389            // «b();
 9390            // c();
 9391            ˇ»//  d();
 9392        }
 9393    "});
 9394
 9395    // If a selection ends at the beginning of a line, that line is not toggled.
 9396    cx.set_selections_state(indoc! {"
 9397        fn a() {
 9398            // b();
 9399            «// c();
 9400        ˇ»    //  d();
 9401        }
 9402    "});
 9403
 9404    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9405
 9406    cx.assert_editor_state(indoc! {"
 9407        fn a() {
 9408            // b();
 9409            «c();
 9410        ˇ»    //  d();
 9411        }
 9412    "});
 9413
 9414    // If a selection span a single line and is empty, the line is toggled.
 9415    cx.set_state(indoc! {"
 9416        fn a() {
 9417            a();
 9418            b();
 9419        ˇ
 9420        }
 9421    "});
 9422
 9423    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9424
 9425    cx.assert_editor_state(indoc! {"
 9426        fn a() {
 9427            a();
 9428            b();
 9429        //•ˇ
 9430        }
 9431    "});
 9432
 9433    // If a selection span multiple lines, empty lines are not toggled.
 9434    cx.set_state(indoc! {"
 9435        fn a() {
 9436            «a();
 9437
 9438            c();ˇ»
 9439        }
 9440    "});
 9441
 9442    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9443
 9444    cx.assert_editor_state(indoc! {"
 9445        fn a() {
 9446            // «a();
 9447
 9448            // c();ˇ»
 9449        }
 9450    "});
 9451
 9452    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9453    cx.set_state(indoc! {"
 9454        fn a() {
 9455            «// a();
 9456            /// b();
 9457            //! c();ˇ»
 9458        }
 9459    "});
 9460
 9461    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9462
 9463    cx.assert_editor_state(indoc! {"
 9464        fn a() {
 9465            «a();
 9466            b();
 9467            c();ˇ»
 9468        }
 9469    "});
 9470}
 9471
 9472#[gpui::test]
 9473async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9474    init_test(cx, |_| {});
 9475    let mut cx = EditorTestContext::new(cx).await;
 9476    let language = Arc::new(Language::new(
 9477        LanguageConfig {
 9478            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9479            ..Default::default()
 9480        },
 9481        Some(tree_sitter_rust::LANGUAGE.into()),
 9482    ));
 9483    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9484
 9485    let toggle_comments = &ToggleComments {
 9486        advance_downwards: false,
 9487        ignore_indent: true,
 9488    };
 9489
 9490    // If multiple selections intersect a line, the line is only toggled once.
 9491    cx.set_state(indoc! {"
 9492        fn a() {
 9493        //    «b();
 9494        //    c();
 9495        //    ˇ» d();
 9496        }
 9497    "});
 9498
 9499    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9500
 9501    cx.assert_editor_state(indoc! {"
 9502        fn a() {
 9503            «b();
 9504            c();
 9505            ˇ» d();
 9506        }
 9507    "});
 9508
 9509    // The comment prefix is inserted at the beginning of each line
 9510    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9511
 9512    cx.assert_editor_state(indoc! {"
 9513        fn a() {
 9514        //    «b();
 9515        //    c();
 9516        //    ˇ» d();
 9517        }
 9518    "});
 9519
 9520    // If a selection ends at the beginning of a line, that line is not toggled.
 9521    cx.set_selections_state(indoc! {"
 9522        fn a() {
 9523        //    b();
 9524        //    «c();
 9525        ˇ»//     d();
 9526        }
 9527    "});
 9528
 9529    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9530
 9531    cx.assert_editor_state(indoc! {"
 9532        fn a() {
 9533        //    b();
 9534            «c();
 9535        ˇ»//     d();
 9536        }
 9537    "});
 9538
 9539    // If a selection span a single line and is empty, the line is toggled.
 9540    cx.set_state(indoc! {"
 9541        fn a() {
 9542            a();
 9543            b();
 9544        ˇ
 9545        }
 9546    "});
 9547
 9548    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9549
 9550    cx.assert_editor_state(indoc! {"
 9551        fn a() {
 9552            a();
 9553            b();
 9554        //ˇ
 9555        }
 9556    "});
 9557
 9558    // If a selection span multiple lines, empty lines are not toggled.
 9559    cx.set_state(indoc! {"
 9560        fn a() {
 9561            «a();
 9562
 9563            c();ˇ»
 9564        }
 9565    "});
 9566
 9567    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9568
 9569    cx.assert_editor_state(indoc! {"
 9570        fn a() {
 9571        //    «a();
 9572
 9573        //    c();ˇ»
 9574        }
 9575    "});
 9576
 9577    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9578    cx.set_state(indoc! {"
 9579        fn a() {
 9580        //    «a();
 9581        ///    b();
 9582        //!    c();ˇ»
 9583        }
 9584    "});
 9585
 9586    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9587
 9588    cx.assert_editor_state(indoc! {"
 9589        fn a() {
 9590            «a();
 9591            b();
 9592            c();ˇ»
 9593        }
 9594    "});
 9595}
 9596
 9597#[gpui::test]
 9598async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 9599    init_test(cx, |_| {});
 9600
 9601    let language = Arc::new(Language::new(
 9602        LanguageConfig {
 9603            line_comments: vec!["// ".into()],
 9604            ..Default::default()
 9605        },
 9606        Some(tree_sitter_rust::LANGUAGE.into()),
 9607    ));
 9608
 9609    let mut cx = EditorTestContext::new(cx).await;
 9610
 9611    cx.language_registry().add(language.clone());
 9612    cx.update_buffer(|buffer, cx| {
 9613        buffer.set_language(Some(language), cx);
 9614    });
 9615
 9616    let toggle_comments = &ToggleComments {
 9617        advance_downwards: true,
 9618        ignore_indent: false,
 9619    };
 9620
 9621    // Single cursor on one line -> advance
 9622    // Cursor moves horizontally 3 characters as well on non-blank line
 9623    cx.set_state(indoc!(
 9624        "fn a() {
 9625             ˇdog();
 9626             cat();
 9627        }"
 9628    ));
 9629    cx.update_editor(|editor, window, cx| {
 9630        editor.toggle_comments(toggle_comments, window, cx);
 9631    });
 9632    cx.assert_editor_state(indoc!(
 9633        "fn a() {
 9634             // dog();
 9635             catˇ();
 9636        }"
 9637    ));
 9638
 9639    // Single selection on one line -> don't advance
 9640    cx.set_state(indoc!(
 9641        "fn a() {
 9642             «dog()ˇ»;
 9643             cat();
 9644        }"
 9645    ));
 9646    cx.update_editor(|editor, window, cx| {
 9647        editor.toggle_comments(toggle_comments, window, cx);
 9648    });
 9649    cx.assert_editor_state(indoc!(
 9650        "fn a() {
 9651             // «dog()ˇ»;
 9652             cat();
 9653        }"
 9654    ));
 9655
 9656    // Multiple cursors on one line -> advance
 9657    cx.set_state(indoc!(
 9658        "fn a() {
 9659             ˇdˇog();
 9660             cat();
 9661        }"
 9662    ));
 9663    cx.update_editor(|editor, window, cx| {
 9664        editor.toggle_comments(toggle_comments, window, cx);
 9665    });
 9666    cx.assert_editor_state(indoc!(
 9667        "fn a() {
 9668             // dog();
 9669             catˇ(ˇ);
 9670        }"
 9671    ));
 9672
 9673    // Multiple cursors on one line, with selection -> don't advance
 9674    cx.set_state(indoc!(
 9675        "fn a() {
 9676             ˇdˇog«()ˇ»;
 9677             cat();
 9678        }"
 9679    ));
 9680    cx.update_editor(|editor, window, cx| {
 9681        editor.toggle_comments(toggle_comments, window, cx);
 9682    });
 9683    cx.assert_editor_state(indoc!(
 9684        "fn a() {
 9685             // ˇdˇog«()ˇ»;
 9686             cat();
 9687        }"
 9688    ));
 9689
 9690    // Single cursor on one line -> advance
 9691    // Cursor moves to column 0 on blank line
 9692    cx.set_state(indoc!(
 9693        "fn a() {
 9694             ˇdog();
 9695
 9696             cat();
 9697        }"
 9698    ));
 9699    cx.update_editor(|editor, window, cx| {
 9700        editor.toggle_comments(toggle_comments, window, cx);
 9701    });
 9702    cx.assert_editor_state(indoc!(
 9703        "fn a() {
 9704             // dog();
 9705        ˇ
 9706             cat();
 9707        }"
 9708    ));
 9709
 9710    // Single cursor on one line -> advance
 9711    // Cursor starts and ends at column 0
 9712    cx.set_state(indoc!(
 9713        "fn a() {
 9714         ˇ    dog();
 9715             cat();
 9716        }"
 9717    ));
 9718    cx.update_editor(|editor, window, cx| {
 9719        editor.toggle_comments(toggle_comments, window, cx);
 9720    });
 9721    cx.assert_editor_state(indoc!(
 9722        "fn a() {
 9723             // dog();
 9724         ˇ    cat();
 9725        }"
 9726    ));
 9727}
 9728
 9729#[gpui::test]
 9730async fn test_toggle_block_comment(cx: &mut TestAppContext) {
 9731    init_test(cx, |_| {});
 9732
 9733    let mut cx = EditorTestContext::new(cx).await;
 9734
 9735    let html_language = Arc::new(
 9736        Language::new(
 9737            LanguageConfig {
 9738                name: "HTML".into(),
 9739                block_comment: Some(("<!-- ".into(), " -->".into())),
 9740                ..Default::default()
 9741            },
 9742            Some(tree_sitter_html::LANGUAGE.into()),
 9743        )
 9744        .with_injection_query(
 9745            r#"
 9746            (script_element
 9747                (raw_text) @injection.content
 9748                (#set! injection.language "javascript"))
 9749            "#,
 9750        )
 9751        .unwrap(),
 9752    );
 9753
 9754    let javascript_language = Arc::new(Language::new(
 9755        LanguageConfig {
 9756            name: "JavaScript".into(),
 9757            line_comments: vec!["// ".into()],
 9758            ..Default::default()
 9759        },
 9760        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9761    ));
 9762
 9763    cx.language_registry().add(html_language.clone());
 9764    cx.language_registry().add(javascript_language.clone());
 9765    cx.update_buffer(|buffer, cx| {
 9766        buffer.set_language(Some(html_language), cx);
 9767    });
 9768
 9769    // Toggle comments for empty selections
 9770    cx.set_state(
 9771        &r#"
 9772            <p>A</p>ˇ
 9773            <p>B</p>ˇ
 9774            <p>C</p>ˇ
 9775        "#
 9776        .unindent(),
 9777    );
 9778    cx.update_editor(|editor, window, cx| {
 9779        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9780    });
 9781    cx.assert_editor_state(
 9782        &r#"
 9783            <!-- <p>A</p>ˇ -->
 9784            <!-- <p>B</p>ˇ -->
 9785            <!-- <p>C</p>ˇ -->
 9786        "#
 9787        .unindent(),
 9788    );
 9789    cx.update_editor(|editor, window, cx| {
 9790        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9791    });
 9792    cx.assert_editor_state(
 9793        &r#"
 9794            <p>A</p>ˇ
 9795            <p>B</p>ˇ
 9796            <p>C</p>ˇ
 9797        "#
 9798        .unindent(),
 9799    );
 9800
 9801    // Toggle comments for mixture of empty and non-empty selections, where
 9802    // multiple selections occupy a given line.
 9803    cx.set_state(
 9804        &r#"
 9805            <p>A«</p>
 9806            <p>ˇ»B</p>ˇ
 9807            <p>C«</p>
 9808            <p>ˇ»D</p>ˇ
 9809        "#
 9810        .unindent(),
 9811    );
 9812
 9813    cx.update_editor(|editor, window, cx| {
 9814        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9815    });
 9816    cx.assert_editor_state(
 9817        &r#"
 9818            <!-- <p>A«</p>
 9819            <p>ˇ»B</p>ˇ -->
 9820            <!-- <p>C«</p>
 9821            <p>ˇ»D</p>ˇ -->
 9822        "#
 9823        .unindent(),
 9824    );
 9825    cx.update_editor(|editor, window, cx| {
 9826        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9827    });
 9828    cx.assert_editor_state(
 9829        &r#"
 9830            <p>A«</p>
 9831            <p>ˇ»B</p>ˇ
 9832            <p>C«</p>
 9833            <p>ˇ»D</p>ˇ
 9834        "#
 9835        .unindent(),
 9836    );
 9837
 9838    // Toggle comments when different languages are active for different
 9839    // selections.
 9840    cx.set_state(
 9841        &r#"
 9842            ˇ<script>
 9843                ˇvar x = new Y();
 9844            ˇ</script>
 9845        "#
 9846        .unindent(),
 9847    );
 9848    cx.executor().run_until_parked();
 9849    cx.update_editor(|editor, window, cx| {
 9850        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9851    });
 9852    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9853    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9854    cx.assert_editor_state(
 9855        &r#"
 9856            <!-- ˇ<script> -->
 9857                // ˇvar x = new Y();
 9858            <!-- ˇ</script> -->
 9859        "#
 9860        .unindent(),
 9861    );
 9862}
 9863
 9864#[gpui::test]
 9865fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9866    init_test(cx, |_| {});
 9867
 9868    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9869    let multibuffer = cx.new(|cx| {
 9870        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9871        multibuffer.push_excerpts(
 9872            buffer.clone(),
 9873            [
 9874                ExcerptRange {
 9875                    context: Point::new(0, 0)..Point::new(0, 4),
 9876                    primary: None,
 9877                },
 9878                ExcerptRange {
 9879                    context: Point::new(1, 0)..Point::new(1, 4),
 9880                    primary: None,
 9881                },
 9882            ],
 9883            cx,
 9884        );
 9885        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9886        multibuffer
 9887    });
 9888
 9889    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9890    editor.update_in(cx, |editor, window, cx| {
 9891        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9892        editor.change_selections(None, window, cx, |s| {
 9893            s.select_ranges([
 9894                Point::new(0, 0)..Point::new(0, 0),
 9895                Point::new(1, 0)..Point::new(1, 0),
 9896            ])
 9897        });
 9898
 9899        editor.handle_input("X", window, cx);
 9900        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9901        assert_eq!(
 9902            editor.selections.ranges(cx),
 9903            [
 9904                Point::new(0, 1)..Point::new(0, 1),
 9905                Point::new(1, 1)..Point::new(1, 1),
 9906            ]
 9907        );
 9908
 9909        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9910        editor.change_selections(None, window, cx, |s| {
 9911            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9912        });
 9913        editor.backspace(&Default::default(), window, cx);
 9914        assert_eq!(editor.text(cx), "Xa\nbbb");
 9915        assert_eq!(
 9916            editor.selections.ranges(cx),
 9917            [Point::new(1, 0)..Point::new(1, 0)]
 9918        );
 9919
 9920        editor.change_selections(None, window, cx, |s| {
 9921            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9922        });
 9923        editor.backspace(&Default::default(), window, cx);
 9924        assert_eq!(editor.text(cx), "X\nbb");
 9925        assert_eq!(
 9926            editor.selections.ranges(cx),
 9927            [Point::new(0, 1)..Point::new(0, 1)]
 9928        );
 9929    });
 9930}
 9931
 9932#[gpui::test]
 9933fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9934    init_test(cx, |_| {});
 9935
 9936    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9937    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9938        indoc! {"
 9939            [aaaa
 9940            (bbbb]
 9941            cccc)",
 9942        },
 9943        markers.clone(),
 9944    );
 9945    let excerpt_ranges = markers.into_iter().map(|marker| {
 9946        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9947        ExcerptRange {
 9948            context,
 9949            primary: None,
 9950        }
 9951    });
 9952    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9953    let multibuffer = cx.new(|cx| {
 9954        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9955        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9956        multibuffer
 9957    });
 9958
 9959    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9960    editor.update_in(cx, |editor, window, cx| {
 9961        let (expected_text, selection_ranges) = marked_text_ranges(
 9962            indoc! {"
 9963                aaaa
 9964                bˇbbb
 9965                bˇbbˇb
 9966                cccc"
 9967            },
 9968            true,
 9969        );
 9970        assert_eq!(editor.text(cx), expected_text);
 9971        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9972
 9973        editor.handle_input("X", window, cx);
 9974
 9975        let (expected_text, expected_selections) = marked_text_ranges(
 9976            indoc! {"
 9977                aaaa
 9978                bXˇbbXb
 9979                bXˇbbXˇb
 9980                cccc"
 9981            },
 9982            false,
 9983        );
 9984        assert_eq!(editor.text(cx), expected_text);
 9985        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9986
 9987        editor.newline(&Newline, window, cx);
 9988        let (expected_text, expected_selections) = marked_text_ranges(
 9989            indoc! {"
 9990                aaaa
 9991                bX
 9992                ˇbbX
 9993                b
 9994                bX
 9995                ˇbbX
 9996                ˇb
 9997                cccc"
 9998            },
 9999            false,
10000        );
10001        assert_eq!(editor.text(cx), expected_text);
10002        assert_eq!(editor.selections.ranges(cx), expected_selections);
10003    });
10004}
10005
10006#[gpui::test]
10007fn test_refresh_selections(cx: &mut TestAppContext) {
10008    init_test(cx, |_| {});
10009
10010    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10011    let mut excerpt1_id = None;
10012    let multibuffer = cx.new(|cx| {
10013        let mut multibuffer = MultiBuffer::new(ReadWrite);
10014        excerpt1_id = multibuffer
10015            .push_excerpts(
10016                buffer.clone(),
10017                [
10018                    ExcerptRange {
10019                        context: Point::new(0, 0)..Point::new(1, 4),
10020                        primary: None,
10021                    },
10022                    ExcerptRange {
10023                        context: Point::new(1, 0)..Point::new(2, 4),
10024                        primary: None,
10025                    },
10026                ],
10027                cx,
10028            )
10029            .into_iter()
10030            .next();
10031        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10032        multibuffer
10033    });
10034
10035    let editor = cx.add_window(|window, cx| {
10036        let mut editor = build_editor(multibuffer.clone(), window, cx);
10037        let snapshot = editor.snapshot(window, cx);
10038        editor.change_selections(None, window, cx, |s| {
10039            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10040        });
10041        editor.begin_selection(
10042            Point::new(2, 1).to_display_point(&snapshot),
10043            true,
10044            1,
10045            window,
10046            cx,
10047        );
10048        assert_eq!(
10049            editor.selections.ranges(cx),
10050            [
10051                Point::new(1, 3)..Point::new(1, 3),
10052                Point::new(2, 1)..Point::new(2, 1),
10053            ]
10054        );
10055        editor
10056    });
10057
10058    // Refreshing selections is a no-op when excerpts haven't changed.
10059    _ = editor.update(cx, |editor, window, cx| {
10060        editor.change_selections(None, window, cx, |s| s.refresh());
10061        assert_eq!(
10062            editor.selections.ranges(cx),
10063            [
10064                Point::new(1, 3)..Point::new(1, 3),
10065                Point::new(2, 1)..Point::new(2, 1),
10066            ]
10067        );
10068    });
10069
10070    multibuffer.update(cx, |multibuffer, cx| {
10071        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10072    });
10073    _ = editor.update(cx, |editor, window, cx| {
10074        // Removing an excerpt causes the first selection to become degenerate.
10075        assert_eq!(
10076            editor.selections.ranges(cx),
10077            [
10078                Point::new(0, 0)..Point::new(0, 0),
10079                Point::new(0, 1)..Point::new(0, 1)
10080            ]
10081        );
10082
10083        // Refreshing selections will relocate the first selection to the original buffer
10084        // location.
10085        editor.change_selections(None, window, cx, |s| s.refresh());
10086        assert_eq!(
10087            editor.selections.ranges(cx),
10088            [
10089                Point::new(0, 1)..Point::new(0, 1),
10090                Point::new(0, 3)..Point::new(0, 3)
10091            ]
10092        );
10093        assert!(editor.selections.pending_anchor().is_some());
10094    });
10095}
10096
10097#[gpui::test]
10098fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10099    init_test(cx, |_| {});
10100
10101    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10102    let mut excerpt1_id = None;
10103    let multibuffer = cx.new(|cx| {
10104        let mut multibuffer = MultiBuffer::new(ReadWrite);
10105        excerpt1_id = multibuffer
10106            .push_excerpts(
10107                buffer.clone(),
10108                [
10109                    ExcerptRange {
10110                        context: Point::new(0, 0)..Point::new(1, 4),
10111                        primary: None,
10112                    },
10113                    ExcerptRange {
10114                        context: Point::new(1, 0)..Point::new(2, 4),
10115                        primary: None,
10116                    },
10117                ],
10118                cx,
10119            )
10120            .into_iter()
10121            .next();
10122        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10123        multibuffer
10124    });
10125
10126    let editor = cx.add_window(|window, cx| {
10127        let mut editor = build_editor(multibuffer.clone(), window, cx);
10128        let snapshot = editor.snapshot(window, cx);
10129        editor.begin_selection(
10130            Point::new(1, 3).to_display_point(&snapshot),
10131            false,
10132            1,
10133            window,
10134            cx,
10135        );
10136        assert_eq!(
10137            editor.selections.ranges(cx),
10138            [Point::new(1, 3)..Point::new(1, 3)]
10139        );
10140        editor
10141    });
10142
10143    multibuffer.update(cx, |multibuffer, cx| {
10144        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10145    });
10146    _ = editor.update(cx, |editor, window, cx| {
10147        assert_eq!(
10148            editor.selections.ranges(cx),
10149            [Point::new(0, 0)..Point::new(0, 0)]
10150        );
10151
10152        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10153        editor.change_selections(None, window, cx, |s| s.refresh());
10154        assert_eq!(
10155            editor.selections.ranges(cx),
10156            [Point::new(0, 3)..Point::new(0, 3)]
10157        );
10158        assert!(editor.selections.pending_anchor().is_some());
10159    });
10160}
10161
10162#[gpui::test]
10163async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10164    init_test(cx, |_| {});
10165
10166    let language = Arc::new(
10167        Language::new(
10168            LanguageConfig {
10169                brackets: BracketPairConfig {
10170                    pairs: vec![
10171                        BracketPair {
10172                            start: "{".to_string(),
10173                            end: "}".to_string(),
10174                            close: true,
10175                            surround: true,
10176                            newline: true,
10177                        },
10178                        BracketPair {
10179                            start: "/* ".to_string(),
10180                            end: " */".to_string(),
10181                            close: true,
10182                            surround: true,
10183                            newline: true,
10184                        },
10185                    ],
10186                    ..Default::default()
10187                },
10188                ..Default::default()
10189            },
10190            Some(tree_sitter_rust::LANGUAGE.into()),
10191        )
10192        .with_indents_query("")
10193        .unwrap(),
10194    );
10195
10196    let text = concat!(
10197        "{   }\n",     //
10198        "  x\n",       //
10199        "  /*   */\n", //
10200        "x\n",         //
10201        "{{} }\n",     //
10202    );
10203
10204    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10205    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10206    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10207    editor
10208        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10209        .await;
10210
10211    editor.update_in(cx, |editor, window, cx| {
10212        editor.change_selections(None, window, cx, |s| {
10213            s.select_display_ranges([
10214                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10215                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10216                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10217            ])
10218        });
10219        editor.newline(&Newline, window, cx);
10220
10221        assert_eq!(
10222            editor.buffer().read(cx).read(cx).text(),
10223            concat!(
10224                "{ \n",    // Suppress rustfmt
10225                "\n",      //
10226                "}\n",     //
10227                "  x\n",   //
10228                "  /* \n", //
10229                "  \n",    //
10230                "  */\n",  //
10231                "x\n",     //
10232                "{{} \n",  //
10233                "}\n",     //
10234            )
10235        );
10236    });
10237}
10238
10239#[gpui::test]
10240fn test_highlighted_ranges(cx: &mut TestAppContext) {
10241    init_test(cx, |_| {});
10242
10243    let editor = cx.add_window(|window, cx| {
10244        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10245        build_editor(buffer.clone(), window, cx)
10246    });
10247
10248    _ = editor.update(cx, |editor, window, cx| {
10249        struct Type1;
10250        struct Type2;
10251
10252        let buffer = editor.buffer.read(cx).snapshot(cx);
10253
10254        let anchor_range =
10255            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10256
10257        editor.highlight_background::<Type1>(
10258            &[
10259                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10260                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10261                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10262                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10263            ],
10264            |_| Hsla::red(),
10265            cx,
10266        );
10267        editor.highlight_background::<Type2>(
10268            &[
10269                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10270                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10271                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10272                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10273            ],
10274            |_| Hsla::green(),
10275            cx,
10276        );
10277
10278        let snapshot = editor.snapshot(window, cx);
10279        let mut highlighted_ranges = editor.background_highlights_in_range(
10280            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10281            &snapshot,
10282            cx.theme().colors(),
10283        );
10284        // Enforce a consistent ordering based on color without relying on the ordering of the
10285        // highlight's `TypeId` which is non-executor.
10286        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10287        assert_eq!(
10288            highlighted_ranges,
10289            &[
10290                (
10291                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10292                    Hsla::red(),
10293                ),
10294                (
10295                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10296                    Hsla::red(),
10297                ),
10298                (
10299                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10300                    Hsla::green(),
10301                ),
10302                (
10303                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10304                    Hsla::green(),
10305                ),
10306            ]
10307        );
10308        assert_eq!(
10309            editor.background_highlights_in_range(
10310                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10311                &snapshot,
10312                cx.theme().colors(),
10313            ),
10314            &[(
10315                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10316                Hsla::red(),
10317            )]
10318        );
10319    });
10320}
10321
10322#[gpui::test]
10323async fn test_following(cx: &mut TestAppContext) {
10324    init_test(cx, |_| {});
10325
10326    let fs = FakeFs::new(cx.executor());
10327    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10328
10329    let buffer = project.update(cx, |project, cx| {
10330        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10331        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10332    });
10333    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10334    let follower = cx.update(|cx| {
10335        cx.open_window(
10336            WindowOptions {
10337                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10338                    gpui::Point::new(px(0.), px(0.)),
10339                    gpui::Point::new(px(10.), px(80.)),
10340                ))),
10341                ..Default::default()
10342            },
10343            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10344        )
10345        .unwrap()
10346    });
10347
10348    let is_still_following = Rc::new(RefCell::new(true));
10349    let follower_edit_event_count = Rc::new(RefCell::new(0));
10350    let pending_update = Rc::new(RefCell::new(None));
10351    let leader_entity = leader.root(cx).unwrap();
10352    let follower_entity = follower.root(cx).unwrap();
10353    _ = follower.update(cx, {
10354        let update = pending_update.clone();
10355        let is_still_following = is_still_following.clone();
10356        let follower_edit_event_count = follower_edit_event_count.clone();
10357        |_, window, cx| {
10358            cx.subscribe_in(
10359                &leader_entity,
10360                window,
10361                move |_, leader, event, window, cx| {
10362                    leader.read(cx).add_event_to_update_proto(
10363                        event,
10364                        &mut update.borrow_mut(),
10365                        window,
10366                        cx,
10367                    );
10368                },
10369            )
10370            .detach();
10371
10372            cx.subscribe_in(
10373                &follower_entity,
10374                window,
10375                move |_, _, event: &EditorEvent, _window, _cx| {
10376                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10377                        *is_still_following.borrow_mut() = false;
10378                    }
10379
10380                    if let EditorEvent::BufferEdited = event {
10381                        *follower_edit_event_count.borrow_mut() += 1;
10382                    }
10383                },
10384            )
10385            .detach();
10386        }
10387    });
10388
10389    // Update the selections only
10390    _ = leader.update(cx, |leader, window, cx| {
10391        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10392    });
10393    follower
10394        .update(cx, |follower, window, cx| {
10395            follower.apply_update_proto(
10396                &project,
10397                pending_update.borrow_mut().take().unwrap(),
10398                window,
10399                cx,
10400            )
10401        })
10402        .unwrap()
10403        .await
10404        .unwrap();
10405    _ = follower.update(cx, |follower, _, cx| {
10406        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10407    });
10408    assert!(*is_still_following.borrow());
10409    assert_eq!(*follower_edit_event_count.borrow(), 0);
10410
10411    // Update the scroll position only
10412    _ = leader.update(cx, |leader, window, cx| {
10413        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10414    });
10415    follower
10416        .update(cx, |follower, window, cx| {
10417            follower.apply_update_proto(
10418                &project,
10419                pending_update.borrow_mut().take().unwrap(),
10420                window,
10421                cx,
10422            )
10423        })
10424        .unwrap()
10425        .await
10426        .unwrap();
10427    assert_eq!(
10428        follower
10429            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10430            .unwrap(),
10431        gpui::Point::new(1.5, 3.5)
10432    );
10433    assert!(*is_still_following.borrow());
10434    assert_eq!(*follower_edit_event_count.borrow(), 0);
10435
10436    // Update the selections and scroll position. The follower's scroll position is updated
10437    // via autoscroll, not via the leader's exact scroll position.
10438    _ = leader.update(cx, |leader, window, cx| {
10439        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10440        leader.request_autoscroll(Autoscroll::newest(), cx);
10441        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10442    });
10443    follower
10444        .update(cx, |follower, window, cx| {
10445            follower.apply_update_proto(
10446                &project,
10447                pending_update.borrow_mut().take().unwrap(),
10448                window,
10449                cx,
10450            )
10451        })
10452        .unwrap()
10453        .await
10454        .unwrap();
10455    _ = follower.update(cx, |follower, _, cx| {
10456        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10457        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10458    });
10459    assert!(*is_still_following.borrow());
10460
10461    // Creating a pending selection that precedes another selection
10462    _ = leader.update(cx, |leader, window, cx| {
10463        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10464        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10465    });
10466    follower
10467        .update(cx, |follower, window, cx| {
10468            follower.apply_update_proto(
10469                &project,
10470                pending_update.borrow_mut().take().unwrap(),
10471                window,
10472                cx,
10473            )
10474        })
10475        .unwrap()
10476        .await
10477        .unwrap();
10478    _ = follower.update(cx, |follower, _, cx| {
10479        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10480    });
10481    assert!(*is_still_following.borrow());
10482
10483    // Extend the pending selection so that it surrounds another selection
10484    _ = leader.update(cx, |leader, window, cx| {
10485        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10486    });
10487    follower
10488        .update(cx, |follower, window, cx| {
10489            follower.apply_update_proto(
10490                &project,
10491                pending_update.borrow_mut().take().unwrap(),
10492                window,
10493                cx,
10494            )
10495        })
10496        .unwrap()
10497        .await
10498        .unwrap();
10499    _ = follower.update(cx, |follower, _, cx| {
10500        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10501    });
10502
10503    // Scrolling locally breaks the follow
10504    _ = follower.update(cx, |follower, window, cx| {
10505        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10506        follower.set_scroll_anchor(
10507            ScrollAnchor {
10508                anchor: top_anchor,
10509                offset: gpui::Point::new(0.0, 0.5),
10510            },
10511            window,
10512            cx,
10513        );
10514    });
10515    assert!(!(*is_still_following.borrow()));
10516}
10517
10518#[gpui::test]
10519async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10520    init_test(cx, |_| {});
10521
10522    let fs = FakeFs::new(cx.executor());
10523    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10524    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10525    let pane = workspace
10526        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10527        .unwrap();
10528
10529    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10530
10531    let leader = pane.update_in(cx, |_, window, cx| {
10532        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10533        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10534    });
10535
10536    // Start following the editor when it has no excerpts.
10537    let mut state_message =
10538        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10539    let workspace_entity = workspace.root(cx).unwrap();
10540    let follower_1 = cx
10541        .update_window(*workspace.deref(), |_, window, cx| {
10542            Editor::from_state_proto(
10543                workspace_entity,
10544                ViewId {
10545                    creator: Default::default(),
10546                    id: 0,
10547                },
10548                &mut state_message,
10549                window,
10550                cx,
10551            )
10552        })
10553        .unwrap()
10554        .unwrap()
10555        .await
10556        .unwrap();
10557
10558    let update_message = Rc::new(RefCell::new(None));
10559    follower_1.update_in(cx, {
10560        let update = update_message.clone();
10561        |_, window, cx| {
10562            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10563                leader.read(cx).add_event_to_update_proto(
10564                    event,
10565                    &mut update.borrow_mut(),
10566                    window,
10567                    cx,
10568                );
10569            })
10570            .detach();
10571        }
10572    });
10573
10574    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10575        (
10576            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10577            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10578        )
10579    });
10580
10581    // Insert some excerpts.
10582    leader.update(cx, |leader, cx| {
10583        leader.buffer.update(cx, |multibuffer, cx| {
10584            let excerpt_ids = multibuffer.push_excerpts(
10585                buffer_1.clone(),
10586                [
10587                    ExcerptRange {
10588                        context: 1..6,
10589                        primary: None,
10590                    },
10591                    ExcerptRange {
10592                        context: 12..15,
10593                        primary: None,
10594                    },
10595                    ExcerptRange {
10596                        context: 0..3,
10597                        primary: None,
10598                    },
10599                ],
10600                cx,
10601            );
10602            multibuffer.insert_excerpts_after(
10603                excerpt_ids[0],
10604                buffer_2.clone(),
10605                [
10606                    ExcerptRange {
10607                        context: 8..12,
10608                        primary: None,
10609                    },
10610                    ExcerptRange {
10611                        context: 0..6,
10612                        primary: None,
10613                    },
10614                ],
10615                cx,
10616            );
10617        });
10618    });
10619
10620    // Apply the update of adding the excerpts.
10621    follower_1
10622        .update_in(cx, |follower, window, cx| {
10623            follower.apply_update_proto(
10624                &project,
10625                update_message.borrow().clone().unwrap(),
10626                window,
10627                cx,
10628            )
10629        })
10630        .await
10631        .unwrap();
10632    assert_eq!(
10633        follower_1.update(cx, |editor, cx| editor.text(cx)),
10634        leader.update(cx, |editor, cx| editor.text(cx))
10635    );
10636    update_message.borrow_mut().take();
10637
10638    // Start following separately after it already has excerpts.
10639    let mut state_message =
10640        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10641    let workspace_entity = workspace.root(cx).unwrap();
10642    let follower_2 = cx
10643        .update_window(*workspace.deref(), |_, window, cx| {
10644            Editor::from_state_proto(
10645                workspace_entity,
10646                ViewId {
10647                    creator: Default::default(),
10648                    id: 0,
10649                },
10650                &mut state_message,
10651                window,
10652                cx,
10653            )
10654        })
10655        .unwrap()
10656        .unwrap()
10657        .await
10658        .unwrap();
10659    assert_eq!(
10660        follower_2.update(cx, |editor, cx| editor.text(cx)),
10661        leader.update(cx, |editor, cx| editor.text(cx))
10662    );
10663
10664    // Remove some excerpts.
10665    leader.update(cx, |leader, cx| {
10666        leader.buffer.update(cx, |multibuffer, cx| {
10667            let excerpt_ids = multibuffer.excerpt_ids();
10668            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10669            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10670        });
10671    });
10672
10673    // Apply the update of removing the excerpts.
10674    follower_1
10675        .update_in(cx, |follower, window, cx| {
10676            follower.apply_update_proto(
10677                &project,
10678                update_message.borrow().clone().unwrap(),
10679                window,
10680                cx,
10681            )
10682        })
10683        .await
10684        .unwrap();
10685    follower_2
10686        .update_in(cx, |follower, window, cx| {
10687            follower.apply_update_proto(
10688                &project,
10689                update_message.borrow().clone().unwrap(),
10690                window,
10691                cx,
10692            )
10693        })
10694        .await
10695        .unwrap();
10696    update_message.borrow_mut().take();
10697    assert_eq!(
10698        follower_1.update(cx, |editor, cx| editor.text(cx)),
10699        leader.update(cx, |editor, cx| editor.text(cx))
10700    );
10701}
10702
10703#[gpui::test]
10704async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
10705    init_test(cx, |_| {});
10706
10707    let mut cx = EditorTestContext::new(cx).await;
10708    let lsp_store =
10709        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10710
10711    cx.set_state(indoc! {"
10712        ˇfn func(abc def: i32) -> u32 {
10713        }
10714    "});
10715
10716    cx.update(|_, cx| {
10717        lsp_store.update(cx, |lsp_store, cx| {
10718            lsp_store
10719                .update_diagnostics(
10720                    LanguageServerId(0),
10721                    lsp::PublishDiagnosticsParams {
10722                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10723                        version: None,
10724                        diagnostics: vec![
10725                            lsp::Diagnostic {
10726                                range: lsp::Range::new(
10727                                    lsp::Position::new(0, 11),
10728                                    lsp::Position::new(0, 12),
10729                                ),
10730                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10731                                ..Default::default()
10732                            },
10733                            lsp::Diagnostic {
10734                                range: lsp::Range::new(
10735                                    lsp::Position::new(0, 12),
10736                                    lsp::Position::new(0, 15),
10737                                ),
10738                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10739                                ..Default::default()
10740                            },
10741                            lsp::Diagnostic {
10742                                range: lsp::Range::new(
10743                                    lsp::Position::new(0, 25),
10744                                    lsp::Position::new(0, 28),
10745                                ),
10746                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10747                                ..Default::default()
10748                            },
10749                        ],
10750                    },
10751                    &[],
10752                    cx,
10753                )
10754                .unwrap()
10755        });
10756    });
10757
10758    executor.run_until_parked();
10759
10760    cx.update_editor(|editor, window, cx| {
10761        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10762    });
10763
10764    cx.assert_editor_state(indoc! {"
10765        fn func(abc def: i32) -> ˇu32 {
10766        }
10767    "});
10768
10769    cx.update_editor(|editor, window, cx| {
10770        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10771    });
10772
10773    cx.assert_editor_state(indoc! {"
10774        fn func(abc ˇdef: i32) -> u32 {
10775        }
10776    "});
10777
10778    cx.update_editor(|editor, window, cx| {
10779        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10780    });
10781
10782    cx.assert_editor_state(indoc! {"
10783        fn func(abcˇ def: i32) -> u32 {
10784        }
10785    "});
10786
10787    cx.update_editor(|editor, window, cx| {
10788        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10789    });
10790
10791    cx.assert_editor_state(indoc! {"
10792        fn func(abc def: i32) -> ˇu32 {
10793        }
10794    "});
10795}
10796
10797#[gpui::test]
10798async fn cycle_through_same_place_diagnostics(
10799    executor: BackgroundExecutor,
10800    cx: &mut TestAppContext,
10801) {
10802    init_test(cx, |_| {});
10803
10804    let mut cx = EditorTestContext::new(cx).await;
10805    let lsp_store =
10806        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10807
10808    cx.set_state(indoc! {"
10809        ˇfn func(abc def: i32) -> u32 {
10810        }
10811    "});
10812
10813    cx.update(|_, cx| {
10814        lsp_store.update(cx, |lsp_store, cx| {
10815            lsp_store
10816                .update_diagnostics(
10817                    LanguageServerId(0),
10818                    lsp::PublishDiagnosticsParams {
10819                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10820                        version: None,
10821                        diagnostics: vec![
10822                            lsp::Diagnostic {
10823                                range: lsp::Range::new(
10824                                    lsp::Position::new(0, 11),
10825                                    lsp::Position::new(0, 12),
10826                                ),
10827                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10828                                ..Default::default()
10829                            },
10830                            lsp::Diagnostic {
10831                                range: lsp::Range::new(
10832                                    lsp::Position::new(0, 12),
10833                                    lsp::Position::new(0, 15),
10834                                ),
10835                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10836                                ..Default::default()
10837                            },
10838                            lsp::Diagnostic {
10839                                range: lsp::Range::new(
10840                                    lsp::Position::new(0, 12),
10841                                    lsp::Position::new(0, 15),
10842                                ),
10843                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10844                                ..Default::default()
10845                            },
10846                            lsp::Diagnostic {
10847                                range: lsp::Range::new(
10848                                    lsp::Position::new(0, 25),
10849                                    lsp::Position::new(0, 28),
10850                                ),
10851                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10852                                ..Default::default()
10853                            },
10854                        ],
10855                    },
10856                    &[],
10857                    cx,
10858                )
10859                .unwrap()
10860        });
10861    });
10862    executor.run_until_parked();
10863
10864    //// Backward
10865
10866    // Fourth diagnostic
10867    cx.update_editor(|editor, window, cx| {
10868        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10869    });
10870    cx.assert_editor_state(indoc! {"
10871        fn func(abc def: i32) -> ˇu32 {
10872        }
10873    "});
10874
10875    // Third diagnostic
10876    cx.update_editor(|editor, window, cx| {
10877        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10878    });
10879    cx.assert_editor_state(indoc! {"
10880        fn func(abc ˇdef: i32) -> u32 {
10881        }
10882    "});
10883
10884    // Second diagnostic, same place
10885    cx.update_editor(|editor, window, cx| {
10886        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10887    });
10888    cx.assert_editor_state(indoc! {"
10889        fn func(abc ˇdef: i32) -> u32 {
10890        }
10891    "});
10892
10893    // First diagnostic
10894    cx.update_editor(|editor, window, cx| {
10895        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10896    });
10897    cx.assert_editor_state(indoc! {"
10898        fn func(abcˇ def: i32) -> u32 {
10899        }
10900    "});
10901
10902    // Wrapped over, fourth diagnostic
10903    cx.update_editor(|editor, window, cx| {
10904        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10905    });
10906    cx.assert_editor_state(indoc! {"
10907        fn func(abc def: i32) -> ˇu32 {
10908        }
10909    "});
10910
10911    cx.update_editor(|editor, window, cx| {
10912        editor.move_to_beginning(&MoveToBeginning, window, cx);
10913    });
10914    cx.assert_editor_state(indoc! {"
10915        ˇfn func(abc def: i32) -> u32 {
10916        }
10917    "});
10918
10919    //// Forward
10920
10921    // First diagnostic
10922    cx.update_editor(|editor, window, cx| {
10923        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10924    });
10925    cx.assert_editor_state(indoc! {"
10926        fn func(abcˇ def: i32) -> u32 {
10927        }
10928    "});
10929
10930    // Second diagnostic
10931    cx.update_editor(|editor, window, cx| {
10932        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10933    });
10934    cx.assert_editor_state(indoc! {"
10935        fn func(abc ˇdef: i32) -> u32 {
10936        }
10937    "});
10938
10939    // Third diagnostic, same place
10940    cx.update_editor(|editor, window, cx| {
10941        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10942    });
10943    cx.assert_editor_state(indoc! {"
10944        fn func(abc ˇdef: i32) -> u32 {
10945        }
10946    "});
10947
10948    // Fourth diagnostic
10949    cx.update_editor(|editor, window, cx| {
10950        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10951    });
10952    cx.assert_editor_state(indoc! {"
10953        fn func(abc def: i32) -> ˇu32 {
10954        }
10955    "});
10956
10957    // Wrapped around, first diagnostic
10958    cx.update_editor(|editor, window, cx| {
10959        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10960    });
10961    cx.assert_editor_state(indoc! {"
10962        fn func(abcˇ def: i32) -> u32 {
10963        }
10964    "});
10965}
10966
10967#[gpui::test]
10968async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10969    init_test(cx, |_| {});
10970
10971    let mut cx = EditorTestContext::new(cx).await;
10972
10973    cx.set_state(indoc! {"
10974        fn func(abˇc def: i32) -> u32 {
10975        }
10976    "});
10977    let lsp_store =
10978        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10979
10980    cx.update(|_, cx| {
10981        lsp_store.update(cx, |lsp_store, cx| {
10982            lsp_store.update_diagnostics(
10983                LanguageServerId(0),
10984                lsp::PublishDiagnosticsParams {
10985                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10986                    version: None,
10987                    diagnostics: vec![lsp::Diagnostic {
10988                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10989                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10990                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10991                        ..Default::default()
10992                    }],
10993                },
10994                &[],
10995                cx,
10996            )
10997        })
10998    }).unwrap();
10999    cx.run_until_parked();
11000    cx.update_editor(|editor, window, cx| {
11001        hover_popover::hover(editor, &Default::default(), window, cx)
11002    });
11003    cx.run_until_parked();
11004    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11005}
11006
11007#[gpui::test]
11008async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11009    init_test(cx, |_| {});
11010
11011    let mut cx = EditorTestContext::new(cx).await;
11012
11013    let diff_base = r#"
11014        use some::mod;
11015
11016        const A: u32 = 42;
11017
11018        fn main() {
11019            println!("hello");
11020
11021            println!("world");
11022        }
11023        "#
11024    .unindent();
11025
11026    // Edits are modified, removed, modified, added
11027    cx.set_state(
11028        &r#"
11029        use some::modified;
11030
11031        ˇ
11032        fn main() {
11033            println!("hello there");
11034
11035            println!("around the");
11036            println!("world");
11037        }
11038        "#
11039        .unindent(),
11040    );
11041
11042    cx.set_diff_base(&diff_base);
11043    executor.run_until_parked();
11044
11045    cx.update_editor(|editor, window, cx| {
11046        //Wrap around the bottom of the buffer
11047        for _ in 0..3 {
11048            editor.go_to_next_hunk(&GoToHunk, window, cx);
11049        }
11050    });
11051
11052    cx.assert_editor_state(
11053        &r#"
11054        ˇuse some::modified;
11055
11056
11057        fn main() {
11058            println!("hello there");
11059
11060            println!("around the");
11061            println!("world");
11062        }
11063        "#
11064        .unindent(),
11065    );
11066
11067    cx.update_editor(|editor, window, cx| {
11068        //Wrap around the top of the buffer
11069        for _ in 0..2 {
11070            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11071        }
11072    });
11073
11074    cx.assert_editor_state(
11075        &r#"
11076        use some::modified;
11077
11078
11079        fn main() {
11080        ˇ    println!("hello there");
11081
11082            println!("around the");
11083            println!("world");
11084        }
11085        "#
11086        .unindent(),
11087    );
11088
11089    cx.update_editor(|editor, window, cx| {
11090        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11091    });
11092
11093    cx.assert_editor_state(
11094        &r#"
11095        use some::modified;
11096
11097        ˇ
11098        fn main() {
11099            println!("hello there");
11100
11101            println!("around the");
11102            println!("world");
11103        }
11104        "#
11105        .unindent(),
11106    );
11107
11108    cx.update_editor(|editor, window, cx| {
11109        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11110    });
11111
11112    cx.assert_editor_state(
11113        &r#"
11114        ˇuse some::modified;
11115
11116
11117        fn main() {
11118            println!("hello there");
11119
11120            println!("around the");
11121            println!("world");
11122        }
11123        "#
11124        .unindent(),
11125    );
11126
11127    cx.update_editor(|editor, window, cx| {
11128        for _ in 0..2 {
11129            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11130        }
11131    });
11132
11133    cx.assert_editor_state(
11134        &r#"
11135        use some::modified;
11136
11137
11138        fn main() {
11139        ˇ    println!("hello there");
11140
11141            println!("around the");
11142            println!("world");
11143        }
11144        "#
11145        .unindent(),
11146    );
11147
11148    cx.update_editor(|editor, window, cx| {
11149        editor.fold(&Fold, window, cx);
11150    });
11151
11152    cx.update_editor(|editor, window, cx| {
11153        editor.go_to_next_hunk(&GoToHunk, window, cx);
11154    });
11155
11156    cx.assert_editor_state(
11157        &r#"
11158        ˇuse some::modified;
11159
11160
11161        fn main() {
11162            println!("hello there");
11163
11164            println!("around the");
11165            println!("world");
11166        }
11167        "#
11168        .unindent(),
11169    );
11170}
11171
11172#[test]
11173fn test_split_words() {
11174    fn split(text: &str) -> Vec<&str> {
11175        split_words(text).collect()
11176    }
11177
11178    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11179    assert_eq!(split("hello_world"), &["hello_", "world"]);
11180    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11181    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11182    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11183    assert_eq!(split("helloworld"), &["helloworld"]);
11184
11185    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11186}
11187
11188#[gpui::test]
11189async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11190    init_test(cx, |_| {});
11191
11192    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11193    let mut assert = |before, after| {
11194        let _state_context = cx.set_state(before);
11195        cx.run_until_parked();
11196        cx.update_editor(|editor, window, cx| {
11197            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11198        });
11199        cx.assert_editor_state(after);
11200    };
11201
11202    // Outside bracket jumps to outside of matching bracket
11203    assert("console.logˇ(var);", "console.log(var)ˇ;");
11204    assert("console.log(var)ˇ;", "console.logˇ(var);");
11205
11206    // Inside bracket jumps to inside of matching bracket
11207    assert("console.log(ˇvar);", "console.log(varˇ);");
11208    assert("console.log(varˇ);", "console.log(ˇvar);");
11209
11210    // When outside a bracket and inside, favor jumping to the inside bracket
11211    assert(
11212        "console.log('foo', [1, 2, 3]ˇ);",
11213        "console.log(ˇ'foo', [1, 2, 3]);",
11214    );
11215    assert(
11216        "console.log(ˇ'foo', [1, 2, 3]);",
11217        "console.log('foo', [1, 2, 3]ˇ);",
11218    );
11219
11220    // Bias forward if two options are equally likely
11221    assert(
11222        "let result = curried_fun()ˇ();",
11223        "let result = curried_fun()()ˇ;",
11224    );
11225
11226    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11227    assert(
11228        indoc! {"
11229            function test() {
11230                console.log('test')ˇ
11231            }"},
11232        indoc! {"
11233            function test() {
11234                console.logˇ('test')
11235            }"},
11236    );
11237}
11238
11239#[gpui::test]
11240async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11241    init_test(cx, |_| {});
11242
11243    let fs = FakeFs::new(cx.executor());
11244    fs.insert_tree(
11245        path!("/a"),
11246        json!({
11247            "main.rs": "fn main() { let a = 5; }",
11248            "other.rs": "// Test file",
11249        }),
11250    )
11251    .await;
11252    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11253
11254    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11255    language_registry.add(Arc::new(Language::new(
11256        LanguageConfig {
11257            name: "Rust".into(),
11258            matcher: LanguageMatcher {
11259                path_suffixes: vec!["rs".to_string()],
11260                ..Default::default()
11261            },
11262            brackets: BracketPairConfig {
11263                pairs: vec![BracketPair {
11264                    start: "{".to_string(),
11265                    end: "}".to_string(),
11266                    close: true,
11267                    surround: true,
11268                    newline: true,
11269                }],
11270                disabled_scopes_by_bracket_ix: Vec::new(),
11271            },
11272            ..Default::default()
11273        },
11274        Some(tree_sitter_rust::LANGUAGE.into()),
11275    )));
11276    let mut fake_servers = language_registry.register_fake_lsp(
11277        "Rust",
11278        FakeLspAdapter {
11279            capabilities: lsp::ServerCapabilities {
11280                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11281                    first_trigger_character: "{".to_string(),
11282                    more_trigger_character: None,
11283                }),
11284                ..Default::default()
11285            },
11286            ..Default::default()
11287        },
11288    );
11289
11290    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11291
11292    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11293
11294    let worktree_id = workspace
11295        .update(cx, |workspace, _, cx| {
11296            workspace.project().update(cx, |project, cx| {
11297                project.worktrees(cx).next().unwrap().read(cx).id()
11298            })
11299        })
11300        .unwrap();
11301
11302    let buffer = project
11303        .update(cx, |project, cx| {
11304            project.open_local_buffer(path!("/a/main.rs"), cx)
11305        })
11306        .await
11307        .unwrap();
11308    let editor_handle = workspace
11309        .update(cx, |workspace, window, cx| {
11310            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11311        })
11312        .unwrap()
11313        .await
11314        .unwrap()
11315        .downcast::<Editor>()
11316        .unwrap();
11317
11318    cx.executor().start_waiting();
11319    let fake_server = fake_servers.next().await.unwrap();
11320
11321    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11322        assert_eq!(
11323            params.text_document_position.text_document.uri,
11324            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11325        );
11326        assert_eq!(
11327            params.text_document_position.position,
11328            lsp::Position::new(0, 21),
11329        );
11330
11331        Ok(Some(vec![lsp::TextEdit {
11332            new_text: "]".to_string(),
11333            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11334        }]))
11335    });
11336
11337    editor_handle.update_in(cx, |editor, window, cx| {
11338        window.focus(&editor.focus_handle(cx));
11339        editor.change_selections(None, window, cx, |s| {
11340            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11341        });
11342        editor.handle_input("{", window, cx);
11343    });
11344
11345    cx.executor().run_until_parked();
11346
11347    buffer.update(cx, |buffer, _| {
11348        assert_eq!(
11349            buffer.text(),
11350            "fn main() { let a = {5}; }",
11351            "No extra braces from on type formatting should appear in the buffer"
11352        )
11353    });
11354}
11355
11356#[gpui::test]
11357async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11358    init_test(cx, |_| {});
11359
11360    let fs = FakeFs::new(cx.executor());
11361    fs.insert_tree(
11362        path!("/a"),
11363        json!({
11364            "main.rs": "fn main() { let a = 5; }",
11365            "other.rs": "// Test file",
11366        }),
11367    )
11368    .await;
11369
11370    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11371
11372    let server_restarts = Arc::new(AtomicUsize::new(0));
11373    let closure_restarts = Arc::clone(&server_restarts);
11374    let language_server_name = "test language server";
11375    let language_name: LanguageName = "Rust".into();
11376
11377    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11378    language_registry.add(Arc::new(Language::new(
11379        LanguageConfig {
11380            name: language_name.clone(),
11381            matcher: LanguageMatcher {
11382                path_suffixes: vec!["rs".to_string()],
11383                ..Default::default()
11384            },
11385            ..Default::default()
11386        },
11387        Some(tree_sitter_rust::LANGUAGE.into()),
11388    )));
11389    let mut fake_servers = language_registry.register_fake_lsp(
11390        "Rust",
11391        FakeLspAdapter {
11392            name: language_server_name,
11393            initialization_options: Some(json!({
11394                "testOptionValue": true
11395            })),
11396            initializer: Some(Box::new(move |fake_server| {
11397                let task_restarts = Arc::clone(&closure_restarts);
11398                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11399                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11400                    futures::future::ready(Ok(()))
11401                });
11402            })),
11403            ..Default::default()
11404        },
11405    );
11406
11407    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11408    let _buffer = project
11409        .update(cx, |project, cx| {
11410            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11411        })
11412        .await
11413        .unwrap();
11414    let _fake_server = fake_servers.next().await.unwrap();
11415    update_test_language_settings(cx, |language_settings| {
11416        language_settings.languages.insert(
11417            language_name.clone(),
11418            LanguageSettingsContent {
11419                tab_size: NonZeroU32::new(8),
11420                ..Default::default()
11421            },
11422        );
11423    });
11424    cx.executor().run_until_parked();
11425    assert_eq!(
11426        server_restarts.load(atomic::Ordering::Acquire),
11427        0,
11428        "Should not restart LSP server on an unrelated change"
11429    );
11430
11431    update_test_project_settings(cx, |project_settings| {
11432        project_settings.lsp.insert(
11433            "Some other server name".into(),
11434            LspSettings {
11435                binary: None,
11436                settings: None,
11437                initialization_options: Some(json!({
11438                    "some other init value": false
11439                })),
11440            },
11441        );
11442    });
11443    cx.executor().run_until_parked();
11444    assert_eq!(
11445        server_restarts.load(atomic::Ordering::Acquire),
11446        0,
11447        "Should not restart LSP server on an unrelated LSP settings change"
11448    );
11449
11450    update_test_project_settings(cx, |project_settings| {
11451        project_settings.lsp.insert(
11452            language_server_name.into(),
11453            LspSettings {
11454                binary: None,
11455                settings: None,
11456                initialization_options: Some(json!({
11457                    "anotherInitValue": false
11458                })),
11459            },
11460        );
11461    });
11462    cx.executor().run_until_parked();
11463    assert_eq!(
11464        server_restarts.load(atomic::Ordering::Acquire),
11465        1,
11466        "Should restart LSP server on a related LSP settings change"
11467    );
11468
11469    update_test_project_settings(cx, |project_settings| {
11470        project_settings.lsp.insert(
11471            language_server_name.into(),
11472            LspSettings {
11473                binary: None,
11474                settings: None,
11475                initialization_options: Some(json!({
11476                    "anotherInitValue": false
11477                })),
11478            },
11479        );
11480    });
11481    cx.executor().run_until_parked();
11482    assert_eq!(
11483        server_restarts.load(atomic::Ordering::Acquire),
11484        1,
11485        "Should not restart LSP server on a related LSP settings change that is the same"
11486    );
11487
11488    update_test_project_settings(cx, |project_settings| {
11489        project_settings.lsp.insert(
11490            language_server_name.into(),
11491            LspSettings {
11492                binary: None,
11493                settings: None,
11494                initialization_options: None,
11495            },
11496        );
11497    });
11498    cx.executor().run_until_parked();
11499    assert_eq!(
11500        server_restarts.load(atomic::Ordering::Acquire),
11501        2,
11502        "Should restart LSP server on another related LSP settings change"
11503    );
11504}
11505
11506#[gpui::test]
11507async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
11508    init_test(cx, |_| {});
11509
11510    let mut cx = EditorLspTestContext::new_rust(
11511        lsp::ServerCapabilities {
11512            completion_provider: Some(lsp::CompletionOptions {
11513                trigger_characters: Some(vec![".".to_string()]),
11514                resolve_provider: Some(true),
11515                ..Default::default()
11516            }),
11517            ..Default::default()
11518        },
11519        cx,
11520    )
11521    .await;
11522
11523    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11524    cx.simulate_keystroke(".");
11525    let completion_item = lsp::CompletionItem {
11526        label: "some".into(),
11527        kind: Some(lsp::CompletionItemKind::SNIPPET),
11528        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11529        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11530            kind: lsp::MarkupKind::Markdown,
11531            value: "```rust\nSome(2)\n```".to_string(),
11532        })),
11533        deprecated: Some(false),
11534        sort_text: Some("fffffff2".to_string()),
11535        filter_text: Some("some".to_string()),
11536        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11537        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11538            range: lsp::Range {
11539                start: lsp::Position {
11540                    line: 0,
11541                    character: 22,
11542                },
11543                end: lsp::Position {
11544                    line: 0,
11545                    character: 22,
11546                },
11547            },
11548            new_text: "Some(2)".to_string(),
11549        })),
11550        additional_text_edits: Some(vec![lsp::TextEdit {
11551            range: lsp::Range {
11552                start: lsp::Position {
11553                    line: 0,
11554                    character: 20,
11555                },
11556                end: lsp::Position {
11557                    line: 0,
11558                    character: 22,
11559                },
11560            },
11561            new_text: "".to_string(),
11562        }]),
11563        ..Default::default()
11564    };
11565
11566    let closure_completion_item = completion_item.clone();
11567    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11568        let task_completion_item = closure_completion_item.clone();
11569        async move {
11570            Ok(Some(lsp::CompletionResponse::Array(vec![
11571                task_completion_item,
11572            ])))
11573        }
11574    });
11575
11576    request.next().await;
11577
11578    cx.condition(|editor, _| editor.context_menu_visible())
11579        .await;
11580    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11581        editor
11582            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11583            .unwrap()
11584    });
11585    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11586
11587    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11588        let task_completion_item = completion_item.clone();
11589        async move { Ok(task_completion_item) }
11590    })
11591    .next()
11592    .await
11593    .unwrap();
11594    apply_additional_edits.await.unwrap();
11595    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11596}
11597
11598#[gpui::test]
11599async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
11600    init_test(cx, |_| {});
11601
11602    let mut cx = EditorLspTestContext::new_rust(
11603        lsp::ServerCapabilities {
11604            completion_provider: Some(lsp::CompletionOptions {
11605                trigger_characters: Some(vec![".".to_string()]),
11606                resolve_provider: Some(true),
11607                ..Default::default()
11608            }),
11609            ..Default::default()
11610        },
11611        cx,
11612    )
11613    .await;
11614
11615    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11616    cx.simulate_keystroke(".");
11617
11618    let item1 = lsp::CompletionItem {
11619        label: "method id()".to_string(),
11620        filter_text: Some("id".to_string()),
11621        detail: None,
11622        documentation: None,
11623        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11624            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11625            new_text: ".id".to_string(),
11626        })),
11627        ..lsp::CompletionItem::default()
11628    };
11629
11630    let item2 = lsp::CompletionItem {
11631        label: "other".to_string(),
11632        filter_text: Some("other".to_string()),
11633        detail: None,
11634        documentation: None,
11635        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11636            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11637            new_text: ".other".to_string(),
11638        })),
11639        ..lsp::CompletionItem::default()
11640    };
11641
11642    let item1 = item1.clone();
11643    cx.handle_request::<lsp::request::Completion, _, _>({
11644        let item1 = item1.clone();
11645        move |_, _, _| {
11646            let item1 = item1.clone();
11647            let item2 = item2.clone();
11648            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11649        }
11650    })
11651    .next()
11652    .await;
11653
11654    cx.condition(|editor, _| editor.context_menu_visible())
11655        .await;
11656    cx.update_editor(|editor, _, _| {
11657        let context_menu = editor.context_menu.borrow_mut();
11658        let context_menu = context_menu
11659            .as_ref()
11660            .expect("Should have the context menu deployed");
11661        match context_menu {
11662            CodeContextMenu::Completions(completions_menu) => {
11663                let completions = completions_menu.completions.borrow_mut();
11664                assert_eq!(
11665                    completions
11666                        .iter()
11667                        .map(|completion| &completion.label.text)
11668                        .collect::<Vec<_>>(),
11669                    vec!["method id()", "other"]
11670                )
11671            }
11672            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11673        }
11674    });
11675
11676    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11677        let item1 = item1.clone();
11678        move |_, item_to_resolve, _| {
11679            let item1 = item1.clone();
11680            async move {
11681                if item1 == item_to_resolve {
11682                    Ok(lsp::CompletionItem {
11683                        label: "method id()".to_string(),
11684                        filter_text: Some("id".to_string()),
11685                        detail: Some("Now resolved!".to_string()),
11686                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11687                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11688                            range: lsp::Range::new(
11689                                lsp::Position::new(0, 22),
11690                                lsp::Position::new(0, 22),
11691                            ),
11692                            new_text: ".id".to_string(),
11693                        })),
11694                        ..lsp::CompletionItem::default()
11695                    })
11696                } else {
11697                    Ok(item_to_resolve)
11698                }
11699            }
11700        }
11701    })
11702    .next()
11703    .await
11704    .unwrap();
11705    cx.run_until_parked();
11706
11707    cx.update_editor(|editor, window, cx| {
11708        editor.context_menu_next(&Default::default(), window, cx);
11709    });
11710
11711    cx.update_editor(|editor, _, _| {
11712        let context_menu = editor.context_menu.borrow_mut();
11713        let context_menu = context_menu
11714            .as_ref()
11715            .expect("Should have the context menu deployed");
11716        match context_menu {
11717            CodeContextMenu::Completions(completions_menu) => {
11718                let completions = completions_menu.completions.borrow_mut();
11719                assert_eq!(
11720                    completions
11721                        .iter()
11722                        .map(|completion| &completion.label.text)
11723                        .collect::<Vec<_>>(),
11724                    vec!["method id() Now resolved!", "other"],
11725                    "Should update first completion label, but not second as the filter text did not match."
11726                );
11727            }
11728            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11729        }
11730    });
11731}
11732
11733#[gpui::test]
11734async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
11735    init_test(cx, |_| {});
11736
11737    let mut cx = EditorLspTestContext::new_rust(
11738        lsp::ServerCapabilities {
11739            completion_provider: Some(lsp::CompletionOptions {
11740                trigger_characters: Some(vec![".".to_string()]),
11741                resolve_provider: Some(true),
11742                ..Default::default()
11743            }),
11744            ..Default::default()
11745        },
11746        cx,
11747    )
11748    .await;
11749
11750    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11751    cx.simulate_keystroke(".");
11752
11753    let unresolved_item_1 = lsp::CompletionItem {
11754        label: "id".to_string(),
11755        filter_text: Some("id".to_string()),
11756        detail: None,
11757        documentation: None,
11758        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11759            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11760            new_text: ".id".to_string(),
11761        })),
11762        ..lsp::CompletionItem::default()
11763    };
11764    let resolved_item_1 = lsp::CompletionItem {
11765        additional_text_edits: Some(vec![lsp::TextEdit {
11766            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11767            new_text: "!!".to_string(),
11768        }]),
11769        ..unresolved_item_1.clone()
11770    };
11771    let unresolved_item_2 = lsp::CompletionItem {
11772        label: "other".to_string(),
11773        filter_text: Some("other".to_string()),
11774        detail: None,
11775        documentation: None,
11776        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11777            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11778            new_text: ".other".to_string(),
11779        })),
11780        ..lsp::CompletionItem::default()
11781    };
11782    let resolved_item_2 = lsp::CompletionItem {
11783        additional_text_edits: Some(vec![lsp::TextEdit {
11784            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11785            new_text: "??".to_string(),
11786        }]),
11787        ..unresolved_item_2.clone()
11788    };
11789
11790    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11791    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11792    cx.lsp
11793        .server
11794        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11795            let unresolved_item_1 = unresolved_item_1.clone();
11796            let resolved_item_1 = resolved_item_1.clone();
11797            let unresolved_item_2 = unresolved_item_2.clone();
11798            let resolved_item_2 = resolved_item_2.clone();
11799            let resolve_requests_1 = resolve_requests_1.clone();
11800            let resolve_requests_2 = resolve_requests_2.clone();
11801            move |unresolved_request, _| {
11802                let unresolved_item_1 = unresolved_item_1.clone();
11803                let resolved_item_1 = resolved_item_1.clone();
11804                let unresolved_item_2 = unresolved_item_2.clone();
11805                let resolved_item_2 = resolved_item_2.clone();
11806                let resolve_requests_1 = resolve_requests_1.clone();
11807                let resolve_requests_2 = resolve_requests_2.clone();
11808                async move {
11809                    if unresolved_request == unresolved_item_1 {
11810                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11811                        Ok(resolved_item_1.clone())
11812                    } else if unresolved_request == unresolved_item_2 {
11813                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11814                        Ok(resolved_item_2.clone())
11815                    } else {
11816                        panic!("Unexpected completion item {unresolved_request:?}")
11817                    }
11818                }
11819            }
11820        })
11821        .detach();
11822
11823    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11824        let unresolved_item_1 = unresolved_item_1.clone();
11825        let unresolved_item_2 = unresolved_item_2.clone();
11826        async move {
11827            Ok(Some(lsp::CompletionResponse::Array(vec![
11828                unresolved_item_1,
11829                unresolved_item_2,
11830            ])))
11831        }
11832    })
11833    .next()
11834    .await;
11835
11836    cx.condition(|editor, _| editor.context_menu_visible())
11837        .await;
11838    cx.update_editor(|editor, _, _| {
11839        let context_menu = editor.context_menu.borrow_mut();
11840        let context_menu = context_menu
11841            .as_ref()
11842            .expect("Should have the context menu deployed");
11843        match context_menu {
11844            CodeContextMenu::Completions(completions_menu) => {
11845                let completions = completions_menu.completions.borrow_mut();
11846                assert_eq!(
11847                    completions
11848                        .iter()
11849                        .map(|completion| &completion.label.text)
11850                        .collect::<Vec<_>>(),
11851                    vec!["id", "other"]
11852                )
11853            }
11854            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11855        }
11856    });
11857    cx.run_until_parked();
11858
11859    cx.update_editor(|editor, window, cx| {
11860        editor.context_menu_next(&ContextMenuNext, window, cx);
11861    });
11862    cx.run_until_parked();
11863    cx.update_editor(|editor, window, cx| {
11864        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11865    });
11866    cx.run_until_parked();
11867    cx.update_editor(|editor, window, cx| {
11868        editor.context_menu_next(&ContextMenuNext, window, cx);
11869    });
11870    cx.run_until_parked();
11871    cx.update_editor(|editor, window, cx| {
11872        editor
11873            .compose_completion(&ComposeCompletion::default(), window, cx)
11874            .expect("No task returned")
11875    })
11876    .await
11877    .expect("Completion failed");
11878    cx.run_until_parked();
11879
11880    cx.update_editor(|editor, _, cx| {
11881        assert_eq!(
11882            resolve_requests_1.load(atomic::Ordering::Acquire),
11883            1,
11884            "Should always resolve once despite multiple selections"
11885        );
11886        assert_eq!(
11887            resolve_requests_2.load(atomic::Ordering::Acquire),
11888            1,
11889            "Should always resolve once after multiple selections and applying the completion"
11890        );
11891        assert_eq!(
11892            editor.text(cx),
11893            "fn main() { let a = ??.other; }",
11894            "Should use resolved data when applying the completion"
11895        );
11896    });
11897}
11898
11899#[gpui::test]
11900async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
11901    init_test(cx, |_| {});
11902
11903    let item_0 = lsp::CompletionItem {
11904        label: "abs".into(),
11905        insert_text: Some("abs".into()),
11906        data: Some(json!({ "very": "special"})),
11907        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11908        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11909            lsp::InsertReplaceEdit {
11910                new_text: "abs".to_string(),
11911                insert: lsp::Range::default(),
11912                replace: lsp::Range::default(),
11913            },
11914        )),
11915        ..lsp::CompletionItem::default()
11916    };
11917    let items = iter::once(item_0.clone())
11918        .chain((11..51).map(|i| lsp::CompletionItem {
11919            label: format!("item_{}", i),
11920            insert_text: Some(format!("item_{}", i)),
11921            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11922            ..lsp::CompletionItem::default()
11923        }))
11924        .collect::<Vec<_>>();
11925
11926    let default_commit_characters = vec!["?".to_string()];
11927    let default_data = json!({ "default": "data"});
11928    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11929    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11930    let default_edit_range = lsp::Range {
11931        start: lsp::Position {
11932            line: 0,
11933            character: 5,
11934        },
11935        end: lsp::Position {
11936            line: 0,
11937            character: 5,
11938        },
11939    };
11940
11941    let item_0_out = lsp::CompletionItem {
11942        commit_characters: Some(default_commit_characters.clone()),
11943        insert_text_format: Some(default_insert_text_format),
11944        ..item_0
11945    };
11946    let items_out = iter::once(item_0_out)
11947        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11948            commit_characters: Some(default_commit_characters.clone()),
11949            data: Some(default_data.clone()),
11950            insert_text_mode: Some(default_insert_text_mode),
11951            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11952                range: default_edit_range,
11953                new_text: item.label.clone(),
11954            })),
11955            ..item.clone()
11956        }))
11957        .collect::<Vec<lsp::CompletionItem>>();
11958
11959    let mut cx = EditorLspTestContext::new_rust(
11960        lsp::ServerCapabilities {
11961            completion_provider: Some(lsp::CompletionOptions {
11962                trigger_characters: Some(vec![".".to_string()]),
11963                resolve_provider: Some(true),
11964                ..Default::default()
11965            }),
11966            ..Default::default()
11967        },
11968        cx,
11969    )
11970    .await;
11971
11972    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11973    cx.simulate_keystroke(".");
11974
11975    let completion_data = default_data.clone();
11976    let completion_characters = default_commit_characters.clone();
11977    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11978        let default_data = completion_data.clone();
11979        let default_commit_characters = completion_characters.clone();
11980        let items = items.clone();
11981        async move {
11982            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11983                items,
11984                item_defaults: Some(lsp::CompletionListItemDefaults {
11985                    data: Some(default_data.clone()),
11986                    commit_characters: Some(default_commit_characters.clone()),
11987                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11988                        default_edit_range,
11989                    )),
11990                    insert_text_format: Some(default_insert_text_format),
11991                    insert_text_mode: Some(default_insert_text_mode),
11992                }),
11993                ..lsp::CompletionList::default()
11994            })))
11995        }
11996    })
11997    .next()
11998    .await;
11999
12000    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12001    cx.lsp
12002        .server
12003        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12004            let closure_resolved_items = resolved_items.clone();
12005            move |item_to_resolve, _| {
12006                let closure_resolved_items = closure_resolved_items.clone();
12007                async move {
12008                    closure_resolved_items.lock().push(item_to_resolve.clone());
12009                    Ok(item_to_resolve)
12010                }
12011            }
12012        })
12013        .detach();
12014
12015    cx.condition(|editor, _| editor.context_menu_visible())
12016        .await;
12017    cx.run_until_parked();
12018    cx.update_editor(|editor, _, _| {
12019        let menu = editor.context_menu.borrow_mut();
12020        match menu.as_ref().expect("should have the completions menu") {
12021            CodeContextMenu::Completions(completions_menu) => {
12022                assert_eq!(
12023                    completions_menu
12024                        .entries
12025                        .borrow()
12026                        .iter()
12027                        .map(|mat| mat.string.clone())
12028                        .collect::<Vec<String>>(),
12029                    items_out
12030                        .iter()
12031                        .map(|completion| completion.label.clone())
12032                        .collect::<Vec<String>>()
12033                );
12034            }
12035            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12036        }
12037    });
12038    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12039    // with 4 from the end.
12040    assert_eq!(
12041        *resolved_items.lock(),
12042        [
12043            &items_out[0..16],
12044            &items_out[items_out.len() - 4..items_out.len()]
12045        ]
12046        .concat()
12047        .iter()
12048        .cloned()
12049        .collect::<Vec<lsp::CompletionItem>>()
12050    );
12051    resolved_items.lock().clear();
12052
12053    cx.update_editor(|editor, window, cx| {
12054        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12055    });
12056    cx.run_until_parked();
12057    // Completions that have already been resolved are skipped.
12058    assert_eq!(
12059        *resolved_items.lock(),
12060        items_out[items_out.len() - 16..items_out.len() - 4]
12061            .iter()
12062            .cloned()
12063            .collect::<Vec<lsp::CompletionItem>>()
12064    );
12065    resolved_items.lock().clear();
12066}
12067
12068#[gpui::test]
12069async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12070    init_test(cx, |_| {});
12071
12072    let mut cx = EditorLspTestContext::new(
12073        Language::new(
12074            LanguageConfig {
12075                matcher: LanguageMatcher {
12076                    path_suffixes: vec!["jsx".into()],
12077                    ..Default::default()
12078                },
12079                overrides: [(
12080                    "element".into(),
12081                    LanguageConfigOverride {
12082                        word_characters: Override::Set(['-'].into_iter().collect()),
12083                        ..Default::default()
12084                    },
12085                )]
12086                .into_iter()
12087                .collect(),
12088                ..Default::default()
12089            },
12090            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12091        )
12092        .with_override_query("(jsx_self_closing_element) @element")
12093        .unwrap(),
12094        lsp::ServerCapabilities {
12095            completion_provider: Some(lsp::CompletionOptions {
12096                trigger_characters: Some(vec![":".to_string()]),
12097                ..Default::default()
12098            }),
12099            ..Default::default()
12100        },
12101        cx,
12102    )
12103    .await;
12104
12105    cx.lsp
12106        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12107            Ok(Some(lsp::CompletionResponse::Array(vec![
12108                lsp::CompletionItem {
12109                    label: "bg-blue".into(),
12110                    ..Default::default()
12111                },
12112                lsp::CompletionItem {
12113                    label: "bg-red".into(),
12114                    ..Default::default()
12115                },
12116                lsp::CompletionItem {
12117                    label: "bg-yellow".into(),
12118                    ..Default::default()
12119                },
12120            ])))
12121        });
12122
12123    cx.set_state(r#"<p class="bgˇ" />"#);
12124
12125    // Trigger completion when typing a dash, because the dash is an extra
12126    // word character in the 'element' scope, which contains the cursor.
12127    cx.simulate_keystroke("-");
12128    cx.executor().run_until_parked();
12129    cx.update_editor(|editor, _, _| {
12130        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12131        {
12132            assert_eq!(
12133                completion_menu_entries(&menu),
12134                &["bg-red", "bg-blue", "bg-yellow"]
12135            );
12136        } else {
12137            panic!("expected completion menu to be open");
12138        }
12139    });
12140
12141    cx.simulate_keystroke("l");
12142    cx.executor().run_until_parked();
12143    cx.update_editor(|editor, _, _| {
12144        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12145        {
12146            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12147        } else {
12148            panic!("expected completion menu to be open");
12149        }
12150    });
12151
12152    // When filtering completions, consider the character after the '-' to
12153    // be the start of a subword.
12154    cx.set_state(r#"<p class="yelˇ" />"#);
12155    cx.simulate_keystroke("l");
12156    cx.executor().run_until_parked();
12157    cx.update_editor(|editor, _, _| {
12158        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12159        {
12160            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12161        } else {
12162            panic!("expected completion menu to be open");
12163        }
12164    });
12165}
12166
12167fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12168    let entries = menu.entries.borrow();
12169    entries.iter().map(|mat| mat.string.clone()).collect()
12170}
12171
12172#[gpui::test]
12173async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12174    init_test(cx, |settings| {
12175        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12176            FormatterList(vec![Formatter::Prettier].into()),
12177        ))
12178    });
12179
12180    let fs = FakeFs::new(cx.executor());
12181    fs.insert_file(path!("/file.ts"), Default::default()).await;
12182
12183    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12184    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12185
12186    language_registry.add(Arc::new(Language::new(
12187        LanguageConfig {
12188            name: "TypeScript".into(),
12189            matcher: LanguageMatcher {
12190                path_suffixes: vec!["ts".to_string()],
12191                ..Default::default()
12192            },
12193            ..Default::default()
12194        },
12195        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12196    )));
12197    update_test_language_settings(cx, |settings| {
12198        settings.defaults.prettier = Some(PrettierSettings {
12199            allowed: true,
12200            ..PrettierSettings::default()
12201        });
12202    });
12203
12204    let test_plugin = "test_plugin";
12205    let _ = language_registry.register_fake_lsp(
12206        "TypeScript",
12207        FakeLspAdapter {
12208            prettier_plugins: vec![test_plugin],
12209            ..Default::default()
12210        },
12211    );
12212
12213    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12214    let buffer = project
12215        .update(cx, |project, cx| {
12216            project.open_local_buffer(path!("/file.ts"), cx)
12217        })
12218        .await
12219        .unwrap();
12220
12221    let buffer_text = "one\ntwo\nthree\n";
12222    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12223    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12224    editor.update_in(cx, |editor, window, cx| {
12225        editor.set_text(buffer_text, window, cx)
12226    });
12227
12228    editor
12229        .update_in(cx, |editor, window, cx| {
12230            editor.perform_format(
12231                project.clone(),
12232                FormatTrigger::Manual,
12233                FormatTarget::Buffers,
12234                window,
12235                cx,
12236            )
12237        })
12238        .unwrap()
12239        .await;
12240    assert_eq!(
12241        editor.update(cx, |editor, cx| editor.text(cx)),
12242        buffer_text.to_string() + prettier_format_suffix,
12243        "Test prettier formatting was not applied to the original buffer text",
12244    );
12245
12246    update_test_language_settings(cx, |settings| {
12247        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12248    });
12249    let format = editor.update_in(cx, |editor, window, cx| {
12250        editor.perform_format(
12251            project.clone(),
12252            FormatTrigger::Manual,
12253            FormatTarget::Buffers,
12254            window,
12255            cx,
12256        )
12257    });
12258    format.await.unwrap();
12259    assert_eq!(
12260        editor.update(cx, |editor, cx| editor.text(cx)),
12261        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12262        "Autoformatting (via test prettier) was not applied to the original buffer text",
12263    );
12264}
12265
12266#[gpui::test]
12267async fn test_addition_reverts(cx: &mut TestAppContext) {
12268    init_test(cx, |_| {});
12269    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12270    let base_text = indoc! {r#"
12271        struct Row;
12272        struct Row1;
12273        struct Row2;
12274
12275        struct Row4;
12276        struct Row5;
12277        struct Row6;
12278
12279        struct Row8;
12280        struct Row9;
12281        struct Row10;"#};
12282
12283    // When addition hunks are not adjacent to carets, no hunk revert is performed
12284    assert_hunk_revert(
12285        indoc! {r#"struct Row;
12286                   struct Row1;
12287                   struct Row1.1;
12288                   struct Row1.2;
12289                   struct Row2;ˇ
12290
12291                   struct Row4;
12292                   struct Row5;
12293                   struct Row6;
12294
12295                   struct Row8;
12296                   ˇstruct Row9;
12297                   struct Row9.1;
12298                   struct Row9.2;
12299                   struct Row9.3;
12300                   struct Row10;"#},
12301        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12302        indoc! {r#"struct Row;
12303                   struct Row1;
12304                   struct Row1.1;
12305                   struct Row1.2;
12306                   struct Row2;ˇ
12307
12308                   struct Row4;
12309                   struct Row5;
12310                   struct Row6;
12311
12312                   struct Row8;
12313                   ˇstruct Row9;
12314                   struct Row9.1;
12315                   struct Row9.2;
12316                   struct Row9.3;
12317                   struct Row10;"#},
12318        base_text,
12319        &mut cx,
12320    );
12321    // Same for selections
12322    assert_hunk_revert(
12323        indoc! {r#"struct Row;
12324                   struct Row1;
12325                   struct Row2;
12326                   struct Row2.1;
12327                   struct Row2.2;
12328                   «ˇ
12329                   struct Row4;
12330                   struct» Row5;
12331                   «struct Row6;
12332                   ˇ»
12333                   struct Row9.1;
12334                   struct Row9.2;
12335                   struct Row9.3;
12336                   struct Row8;
12337                   struct Row9;
12338                   struct Row10;"#},
12339        vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
12340        indoc! {r#"struct Row;
12341                   struct Row1;
12342                   struct Row2;
12343                   struct Row2.1;
12344                   struct Row2.2;
12345                   «ˇ
12346                   struct Row4;
12347                   struct» Row5;
12348                   «struct Row6;
12349                   ˇ»
12350                   struct Row9.1;
12351                   struct Row9.2;
12352                   struct Row9.3;
12353                   struct Row8;
12354                   struct Row9;
12355                   struct Row10;"#},
12356        base_text,
12357        &mut cx,
12358    );
12359
12360    // When carets and selections intersect the addition hunks, those are reverted.
12361    // Adjacent carets got merged.
12362    assert_hunk_revert(
12363        indoc! {r#"struct Row;
12364                   ˇ// something on the top
12365                   struct Row1;
12366                   struct Row2;
12367                   struct Roˇw3.1;
12368                   struct Row2.2;
12369                   struct Row2.3;ˇ
12370
12371                   struct Row4;
12372                   struct ˇRow5.1;
12373                   struct Row5.2;
12374                   struct «Rowˇ»5.3;
12375                   struct Row5;
12376                   struct Row6;
12377                   ˇ
12378                   struct Row9.1;
12379                   struct «Rowˇ»9.2;
12380                   struct «ˇRow»9.3;
12381                   struct Row8;
12382                   struct Row9;
12383                   «ˇ// something on bottom»
12384                   struct Row10;"#},
12385        vec![
12386            DiffHunkStatus::added_none(),
12387            DiffHunkStatus::added_none(),
12388            DiffHunkStatus::added_none(),
12389            DiffHunkStatus::added_none(),
12390            DiffHunkStatus::added_none(),
12391        ],
12392        indoc! {r#"struct Row;
12393                   ˇstruct Row1;
12394                   struct Row2;
12395                   ˇ
12396                   struct Row4;
12397                   ˇstruct Row5;
12398                   struct Row6;
12399                   ˇ
12400                   ˇstruct Row8;
12401                   struct Row9;
12402                   ˇstruct Row10;"#},
12403        base_text,
12404        &mut cx,
12405    );
12406}
12407
12408#[gpui::test]
12409async fn test_modification_reverts(cx: &mut TestAppContext) {
12410    init_test(cx, |_| {});
12411    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12412    let base_text = indoc! {r#"
12413        struct Row;
12414        struct Row1;
12415        struct Row2;
12416
12417        struct Row4;
12418        struct Row5;
12419        struct Row6;
12420
12421        struct Row8;
12422        struct Row9;
12423        struct Row10;"#};
12424
12425    // Modification hunks behave the same as the addition ones.
12426    assert_hunk_revert(
12427        indoc! {r#"struct Row;
12428                   struct Row1;
12429                   struct Row33;
12430                   ˇ
12431                   struct Row4;
12432                   struct Row5;
12433                   struct Row6;
12434                   ˇ
12435                   struct Row99;
12436                   struct Row9;
12437                   struct Row10;"#},
12438        vec![
12439            DiffHunkStatus::modified_none(),
12440            DiffHunkStatus::modified_none(),
12441        ],
12442        indoc! {r#"struct Row;
12443                   struct Row1;
12444                   struct Row33;
12445                   ˇ
12446                   struct Row4;
12447                   struct Row5;
12448                   struct Row6;
12449                   ˇ
12450                   struct Row99;
12451                   struct Row9;
12452                   struct Row10;"#},
12453        base_text,
12454        &mut cx,
12455    );
12456    assert_hunk_revert(
12457        indoc! {r#"struct Row;
12458                   struct Row1;
12459                   struct Row33;
12460                   «ˇ
12461                   struct Row4;
12462                   struct» Row5;
12463                   «struct Row6;
12464                   ˇ»
12465                   struct Row99;
12466                   struct Row9;
12467                   struct Row10;"#},
12468        vec![
12469            DiffHunkStatus::modified_none(),
12470            DiffHunkStatus::modified_none(),
12471        ],
12472        indoc! {r#"struct Row;
12473                   struct Row1;
12474                   struct Row33;
12475                   «ˇ
12476                   struct Row4;
12477                   struct» Row5;
12478                   «struct Row6;
12479                   ˇ»
12480                   struct Row99;
12481                   struct Row9;
12482                   struct Row10;"#},
12483        base_text,
12484        &mut cx,
12485    );
12486
12487    assert_hunk_revert(
12488        indoc! {r#"ˇstruct Row1.1;
12489                   struct Row1;
12490                   «ˇstr»uct Row22;
12491
12492                   struct ˇRow44;
12493                   struct Row5;
12494                   struct «Rˇ»ow66;ˇ
12495
12496                   «struˇ»ct Row88;
12497                   struct Row9;
12498                   struct Row1011;ˇ"#},
12499        vec![
12500            DiffHunkStatus::modified_none(),
12501            DiffHunkStatus::modified_none(),
12502            DiffHunkStatus::modified_none(),
12503            DiffHunkStatus::modified_none(),
12504            DiffHunkStatus::modified_none(),
12505            DiffHunkStatus::modified_none(),
12506        ],
12507        indoc! {r#"struct Row;
12508                   ˇstruct Row1;
12509                   struct Row2;
12510                   ˇ
12511                   struct Row4;
12512                   ˇstruct Row5;
12513                   struct Row6;
12514                   ˇ
12515                   struct Row8;
12516                   ˇstruct Row9;
12517                   struct Row10;ˇ"#},
12518        base_text,
12519        &mut cx,
12520    );
12521}
12522
12523#[gpui::test]
12524async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
12525    init_test(cx, |_| {});
12526    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12527    let base_text = indoc! {r#"
12528        one
12529
12530        two
12531        three
12532        "#};
12533
12534    cx.set_diff_base(base_text);
12535    cx.set_state("\nˇ\n");
12536    cx.executor().run_until_parked();
12537    cx.update_editor(|editor, _window, cx| {
12538        editor.expand_selected_diff_hunks(cx);
12539    });
12540    cx.executor().run_until_parked();
12541    cx.update_editor(|editor, window, cx| {
12542        editor.backspace(&Default::default(), window, cx);
12543    });
12544    cx.run_until_parked();
12545    cx.assert_state_with_diff(
12546        indoc! {r#"
12547
12548        - two
12549        - threeˇ
12550        +
12551        "#}
12552        .to_string(),
12553    );
12554}
12555
12556#[gpui::test]
12557async fn test_deletion_reverts(cx: &mut TestAppContext) {
12558    init_test(cx, |_| {});
12559    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12560    let base_text = indoc! {r#"struct Row;
12561struct Row1;
12562struct Row2;
12563
12564struct Row4;
12565struct Row5;
12566struct Row6;
12567
12568struct Row8;
12569struct Row9;
12570struct Row10;"#};
12571
12572    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12573    assert_hunk_revert(
12574        indoc! {r#"struct Row;
12575                   struct Row2;
12576
12577                   ˇstruct Row4;
12578                   struct Row5;
12579                   struct Row6;
12580                   ˇ
12581                   struct Row8;
12582                   struct Row10;"#},
12583        vec![
12584            DiffHunkStatus::deleted_none(),
12585            DiffHunkStatus::deleted_none(),
12586        ],
12587        indoc! {r#"struct Row;
12588                   struct Row2;
12589
12590                   ˇstruct Row4;
12591                   struct Row5;
12592                   struct Row6;
12593                   ˇ
12594                   struct Row8;
12595                   struct Row10;"#},
12596        base_text,
12597        &mut cx,
12598    );
12599    assert_hunk_revert(
12600        indoc! {r#"struct Row;
12601                   struct Row2;
12602
12603                   «ˇstruct Row4;
12604                   struct» Row5;
12605                   «struct Row6;
12606                   ˇ»
12607                   struct Row8;
12608                   struct Row10;"#},
12609        vec![
12610            DiffHunkStatus::deleted_none(),
12611            DiffHunkStatus::deleted_none(),
12612        ],
12613        indoc! {r#"struct Row;
12614                   struct Row2;
12615
12616                   «ˇstruct Row4;
12617                   struct» Row5;
12618                   «struct Row6;
12619                   ˇ»
12620                   struct Row8;
12621                   struct Row10;"#},
12622        base_text,
12623        &mut cx,
12624    );
12625
12626    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12627    assert_hunk_revert(
12628        indoc! {r#"struct Row;
12629                   ˇstruct Row2;
12630
12631                   struct Row4;
12632                   struct Row5;
12633                   struct Row6;
12634
12635                   struct Row8;ˇ
12636                   struct Row10;"#},
12637        vec![
12638            DiffHunkStatus::deleted_none(),
12639            DiffHunkStatus::deleted_none(),
12640        ],
12641        indoc! {r#"struct Row;
12642                   struct Row1;
12643                   ˇstruct Row2;
12644
12645                   struct Row4;
12646                   struct Row5;
12647                   struct Row6;
12648
12649                   struct Row8;ˇ
12650                   struct Row9;
12651                   struct Row10;"#},
12652        base_text,
12653        &mut cx,
12654    );
12655    assert_hunk_revert(
12656        indoc! {r#"struct Row;
12657                   struct Row2«ˇ;
12658                   struct Row4;
12659                   struct» Row5;
12660                   «struct Row6;
12661
12662                   struct Row8;ˇ»
12663                   struct Row10;"#},
12664        vec![
12665            DiffHunkStatus::deleted_none(),
12666            DiffHunkStatus::deleted_none(),
12667            DiffHunkStatus::deleted_none(),
12668        ],
12669        indoc! {r#"struct Row;
12670                   struct Row1;
12671                   struct Row2«ˇ;
12672
12673                   struct Row4;
12674                   struct» Row5;
12675                   «struct Row6;
12676
12677                   struct Row8;ˇ»
12678                   struct Row9;
12679                   struct Row10;"#},
12680        base_text,
12681        &mut cx,
12682    );
12683}
12684
12685#[gpui::test]
12686async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
12687    init_test(cx, |_| {});
12688
12689    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12690    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12691    let base_text_3 =
12692        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12693
12694    let text_1 = edit_first_char_of_every_line(base_text_1);
12695    let text_2 = edit_first_char_of_every_line(base_text_2);
12696    let text_3 = edit_first_char_of_every_line(base_text_3);
12697
12698    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12699    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12700    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12701
12702    let multibuffer = cx.new(|cx| {
12703        let mut multibuffer = MultiBuffer::new(ReadWrite);
12704        multibuffer.push_excerpts(
12705            buffer_1.clone(),
12706            [
12707                ExcerptRange {
12708                    context: Point::new(0, 0)..Point::new(3, 0),
12709                    primary: None,
12710                },
12711                ExcerptRange {
12712                    context: Point::new(5, 0)..Point::new(7, 0),
12713                    primary: None,
12714                },
12715                ExcerptRange {
12716                    context: Point::new(9, 0)..Point::new(10, 4),
12717                    primary: None,
12718                },
12719            ],
12720            cx,
12721        );
12722        multibuffer.push_excerpts(
12723            buffer_2.clone(),
12724            [
12725                ExcerptRange {
12726                    context: Point::new(0, 0)..Point::new(3, 0),
12727                    primary: None,
12728                },
12729                ExcerptRange {
12730                    context: Point::new(5, 0)..Point::new(7, 0),
12731                    primary: None,
12732                },
12733                ExcerptRange {
12734                    context: Point::new(9, 0)..Point::new(10, 4),
12735                    primary: None,
12736                },
12737            ],
12738            cx,
12739        );
12740        multibuffer.push_excerpts(
12741            buffer_3.clone(),
12742            [
12743                ExcerptRange {
12744                    context: Point::new(0, 0)..Point::new(3, 0),
12745                    primary: None,
12746                },
12747                ExcerptRange {
12748                    context: Point::new(5, 0)..Point::new(7, 0),
12749                    primary: None,
12750                },
12751                ExcerptRange {
12752                    context: Point::new(9, 0)..Point::new(10, 4),
12753                    primary: None,
12754                },
12755            ],
12756            cx,
12757        );
12758        multibuffer
12759    });
12760
12761    let fs = FakeFs::new(cx.executor());
12762    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
12763    let (editor, cx) = cx
12764        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
12765    editor.update_in(cx, |editor, _window, cx| {
12766        for (buffer, diff_base) in [
12767            (buffer_1.clone(), base_text_1),
12768            (buffer_2.clone(), base_text_2),
12769            (buffer_3.clone(), base_text_3),
12770        ] {
12771            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12772            editor
12773                .buffer
12774                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12775        }
12776    });
12777    cx.executor().run_until_parked();
12778
12779    editor.update_in(cx, |editor, window, cx| {
12780        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}");
12781        editor.select_all(&SelectAll, window, cx);
12782        editor.git_restore(&Default::default(), window, cx);
12783    });
12784    cx.executor().run_until_parked();
12785
12786    // When all ranges are selected, all buffer hunks are reverted.
12787    editor.update(cx, |editor, cx| {
12788        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");
12789    });
12790    buffer_1.update(cx, |buffer, _| {
12791        assert_eq!(buffer.text(), base_text_1);
12792    });
12793    buffer_2.update(cx, |buffer, _| {
12794        assert_eq!(buffer.text(), base_text_2);
12795    });
12796    buffer_3.update(cx, |buffer, _| {
12797        assert_eq!(buffer.text(), base_text_3);
12798    });
12799
12800    editor.update_in(cx, |editor, window, cx| {
12801        editor.undo(&Default::default(), window, cx);
12802    });
12803
12804    editor.update_in(cx, |editor, window, cx| {
12805        editor.change_selections(None, window, cx, |s| {
12806            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12807        });
12808        editor.git_restore(&Default::default(), window, cx);
12809    });
12810
12811    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12812    // but not affect buffer_2 and its related excerpts.
12813    editor.update(cx, |editor, cx| {
12814        assert_eq!(
12815            editor.text(cx),
12816            "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}"
12817        );
12818    });
12819    buffer_1.update(cx, |buffer, _| {
12820        assert_eq!(buffer.text(), base_text_1);
12821    });
12822    buffer_2.update(cx, |buffer, _| {
12823        assert_eq!(
12824            buffer.text(),
12825            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12826        );
12827    });
12828    buffer_3.update(cx, |buffer, _| {
12829        assert_eq!(
12830            buffer.text(),
12831            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12832        );
12833    });
12834
12835    fn edit_first_char_of_every_line(text: &str) -> String {
12836        text.split('\n')
12837            .map(|line| format!("X{}", &line[1..]))
12838            .collect::<Vec<_>>()
12839            .join("\n")
12840    }
12841}
12842
12843#[gpui::test]
12844async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
12845    init_test(cx, |_| {});
12846
12847    let cols = 4;
12848    let rows = 10;
12849    let sample_text_1 = sample_text(rows, cols, 'a');
12850    assert_eq!(
12851        sample_text_1,
12852        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12853    );
12854    let sample_text_2 = sample_text(rows, cols, 'l');
12855    assert_eq!(
12856        sample_text_2,
12857        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12858    );
12859    let sample_text_3 = sample_text(rows, cols, 'v');
12860    assert_eq!(
12861        sample_text_3,
12862        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12863    );
12864
12865    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12866    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12867    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12868
12869    let multi_buffer = cx.new(|cx| {
12870        let mut multibuffer = MultiBuffer::new(ReadWrite);
12871        multibuffer.push_excerpts(
12872            buffer_1.clone(),
12873            [
12874                ExcerptRange {
12875                    context: Point::new(0, 0)..Point::new(3, 0),
12876                    primary: None,
12877                },
12878                ExcerptRange {
12879                    context: Point::new(5, 0)..Point::new(7, 0),
12880                    primary: None,
12881                },
12882                ExcerptRange {
12883                    context: Point::new(9, 0)..Point::new(10, 4),
12884                    primary: None,
12885                },
12886            ],
12887            cx,
12888        );
12889        multibuffer.push_excerpts(
12890            buffer_2.clone(),
12891            [
12892                ExcerptRange {
12893                    context: Point::new(0, 0)..Point::new(3, 0),
12894                    primary: None,
12895                },
12896                ExcerptRange {
12897                    context: Point::new(5, 0)..Point::new(7, 0),
12898                    primary: None,
12899                },
12900                ExcerptRange {
12901                    context: Point::new(9, 0)..Point::new(10, 4),
12902                    primary: None,
12903                },
12904            ],
12905            cx,
12906        );
12907        multibuffer.push_excerpts(
12908            buffer_3.clone(),
12909            [
12910                ExcerptRange {
12911                    context: Point::new(0, 0)..Point::new(3, 0),
12912                    primary: None,
12913                },
12914                ExcerptRange {
12915                    context: Point::new(5, 0)..Point::new(7, 0),
12916                    primary: None,
12917                },
12918                ExcerptRange {
12919                    context: Point::new(9, 0)..Point::new(10, 4),
12920                    primary: None,
12921                },
12922            ],
12923            cx,
12924        );
12925        multibuffer
12926    });
12927
12928    let fs = FakeFs::new(cx.executor());
12929    fs.insert_tree(
12930        "/a",
12931        json!({
12932            "main.rs": sample_text_1,
12933            "other.rs": sample_text_2,
12934            "lib.rs": sample_text_3,
12935        }),
12936    )
12937    .await;
12938    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12939    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12940    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12941    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12942        Editor::new(
12943            EditorMode::Full,
12944            multi_buffer,
12945            Some(project.clone()),
12946            true,
12947            window,
12948            cx,
12949        )
12950    });
12951    let multibuffer_item_id = workspace
12952        .update(cx, |workspace, window, cx| {
12953            assert!(
12954                workspace.active_item(cx).is_none(),
12955                "active item should be None before the first item is added"
12956            );
12957            workspace.add_item_to_active_pane(
12958                Box::new(multi_buffer_editor.clone()),
12959                None,
12960                true,
12961                window,
12962                cx,
12963            );
12964            let active_item = workspace
12965                .active_item(cx)
12966                .expect("should have an active item after adding the multi buffer");
12967            assert!(
12968                !active_item.is_singleton(cx),
12969                "A multi buffer was expected to active after adding"
12970            );
12971            active_item.item_id()
12972        })
12973        .unwrap();
12974    cx.executor().run_until_parked();
12975
12976    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12977        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12978            s.select_ranges(Some(1..2))
12979        });
12980        editor.open_excerpts(&OpenExcerpts, window, cx);
12981    });
12982    cx.executor().run_until_parked();
12983    let first_item_id = workspace
12984        .update(cx, |workspace, window, cx| {
12985            let active_item = workspace
12986                .active_item(cx)
12987                .expect("should have an active item after navigating into the 1st buffer");
12988            let first_item_id = active_item.item_id();
12989            assert_ne!(
12990                first_item_id, multibuffer_item_id,
12991                "Should navigate into the 1st buffer and activate it"
12992            );
12993            assert!(
12994                active_item.is_singleton(cx),
12995                "New active item should be a singleton buffer"
12996            );
12997            assert_eq!(
12998                active_item
12999                    .act_as::<Editor>(cx)
13000                    .expect("should have navigated into an editor for the 1st buffer")
13001                    .read(cx)
13002                    .text(cx),
13003                sample_text_1
13004            );
13005
13006            workspace
13007                .go_back(workspace.active_pane().downgrade(), window, cx)
13008                .detach_and_log_err(cx);
13009
13010            first_item_id
13011        })
13012        .unwrap();
13013    cx.executor().run_until_parked();
13014    workspace
13015        .update(cx, |workspace, _, cx| {
13016            let active_item = workspace
13017                .active_item(cx)
13018                .expect("should have an active item after navigating back");
13019            assert_eq!(
13020                active_item.item_id(),
13021                multibuffer_item_id,
13022                "Should navigate back to the multi buffer"
13023            );
13024            assert!(!active_item.is_singleton(cx));
13025        })
13026        .unwrap();
13027
13028    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13029        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13030            s.select_ranges(Some(39..40))
13031        });
13032        editor.open_excerpts(&OpenExcerpts, window, cx);
13033    });
13034    cx.executor().run_until_parked();
13035    let second_item_id = workspace
13036        .update(cx, |workspace, window, cx| {
13037            let active_item = workspace
13038                .active_item(cx)
13039                .expect("should have an active item after navigating into the 2nd buffer");
13040            let second_item_id = active_item.item_id();
13041            assert_ne!(
13042                second_item_id, multibuffer_item_id,
13043                "Should navigate away from the multibuffer"
13044            );
13045            assert_ne!(
13046                second_item_id, first_item_id,
13047                "Should navigate into the 2nd buffer and activate it"
13048            );
13049            assert!(
13050                active_item.is_singleton(cx),
13051                "New active item should be a singleton buffer"
13052            );
13053            assert_eq!(
13054                active_item
13055                    .act_as::<Editor>(cx)
13056                    .expect("should have navigated into an editor")
13057                    .read(cx)
13058                    .text(cx),
13059                sample_text_2
13060            );
13061
13062            workspace
13063                .go_back(workspace.active_pane().downgrade(), window, cx)
13064                .detach_and_log_err(cx);
13065
13066            second_item_id
13067        })
13068        .unwrap();
13069    cx.executor().run_until_parked();
13070    workspace
13071        .update(cx, |workspace, _, cx| {
13072            let active_item = workspace
13073                .active_item(cx)
13074                .expect("should have an active item after navigating back from the 2nd buffer");
13075            assert_eq!(
13076                active_item.item_id(),
13077                multibuffer_item_id,
13078                "Should navigate back from the 2nd buffer to the multi buffer"
13079            );
13080            assert!(!active_item.is_singleton(cx));
13081        })
13082        .unwrap();
13083
13084    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13085        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13086            s.select_ranges(Some(70..70))
13087        });
13088        editor.open_excerpts(&OpenExcerpts, window, cx);
13089    });
13090    cx.executor().run_until_parked();
13091    workspace
13092        .update(cx, |workspace, window, cx| {
13093            let active_item = workspace
13094                .active_item(cx)
13095                .expect("should have an active item after navigating into the 3rd buffer");
13096            let third_item_id = active_item.item_id();
13097            assert_ne!(
13098                third_item_id, multibuffer_item_id,
13099                "Should navigate into the 3rd buffer and activate it"
13100            );
13101            assert_ne!(third_item_id, first_item_id);
13102            assert_ne!(third_item_id, second_item_id);
13103            assert!(
13104                active_item.is_singleton(cx),
13105                "New active item should be a singleton buffer"
13106            );
13107            assert_eq!(
13108                active_item
13109                    .act_as::<Editor>(cx)
13110                    .expect("should have navigated into an editor")
13111                    .read(cx)
13112                    .text(cx),
13113                sample_text_3
13114            );
13115
13116            workspace
13117                .go_back(workspace.active_pane().downgrade(), window, cx)
13118                .detach_and_log_err(cx);
13119        })
13120        .unwrap();
13121    cx.executor().run_until_parked();
13122    workspace
13123        .update(cx, |workspace, _, cx| {
13124            let active_item = workspace
13125                .active_item(cx)
13126                .expect("should have an active item after navigating back from the 3rd buffer");
13127            assert_eq!(
13128                active_item.item_id(),
13129                multibuffer_item_id,
13130                "Should navigate back from the 3rd buffer to the multi buffer"
13131            );
13132            assert!(!active_item.is_singleton(cx));
13133        })
13134        .unwrap();
13135}
13136
13137#[gpui::test]
13138async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13139    init_test(cx, |_| {});
13140
13141    let mut cx = EditorTestContext::new(cx).await;
13142
13143    let diff_base = r#"
13144        use some::mod;
13145
13146        const A: u32 = 42;
13147
13148        fn main() {
13149            println!("hello");
13150
13151            println!("world");
13152        }
13153        "#
13154    .unindent();
13155
13156    cx.set_state(
13157        &r#"
13158        use some::modified;
13159
13160        ˇ
13161        fn main() {
13162            println!("hello there");
13163
13164            println!("around the");
13165            println!("world");
13166        }
13167        "#
13168        .unindent(),
13169    );
13170
13171    cx.set_diff_base(&diff_base);
13172    executor.run_until_parked();
13173
13174    cx.update_editor(|editor, window, cx| {
13175        editor.go_to_next_hunk(&GoToHunk, window, cx);
13176        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13177    });
13178    executor.run_until_parked();
13179    cx.assert_state_with_diff(
13180        r#"
13181          use some::modified;
13182
13183
13184          fn main() {
13185        -     println!("hello");
13186        + ˇ    println!("hello there");
13187
13188              println!("around the");
13189              println!("world");
13190          }
13191        "#
13192        .unindent(),
13193    );
13194
13195    cx.update_editor(|editor, window, cx| {
13196        for _ in 0..2 {
13197            editor.go_to_next_hunk(&GoToHunk, window, cx);
13198            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13199        }
13200    });
13201    executor.run_until_parked();
13202    cx.assert_state_with_diff(
13203        r#"
13204        - use some::mod;
13205        + ˇuse some::modified;
13206
13207
13208          fn main() {
13209        -     println!("hello");
13210        +     println!("hello there");
13211
13212        +     println!("around the");
13213              println!("world");
13214          }
13215        "#
13216        .unindent(),
13217    );
13218
13219    cx.update_editor(|editor, window, cx| {
13220        editor.go_to_next_hunk(&GoToHunk, window, cx);
13221        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13222    });
13223    executor.run_until_parked();
13224    cx.assert_state_with_diff(
13225        r#"
13226        - use some::mod;
13227        + use some::modified;
13228
13229        - const A: u32 = 42;
13230          ˇ
13231          fn main() {
13232        -     println!("hello");
13233        +     println!("hello there");
13234
13235        +     println!("around the");
13236              println!("world");
13237          }
13238        "#
13239        .unindent(),
13240    );
13241
13242    cx.update_editor(|editor, window, cx| {
13243        editor.cancel(&Cancel, window, cx);
13244    });
13245
13246    cx.assert_state_with_diff(
13247        r#"
13248          use some::modified;
13249
13250          ˇ
13251          fn main() {
13252              println!("hello there");
13253
13254              println!("around the");
13255              println!("world");
13256          }
13257        "#
13258        .unindent(),
13259    );
13260}
13261
13262#[gpui::test]
13263async fn test_diff_base_change_with_expanded_diff_hunks(
13264    executor: BackgroundExecutor,
13265    cx: &mut TestAppContext,
13266) {
13267    init_test(cx, |_| {});
13268
13269    let mut cx = EditorTestContext::new(cx).await;
13270
13271    let diff_base = r#"
13272        use some::mod1;
13273        use some::mod2;
13274
13275        const A: u32 = 42;
13276        const B: u32 = 42;
13277        const C: u32 = 42;
13278
13279        fn main() {
13280            println!("hello");
13281
13282            println!("world");
13283        }
13284        "#
13285    .unindent();
13286
13287    cx.set_state(
13288        &r#"
13289        use some::mod2;
13290
13291        const A: u32 = 42;
13292        const C: u32 = 42;
13293
13294        fn main(ˇ) {
13295            //println!("hello");
13296
13297            println!("world");
13298            //
13299            //
13300        }
13301        "#
13302        .unindent(),
13303    );
13304
13305    cx.set_diff_base(&diff_base);
13306    executor.run_until_parked();
13307
13308    cx.update_editor(|editor, window, cx| {
13309        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13310    });
13311    executor.run_until_parked();
13312    cx.assert_state_with_diff(
13313        r#"
13314        - use some::mod1;
13315          use some::mod2;
13316
13317          const A: u32 = 42;
13318        - const B: u32 = 42;
13319          const C: u32 = 42;
13320
13321          fn main(ˇ) {
13322        -     println!("hello");
13323        +     //println!("hello");
13324
13325              println!("world");
13326        +     //
13327        +     //
13328          }
13329        "#
13330        .unindent(),
13331    );
13332
13333    cx.set_diff_base("new diff base!");
13334    executor.run_until_parked();
13335    cx.assert_state_with_diff(
13336        r#"
13337        - new diff base!
13338        + use some::mod2;
13339        +
13340        + const A: u32 = 42;
13341        + const C: u32 = 42;
13342        +
13343        + fn main(ˇ) {
13344        +     //println!("hello");
13345        +
13346        +     println!("world");
13347        +     //
13348        +     //
13349        + }
13350        "#
13351        .unindent(),
13352    );
13353}
13354
13355#[gpui::test]
13356async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13357    init_test(cx, |_| {});
13358
13359    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13360    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13361    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13362    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13363    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13364    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13365
13366    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13367    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13368    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13369
13370    let multi_buffer = cx.new(|cx| {
13371        let mut multibuffer = MultiBuffer::new(ReadWrite);
13372        multibuffer.push_excerpts(
13373            buffer_1.clone(),
13374            [
13375                ExcerptRange {
13376                    context: Point::new(0, 0)..Point::new(3, 0),
13377                    primary: None,
13378                },
13379                ExcerptRange {
13380                    context: Point::new(5, 0)..Point::new(7, 0),
13381                    primary: None,
13382                },
13383                ExcerptRange {
13384                    context: Point::new(9, 0)..Point::new(10, 3),
13385                    primary: None,
13386                },
13387            ],
13388            cx,
13389        );
13390        multibuffer.push_excerpts(
13391            buffer_2.clone(),
13392            [
13393                ExcerptRange {
13394                    context: Point::new(0, 0)..Point::new(3, 0),
13395                    primary: None,
13396                },
13397                ExcerptRange {
13398                    context: Point::new(5, 0)..Point::new(7, 0),
13399                    primary: None,
13400                },
13401                ExcerptRange {
13402                    context: Point::new(9, 0)..Point::new(10, 3),
13403                    primary: None,
13404                },
13405            ],
13406            cx,
13407        );
13408        multibuffer.push_excerpts(
13409            buffer_3.clone(),
13410            [
13411                ExcerptRange {
13412                    context: Point::new(0, 0)..Point::new(3, 0),
13413                    primary: None,
13414                },
13415                ExcerptRange {
13416                    context: Point::new(5, 0)..Point::new(7, 0),
13417                    primary: None,
13418                },
13419                ExcerptRange {
13420                    context: Point::new(9, 0)..Point::new(10, 3),
13421                    primary: None,
13422                },
13423            ],
13424            cx,
13425        );
13426        multibuffer
13427    });
13428
13429    let editor = cx.add_window(|window, cx| {
13430        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13431    });
13432    editor
13433        .update(cx, |editor, _window, cx| {
13434            for (buffer, diff_base) in [
13435                (buffer_1.clone(), file_1_old),
13436                (buffer_2.clone(), file_2_old),
13437                (buffer_3.clone(), file_3_old),
13438            ] {
13439                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13440                editor
13441                    .buffer
13442                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13443            }
13444        })
13445        .unwrap();
13446
13447    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13448    cx.run_until_parked();
13449
13450    cx.assert_editor_state(
13451        &"
13452            ˇaaa
13453            ccc
13454            ddd
13455
13456            ggg
13457            hhh
13458
13459
13460            lll
13461            mmm
13462            NNN
13463
13464            qqq
13465            rrr
13466
13467            uuu
13468            111
13469            222
13470            333
13471
13472            666
13473            777
13474
13475            000
13476            !!!"
13477        .unindent(),
13478    );
13479
13480    cx.update_editor(|editor, window, cx| {
13481        editor.select_all(&SelectAll, window, cx);
13482        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13483    });
13484    cx.executor().run_until_parked();
13485
13486    cx.assert_state_with_diff(
13487        "
13488            «aaa
13489          - bbb
13490            ccc
13491            ddd
13492
13493            ggg
13494            hhh
13495
13496
13497            lll
13498            mmm
13499          - nnn
13500          + NNN
13501
13502            qqq
13503            rrr
13504
13505            uuu
13506            111
13507            222
13508            333
13509
13510          + 666
13511            777
13512
13513            000
13514            !!!ˇ»"
13515            .unindent(),
13516    );
13517}
13518
13519#[gpui::test]
13520async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
13521    init_test(cx, |_| {});
13522
13523    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13524    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13525
13526    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13527    let multi_buffer = cx.new(|cx| {
13528        let mut multibuffer = MultiBuffer::new(ReadWrite);
13529        multibuffer.push_excerpts(
13530            buffer.clone(),
13531            [
13532                ExcerptRange {
13533                    context: Point::new(0, 0)..Point::new(2, 0),
13534                    primary: None,
13535                },
13536                ExcerptRange {
13537                    context: Point::new(4, 0)..Point::new(7, 0),
13538                    primary: None,
13539                },
13540                ExcerptRange {
13541                    context: Point::new(9, 0)..Point::new(10, 0),
13542                    primary: None,
13543                },
13544            ],
13545            cx,
13546        );
13547        multibuffer
13548    });
13549
13550    let editor = cx.add_window(|window, cx| {
13551        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13552    });
13553    editor
13554        .update(cx, |editor, _window, cx| {
13555            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13556            editor
13557                .buffer
13558                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13559        })
13560        .unwrap();
13561
13562    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13563    cx.run_until_parked();
13564
13565    cx.update_editor(|editor, window, cx| {
13566        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13567    });
13568    cx.executor().run_until_parked();
13569
13570    // When the start of a hunk coincides with the start of its excerpt,
13571    // the hunk is expanded. When the start of a a hunk is earlier than
13572    // the start of its excerpt, the hunk is not expanded.
13573    cx.assert_state_with_diff(
13574        "
13575            ˇaaa
13576          - bbb
13577          + BBB
13578
13579          - ddd
13580          - eee
13581          + DDD
13582          + EEE
13583            fff
13584
13585            iii
13586        "
13587        .unindent(),
13588    );
13589}
13590
13591#[gpui::test]
13592async fn test_edits_around_expanded_insertion_hunks(
13593    executor: BackgroundExecutor,
13594    cx: &mut TestAppContext,
13595) {
13596    init_test(cx, |_| {});
13597
13598    let mut cx = EditorTestContext::new(cx).await;
13599
13600    let diff_base = r#"
13601        use some::mod1;
13602        use some::mod2;
13603
13604        const A: u32 = 42;
13605
13606        fn main() {
13607            println!("hello");
13608
13609            println!("world");
13610        }
13611        "#
13612    .unindent();
13613    executor.run_until_parked();
13614    cx.set_state(
13615        &r#"
13616        use some::mod1;
13617        use some::mod2;
13618
13619        const A: u32 = 42;
13620        const B: u32 = 42;
13621        const C: u32 = 42;
13622        ˇ
13623
13624        fn main() {
13625            println!("hello");
13626
13627            println!("world");
13628        }
13629        "#
13630        .unindent(),
13631    );
13632
13633    cx.set_diff_base(&diff_base);
13634    executor.run_until_parked();
13635
13636    cx.update_editor(|editor, window, cx| {
13637        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13638    });
13639    executor.run_until_parked();
13640
13641    cx.assert_state_with_diff(
13642        r#"
13643        use some::mod1;
13644        use some::mod2;
13645
13646        const A: u32 = 42;
13647      + const B: u32 = 42;
13648      + const C: u32 = 42;
13649      + ˇ
13650
13651        fn main() {
13652            println!("hello");
13653
13654            println!("world");
13655        }
13656      "#
13657        .unindent(),
13658    );
13659
13660    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13661    executor.run_until_parked();
13662
13663    cx.assert_state_with_diff(
13664        r#"
13665        use some::mod1;
13666        use some::mod2;
13667
13668        const A: u32 = 42;
13669      + const B: u32 = 42;
13670      + const C: u32 = 42;
13671      + const D: u32 = 42;
13672      + ˇ
13673
13674        fn main() {
13675            println!("hello");
13676
13677            println!("world");
13678        }
13679      "#
13680        .unindent(),
13681    );
13682
13683    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13684    executor.run_until_parked();
13685
13686    cx.assert_state_with_diff(
13687        r#"
13688        use some::mod1;
13689        use some::mod2;
13690
13691        const A: u32 = 42;
13692      + const B: u32 = 42;
13693      + const C: u32 = 42;
13694      + const D: u32 = 42;
13695      + const E: u32 = 42;
13696      + ˇ
13697
13698        fn main() {
13699            println!("hello");
13700
13701            println!("world");
13702        }
13703      "#
13704        .unindent(),
13705    );
13706
13707    cx.update_editor(|editor, window, cx| {
13708        editor.delete_line(&DeleteLine, window, cx);
13709    });
13710    executor.run_until_parked();
13711
13712    cx.assert_state_with_diff(
13713        r#"
13714        use some::mod1;
13715        use some::mod2;
13716
13717        const A: u32 = 42;
13718      + const B: u32 = 42;
13719      + const C: u32 = 42;
13720      + const D: u32 = 42;
13721      + const E: u32 = 42;
13722        ˇ
13723        fn main() {
13724            println!("hello");
13725
13726            println!("world");
13727        }
13728      "#
13729        .unindent(),
13730    );
13731
13732    cx.update_editor(|editor, window, cx| {
13733        editor.move_up(&MoveUp, window, cx);
13734        editor.delete_line(&DeleteLine, window, cx);
13735        editor.move_up(&MoveUp, window, cx);
13736        editor.delete_line(&DeleteLine, window, cx);
13737        editor.move_up(&MoveUp, window, cx);
13738        editor.delete_line(&DeleteLine, window, cx);
13739    });
13740    executor.run_until_parked();
13741    cx.assert_state_with_diff(
13742        r#"
13743        use some::mod1;
13744        use some::mod2;
13745
13746        const A: u32 = 42;
13747      + const B: u32 = 42;
13748        ˇ
13749        fn main() {
13750            println!("hello");
13751
13752            println!("world");
13753        }
13754      "#
13755        .unindent(),
13756    );
13757
13758    cx.update_editor(|editor, window, cx| {
13759        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13760        editor.delete_line(&DeleteLine, window, cx);
13761    });
13762    executor.run_until_parked();
13763    cx.assert_state_with_diff(
13764        r#"
13765        ˇ
13766        fn main() {
13767            println!("hello");
13768
13769            println!("world");
13770        }
13771      "#
13772        .unindent(),
13773    );
13774}
13775
13776#[gpui::test]
13777async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13778    init_test(cx, |_| {});
13779
13780    let mut cx = EditorTestContext::new(cx).await;
13781    cx.set_diff_base(indoc! { "
13782        one
13783        two
13784        three
13785        four
13786        five
13787        "
13788    });
13789    cx.set_state(indoc! { "
13790        one
13791        ˇthree
13792        five
13793    "});
13794    cx.run_until_parked();
13795    cx.update_editor(|editor, window, cx| {
13796        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13797    });
13798    cx.assert_state_with_diff(
13799        indoc! { "
13800        one
13801      - two
13802        ˇthree
13803      - four
13804        five
13805    "}
13806        .to_string(),
13807    );
13808    cx.update_editor(|editor, window, cx| {
13809        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13810    });
13811
13812    cx.assert_state_with_diff(
13813        indoc! { "
13814        one
13815        ˇthree
13816        five
13817    "}
13818        .to_string(),
13819    );
13820
13821    cx.set_state(indoc! { "
13822        one
13823        ˇTWO
13824        three
13825        four
13826        five
13827    "});
13828    cx.run_until_parked();
13829    cx.update_editor(|editor, window, cx| {
13830        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13831    });
13832
13833    cx.assert_state_with_diff(
13834        indoc! { "
13835            one
13836          - two
13837          + ˇTWO
13838            three
13839            four
13840            five
13841        "}
13842        .to_string(),
13843    );
13844    cx.update_editor(|editor, window, cx| {
13845        editor.move_up(&Default::default(), window, cx);
13846        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13847    });
13848    cx.assert_state_with_diff(
13849        indoc! { "
13850            one
13851            ˇTWO
13852            three
13853            four
13854            five
13855        "}
13856        .to_string(),
13857    );
13858}
13859
13860#[gpui::test]
13861async fn test_edits_around_expanded_deletion_hunks(
13862    executor: BackgroundExecutor,
13863    cx: &mut TestAppContext,
13864) {
13865    init_test(cx, |_| {});
13866
13867    let mut cx = EditorTestContext::new(cx).await;
13868
13869    let diff_base = r#"
13870        use some::mod1;
13871        use some::mod2;
13872
13873        const A: u32 = 42;
13874        const B: u32 = 42;
13875        const C: u32 = 42;
13876
13877
13878        fn main() {
13879            println!("hello");
13880
13881            println!("world");
13882        }
13883    "#
13884    .unindent();
13885    executor.run_until_parked();
13886    cx.set_state(
13887        &r#"
13888        use some::mod1;
13889        use some::mod2;
13890
13891        ˇconst B: u32 = 42;
13892        const C: u32 = 42;
13893
13894
13895        fn main() {
13896            println!("hello");
13897
13898            println!("world");
13899        }
13900        "#
13901        .unindent(),
13902    );
13903
13904    cx.set_diff_base(&diff_base);
13905    executor.run_until_parked();
13906
13907    cx.update_editor(|editor, window, cx| {
13908        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13909    });
13910    executor.run_until_parked();
13911
13912    cx.assert_state_with_diff(
13913        r#"
13914        use some::mod1;
13915        use some::mod2;
13916
13917      - const A: u32 = 42;
13918        ˇconst B: u32 = 42;
13919        const C: u32 = 42;
13920
13921
13922        fn main() {
13923            println!("hello");
13924
13925            println!("world");
13926        }
13927      "#
13928        .unindent(),
13929    );
13930
13931    cx.update_editor(|editor, window, cx| {
13932        editor.delete_line(&DeleteLine, window, cx);
13933    });
13934    executor.run_until_parked();
13935    cx.assert_state_with_diff(
13936        r#"
13937        use some::mod1;
13938        use some::mod2;
13939
13940      - const A: u32 = 42;
13941      - const B: u32 = 42;
13942        ˇconst C: u32 = 42;
13943
13944
13945        fn main() {
13946            println!("hello");
13947
13948            println!("world");
13949        }
13950      "#
13951        .unindent(),
13952    );
13953
13954    cx.update_editor(|editor, window, cx| {
13955        editor.delete_line(&DeleteLine, window, cx);
13956    });
13957    executor.run_until_parked();
13958    cx.assert_state_with_diff(
13959        r#"
13960        use some::mod1;
13961        use some::mod2;
13962
13963      - const A: u32 = 42;
13964      - const B: u32 = 42;
13965      - const C: u32 = 42;
13966        ˇ
13967
13968        fn main() {
13969            println!("hello");
13970
13971            println!("world");
13972        }
13973      "#
13974        .unindent(),
13975    );
13976
13977    cx.update_editor(|editor, window, cx| {
13978        editor.handle_input("replacement", window, cx);
13979    });
13980    executor.run_until_parked();
13981    cx.assert_state_with_diff(
13982        r#"
13983        use some::mod1;
13984        use some::mod2;
13985
13986      - const A: u32 = 42;
13987      - const B: u32 = 42;
13988      - const C: u32 = 42;
13989      -
13990      + replacementˇ
13991
13992        fn main() {
13993            println!("hello");
13994
13995            println!("world");
13996        }
13997      "#
13998        .unindent(),
13999    );
14000}
14001
14002#[gpui::test]
14003async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14004    init_test(cx, |_| {});
14005
14006    let mut cx = EditorTestContext::new(cx).await;
14007
14008    let base_text = r#"
14009        one
14010        two
14011        three
14012        four
14013        five
14014    "#
14015    .unindent();
14016    executor.run_until_parked();
14017    cx.set_state(
14018        &r#"
14019        one
14020        two
14021        fˇour
14022        five
14023        "#
14024        .unindent(),
14025    );
14026
14027    cx.set_diff_base(&base_text);
14028    executor.run_until_parked();
14029
14030    cx.update_editor(|editor, window, cx| {
14031        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14032    });
14033    executor.run_until_parked();
14034
14035    cx.assert_state_with_diff(
14036        r#"
14037          one
14038          two
14039        - three
14040          fˇour
14041          five
14042        "#
14043        .unindent(),
14044    );
14045
14046    cx.update_editor(|editor, window, cx| {
14047        editor.backspace(&Backspace, window, cx);
14048        editor.backspace(&Backspace, window, cx);
14049    });
14050    executor.run_until_parked();
14051    cx.assert_state_with_diff(
14052        r#"
14053          one
14054          two
14055        - threeˇ
14056        - four
14057        + our
14058          five
14059        "#
14060        .unindent(),
14061    );
14062}
14063
14064#[gpui::test]
14065async fn test_edit_after_expanded_modification_hunk(
14066    executor: BackgroundExecutor,
14067    cx: &mut TestAppContext,
14068) {
14069    init_test(cx, |_| {});
14070
14071    let mut cx = EditorTestContext::new(cx).await;
14072
14073    let diff_base = r#"
14074        use some::mod1;
14075        use some::mod2;
14076
14077        const A: u32 = 42;
14078        const B: u32 = 42;
14079        const C: u32 = 42;
14080        const D: u32 = 42;
14081
14082
14083        fn main() {
14084            println!("hello");
14085
14086            println!("world");
14087        }"#
14088    .unindent();
14089
14090    cx.set_state(
14091        &r#"
14092        use some::mod1;
14093        use some::mod2;
14094
14095        const A: u32 = 42;
14096        const B: u32 = 42;
14097        const C: u32 = 43ˇ
14098        const D: u32 = 42;
14099
14100
14101        fn main() {
14102            println!("hello");
14103
14104            println!("world");
14105        }"#
14106        .unindent(),
14107    );
14108
14109    cx.set_diff_base(&diff_base);
14110    executor.run_until_parked();
14111    cx.update_editor(|editor, window, cx| {
14112        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14113    });
14114    executor.run_until_parked();
14115
14116    cx.assert_state_with_diff(
14117        r#"
14118        use some::mod1;
14119        use some::mod2;
14120
14121        const A: u32 = 42;
14122        const B: u32 = 42;
14123      - const C: u32 = 42;
14124      + const C: u32 = 43ˇ
14125        const D: u32 = 42;
14126
14127
14128        fn main() {
14129            println!("hello");
14130
14131            println!("world");
14132        }"#
14133        .unindent(),
14134    );
14135
14136    cx.update_editor(|editor, window, cx| {
14137        editor.handle_input("\nnew_line\n", window, cx);
14138    });
14139    executor.run_until_parked();
14140
14141    cx.assert_state_with_diff(
14142        r#"
14143        use some::mod1;
14144        use some::mod2;
14145
14146        const A: u32 = 42;
14147        const B: u32 = 42;
14148      - const C: u32 = 42;
14149      + const C: u32 = 43
14150      + new_line
14151      + ˇ
14152        const D: u32 = 42;
14153
14154
14155        fn main() {
14156            println!("hello");
14157
14158            println!("world");
14159        }"#
14160        .unindent(),
14161    );
14162}
14163
14164#[gpui::test]
14165async fn test_stage_and_unstage_added_file_hunk(
14166    executor: BackgroundExecutor,
14167    cx: &mut TestAppContext,
14168) {
14169    init_test(cx, |_| {});
14170
14171    let mut cx = EditorTestContext::new(cx).await;
14172    cx.update_editor(|editor, _, cx| {
14173        editor.set_expand_all_diff_hunks(cx);
14174    });
14175
14176    let working_copy = r#"
14177            ˇfn main() {
14178                println!("hello, world!");
14179            }
14180        "#
14181    .unindent();
14182
14183    cx.set_state(&working_copy);
14184    executor.run_until_parked();
14185
14186    cx.assert_state_with_diff(
14187        r#"
14188            + ˇfn main() {
14189            +     println!("hello, world!");
14190            + }
14191        "#
14192        .unindent(),
14193    );
14194    cx.assert_index_text(None);
14195
14196    cx.update_editor(|editor, window, cx| {
14197        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14198    });
14199    executor.run_until_parked();
14200    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14201    cx.assert_state_with_diff(
14202        r#"
14203            + ˇfn main() {
14204            +     println!("hello, world!");
14205            + }
14206        "#
14207        .unindent(),
14208    );
14209
14210    cx.update_editor(|editor, window, cx| {
14211        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14212    });
14213    executor.run_until_parked();
14214    cx.assert_index_text(None);
14215}
14216
14217async fn setup_indent_guides_editor(
14218    text: &str,
14219    cx: &mut TestAppContext,
14220) -> (BufferId, EditorTestContext) {
14221    init_test(cx, |_| {});
14222
14223    let mut cx = EditorTestContext::new(cx).await;
14224
14225    let buffer_id = cx.update_editor(|editor, window, cx| {
14226        editor.set_text(text, window, cx);
14227        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14228
14229        buffer_ids[0]
14230    });
14231
14232    (buffer_id, cx)
14233}
14234
14235fn assert_indent_guides(
14236    range: Range<u32>,
14237    expected: Vec<IndentGuide>,
14238    active_indices: Option<Vec<usize>>,
14239    cx: &mut EditorTestContext,
14240) {
14241    let indent_guides = cx.update_editor(|editor, window, cx| {
14242        let snapshot = editor.snapshot(window, cx).display_snapshot;
14243        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14244            editor,
14245            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14246            true,
14247            &snapshot,
14248            cx,
14249        );
14250
14251        indent_guides.sort_by(|a, b| {
14252            a.depth.cmp(&b.depth).then(
14253                a.start_row
14254                    .cmp(&b.start_row)
14255                    .then(a.end_row.cmp(&b.end_row)),
14256            )
14257        });
14258        indent_guides
14259    });
14260
14261    if let Some(expected) = active_indices {
14262        let active_indices = cx.update_editor(|editor, window, cx| {
14263            let snapshot = editor.snapshot(window, cx).display_snapshot;
14264            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14265        });
14266
14267        assert_eq!(
14268            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14269            expected,
14270            "Active indent guide indices do not match"
14271        );
14272    }
14273
14274    assert_eq!(indent_guides, expected, "Indent guides do not match");
14275}
14276
14277fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14278    IndentGuide {
14279        buffer_id,
14280        start_row: MultiBufferRow(start_row),
14281        end_row: MultiBufferRow(end_row),
14282        depth,
14283        tab_size: 4,
14284        settings: IndentGuideSettings {
14285            enabled: true,
14286            line_width: 1,
14287            active_line_width: 1,
14288            ..Default::default()
14289        },
14290    }
14291}
14292
14293#[gpui::test]
14294async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14295    let (buffer_id, mut cx) = setup_indent_guides_editor(
14296        &"
14297    fn main() {
14298        let a = 1;
14299    }"
14300        .unindent(),
14301        cx,
14302    )
14303    .await;
14304
14305    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14306}
14307
14308#[gpui::test]
14309async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14310    let (buffer_id, mut cx) = setup_indent_guides_editor(
14311        &"
14312    fn main() {
14313        let a = 1;
14314        let b = 2;
14315    }"
14316        .unindent(),
14317        cx,
14318    )
14319    .await;
14320
14321    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14322}
14323
14324#[gpui::test]
14325async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14326    let (buffer_id, mut cx) = setup_indent_guides_editor(
14327        &"
14328    fn main() {
14329        let a = 1;
14330        if a == 3 {
14331            let b = 2;
14332        } else {
14333            let c = 3;
14334        }
14335    }"
14336        .unindent(),
14337        cx,
14338    )
14339    .await;
14340
14341    assert_indent_guides(
14342        0..8,
14343        vec![
14344            indent_guide(buffer_id, 1, 6, 0),
14345            indent_guide(buffer_id, 3, 3, 1),
14346            indent_guide(buffer_id, 5, 5, 1),
14347        ],
14348        None,
14349        &mut cx,
14350    );
14351}
14352
14353#[gpui::test]
14354async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14355    let (buffer_id, mut cx) = setup_indent_guides_editor(
14356        &"
14357    fn main() {
14358        let a = 1;
14359            let b = 2;
14360        let c = 3;
14361    }"
14362        .unindent(),
14363        cx,
14364    )
14365    .await;
14366
14367    assert_indent_guides(
14368        0..5,
14369        vec![
14370            indent_guide(buffer_id, 1, 3, 0),
14371            indent_guide(buffer_id, 2, 2, 1),
14372        ],
14373        None,
14374        &mut cx,
14375    );
14376}
14377
14378#[gpui::test]
14379async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14380    let (buffer_id, mut cx) = setup_indent_guides_editor(
14381        &"
14382        fn main() {
14383            let a = 1;
14384
14385            let c = 3;
14386        }"
14387        .unindent(),
14388        cx,
14389    )
14390    .await;
14391
14392    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14393}
14394
14395#[gpui::test]
14396async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14397    let (buffer_id, mut cx) = setup_indent_guides_editor(
14398        &"
14399        fn main() {
14400            let a = 1;
14401
14402            let c = 3;
14403
14404            if a == 3 {
14405                let b = 2;
14406            } else {
14407                let c = 3;
14408            }
14409        }"
14410        .unindent(),
14411        cx,
14412    )
14413    .await;
14414
14415    assert_indent_guides(
14416        0..11,
14417        vec![
14418            indent_guide(buffer_id, 1, 9, 0),
14419            indent_guide(buffer_id, 6, 6, 1),
14420            indent_guide(buffer_id, 8, 8, 1),
14421        ],
14422        None,
14423        &mut cx,
14424    );
14425}
14426
14427#[gpui::test]
14428async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14429    let (buffer_id, mut cx) = setup_indent_guides_editor(
14430        &"
14431        fn main() {
14432            let a = 1;
14433
14434            let c = 3;
14435
14436            if a == 3 {
14437                let b = 2;
14438            } else {
14439                let c = 3;
14440            }
14441        }"
14442        .unindent(),
14443        cx,
14444    )
14445    .await;
14446
14447    assert_indent_guides(
14448        1..11,
14449        vec![
14450            indent_guide(buffer_id, 1, 9, 0),
14451            indent_guide(buffer_id, 6, 6, 1),
14452            indent_guide(buffer_id, 8, 8, 1),
14453        ],
14454        None,
14455        &mut cx,
14456    );
14457}
14458
14459#[gpui::test]
14460async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
14461    let (buffer_id, mut cx) = setup_indent_guides_editor(
14462        &"
14463        fn main() {
14464            let a = 1;
14465
14466            let c = 3;
14467
14468            if a == 3 {
14469                let b = 2;
14470            } else {
14471                let c = 3;
14472            }
14473        }"
14474        .unindent(),
14475        cx,
14476    )
14477    .await;
14478
14479    assert_indent_guides(
14480        1..10,
14481        vec![
14482            indent_guide(buffer_id, 1, 9, 0),
14483            indent_guide(buffer_id, 6, 6, 1),
14484            indent_guide(buffer_id, 8, 8, 1),
14485        ],
14486        None,
14487        &mut cx,
14488    );
14489}
14490
14491#[gpui::test]
14492async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
14493    let (buffer_id, mut cx) = setup_indent_guides_editor(
14494        &"
14495        block1
14496            block2
14497                block3
14498                    block4
14499            block2
14500        block1
14501        block1"
14502            .unindent(),
14503        cx,
14504    )
14505    .await;
14506
14507    assert_indent_guides(
14508        1..10,
14509        vec![
14510            indent_guide(buffer_id, 1, 4, 0),
14511            indent_guide(buffer_id, 2, 3, 1),
14512            indent_guide(buffer_id, 3, 3, 2),
14513        ],
14514        None,
14515        &mut cx,
14516    );
14517}
14518
14519#[gpui::test]
14520async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
14521    let (buffer_id, mut cx) = setup_indent_guides_editor(
14522        &"
14523        block1
14524            block2
14525                block3
14526
14527        block1
14528        block1"
14529            .unindent(),
14530        cx,
14531    )
14532    .await;
14533
14534    assert_indent_guides(
14535        0..6,
14536        vec![
14537            indent_guide(buffer_id, 1, 2, 0),
14538            indent_guide(buffer_id, 2, 2, 1),
14539        ],
14540        None,
14541        &mut cx,
14542    );
14543}
14544
14545#[gpui::test]
14546async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
14547    let (buffer_id, mut cx) = setup_indent_guides_editor(
14548        &"
14549        block1
14550
14551
14552
14553            block2
14554        "
14555        .unindent(),
14556        cx,
14557    )
14558    .await;
14559
14560    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14561}
14562
14563#[gpui::test]
14564async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
14565    let (buffer_id, mut cx) = setup_indent_guides_editor(
14566        &"
14567        def a:
14568        \tb = 3
14569        \tif True:
14570        \t\tc = 4
14571        \t\td = 5
14572        \tprint(b)
14573        "
14574        .unindent(),
14575        cx,
14576    )
14577    .await;
14578
14579    assert_indent_guides(
14580        0..6,
14581        vec![
14582            indent_guide(buffer_id, 1, 6, 0),
14583            indent_guide(buffer_id, 3, 4, 1),
14584        ],
14585        None,
14586        &mut cx,
14587    );
14588}
14589
14590#[gpui::test]
14591async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
14592    let (buffer_id, mut cx) = setup_indent_guides_editor(
14593        &"
14594    fn main() {
14595        let a = 1;
14596    }"
14597        .unindent(),
14598        cx,
14599    )
14600    .await;
14601
14602    cx.update_editor(|editor, window, cx| {
14603        editor.change_selections(None, window, cx, |s| {
14604            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14605        });
14606    });
14607
14608    assert_indent_guides(
14609        0..3,
14610        vec![indent_guide(buffer_id, 1, 1, 0)],
14611        Some(vec![0]),
14612        &mut cx,
14613    );
14614}
14615
14616#[gpui::test]
14617async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
14618    let (buffer_id, mut cx) = setup_indent_guides_editor(
14619        &"
14620    fn main() {
14621        if 1 == 2 {
14622            let a = 1;
14623        }
14624    }"
14625        .unindent(),
14626        cx,
14627    )
14628    .await;
14629
14630    cx.update_editor(|editor, window, cx| {
14631        editor.change_selections(None, window, cx, |s| {
14632            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14633        });
14634    });
14635
14636    assert_indent_guides(
14637        0..4,
14638        vec![
14639            indent_guide(buffer_id, 1, 3, 0),
14640            indent_guide(buffer_id, 2, 2, 1),
14641        ],
14642        Some(vec![1]),
14643        &mut cx,
14644    );
14645
14646    cx.update_editor(|editor, window, cx| {
14647        editor.change_selections(None, window, cx, |s| {
14648            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14649        });
14650    });
14651
14652    assert_indent_guides(
14653        0..4,
14654        vec![
14655            indent_guide(buffer_id, 1, 3, 0),
14656            indent_guide(buffer_id, 2, 2, 1),
14657        ],
14658        Some(vec![1]),
14659        &mut cx,
14660    );
14661
14662    cx.update_editor(|editor, window, cx| {
14663        editor.change_selections(None, window, cx, |s| {
14664            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14665        });
14666    });
14667
14668    assert_indent_guides(
14669        0..4,
14670        vec![
14671            indent_guide(buffer_id, 1, 3, 0),
14672            indent_guide(buffer_id, 2, 2, 1),
14673        ],
14674        Some(vec![0]),
14675        &mut cx,
14676    );
14677}
14678
14679#[gpui::test]
14680async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
14681    let (buffer_id, mut cx) = setup_indent_guides_editor(
14682        &"
14683    fn main() {
14684        let a = 1;
14685
14686        let b = 2;
14687    }"
14688        .unindent(),
14689        cx,
14690    )
14691    .await;
14692
14693    cx.update_editor(|editor, window, cx| {
14694        editor.change_selections(None, window, cx, |s| {
14695            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14696        });
14697    });
14698
14699    assert_indent_guides(
14700        0..5,
14701        vec![indent_guide(buffer_id, 1, 3, 0)],
14702        Some(vec![0]),
14703        &mut cx,
14704    );
14705}
14706
14707#[gpui::test]
14708async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
14709    let (buffer_id, mut cx) = setup_indent_guides_editor(
14710        &"
14711    def m:
14712        a = 1
14713        pass"
14714            .unindent(),
14715        cx,
14716    )
14717    .await;
14718
14719    cx.update_editor(|editor, window, cx| {
14720        editor.change_selections(None, window, cx, |s| {
14721            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14722        });
14723    });
14724
14725    assert_indent_guides(
14726        0..3,
14727        vec![indent_guide(buffer_id, 1, 2, 0)],
14728        Some(vec![0]),
14729        &mut cx,
14730    );
14731}
14732
14733#[gpui::test]
14734async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
14735    init_test(cx, |_| {});
14736    let mut cx = EditorTestContext::new(cx).await;
14737    let text = indoc! {
14738        "
14739        impl A {
14740            fn b() {
14741                0;
14742                3;
14743                5;
14744                6;
14745                7;
14746            }
14747        }
14748        "
14749    };
14750    let base_text = indoc! {
14751        "
14752        impl A {
14753            fn b() {
14754                0;
14755                1;
14756                2;
14757                3;
14758                4;
14759            }
14760            fn c() {
14761                5;
14762                6;
14763                7;
14764            }
14765        }
14766        "
14767    };
14768
14769    cx.update_editor(|editor, window, cx| {
14770        editor.set_text(text, window, cx);
14771
14772        editor.buffer().update(cx, |multibuffer, cx| {
14773            let buffer = multibuffer.as_singleton().unwrap();
14774            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14775
14776            multibuffer.set_all_diff_hunks_expanded(cx);
14777            multibuffer.add_diff(diff, cx);
14778
14779            buffer.read(cx).remote_id()
14780        })
14781    });
14782    cx.run_until_parked();
14783
14784    cx.assert_state_with_diff(
14785        indoc! { "
14786          impl A {
14787              fn b() {
14788                  0;
14789        -         1;
14790        -         2;
14791                  3;
14792        -         4;
14793        -     }
14794        -     fn c() {
14795                  5;
14796                  6;
14797                  7;
14798              }
14799          }
14800          ˇ"
14801        }
14802        .to_string(),
14803    );
14804
14805    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14806        editor
14807            .snapshot(window, cx)
14808            .buffer_snapshot
14809            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14810            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14811            .collect::<Vec<_>>()
14812    });
14813    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14814    assert_eq!(
14815        actual_guides,
14816        vec![
14817            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14818            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14819            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14820        ]
14821    );
14822}
14823
14824#[gpui::test]
14825async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14826    init_test(cx, |_| {});
14827    let mut cx = EditorTestContext::new(cx).await;
14828
14829    let diff_base = r#"
14830        a
14831        b
14832        c
14833        "#
14834    .unindent();
14835
14836    cx.set_state(
14837        &r#"
14838        ˇA
14839        b
14840        C
14841        "#
14842        .unindent(),
14843    );
14844    cx.set_diff_base(&diff_base);
14845    cx.update_editor(|editor, window, cx| {
14846        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14847    });
14848    executor.run_until_parked();
14849
14850    let both_hunks_expanded = r#"
14851        - a
14852        + ˇA
14853          b
14854        - c
14855        + C
14856        "#
14857    .unindent();
14858
14859    cx.assert_state_with_diff(both_hunks_expanded.clone());
14860
14861    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14862        let snapshot = editor.snapshot(window, cx);
14863        let hunks = editor
14864            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14865            .collect::<Vec<_>>();
14866        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14867        let buffer_id = hunks[0].buffer_id;
14868        hunks
14869            .into_iter()
14870            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14871            .collect::<Vec<_>>()
14872    });
14873    assert_eq!(hunk_ranges.len(), 2);
14874
14875    cx.update_editor(|editor, _, cx| {
14876        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14877    });
14878    executor.run_until_parked();
14879
14880    let second_hunk_expanded = r#"
14881          ˇA
14882          b
14883        - c
14884        + C
14885        "#
14886    .unindent();
14887
14888    cx.assert_state_with_diff(second_hunk_expanded);
14889
14890    cx.update_editor(|editor, _, cx| {
14891        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
14892    });
14893    executor.run_until_parked();
14894
14895    cx.assert_state_with_diff(both_hunks_expanded.clone());
14896
14897    cx.update_editor(|editor, _, cx| {
14898        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14899    });
14900    executor.run_until_parked();
14901
14902    let first_hunk_expanded = r#"
14903        - a
14904        + ˇA
14905          b
14906          C
14907        "#
14908    .unindent();
14909
14910    cx.assert_state_with_diff(first_hunk_expanded);
14911
14912    cx.update_editor(|editor, _, cx| {
14913        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14914    });
14915    executor.run_until_parked();
14916
14917    cx.assert_state_with_diff(both_hunks_expanded);
14918
14919    cx.set_state(
14920        &r#"
14921        ˇA
14922        b
14923        "#
14924        .unindent(),
14925    );
14926    cx.run_until_parked();
14927
14928    // TODO this cursor position seems bad
14929    cx.assert_state_with_diff(
14930        r#"
14931        - ˇa
14932        + A
14933          b
14934        "#
14935        .unindent(),
14936    );
14937
14938    cx.update_editor(|editor, window, cx| {
14939        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14940    });
14941
14942    cx.assert_state_with_diff(
14943        r#"
14944            - ˇa
14945            + A
14946              b
14947            - c
14948            "#
14949        .unindent(),
14950    );
14951
14952    let hunk_ranges = cx.update_editor(|editor, window, cx| {
14953        let snapshot = editor.snapshot(window, cx);
14954        let hunks = editor
14955            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
14956            .collect::<Vec<_>>();
14957        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
14958        let buffer_id = hunks[0].buffer_id;
14959        hunks
14960            .into_iter()
14961            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
14962            .collect::<Vec<_>>()
14963    });
14964    assert_eq!(hunk_ranges.len(), 2);
14965
14966    cx.update_editor(|editor, _, cx| {
14967        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
14968    });
14969    executor.run_until_parked();
14970
14971    cx.assert_state_with_diff(
14972        r#"
14973        - ˇa
14974        + A
14975          b
14976        "#
14977        .unindent(),
14978    );
14979}
14980
14981#[gpui::test]
14982fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14983    init_test(cx, |_| {});
14984
14985    let editor = cx.add_window(|window, cx| {
14986        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14987        build_editor(buffer, window, cx)
14988    });
14989
14990    let render_args = Arc::new(Mutex::new(None));
14991    let snapshot = editor
14992        .update(cx, |editor, window, cx| {
14993            let snapshot = editor.buffer().read(cx).snapshot(cx);
14994            let range =
14995                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14996
14997            struct RenderArgs {
14998                row: MultiBufferRow,
14999                folded: bool,
15000                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15001            }
15002
15003            let crease = Crease::inline(
15004                range,
15005                FoldPlaceholder::test(),
15006                {
15007                    let toggle_callback = render_args.clone();
15008                    move |row, folded, callback, _window, _cx| {
15009                        *toggle_callback.lock() = Some(RenderArgs {
15010                            row,
15011                            folded,
15012                            callback,
15013                        });
15014                        div()
15015                    }
15016                },
15017                |_row, _folded, _window, _cx| div(),
15018            );
15019
15020            editor.insert_creases(Some(crease), cx);
15021            let snapshot = editor.snapshot(window, cx);
15022            let _div = snapshot.render_crease_toggle(
15023                MultiBufferRow(1),
15024                false,
15025                cx.entity().clone(),
15026                window,
15027                cx,
15028            );
15029            snapshot
15030        })
15031        .unwrap();
15032
15033    let render_args = render_args.lock().take().unwrap();
15034    assert_eq!(render_args.row, MultiBufferRow(1));
15035    assert!(!render_args.folded);
15036    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15037
15038    cx.update_window(*editor, |_, window, cx| {
15039        (render_args.callback)(true, window, cx)
15040    })
15041    .unwrap();
15042    let snapshot = editor
15043        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15044        .unwrap();
15045    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15046
15047    cx.update_window(*editor, |_, window, cx| {
15048        (render_args.callback)(false, window, cx)
15049    })
15050    .unwrap();
15051    let snapshot = editor
15052        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15053        .unwrap();
15054    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15055}
15056
15057#[gpui::test]
15058async fn test_input_text(cx: &mut TestAppContext) {
15059    init_test(cx, |_| {});
15060    let mut cx = EditorTestContext::new(cx).await;
15061
15062    cx.set_state(
15063        &r#"ˇone
15064        two
15065
15066        three
15067        fourˇ
15068        five
15069
15070        siˇx"#
15071            .unindent(),
15072    );
15073
15074    cx.dispatch_action(HandleInput(String::new()));
15075    cx.assert_editor_state(
15076        &r#"ˇone
15077        two
15078
15079        three
15080        fourˇ
15081        five
15082
15083        siˇx"#
15084            .unindent(),
15085    );
15086
15087    cx.dispatch_action(HandleInput("AAAA".to_string()));
15088    cx.assert_editor_state(
15089        &r#"AAAAˇone
15090        two
15091
15092        three
15093        fourAAAAˇ
15094        five
15095
15096        siAAAAˇx"#
15097            .unindent(),
15098    );
15099}
15100
15101#[gpui::test]
15102async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15103    init_test(cx, |_| {});
15104
15105    let mut cx = EditorTestContext::new(cx).await;
15106    cx.set_state(
15107        r#"let foo = 1;
15108let foo = 2;
15109let foo = 3;
15110let fooˇ = 4;
15111let foo = 5;
15112let foo = 6;
15113let foo = 7;
15114let foo = 8;
15115let foo = 9;
15116let foo = 10;
15117let foo = 11;
15118let foo = 12;
15119let foo = 13;
15120let foo = 14;
15121let foo = 15;"#,
15122    );
15123
15124    cx.update_editor(|e, window, cx| {
15125        assert_eq!(
15126            e.next_scroll_position,
15127            NextScrollCursorCenterTopBottom::Center,
15128            "Default next scroll direction is center",
15129        );
15130
15131        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15132        assert_eq!(
15133            e.next_scroll_position,
15134            NextScrollCursorCenterTopBottom::Top,
15135            "After center, next scroll direction should be top",
15136        );
15137
15138        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15139        assert_eq!(
15140            e.next_scroll_position,
15141            NextScrollCursorCenterTopBottom::Bottom,
15142            "After top, next scroll direction should be bottom",
15143        );
15144
15145        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15146        assert_eq!(
15147            e.next_scroll_position,
15148            NextScrollCursorCenterTopBottom::Center,
15149            "After bottom, scrolling should start over",
15150        );
15151
15152        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15153        assert_eq!(
15154            e.next_scroll_position,
15155            NextScrollCursorCenterTopBottom::Top,
15156            "Scrolling continues if retriggered fast enough"
15157        );
15158    });
15159
15160    cx.executor()
15161        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15162    cx.executor().run_until_parked();
15163    cx.update_editor(|e, _, _| {
15164        assert_eq!(
15165            e.next_scroll_position,
15166            NextScrollCursorCenterTopBottom::Center,
15167            "If scrolling is not triggered fast enough, it should reset"
15168        );
15169    });
15170}
15171
15172#[gpui::test]
15173async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15174    init_test(cx, |_| {});
15175    let mut cx = EditorLspTestContext::new_rust(
15176        lsp::ServerCapabilities {
15177            definition_provider: Some(lsp::OneOf::Left(true)),
15178            references_provider: Some(lsp::OneOf::Left(true)),
15179            ..lsp::ServerCapabilities::default()
15180        },
15181        cx,
15182    )
15183    .await;
15184
15185    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15186        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15187            move |params, _| async move {
15188                if empty_go_to_definition {
15189                    Ok(None)
15190                } else {
15191                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15192                        uri: params.text_document_position_params.text_document.uri,
15193                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15194                    })))
15195                }
15196            },
15197        );
15198        let references =
15199            cx.lsp
15200                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15201                    Ok(Some(vec![lsp::Location {
15202                        uri: params.text_document_position.text_document.uri,
15203                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15204                    }]))
15205                });
15206        (go_to_definition, references)
15207    };
15208
15209    cx.set_state(
15210        &r#"fn one() {
15211            let mut a = ˇtwo();
15212        }
15213
15214        fn two() {}"#
15215            .unindent(),
15216    );
15217    set_up_lsp_handlers(false, &mut cx);
15218    let navigated = cx
15219        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15220        .await
15221        .expect("Failed to navigate to definition");
15222    assert_eq!(
15223        navigated,
15224        Navigated::Yes,
15225        "Should have navigated to definition from the GetDefinition response"
15226    );
15227    cx.assert_editor_state(
15228        &r#"fn one() {
15229            let mut a = two();
15230        }
15231
15232        fn «twoˇ»() {}"#
15233            .unindent(),
15234    );
15235
15236    let editors = cx.update_workspace(|workspace, _, cx| {
15237        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15238    });
15239    cx.update_editor(|_, _, test_editor_cx| {
15240        assert_eq!(
15241            editors.len(),
15242            1,
15243            "Initially, only one, test, editor should be open in the workspace"
15244        );
15245        assert_eq!(
15246            test_editor_cx.entity(),
15247            editors.last().expect("Asserted len is 1").clone()
15248        );
15249    });
15250
15251    set_up_lsp_handlers(true, &mut cx);
15252    let navigated = cx
15253        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15254        .await
15255        .expect("Failed to navigate to lookup references");
15256    assert_eq!(
15257        navigated,
15258        Navigated::Yes,
15259        "Should have navigated to references as a fallback after empty GoToDefinition response"
15260    );
15261    // We should not change the selections in the existing file,
15262    // if opening another milti buffer with the references
15263    cx.assert_editor_state(
15264        &r#"fn one() {
15265            let mut a = two();
15266        }
15267
15268        fn «twoˇ»() {}"#
15269            .unindent(),
15270    );
15271    let editors = cx.update_workspace(|workspace, _, cx| {
15272        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15273    });
15274    cx.update_editor(|_, _, test_editor_cx| {
15275        assert_eq!(
15276            editors.len(),
15277            2,
15278            "After falling back to references search, we open a new editor with the results"
15279        );
15280        let references_fallback_text = editors
15281            .into_iter()
15282            .find(|new_editor| *new_editor != test_editor_cx.entity())
15283            .expect("Should have one non-test editor now")
15284            .read(test_editor_cx)
15285            .text(test_editor_cx);
15286        assert_eq!(
15287            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15288            "Should use the range from the references response and not the GoToDefinition one"
15289        );
15290    });
15291}
15292
15293#[gpui::test]
15294async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
15295    init_test(cx, |_| {});
15296
15297    let language = Arc::new(Language::new(
15298        LanguageConfig::default(),
15299        Some(tree_sitter_rust::LANGUAGE.into()),
15300    ));
15301
15302    let text = r#"
15303        #[cfg(test)]
15304        mod tests() {
15305            #[test]
15306            fn runnable_1() {
15307                let a = 1;
15308            }
15309
15310            #[test]
15311            fn runnable_2() {
15312                let a = 1;
15313                let b = 2;
15314            }
15315        }
15316    "#
15317    .unindent();
15318
15319    let fs = FakeFs::new(cx.executor());
15320    fs.insert_file("/file.rs", Default::default()).await;
15321
15322    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15323    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15324    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15325    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15326    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15327
15328    let editor = cx.new_window_entity(|window, cx| {
15329        Editor::new(
15330            EditorMode::Full,
15331            multi_buffer,
15332            Some(project.clone()),
15333            true,
15334            window,
15335            cx,
15336        )
15337    });
15338
15339    editor.update_in(cx, |editor, window, cx| {
15340        editor.tasks.insert(
15341            (buffer.read(cx).remote_id(), 3),
15342            RunnableTasks {
15343                templates: vec![],
15344                offset: MultiBufferOffset(43),
15345                column: 0,
15346                extra_variables: HashMap::default(),
15347                context_range: BufferOffset(43)..BufferOffset(85),
15348            },
15349        );
15350        editor.tasks.insert(
15351            (buffer.read(cx).remote_id(), 8),
15352            RunnableTasks {
15353                templates: vec![],
15354                offset: MultiBufferOffset(86),
15355                column: 0,
15356                extra_variables: HashMap::default(),
15357                context_range: BufferOffset(86)..BufferOffset(191),
15358            },
15359        );
15360
15361        // Test finding task when cursor is inside function body
15362        editor.change_selections(None, window, cx, |s| {
15363            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15364        });
15365        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15366        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15367
15368        // Test finding task when cursor is on function name
15369        editor.change_selections(None, window, cx, |s| {
15370            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15371        });
15372        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15373        assert_eq!(row, 8, "Should find task when cursor is on function name");
15374    });
15375}
15376
15377#[gpui::test]
15378async fn test_multi_buffer_folding(cx: &mut TestAppContext) {
15379    init_test(cx, |_| {});
15380
15381    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15382    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15383    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15384
15385    let fs = FakeFs::new(cx.executor());
15386    fs.insert_tree(
15387        path!("/a"),
15388        json!({
15389            "first.rs": sample_text_1,
15390            "second.rs": sample_text_2,
15391            "third.rs": sample_text_3,
15392        }),
15393    )
15394    .await;
15395    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15396    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15397    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15398    let worktree = project.update(cx, |project, cx| {
15399        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15400        assert_eq!(worktrees.len(), 1);
15401        worktrees.pop().unwrap()
15402    });
15403    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15404
15405    let buffer_1 = project
15406        .update(cx, |project, cx| {
15407            project.open_buffer((worktree_id, "first.rs"), cx)
15408        })
15409        .await
15410        .unwrap();
15411    let buffer_2 = project
15412        .update(cx, |project, cx| {
15413            project.open_buffer((worktree_id, "second.rs"), cx)
15414        })
15415        .await
15416        .unwrap();
15417    let buffer_3 = project
15418        .update(cx, |project, cx| {
15419            project.open_buffer((worktree_id, "third.rs"), cx)
15420        })
15421        .await
15422        .unwrap();
15423
15424    let multi_buffer = cx.new(|cx| {
15425        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15426        multi_buffer.push_excerpts(
15427            buffer_1.clone(),
15428            [
15429                ExcerptRange {
15430                    context: Point::new(0, 0)..Point::new(3, 0),
15431                    primary: None,
15432                },
15433                ExcerptRange {
15434                    context: Point::new(5, 0)..Point::new(7, 0),
15435                    primary: None,
15436                },
15437                ExcerptRange {
15438                    context: Point::new(9, 0)..Point::new(10, 4),
15439                    primary: None,
15440                },
15441            ],
15442            cx,
15443        );
15444        multi_buffer.push_excerpts(
15445            buffer_2.clone(),
15446            [
15447                ExcerptRange {
15448                    context: Point::new(0, 0)..Point::new(3, 0),
15449                    primary: None,
15450                },
15451                ExcerptRange {
15452                    context: Point::new(5, 0)..Point::new(7, 0),
15453                    primary: None,
15454                },
15455                ExcerptRange {
15456                    context: Point::new(9, 0)..Point::new(10, 4),
15457                    primary: None,
15458                },
15459            ],
15460            cx,
15461        );
15462        multi_buffer.push_excerpts(
15463            buffer_3.clone(),
15464            [
15465                ExcerptRange {
15466                    context: Point::new(0, 0)..Point::new(3, 0),
15467                    primary: None,
15468                },
15469                ExcerptRange {
15470                    context: Point::new(5, 0)..Point::new(7, 0),
15471                    primary: None,
15472                },
15473                ExcerptRange {
15474                    context: Point::new(9, 0)..Point::new(10, 4),
15475                    primary: None,
15476                },
15477            ],
15478            cx,
15479        );
15480        multi_buffer
15481    });
15482    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15483        Editor::new(
15484            EditorMode::Full,
15485            multi_buffer,
15486            Some(project.clone()),
15487            true,
15488            window,
15489            cx,
15490        )
15491    });
15492
15493    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";
15494    assert_eq!(
15495        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15496        full_text,
15497    );
15498
15499    multi_buffer_editor.update(cx, |editor, cx| {
15500        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15501    });
15502    assert_eq!(
15503        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15504        "\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",
15505        "After folding the first buffer, its text should not be displayed"
15506    );
15507
15508    multi_buffer_editor.update(cx, |editor, cx| {
15509        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15510    });
15511    assert_eq!(
15512        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15513        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15514        "After folding the second buffer, its text should not be displayed"
15515    );
15516
15517    multi_buffer_editor.update(cx, |editor, cx| {
15518        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15519    });
15520    assert_eq!(
15521        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15522        "\n\n\n\n\n",
15523        "After folding the third buffer, its text should not be displayed"
15524    );
15525
15526    // Emulate selection inside the fold logic, that should work
15527    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15528        editor
15529            .snapshot(window, cx)
15530            .next_line_boundary(Point::new(0, 4));
15531    });
15532
15533    multi_buffer_editor.update(cx, |editor, cx| {
15534        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15535    });
15536    assert_eq!(
15537        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15538        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15539        "After unfolding the second buffer, its text should be displayed"
15540    );
15541
15542    multi_buffer_editor.update(cx, |editor, cx| {
15543        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15544    });
15545    assert_eq!(
15546        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15547        "\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",
15548        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15549    );
15550
15551    multi_buffer_editor.update(cx, |editor, cx| {
15552        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15553    });
15554    assert_eq!(
15555        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15556        full_text,
15557        "After unfolding the all buffers, all original text should be displayed"
15558    );
15559}
15560
15561#[gpui::test]
15562async fn test_multi_buffer_single_excerpts_folding(cx: &mut TestAppContext) {
15563    init_test(cx, |_| {});
15564
15565    let sample_text_1 = "1111\n2222\n3333".to_string();
15566    let sample_text_2 = "4444\n5555\n6666".to_string();
15567    let sample_text_3 = "7777\n8888\n9999".to_string();
15568
15569    let fs = FakeFs::new(cx.executor());
15570    fs.insert_tree(
15571        path!("/a"),
15572        json!({
15573            "first.rs": sample_text_1,
15574            "second.rs": sample_text_2,
15575            "third.rs": sample_text_3,
15576        }),
15577    )
15578    .await;
15579    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15580    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15581    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15582    let worktree = project.update(cx, |project, cx| {
15583        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15584        assert_eq!(worktrees.len(), 1);
15585        worktrees.pop().unwrap()
15586    });
15587    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15588
15589    let buffer_1 = project
15590        .update(cx, |project, cx| {
15591            project.open_buffer((worktree_id, "first.rs"), cx)
15592        })
15593        .await
15594        .unwrap();
15595    let buffer_2 = project
15596        .update(cx, |project, cx| {
15597            project.open_buffer((worktree_id, "second.rs"), cx)
15598        })
15599        .await
15600        .unwrap();
15601    let buffer_3 = project
15602        .update(cx, |project, cx| {
15603            project.open_buffer((worktree_id, "third.rs"), cx)
15604        })
15605        .await
15606        .unwrap();
15607
15608    let multi_buffer = cx.new(|cx| {
15609        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15610        multi_buffer.push_excerpts(
15611            buffer_1.clone(),
15612            [ExcerptRange {
15613                context: Point::new(0, 0)..Point::new(3, 0),
15614                primary: None,
15615            }],
15616            cx,
15617        );
15618        multi_buffer.push_excerpts(
15619            buffer_2.clone(),
15620            [ExcerptRange {
15621                context: Point::new(0, 0)..Point::new(3, 0),
15622                primary: None,
15623            }],
15624            cx,
15625        );
15626        multi_buffer.push_excerpts(
15627            buffer_3.clone(),
15628            [ExcerptRange {
15629                context: Point::new(0, 0)..Point::new(3, 0),
15630                primary: None,
15631            }],
15632            cx,
15633        );
15634        multi_buffer
15635    });
15636
15637    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15638        Editor::new(
15639            EditorMode::Full,
15640            multi_buffer,
15641            Some(project.clone()),
15642            true,
15643            window,
15644            cx,
15645        )
15646    });
15647
15648    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15649    assert_eq!(
15650        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15651        full_text,
15652    );
15653
15654    multi_buffer_editor.update(cx, |editor, cx| {
15655        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15656    });
15657    assert_eq!(
15658        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15659        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15660        "After folding the first buffer, its text should not be displayed"
15661    );
15662
15663    multi_buffer_editor.update(cx, |editor, cx| {
15664        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15665    });
15666
15667    assert_eq!(
15668        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15669        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15670        "After folding the second buffer, its text should not be displayed"
15671    );
15672
15673    multi_buffer_editor.update(cx, |editor, cx| {
15674        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15675    });
15676    assert_eq!(
15677        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15678        "\n\n\n\n\n",
15679        "After folding the third buffer, its text should not be displayed"
15680    );
15681
15682    multi_buffer_editor.update(cx, |editor, cx| {
15683        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15684    });
15685    assert_eq!(
15686        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15687        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15688        "After unfolding the second buffer, its text should be displayed"
15689    );
15690
15691    multi_buffer_editor.update(cx, |editor, cx| {
15692        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15693    });
15694    assert_eq!(
15695        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15696        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15697        "After unfolding the first buffer, its text should be displayed"
15698    );
15699
15700    multi_buffer_editor.update(cx, |editor, cx| {
15701        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15702    });
15703    assert_eq!(
15704        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15705        full_text,
15706        "After unfolding all buffers, all original text should be displayed"
15707    );
15708}
15709
15710#[gpui::test]
15711async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut TestAppContext) {
15712    init_test(cx, |_| {});
15713
15714    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15715
15716    let fs = FakeFs::new(cx.executor());
15717    fs.insert_tree(
15718        path!("/a"),
15719        json!({
15720            "main.rs": sample_text,
15721        }),
15722    )
15723    .await;
15724    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15725    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15726    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15727    let worktree = project.update(cx, |project, cx| {
15728        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15729        assert_eq!(worktrees.len(), 1);
15730        worktrees.pop().unwrap()
15731    });
15732    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15733
15734    let buffer_1 = project
15735        .update(cx, |project, cx| {
15736            project.open_buffer((worktree_id, "main.rs"), cx)
15737        })
15738        .await
15739        .unwrap();
15740
15741    let multi_buffer = cx.new(|cx| {
15742        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15743        multi_buffer.push_excerpts(
15744            buffer_1.clone(),
15745            [ExcerptRange {
15746                context: Point::new(0, 0)
15747                    ..Point::new(
15748                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15749                        0,
15750                    ),
15751                primary: None,
15752            }],
15753            cx,
15754        );
15755        multi_buffer
15756    });
15757    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15758        Editor::new(
15759            EditorMode::Full,
15760            multi_buffer,
15761            Some(project.clone()),
15762            true,
15763            window,
15764            cx,
15765        )
15766    });
15767
15768    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15769    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15770        enum TestHighlight {}
15771        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15772        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15773        editor.highlight_text::<TestHighlight>(
15774            vec![highlight_range.clone()],
15775            HighlightStyle::color(Hsla::green()),
15776            cx,
15777        );
15778        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15779    });
15780
15781    let full_text = format!("\n\n\n{sample_text}\n");
15782    assert_eq!(
15783        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15784        full_text,
15785    );
15786}
15787
15788#[gpui::test]
15789async fn test_inline_completion_text(cx: &mut TestAppContext) {
15790    init_test(cx, |_| {});
15791
15792    // Simple insertion
15793    assert_highlighted_edits(
15794        "Hello, world!",
15795        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15796        true,
15797        cx,
15798        |highlighted_edits, cx| {
15799            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15800            assert_eq!(highlighted_edits.highlights.len(), 1);
15801            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15802            assert_eq!(
15803                highlighted_edits.highlights[0].1.background_color,
15804                Some(cx.theme().status().created_background)
15805            );
15806        },
15807    )
15808    .await;
15809
15810    // Replacement
15811    assert_highlighted_edits(
15812        "This is a test.",
15813        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15814        false,
15815        cx,
15816        |highlighted_edits, cx| {
15817            assert_eq!(highlighted_edits.text, "That is a test.");
15818            assert_eq!(highlighted_edits.highlights.len(), 1);
15819            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15820            assert_eq!(
15821                highlighted_edits.highlights[0].1.background_color,
15822                Some(cx.theme().status().created_background)
15823            );
15824        },
15825    )
15826    .await;
15827
15828    // Multiple edits
15829    assert_highlighted_edits(
15830        "Hello, world!",
15831        vec![
15832            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15833            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15834        ],
15835        false,
15836        cx,
15837        |highlighted_edits, cx| {
15838            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15839            assert_eq!(highlighted_edits.highlights.len(), 2);
15840            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15841            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15842            assert_eq!(
15843                highlighted_edits.highlights[0].1.background_color,
15844                Some(cx.theme().status().created_background)
15845            );
15846            assert_eq!(
15847                highlighted_edits.highlights[1].1.background_color,
15848                Some(cx.theme().status().created_background)
15849            );
15850        },
15851    )
15852    .await;
15853
15854    // Multiple lines with edits
15855    assert_highlighted_edits(
15856        "First line\nSecond line\nThird line\nFourth line",
15857        vec![
15858            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15859            (
15860                Point::new(2, 0)..Point::new(2, 10),
15861                "New third line".to_string(),
15862            ),
15863            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15864        ],
15865        false,
15866        cx,
15867        |highlighted_edits, cx| {
15868            assert_eq!(
15869                highlighted_edits.text,
15870                "Second modified\nNew third line\nFourth updated line"
15871            );
15872            assert_eq!(highlighted_edits.highlights.len(), 3);
15873            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15874            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15875            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15876            for highlight in &highlighted_edits.highlights {
15877                assert_eq!(
15878                    highlight.1.background_color,
15879                    Some(cx.theme().status().created_background)
15880                );
15881            }
15882        },
15883    )
15884    .await;
15885}
15886
15887#[gpui::test]
15888async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15889    init_test(cx, |_| {});
15890
15891    // Deletion
15892    assert_highlighted_edits(
15893        "Hello, world!",
15894        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15895        true,
15896        cx,
15897        |highlighted_edits, cx| {
15898            assert_eq!(highlighted_edits.text, "Hello, world!");
15899            assert_eq!(highlighted_edits.highlights.len(), 1);
15900            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15901            assert_eq!(
15902                highlighted_edits.highlights[0].1.background_color,
15903                Some(cx.theme().status().deleted_background)
15904            );
15905        },
15906    )
15907    .await;
15908
15909    // Insertion
15910    assert_highlighted_edits(
15911        "Hello, world!",
15912        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15913        true,
15914        cx,
15915        |highlighted_edits, cx| {
15916            assert_eq!(highlighted_edits.highlights.len(), 1);
15917            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15918            assert_eq!(
15919                highlighted_edits.highlights[0].1.background_color,
15920                Some(cx.theme().status().created_background)
15921            );
15922        },
15923    )
15924    .await;
15925}
15926
15927async fn assert_highlighted_edits(
15928    text: &str,
15929    edits: Vec<(Range<Point>, String)>,
15930    include_deletions: bool,
15931    cx: &mut TestAppContext,
15932    assertion_fn: impl Fn(HighlightedText, &App),
15933) {
15934    let window = cx.add_window(|window, cx| {
15935        let buffer = MultiBuffer::build_simple(text, cx);
15936        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15937    });
15938    let cx = &mut VisualTestContext::from_window(*window, cx);
15939
15940    let (buffer, snapshot) = window
15941        .update(cx, |editor, _window, cx| {
15942            (
15943                editor.buffer().clone(),
15944                editor.buffer().read(cx).snapshot(cx),
15945            )
15946        })
15947        .unwrap();
15948
15949    let edits = edits
15950        .into_iter()
15951        .map(|(range, edit)| {
15952            (
15953                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15954                edit,
15955            )
15956        })
15957        .collect::<Vec<_>>();
15958
15959    let text_anchor_edits = edits
15960        .clone()
15961        .into_iter()
15962        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15963        .collect::<Vec<_>>();
15964
15965    let edit_preview = window
15966        .update(cx, |_, _window, cx| {
15967            buffer
15968                .read(cx)
15969                .as_singleton()
15970                .unwrap()
15971                .read(cx)
15972                .preview_edits(text_anchor_edits.into(), cx)
15973        })
15974        .unwrap()
15975        .await;
15976
15977    cx.update(|_window, cx| {
15978        let highlighted_edits = inline_completion_edit_text(
15979            &snapshot.as_singleton().unwrap().2,
15980            &edits,
15981            &edit_preview,
15982            include_deletions,
15983            cx,
15984        );
15985        assertion_fn(highlighted_edits, cx)
15986    });
15987}
15988
15989#[gpui::test]
15990async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
15991    init_test(cx, |_| {});
15992    let capabilities = lsp::ServerCapabilities {
15993        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15994            prepare_provider: Some(true),
15995            work_done_progress_options: Default::default(),
15996        })),
15997        ..Default::default()
15998    };
15999    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16000
16001    cx.set_state(indoc! {"
16002        struct Fˇoo {}
16003    "});
16004
16005    cx.update_editor(|editor, _, cx| {
16006        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16007        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16008        editor.highlight_background::<DocumentHighlightRead>(
16009            &[highlight_range],
16010            |c| c.editor_document_highlight_read_background,
16011            cx,
16012        );
16013    });
16014
16015    let mut prepare_rename_handler =
16016        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16017            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16018                start: lsp::Position {
16019                    line: 0,
16020                    character: 7,
16021                },
16022                end: lsp::Position {
16023                    line: 0,
16024                    character: 10,
16025                },
16026            })))
16027        });
16028    let prepare_rename_task = cx
16029        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16030        .expect("Prepare rename was not started");
16031    prepare_rename_handler.next().await.unwrap();
16032    prepare_rename_task.await.expect("Prepare rename failed");
16033
16034    let mut rename_handler =
16035        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16036            let edit = lsp::TextEdit {
16037                range: lsp::Range {
16038                    start: lsp::Position {
16039                        line: 0,
16040                        character: 7,
16041                    },
16042                    end: lsp::Position {
16043                        line: 0,
16044                        character: 10,
16045                    },
16046                },
16047                new_text: "FooRenamed".to_string(),
16048            };
16049            Ok(Some(lsp::WorkspaceEdit::new(
16050                // Specify the same edit twice
16051                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
16052            )))
16053        });
16054    let rename_task = cx
16055        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16056        .expect("Confirm rename was not started");
16057    rename_handler.next().await.unwrap();
16058    rename_task.await.expect("Confirm rename failed");
16059    cx.run_until_parked();
16060
16061    // Despite two edits, only one is actually applied as those are identical
16062    cx.assert_editor_state(indoc! {"
16063        struct FooRenamedˇ {}
16064    "});
16065}
16066
16067#[gpui::test]
16068async fn test_rename_without_prepare(cx: &mut TestAppContext) {
16069    init_test(cx, |_| {});
16070    // These capabilities indicate that the server does not support prepare rename.
16071    let capabilities = lsp::ServerCapabilities {
16072        rename_provider: Some(lsp::OneOf::Left(true)),
16073        ..Default::default()
16074    };
16075    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16076
16077    cx.set_state(indoc! {"
16078        struct Fˇoo {}
16079    "});
16080
16081    cx.update_editor(|editor, _window, cx| {
16082        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16083        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16084        editor.highlight_background::<DocumentHighlightRead>(
16085            &[highlight_range],
16086            |c| c.editor_document_highlight_read_background,
16087            cx,
16088        );
16089    });
16090
16091    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
16092        .expect("Prepare rename was not started")
16093        .await
16094        .expect("Prepare rename failed");
16095
16096    let mut rename_handler =
16097        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
16098            let edit = lsp::TextEdit {
16099                range: lsp::Range {
16100                    start: lsp::Position {
16101                        line: 0,
16102                        character: 7,
16103                    },
16104                    end: lsp::Position {
16105                        line: 0,
16106                        character: 10,
16107                    },
16108                },
16109                new_text: "FooRenamed".to_string(),
16110            };
16111            Ok(Some(lsp::WorkspaceEdit::new(
16112                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
16113            )))
16114        });
16115    let rename_task = cx
16116        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
16117        .expect("Confirm rename was not started");
16118    rename_handler.next().await.unwrap();
16119    rename_task.await.expect("Confirm rename failed");
16120    cx.run_until_parked();
16121
16122    // Correct range is renamed, as `surrounding_word` is used to find it.
16123    cx.assert_editor_state(indoc! {"
16124        struct FooRenamedˇ {}
16125    "});
16126}
16127
16128#[gpui::test]
16129async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
16130    init_test(cx, |_| {});
16131    let mut cx = EditorTestContext::new(cx).await;
16132
16133    let language = Arc::new(
16134        Language::new(
16135            LanguageConfig::default(),
16136            Some(tree_sitter_html::LANGUAGE.into()),
16137        )
16138        .with_brackets_query(
16139            r#"
16140            ("<" @open "/>" @close)
16141            ("</" @open ">" @close)
16142            ("<" @open ">" @close)
16143            ("\"" @open "\"" @close)
16144            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
16145        "#,
16146        )
16147        .unwrap(),
16148    );
16149    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
16150
16151    cx.set_state(indoc! {"
16152        <span>ˇ</span>
16153    "});
16154    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16155    cx.assert_editor_state(indoc! {"
16156        <span>
16157        ˇ
16158        </span>
16159    "});
16160
16161    cx.set_state(indoc! {"
16162        <span><span></span>ˇ</span>
16163    "});
16164    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16165    cx.assert_editor_state(indoc! {"
16166        <span><span></span>
16167        ˇ</span>
16168    "});
16169
16170    cx.set_state(indoc! {"
16171        <span>ˇ
16172        </span>
16173    "});
16174    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
16175    cx.assert_editor_state(indoc! {"
16176        <span>
16177        ˇ
16178        </span>
16179    "});
16180}
16181
16182fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
16183    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
16184    point..point
16185}
16186
16187fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
16188    let (text, ranges) = marked_text_ranges(marked_text, true);
16189    assert_eq!(editor.text(cx), text);
16190    assert_eq!(
16191        editor.selections.ranges(cx),
16192        ranges,
16193        "Assert selections are {}",
16194        marked_text
16195    );
16196}
16197
16198pub fn handle_signature_help_request(
16199    cx: &mut EditorLspTestContext,
16200    mocked_response: lsp::SignatureHelp,
16201) -> impl Future<Output = ()> {
16202    let mut request =
16203        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
16204            let mocked_response = mocked_response.clone();
16205            async move { Ok(Some(mocked_response)) }
16206        });
16207
16208    async move {
16209        request.next().await;
16210    }
16211}
16212
16213/// Handle completion request passing a marked string specifying where the completion
16214/// should be triggered from using '|' character, what range should be replaced, and what completions
16215/// should be returned using '<' and '>' to delimit the range
16216pub fn handle_completion_request(
16217    cx: &mut EditorLspTestContext,
16218    marked_string: &str,
16219    completions: Vec<&'static str>,
16220    counter: Arc<AtomicUsize>,
16221) -> impl Future<Output = ()> {
16222    let complete_from_marker: TextRangeMarker = '|'.into();
16223    let replace_range_marker: TextRangeMarker = ('<', '>').into();
16224    let (_, mut marked_ranges) = marked_text_ranges_by(
16225        marked_string,
16226        vec![complete_from_marker.clone(), replace_range_marker.clone()],
16227    );
16228
16229    let complete_from_position =
16230        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
16231    let replace_range =
16232        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
16233
16234    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
16235        let completions = completions.clone();
16236        counter.fetch_add(1, atomic::Ordering::Release);
16237        async move {
16238            assert_eq!(params.text_document_position.text_document.uri, url.clone());
16239            assert_eq!(
16240                params.text_document_position.position,
16241                complete_from_position
16242            );
16243            Ok(Some(lsp::CompletionResponse::Array(
16244                completions
16245                    .iter()
16246                    .map(|completion_text| lsp::CompletionItem {
16247                        label: completion_text.to_string(),
16248                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16249                            range: replace_range,
16250                            new_text: completion_text.to_string(),
16251                        })),
16252                        ..Default::default()
16253                    })
16254                    .collect(),
16255            )))
16256        }
16257    });
16258
16259    async move {
16260        request.next().await;
16261    }
16262}
16263
16264fn handle_resolve_completion_request(
16265    cx: &mut EditorLspTestContext,
16266    edits: Option<Vec<(&'static str, &'static str)>>,
16267) -> impl Future<Output = ()> {
16268    let edits = edits.map(|edits| {
16269        edits
16270            .iter()
16271            .map(|(marked_string, new_text)| {
16272                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16273                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16274                lsp::TextEdit::new(replace_range, new_text.to_string())
16275            })
16276            .collect::<Vec<_>>()
16277    });
16278
16279    let mut request =
16280        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16281            let edits = edits.clone();
16282            async move {
16283                Ok(lsp::CompletionItem {
16284                    additional_text_edits: edits,
16285                    ..Default::default()
16286                })
16287            }
16288        });
16289
16290    async move {
16291        request.next().await;
16292    }
16293}
16294
16295pub(crate) fn update_test_language_settings(
16296    cx: &mut TestAppContext,
16297    f: impl Fn(&mut AllLanguageSettingsContent),
16298) {
16299    cx.update(|cx| {
16300        SettingsStore::update_global(cx, |store, cx| {
16301            store.update_user_settings::<AllLanguageSettings>(cx, f);
16302        });
16303    });
16304}
16305
16306pub(crate) fn update_test_project_settings(
16307    cx: &mut TestAppContext,
16308    f: impl Fn(&mut ProjectSettings),
16309) {
16310    cx.update(|cx| {
16311        SettingsStore::update_global(cx, |store, cx| {
16312            store.update_user_settings::<ProjectSettings>(cx, f);
16313        });
16314    });
16315}
16316
16317pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16318    cx.update(|cx| {
16319        assets::Assets.load_test_fonts(cx);
16320        let store = SettingsStore::test(cx);
16321        cx.set_global(store);
16322        theme::init(theme::LoadThemes::JustBase, cx);
16323        release_channel::init(SemanticVersion::default(), cx);
16324        client::init_settings(cx);
16325        language::init(cx);
16326        Project::init_settings(cx);
16327        workspace::init_settings(cx);
16328        crate::init(cx);
16329    });
16330
16331    update_test_language_settings(cx, f);
16332}
16333
16334#[track_caller]
16335fn assert_hunk_revert(
16336    not_reverted_text_with_selections: &str,
16337    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16338    expected_reverted_text_with_selections: &str,
16339    base_text: &str,
16340    cx: &mut EditorLspTestContext,
16341) {
16342    cx.set_state(not_reverted_text_with_selections);
16343    cx.set_diff_base(base_text);
16344    cx.executor().run_until_parked();
16345
16346    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16347        let snapshot = editor.snapshot(window, cx);
16348        let reverted_hunk_statuses = snapshot
16349            .buffer_snapshot
16350            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16351            .map(|hunk| hunk.status())
16352            .collect::<Vec<_>>();
16353
16354        editor.git_restore(&Default::default(), window, cx);
16355        reverted_hunk_statuses
16356    });
16357    cx.executor().run_until_parked();
16358    cx.assert_editor_state(expected_reverted_text_with_selections);
16359    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16360}