editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use buffer_diff::{BufferDiff, DiffHunkStatus};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::IndentGuide;
   28use parking_lot::Mutex;
   29use pretty_assertions::{assert_eq, assert_ne};
   30use project::project_settings::{LspSettings, ProjectSettings};
   31use project::FakeFs;
   32use serde_json::{self, json};
   33use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   34use std::{
   35    iter,
   36    sync::atomic::{self, AtomicUsize},
   37};
   38use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   39use unindent::Unindent;
   40use util::{
   41    assert_set_eq, path,
   42    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   43    uri,
   44};
   45use workspace::{
   46    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   47    NavigationEntry, ViewId,
   48};
   49
   50#[gpui::test]
   51fn test_edit_events(cx: &mut TestAppContext) {
   52    init_test(cx, |_| {});
   53
   54    let buffer = cx.new(|cx| {
   55        let mut buffer = language::Buffer::local("123456", cx);
   56        buffer.set_group_interval(Duration::from_secs(1));
   57        buffer
   58    });
   59
   60    let events = Rc::new(RefCell::new(Vec::new()));
   61    let editor1 = cx.add_window({
   62        let events = events.clone();
   63        |window, cx| {
   64            let entity = cx.entity().clone();
   65            cx.subscribe_in(
   66                &entity,
   67                window,
   68                move |_, _, event: &EditorEvent, _, _| match event {
   69                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   70                    EditorEvent::BufferEdited => {
   71                        events.borrow_mut().push(("editor1", "buffer edited"))
   72                    }
   73                    _ => {}
   74                },
   75            )
   76            .detach();
   77            Editor::for_buffer(buffer.clone(), None, window, cx)
   78        }
   79    });
   80
   81    let editor2 = cx.add_window({
   82        let events = events.clone();
   83        |window, cx| {
   84            cx.subscribe_in(
   85                &cx.entity().clone(),
   86                window,
   87                move |_, _, event: &EditorEvent, _, _| match event {
   88                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   89                    EditorEvent::BufferEdited => {
   90                        events.borrow_mut().push(("editor2", "buffer edited"))
   91                    }
   92                    _ => {}
   93                },
   94            )
   95            .detach();
   96            Editor::for_buffer(buffer.clone(), None, window, cx)
   97        }
   98    });
   99
  100    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  101
  102    // Mutating editor 1 will emit an `Edited` event only for that editor.
  103    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  104    assert_eq!(
  105        mem::take(&mut *events.borrow_mut()),
  106        [
  107            ("editor1", "edited"),
  108            ("editor1", "buffer edited"),
  109            ("editor2", "buffer edited"),
  110        ]
  111    );
  112
  113    // Mutating editor 2 will emit an `Edited` event only for that editor.
  114    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  115    assert_eq!(
  116        mem::take(&mut *events.borrow_mut()),
  117        [
  118            ("editor2", "edited"),
  119            ("editor1", "buffer edited"),
  120            ("editor2", "buffer edited"),
  121        ]
  122    );
  123
  124    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  125    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  126    assert_eq!(
  127        mem::take(&mut *events.borrow_mut()),
  128        [
  129            ("editor1", "edited"),
  130            ("editor1", "buffer edited"),
  131            ("editor2", "buffer edited"),
  132        ]
  133    );
  134
  135    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  136    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  137    assert_eq!(
  138        mem::take(&mut *events.borrow_mut()),
  139        [
  140            ("editor1", "edited"),
  141            ("editor1", "buffer edited"),
  142            ("editor2", "buffer edited"),
  143        ]
  144    );
  145
  146    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  147    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  148    assert_eq!(
  149        mem::take(&mut *events.borrow_mut()),
  150        [
  151            ("editor2", "edited"),
  152            ("editor1", "buffer edited"),
  153            ("editor2", "buffer edited"),
  154        ]
  155    );
  156
  157    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  158    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  159    assert_eq!(
  160        mem::take(&mut *events.borrow_mut()),
  161        [
  162            ("editor2", "edited"),
  163            ("editor1", "buffer edited"),
  164            ("editor2", "buffer edited"),
  165        ]
  166    );
  167
  168    // No event is emitted when the mutation is a no-op.
  169    _ = editor2.update(cx, |editor, window, cx| {
  170        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  171
  172        editor.backspace(&Backspace, window, cx);
  173    });
  174    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  175}
  176
  177#[gpui::test]
  178fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  179    init_test(cx, |_| {});
  180
  181    let mut now = Instant::now();
  182    let group_interval = Duration::from_millis(1);
  183    let buffer = cx.new(|cx| {
  184        let mut buf = language::Buffer::local("123456", cx);
  185        buf.set_group_interval(group_interval);
  186        buf
  187    });
  188    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  189    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  190
  191    _ = editor.update(cx, |editor, window, cx| {
  192        editor.start_transaction_at(now, window, cx);
  193        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  194
  195        editor.insert("cd", window, cx);
  196        editor.end_transaction_at(now, cx);
  197        assert_eq!(editor.text(cx), "12cd56");
  198        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  199
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  202        editor.insert("e", window, cx);
  203        editor.end_transaction_at(now, cx);
  204        assert_eq!(editor.text(cx), "12cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  206
  207        now += group_interval + Duration::from_millis(1);
  208        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  209
  210        // Simulate an edit in another editor
  211        buffer.update(cx, |buffer, cx| {
  212            buffer.start_transaction_at(now, cx);
  213            buffer.edit([(0..1, "a")], None, cx);
  214            buffer.edit([(1..1, "b")], None, cx);
  215            buffer.end_transaction_at(now, cx);
  216        });
  217
  218        assert_eq!(editor.text(cx), "ab2cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  220
  221        // Last transaction happened past the group interval in a different editor.
  222        // Undo it individually and don't restore selections.
  223        editor.undo(&Undo, window, cx);
  224        assert_eq!(editor.text(cx), "12cde6");
  225        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  226
  227        // First two transactions happened within the group interval in this editor.
  228        // Undo them together and restore selections.
  229        editor.undo(&Undo, window, cx);
  230        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  231        assert_eq!(editor.text(cx), "123456");
  232        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  233
  234        // Redo the first two transactions together.
  235        editor.redo(&Redo, window, cx);
  236        assert_eq!(editor.text(cx), "12cde6");
  237        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  238
  239        // Redo the last transaction on its own.
  240        editor.redo(&Redo, window, cx);
  241        assert_eq!(editor.text(cx), "ab2cde6");
  242        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  243
  244        // Test empty transactions.
  245        editor.start_transaction_at(now, window, cx);
  246        editor.end_transaction_at(now, cx);
  247        editor.undo(&Undo, window, cx);
  248        assert_eq!(editor.text(cx), "12cde6");
  249    });
  250}
  251
  252#[gpui::test]
  253fn test_ime_composition(cx: &mut TestAppContext) {
  254    init_test(cx, |_| {});
  255
  256    let buffer = cx.new(|cx| {
  257        let mut buffer = language::Buffer::local("abcde", cx);
  258        // Ensure automatic grouping doesn't occur.
  259        buffer.set_group_interval(Duration::ZERO);
  260        buffer
  261    });
  262
  263    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  264    cx.add_window(|window, cx| {
  265        let mut editor = build_editor(buffer.clone(), window, cx);
  266
  267        // Start a new IME composition.
  268        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  269        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  270        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  271        assert_eq!(editor.text(cx), "äbcde");
  272        assert_eq!(
  273            editor.marked_text_ranges(cx),
  274            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  275        );
  276
  277        // Finalize IME composition.
  278        editor.replace_text_in_range(None, "ā", window, cx);
  279        assert_eq!(editor.text(cx), "ābcde");
  280        assert_eq!(editor.marked_text_ranges(cx), None);
  281
  282        // IME composition edits are grouped and are undone/redone at once.
  283        editor.undo(&Default::default(), window, cx);
  284        assert_eq!(editor.text(cx), "abcde");
  285        assert_eq!(editor.marked_text_ranges(cx), None);
  286        editor.redo(&Default::default(), window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // Start a new IME composition.
  291        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  295        );
  296
  297        // Undoing during an IME composition cancels it.
  298        editor.undo(&Default::default(), window, cx);
  299        assert_eq!(editor.text(cx), "ābcde");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  303        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  304        assert_eq!(editor.text(cx), "ābcdè");
  305        assert_eq!(
  306            editor.marked_text_ranges(cx),
  307            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  308        );
  309
  310        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  311        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  312        assert_eq!(editor.text(cx), "ābcdę");
  313        assert_eq!(editor.marked_text_ranges(cx), None);
  314
  315        // Start a new IME composition with multiple cursors.
  316        editor.change_selections(None, window, cx, |s| {
  317            s.select_ranges([
  318                OffsetUtf16(1)..OffsetUtf16(1),
  319                OffsetUtf16(3)..OffsetUtf16(3),
  320                OffsetUtf16(5)..OffsetUtf16(5),
  321            ])
  322        });
  323        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  324        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  325        assert_eq!(
  326            editor.marked_text_ranges(cx),
  327            Some(vec![
  328                OffsetUtf16(0)..OffsetUtf16(3),
  329                OffsetUtf16(4)..OffsetUtf16(7),
  330                OffsetUtf16(8)..OffsetUtf16(11)
  331            ])
  332        );
  333
  334        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  335        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  336        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  337        assert_eq!(
  338            editor.marked_text_ranges(cx),
  339            Some(vec![
  340                OffsetUtf16(1)..OffsetUtf16(2),
  341                OffsetUtf16(5)..OffsetUtf16(6),
  342                OffsetUtf16(9)..OffsetUtf16(10)
  343            ])
  344        );
  345
  346        // Finalize IME composition with multiple cursors.
  347        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  348        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  349        assert_eq!(editor.marked_text_ranges(cx), None);
  350
  351        editor
  352    });
  353}
  354
  355#[gpui::test]
  356fn test_selection_with_mouse(cx: &mut TestAppContext) {
  357    init_test(cx, |_| {});
  358
  359    let editor = cx.add_window(|window, cx| {
  360        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  361        build_editor(buffer, window, cx)
  362    });
  363
  364    _ = editor.update(cx, |editor, window, cx| {
  365        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  366    });
  367    assert_eq!(
  368        editor
  369            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  370            .unwrap(),
  371        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  372    );
  373
  374    _ = editor.update(cx, |editor, window, cx| {
  375        editor.update_selection(
  376            DisplayPoint::new(DisplayRow(3), 3),
  377            0,
  378            gpui::Point::<f32>::default(),
  379            window,
  380            cx,
  381        );
  382    });
  383
  384    assert_eq!(
  385        editor
  386            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  387            .unwrap(),
  388        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  389    );
  390
  391    _ = editor.update(cx, |editor, window, cx| {
  392        editor.update_selection(
  393            DisplayPoint::new(DisplayRow(1), 1),
  394            0,
  395            gpui::Point::<f32>::default(),
  396            window,
  397            cx,
  398        );
  399    });
  400
  401    assert_eq!(
  402        editor
  403            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  404            .unwrap(),
  405        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  406    );
  407
  408    _ = editor.update(cx, |editor, window, cx| {
  409        editor.end_selection(window, cx);
  410        editor.update_selection(
  411            DisplayPoint::new(DisplayRow(3), 3),
  412            0,
  413            gpui::Point::<f32>::default(),
  414            window,
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  422            .unwrap(),
  423        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  424    );
  425
  426    _ = editor.update(cx, |editor, window, cx| {
  427        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  428        editor.update_selection(
  429            DisplayPoint::new(DisplayRow(0), 0),
  430            0,
  431            gpui::Point::<f32>::default(),
  432            window,
  433            cx,
  434        );
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  440            .unwrap(),
  441        [
  442            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  443            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  444        ]
  445    );
  446
  447    _ = editor.update(cx, |editor, window, cx| {
  448        editor.end_selection(window, cx);
  449    });
  450
  451    assert_eq!(
  452        editor
  453            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  454            .unwrap(),
  455        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  456    );
  457}
  458
  459#[gpui::test]
  460fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  461    init_test(cx, |_| {});
  462
  463    let editor = cx.add_window(|window, cx| {
  464        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  465        build_editor(buffer, window, cx)
  466    });
  467
  468    _ = editor.update(cx, |editor, window, cx| {
  469        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  470    });
  471
  472    _ = editor.update(cx, |editor, window, cx| {
  473        editor.end_selection(window, cx);
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  487            .unwrap(),
  488        [
  489            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  490            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  491        ]
  492    );
  493
  494    _ = editor.update(cx, |editor, window, cx| {
  495        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  496    });
  497
  498    _ = editor.update(cx, |editor, window, cx| {
  499        editor.end_selection(window, cx);
  500    });
  501
  502    assert_eq!(
  503        editor
  504            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  505            .unwrap(),
  506        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  507    );
  508}
  509
  510#[gpui::test]
  511fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  512    init_test(cx, |_| {});
  513
  514    let editor = cx.add_window(|window, cx| {
  515        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  516        build_editor(buffer, window, cx)
  517    });
  518
  519    _ = editor.update(cx, |editor, window, cx| {
  520        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  521        assert_eq!(
  522            editor.selections.display_ranges(cx),
  523            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  524        );
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.update_selection(
  529            DisplayPoint::new(DisplayRow(3), 3),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            window,
  533            cx,
  534        );
  535        assert_eq!(
  536            editor.selections.display_ranges(cx),
  537            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  538        );
  539    });
  540
  541    _ = editor.update(cx, |editor, window, cx| {
  542        editor.cancel(&Cancel, window, cx);
  543        editor.update_selection(
  544            DisplayPoint::new(DisplayRow(1), 1),
  545            0,
  546            gpui::Point::<f32>::default(),
  547            window,
  548            cx,
  549        );
  550        assert_eq!(
  551            editor.selections.display_ranges(cx),
  552            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  553        );
  554    });
  555}
  556
  557#[gpui::test]
  558fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  559    init_test(cx, |_| {});
  560
  561    let editor = cx.add_window(|window, cx| {
  562        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  563        build_editor(buffer, window, cx)
  564    });
  565
  566    _ = editor.update(cx, |editor, window, cx| {
  567        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  568        assert_eq!(
  569            editor.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        editor.move_down(&Default::default(), window, cx);
  574        assert_eq!(
  575            editor.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  577        );
  578
  579        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  580        assert_eq!(
  581            editor.selections.display_ranges(cx),
  582            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  583        );
  584
  585        editor.move_up(&Default::default(), window, cx);
  586        assert_eq!(
  587            editor.selections.display_ranges(cx),
  588            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  589        );
  590    });
  591}
  592
  593#[gpui::test]
  594fn test_clone(cx: &mut TestAppContext) {
  595    init_test(cx, |_| {});
  596
  597    let (text, selection_ranges) = marked_text_ranges(
  598        indoc! {"
  599            one
  600            two
  601            threeˇ
  602            four
  603            fiveˇ
  604        "},
  605        true,
  606    );
  607
  608    let editor = cx.add_window(|window, cx| {
  609        let buffer = MultiBuffer::build_simple(&text, cx);
  610        build_editor(buffer, window, cx)
  611    });
  612
  613    _ = editor.update(cx, |editor, window, cx| {
  614        editor.change_selections(None, window, cx, |s| {
  615            s.select_ranges(selection_ranges.clone())
  616        });
  617        editor.fold_creases(
  618            vec![
  619                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  620                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  621            ],
  622            true,
  623            window,
  624            cx,
  625        );
  626    });
  627
  628    let cloned_editor = editor
  629        .update(cx, |editor, _, cx| {
  630            cx.open_window(Default::default(), |window, cx| {
  631                cx.new(|cx| editor.clone(window, cx))
  632            })
  633        })
  634        .unwrap()
  635        .unwrap();
  636
  637    let snapshot = editor
  638        .update(cx, |e, window, cx| e.snapshot(window, cx))
  639        .unwrap();
  640    let cloned_snapshot = cloned_editor
  641        .update(cx, |e, window, cx| e.snapshot(window, cx))
  642        .unwrap();
  643
  644    assert_eq!(
  645        cloned_editor
  646            .update(cx, |e, _, cx| e.display_text(cx))
  647            .unwrap(),
  648        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  649    );
  650    assert_eq!(
  651        cloned_snapshot
  652            .folds_in_range(0..text.len())
  653            .collect::<Vec<_>>(),
  654        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  655    );
  656    assert_set_eq!(
  657        cloned_editor
  658            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  659            .unwrap(),
  660        editor
  661            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  662            .unwrap()
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  670            .unwrap()
  671    );
  672}
  673
  674#[gpui::test]
  675async fn test_navigation_history(cx: &mut TestAppContext) {
  676    init_test(cx, |_| {});
  677
  678    use workspace::item::Item;
  679
  680    let fs = FakeFs::new(cx.executor());
  681    let project = Project::test(fs, [], cx).await;
  682    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  683    let pane = workspace
  684        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  685        .unwrap();
  686
  687    _ = workspace.update(cx, |_v, window, cx| {
  688        cx.new(|cx| {
  689            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  690            let mut editor = build_editor(buffer.clone(), window, cx);
  691            let handle = cx.entity();
  692            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  693
  694            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  695                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  696            }
  697
  698            // Move the cursor a small distance.
  699            // Nothing is added to the navigation history.
  700            editor.change_selections(None, window, cx, |s| {
  701                s.select_display_ranges([
  702                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  703                ])
  704            });
  705            editor.change_selections(None, window, cx, |s| {
  706                s.select_display_ranges([
  707                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  708                ])
  709            });
  710            assert!(pop_history(&mut editor, cx).is_none());
  711
  712            // Move the cursor a large distance.
  713            // The history can jump back to the previous position.
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  717                ])
  718            });
  719            let nav_entry = pop_history(&mut editor, cx).unwrap();
  720            editor.navigate(nav_entry.data.unwrap(), window, cx);
  721            assert_eq!(nav_entry.item.id(), cx.entity_id());
  722            assert_eq!(
  723                editor.selections.display_ranges(cx),
  724                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  725            );
  726            assert!(pop_history(&mut editor, cx).is_none());
  727
  728            // Move the cursor a small distance via the mouse.
  729            // Nothing is added to the navigation history.
  730            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  731            editor.end_selection(window, cx);
  732            assert_eq!(
  733                editor.selections.display_ranges(cx),
  734                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  735            );
  736            assert!(pop_history(&mut editor, cx).is_none());
  737
  738            // Move the cursor a large distance via the mouse.
  739            // The history can jump back to the previous position.
  740            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  741            editor.end_selection(window, cx);
  742            assert_eq!(
  743                editor.selections.display_ranges(cx),
  744                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  745            );
  746            let nav_entry = pop_history(&mut editor, cx).unwrap();
  747            editor.navigate(nav_entry.data.unwrap(), window, cx);
  748            assert_eq!(nav_entry.item.id(), cx.entity_id());
  749            assert_eq!(
  750                editor.selections.display_ranges(cx),
  751                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  752            );
  753            assert!(pop_history(&mut editor, cx).is_none());
  754
  755            // Set scroll position to check later
  756            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  757            let original_scroll_position = editor.scroll_manager.anchor();
  758
  759            // Jump to the end of the document and adjust scroll
  760            editor.move_to_end(&MoveToEnd, window, cx);
  761            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  762            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  763
  764            let nav_entry = pop_history(&mut editor, cx).unwrap();
  765            editor.navigate(nav_entry.data.unwrap(), window, cx);
  766            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  767
  768            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  769            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  770            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  771            let invalid_point = Point::new(9999, 0);
  772            editor.navigate(
  773                Box::new(NavigationData {
  774                    cursor_anchor: invalid_anchor,
  775                    cursor_position: invalid_point,
  776                    scroll_anchor: ScrollAnchor {
  777                        anchor: invalid_anchor,
  778                        offset: Default::default(),
  779                    },
  780                    scroll_top_row: invalid_point.row,
  781                }),
  782                window,
  783                cx,
  784            );
  785            assert_eq!(
  786                editor.selections.display_ranges(cx),
  787                &[editor.max_point(cx)..editor.max_point(cx)]
  788            );
  789            assert_eq!(
  790                editor.scroll_position(cx),
  791                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  792            );
  793
  794            editor
  795        })
  796    });
  797}
  798
  799#[gpui::test]
  800fn test_cancel(cx: &mut TestAppContext) {
  801    init_test(cx, |_| {});
  802
  803    let editor = cx.add_window(|window, cx| {
  804        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  805        build_editor(buffer, window, cx)
  806    });
  807
  808    _ = editor.update(cx, |editor, window, cx| {
  809        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  810        editor.update_selection(
  811            DisplayPoint::new(DisplayRow(1), 1),
  812            0,
  813            gpui::Point::<f32>::default(),
  814            window,
  815            cx,
  816        );
  817        editor.end_selection(window, cx);
  818
  819        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  820        editor.update_selection(
  821            DisplayPoint::new(DisplayRow(0), 3),
  822            0,
  823            gpui::Point::<f32>::default(),
  824            window,
  825            cx,
  826        );
  827        editor.end_selection(window, cx);
  828        assert_eq!(
  829            editor.selections.display_ranges(cx),
  830            [
  831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  832                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  833            ]
  834        );
  835    });
  836
  837    _ = editor.update(cx, |editor, window, cx| {
  838        editor.cancel(&Cancel, window, cx);
  839        assert_eq!(
  840            editor.selections.display_ranges(cx),
  841            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852}
  853
  854#[gpui::test]
  855fn test_fold_action(cx: &mut TestAppContext) {
  856    init_test(cx, |_| {});
  857
  858    let editor = cx.add_window(|window, cx| {
  859        let buffer = MultiBuffer::build_simple(
  860            &"
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {
  869                        2
  870                    }
  871
  872                    fn c() {
  873                        3
  874                    }
  875                }
  876            "
  877            .unindent(),
  878            cx,
  879        );
  880        build_editor(buffer.clone(), window, cx)
  881    });
  882
  883    _ = editor.update(cx, |editor, window, cx| {
  884        editor.change_selections(None, window, cx, |s| {
  885            s.select_display_ranges([
  886                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  887            ]);
  888        });
  889        editor.fold(&Fold, window, cx);
  890        assert_eq!(
  891            editor.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        editor.fold(&Fold, window, cx);
  911        assert_eq!(
  912            editor.display_text(cx),
  913            "
  914                impl Foo {⋯
  915                }
  916            "
  917            .unindent(),
  918        );
  919
  920        editor.unfold_lines(&UnfoldLines, window, cx);
  921        assert_eq!(
  922            editor.display_text(cx),
  923            "
  924                impl Foo {
  925                    // Hello!
  926
  927                    fn a() {
  928                        1
  929                    }
  930
  931                    fn b() {⋯
  932                    }
  933
  934                    fn c() {⋯
  935                    }
  936                }
  937            "
  938            .unindent(),
  939        );
  940
  941        editor.unfold_lines(&UnfoldLines, window, cx);
  942        assert_eq!(
  943            editor.display_text(cx),
  944            editor.buffer.read(cx).read(cx).text()
  945        );
  946    });
  947}
  948
  949#[gpui::test]
  950fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  951    init_test(cx, |_| {});
  952
  953    let editor = cx.add_window(|window, cx| {
  954        let buffer = MultiBuffer::build_simple(
  955            &"
  956                class Foo:
  957                    # Hello!
  958
  959                    def a():
  960                        print(1)
  961
  962                    def b():
  963                        print(2)
  964
  965                    def c():
  966                        print(3)
  967            "
  968            .unindent(),
  969            cx,
  970        );
  971        build_editor(buffer.clone(), window, cx)
  972    });
  973
  974    _ = editor.update(cx, |editor, window, cx| {
  975        editor.change_selections(None, window, cx, |s| {
  976            s.select_display_ranges([
  977                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  978            ]);
  979        });
  980        editor.fold(&Fold, window, cx);
  981        assert_eq!(
  982            editor.display_text(cx),
  983            "
  984                class Foo:
  985                    # Hello!
  986
  987                    def a():
  988                        print(1)
  989
  990                    def b():⋯
  991
  992                    def c():⋯
  993            "
  994            .unindent(),
  995        );
  996
  997        editor.fold(&Fold, window, cx);
  998        assert_eq!(
  999            editor.display_text(cx),
 1000            "
 1001                class Foo:⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.unfold_lines(&UnfoldLines, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:
 1011                    # Hello!
 1012
 1013                    def a():
 1014                        print(1)
 1015
 1016                    def b():⋯
 1017
 1018                    def c():⋯
 1019            "
 1020            .unindent(),
 1021        );
 1022
 1023        editor.unfold_lines(&UnfoldLines, window, cx);
 1024        assert_eq!(
 1025            editor.display_text(cx),
 1026            editor.buffer.read(cx).read(cx).text()
 1027        );
 1028    });
 1029}
 1030
 1031#[gpui::test]
 1032fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1033    init_test(cx, |_| {});
 1034
 1035    let editor = cx.add_window(|window, cx| {
 1036        let buffer = MultiBuffer::build_simple(
 1037            &"
 1038                class Foo:
 1039                    # Hello!
 1040
 1041                    def a():
 1042                        print(1)
 1043
 1044                    def b():
 1045                        print(2)
 1046
 1047
 1048                    def c():
 1049                        print(3)
 1050
 1051
 1052            "
 1053            .unindent(),
 1054            cx,
 1055        );
 1056        build_editor(buffer.clone(), window, cx)
 1057    });
 1058
 1059    _ = editor.update(cx, |editor, window, cx| {
 1060        editor.change_selections(None, window, cx, |s| {
 1061            s.select_display_ranges([
 1062                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1063            ]);
 1064        });
 1065        editor.fold(&Fold, window, cx);
 1066        assert_eq!(
 1067            editor.display_text(cx),
 1068            "
 1069                class Foo:
 1070                    # Hello!
 1071
 1072                    def a():
 1073                        print(1)
 1074
 1075                    def b():⋯
 1076
 1077
 1078                    def c():⋯
 1079
 1080
 1081            "
 1082            .unindent(),
 1083        );
 1084
 1085        editor.fold(&Fold, window, cx);
 1086        assert_eq!(
 1087            editor.display_text(cx),
 1088            "
 1089                class Foo:⋯
 1090
 1091
 1092            "
 1093            .unindent(),
 1094        );
 1095
 1096        editor.unfold_lines(&UnfoldLines, window, cx);
 1097        assert_eq!(
 1098            editor.display_text(cx),
 1099            "
 1100                class Foo:
 1101                    # Hello!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():⋯
 1107
 1108
 1109                    def c():⋯
 1110
 1111
 1112            "
 1113            .unindent(),
 1114        );
 1115
 1116        editor.unfold_lines(&UnfoldLines, window, cx);
 1117        assert_eq!(
 1118            editor.display_text(cx),
 1119            editor.buffer.read(cx).read(cx).text()
 1120        );
 1121    });
 1122}
 1123
 1124#[gpui::test]
 1125fn test_fold_at_level(cx: &mut TestAppContext) {
 1126    init_test(cx, |_| {});
 1127
 1128    let editor = cx.add_window(|window, cx| {
 1129        let buffer = MultiBuffer::build_simple(
 1130            &"
 1131                class Foo:
 1132                    # Hello!
 1133
 1134                    def a():
 1135                        print(1)
 1136
 1137                    def b():
 1138                        print(2)
 1139
 1140
 1141                class Bar:
 1142                    # World!
 1143
 1144                    def a():
 1145                        print(1)
 1146
 1147                    def b():
 1148                        print(2)
 1149
 1150
 1151            "
 1152            .unindent(),
 1153            cx,
 1154        );
 1155        build_editor(buffer.clone(), window, cx)
 1156    });
 1157
 1158    _ = editor.update(cx, |editor, window, cx| {
 1159        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1160        assert_eq!(
 1161            editor.display_text(cx),
 1162            "
 1163                class Foo:
 1164                    # Hello!
 1165
 1166                    def a():⋯
 1167
 1168                    def b():⋯
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179            "
 1180            .unindent(),
 1181        );
 1182
 1183        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1184        assert_eq!(
 1185            editor.display_text(cx),
 1186            "
 1187                class Foo:⋯
 1188
 1189
 1190                class Bar:⋯
 1191
 1192
 1193            "
 1194            .unindent(),
 1195        );
 1196
 1197        editor.unfold_all(&UnfoldAll, window, cx);
 1198        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1199        assert_eq!(
 1200            editor.display_text(cx),
 1201            "
 1202                class Foo:
 1203                    # Hello!
 1204
 1205                    def a():
 1206                        print(1)
 1207
 1208                    def b():
 1209                        print(2)
 1210
 1211
 1212                class Bar:
 1213                    # World!
 1214
 1215                    def a():
 1216                        print(1)
 1217
 1218                    def b():
 1219                        print(2)
 1220
 1221
 1222            "
 1223            .unindent(),
 1224        );
 1225
 1226        assert_eq!(
 1227            editor.display_text(cx),
 1228            editor.buffer.read(cx).read(cx).text()
 1229        );
 1230    });
 1231}
 1232
 1233#[gpui::test]
 1234fn test_move_cursor(cx: &mut TestAppContext) {
 1235    init_test(cx, |_| {});
 1236
 1237    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1238    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1239
 1240    buffer.update(cx, |buffer, cx| {
 1241        buffer.edit(
 1242            vec![
 1243                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1244                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1245            ],
 1246            None,
 1247            cx,
 1248        );
 1249    });
 1250    _ = editor.update(cx, |editor, window, cx| {
 1251        assert_eq!(
 1252            editor.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        editor.move_down(&MoveDown, window, cx);
 1257        assert_eq!(
 1258            editor.selections.display_ranges(cx),
 1259            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1260        );
 1261
 1262        editor.move_right(&MoveRight, window, cx);
 1263        assert_eq!(
 1264            editor.selections.display_ranges(cx),
 1265            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1266        );
 1267
 1268        editor.move_left(&MoveLeft, window, cx);
 1269        assert_eq!(
 1270            editor.selections.display_ranges(cx),
 1271            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1272        );
 1273
 1274        editor.move_up(&MoveUp, window, cx);
 1275        assert_eq!(
 1276            editor.selections.display_ranges(cx),
 1277            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1278        );
 1279
 1280        editor.move_to_end(&MoveToEnd, window, cx);
 1281        assert_eq!(
 1282            editor.selections.display_ranges(cx),
 1283            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1284        );
 1285
 1286        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1287        assert_eq!(
 1288            editor.selections.display_ranges(cx),
 1289            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1290        );
 1291
 1292        editor.change_selections(None, window, cx, |s| {
 1293            s.select_display_ranges([
 1294                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1295            ]);
 1296        });
 1297        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1298        assert_eq!(
 1299            editor.selections.display_ranges(cx),
 1300            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1301        );
 1302
 1303        editor.select_to_end(&SelectToEnd, window, cx);
 1304        assert_eq!(
 1305            editor.selections.display_ranges(cx),
 1306            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1307        );
 1308    });
 1309}
 1310
 1311// TODO: Re-enable this test
 1312#[cfg(target_os = "macos")]
 1313#[gpui::test]
 1314fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1315    init_test(cx, |_| {});
 1316
 1317    let editor = cx.add_window(|window, cx| {
 1318        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1319        build_editor(buffer.clone(), window, cx)
 1320    });
 1321
 1322    assert_eq!('🟥'.len_utf8(), 4);
 1323    assert_eq!('α'.len_utf8(), 2);
 1324
 1325    _ = editor.update(cx, |editor, window, cx| {
 1326        editor.fold_creases(
 1327            vec![
 1328                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1329                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1330                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1331            ],
 1332            true,
 1333            window,
 1334            cx,
 1335        );
 1336        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1337
 1338        editor.move_right(&MoveRight, window, cx);
 1339        assert_eq!(
 1340            editor.selections.display_ranges(cx),
 1341            &[empty_range(0, "🟥".len())]
 1342        );
 1343        editor.move_right(&MoveRight, window, cx);
 1344        assert_eq!(
 1345            editor.selections.display_ranges(cx),
 1346            &[empty_range(0, "🟥🟧".len())]
 1347        );
 1348        editor.move_right(&MoveRight, window, cx);
 1349        assert_eq!(
 1350            editor.selections.display_ranges(cx),
 1351            &[empty_range(0, "🟥🟧⋯".len())]
 1352        );
 1353
 1354        editor.move_down(&MoveDown, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(1, "ab⋯e".len())]
 1358        );
 1359        editor.move_left(&MoveLeft, window, cx);
 1360        assert_eq!(
 1361            editor.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯".len())]
 1363        );
 1364        editor.move_left(&MoveLeft, window, cx);
 1365        assert_eq!(
 1366            editor.selections.display_ranges(cx),
 1367            &[empty_range(1, "ab".len())]
 1368        );
 1369        editor.move_left(&MoveLeft, window, cx);
 1370        assert_eq!(
 1371            editor.selections.display_ranges(cx),
 1372            &[empty_range(1, "a".len())]
 1373        );
 1374
 1375        editor.move_down(&MoveDown, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(2, "α".len())]
 1379        );
 1380        editor.move_right(&MoveRight, window, cx);
 1381        assert_eq!(
 1382            editor.selections.display_ranges(cx),
 1383            &[empty_range(2, "αβ".len())]
 1384        );
 1385        editor.move_right(&MoveRight, window, cx);
 1386        assert_eq!(
 1387            editor.selections.display_ranges(cx),
 1388            &[empty_range(2, "αβ⋯".len())]
 1389        );
 1390        editor.move_right(&MoveRight, window, cx);
 1391        assert_eq!(
 1392            editor.selections.display_ranges(cx),
 1393            &[empty_range(2, "αβ⋯ε".len())]
 1394        );
 1395
 1396        editor.move_up(&MoveUp, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(1, "ab⋯e".len())]
 1400        );
 1401        editor.move_down(&MoveDown, window, cx);
 1402        assert_eq!(
 1403            editor.selections.display_ranges(cx),
 1404            &[empty_range(2, "αβ⋯ε".len())]
 1405        );
 1406        editor.move_up(&MoveUp, window, cx);
 1407        assert_eq!(
 1408            editor.selections.display_ranges(cx),
 1409            &[empty_range(1, "ab⋯e".len())]
 1410        );
 1411
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(0, "🟥🟧".len())]
 1416        );
 1417        editor.move_left(&MoveLeft, window, cx);
 1418        assert_eq!(
 1419            editor.selections.display_ranges(cx),
 1420            &[empty_range(0, "🟥".len())]
 1421        );
 1422        editor.move_left(&MoveLeft, window, cx);
 1423        assert_eq!(
 1424            editor.selections.display_ranges(cx),
 1425            &[empty_range(0, "".len())]
 1426        );
 1427    });
 1428}
 1429
 1430#[gpui::test]
 1431fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1432    init_test(cx, |_| {});
 1433
 1434    let editor = cx.add_window(|window, cx| {
 1435        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1436        build_editor(buffer.clone(), window, cx)
 1437    });
 1438    _ = editor.update(cx, |editor, window, cx| {
 1439        editor.change_selections(None, window, cx, |s| {
 1440            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1441        });
 1442
 1443        // moving above start of document should move selection to start of document,
 1444        // but the next move down should still be at the original goal_x
 1445        editor.move_up(&MoveUp, window, cx);
 1446        assert_eq!(
 1447            editor.selections.display_ranges(cx),
 1448            &[empty_range(0, "".len())]
 1449        );
 1450
 1451        editor.move_down(&MoveDown, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(1, "abcd".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(2, "αβγ".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(3, "abcd".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1473        );
 1474
 1475        // moving past end of document should not change goal_x
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(5, "".len())]
 1480        );
 1481
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_up(&MoveUp, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(3, "abcd".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(2, "αβγ".len())]
 1504        );
 1505    });
 1506}
 1507
 1508#[gpui::test]
 1509fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1510    init_test(cx, |_| {});
 1511    let move_to_beg = MoveToBeginningOfLine {
 1512        stop_at_soft_wraps: true,
 1513    };
 1514
 1515    let move_to_end = MoveToEndOfLine {
 1516        stop_at_soft_wraps: true,
 1517    };
 1518
 1519    let editor = cx.add_window(|window, cx| {
 1520        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1521        build_editor(buffer, window, cx)
 1522    });
 1523    _ = editor.update(cx, |editor, window, cx| {
 1524        editor.change_selections(None, window, cx, |s| {
 1525            s.select_display_ranges([
 1526                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1527                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1528            ]);
 1529        });
 1530    });
 1531
 1532    _ = editor.update(cx, |editor, window, cx| {
 1533        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1534        assert_eq!(
 1535            editor.selections.display_ranges(cx),
 1536            &[
 1537                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1538                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1539            ]
 1540        );
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_end_of_line(&move_to_end, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1571                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1572            ]
 1573        );
 1574    });
 1575
 1576    // Moving to the end of line again is a no-op.
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_left(&MoveLeft, window, cx);
 1590        editor.select_to_beginning_of_line(
 1591            &SelectToBeginningOfLine {
 1592                stop_at_soft_wraps: true,
 1593            },
 1594            window,
 1595            cx,
 1596        );
 1597        assert_eq!(
 1598            editor.selections.display_ranges(cx),
 1599            &[
 1600                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1601                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1602            ]
 1603        );
 1604    });
 1605
 1606    _ = editor.update(cx, |editor, window, cx| {
 1607        editor.select_to_beginning_of_line(
 1608            &SelectToBeginningOfLine {
 1609                stop_at_soft_wraps: true,
 1610            },
 1611            window,
 1612            cx,
 1613        );
 1614        assert_eq!(
 1615            editor.selections.display_ranges(cx),
 1616            &[
 1617                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1618                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1619            ]
 1620        );
 1621    });
 1622
 1623    _ = editor.update(cx, |editor, window, cx| {
 1624        editor.select_to_beginning_of_line(
 1625            &SelectToBeginningOfLine {
 1626                stop_at_soft_wraps: true,
 1627            },
 1628            window,
 1629            cx,
 1630        );
 1631        assert_eq!(
 1632            editor.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1636            ]
 1637        );
 1638    });
 1639
 1640    _ = editor.update(cx, |editor, window, cx| {
 1641        editor.select_to_end_of_line(
 1642            &SelectToEndOfLine {
 1643                stop_at_soft_wraps: true,
 1644            },
 1645            window,
 1646            cx,
 1647        );
 1648        assert_eq!(
 1649            editor.selections.display_ranges(cx),
 1650            &[
 1651                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1652                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1653            ]
 1654        );
 1655    });
 1656
 1657    _ = editor.update(cx, |editor, window, cx| {
 1658        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1659        assert_eq!(editor.display_text(cx), "ab\n  de");
 1660        assert_eq!(
 1661            editor.selections.display_ranges(cx),
 1662            &[
 1663                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1664                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1665            ]
 1666        );
 1667    });
 1668
 1669    _ = editor.update(cx, |editor, window, cx| {
 1670        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1671        assert_eq!(editor.display_text(cx), "\n");
 1672        assert_eq!(
 1673            editor.selections.display_ranges(cx),
 1674            &[
 1675                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1676                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1677            ]
 1678        );
 1679    });
 1680}
 1681
 1682#[gpui::test]
 1683fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1684    init_test(cx, |_| {});
 1685    let move_to_beg = MoveToBeginningOfLine {
 1686        stop_at_soft_wraps: false,
 1687    };
 1688
 1689    let move_to_end = MoveToEndOfLine {
 1690        stop_at_soft_wraps: false,
 1691    };
 1692
 1693    let editor = cx.add_window(|window, cx| {
 1694        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1695        build_editor(buffer, window, cx)
 1696    });
 1697
 1698    _ = editor.update(cx, |editor, window, cx| {
 1699        editor.set_wrap_width(Some(140.0.into()), cx);
 1700
 1701        // We expect the following lines after wrapping
 1702        // ```
 1703        // thequickbrownfox
 1704        // jumpedoverthelazydo
 1705        // gs
 1706        // ```
 1707        // The final `gs` was soft-wrapped onto a new line.
 1708        assert_eq!(
 1709            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1710            editor.display_text(cx),
 1711        );
 1712
 1713        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1714        // Start the cursor at the `k` on the first line
 1715        editor.change_selections(None, window, cx, |s| {
 1716            s.select_display_ranges([
 1717                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1718            ]);
 1719        });
 1720
 1721        // Moving to the beginning of the line should put us at the beginning of the line.
 1722        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1723        assert_eq!(
 1724            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1725            editor.selections.display_ranges(cx)
 1726        );
 1727
 1728        // Moving to the end of the line should put us at the end of the line.
 1729        editor.move_to_end_of_line(&move_to_end, window, cx);
 1730        assert_eq!(
 1731            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1732            editor.selections.display_ranges(cx)
 1733        );
 1734
 1735        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1736        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1737        editor.change_selections(None, window, cx, |s| {
 1738            s.select_display_ranges([
 1739                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1740            ]);
 1741        });
 1742
 1743        // Moving to the beginning of the line should put us at the start of the second line of
 1744        // display text, i.e., the `j`.
 1745        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Moving to the beginning of the line again should be a no-op.
 1752        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1753        assert_eq!(
 1754            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1755            editor.selections.display_ranges(cx)
 1756        );
 1757
 1758        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1759        // next display line.
 1760        editor.move_to_end_of_line(&move_to_end, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the end of the line again should be a no-op.
 1767        editor.move_to_end_of_line(&move_to_end, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772    });
 1773}
 1774
 1775#[gpui::test]
 1776fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1777    init_test(cx, |_| {});
 1778
 1779    let editor = cx.add_window(|window, cx| {
 1780        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1781        build_editor(buffer, window, cx)
 1782    });
 1783    _ = editor.update(cx, |editor, window, cx| {
 1784        editor.change_selections(None, window, cx, |s| {
 1785            s.select_display_ranges([
 1786                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1787                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1788            ])
 1789        });
 1790
 1791        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1792        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1793
 1794        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1795        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1796
 1797        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1798        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1799
 1800        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1801        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1802
 1803        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1804        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1805
 1806        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1807        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1808
 1809        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1810        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1811
 1812        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1813        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1814
 1815        editor.move_right(&MoveRight, window, cx);
 1816        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1817        assert_selection_ranges(
 1818            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1819            editor,
 1820            cx,
 1821        );
 1822
 1823        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1824        assert_selection_ranges(
 1825            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1826            editor,
 1827            cx,
 1828        );
 1829
 1830        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1831        assert_selection_ranges(
 1832            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1833            editor,
 1834            cx,
 1835        );
 1836    });
 1837}
 1838
 1839#[gpui::test]
 1840fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1841    init_test(cx, |_| {});
 1842
 1843    let editor = cx.add_window(|window, cx| {
 1844        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1845        build_editor(buffer, window, cx)
 1846    });
 1847
 1848    _ = editor.update(cx, |editor, window, cx| {
 1849        editor.set_wrap_width(Some(140.0.into()), cx);
 1850        assert_eq!(
 1851            editor.display_text(cx),
 1852            "use one::{\n    two::three::\n    four::five\n};"
 1853        );
 1854
 1855        editor.change_selections(None, window, cx, |s| {
 1856            s.select_display_ranges([
 1857                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1858            ]);
 1859        });
 1860
 1861        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1862        assert_eq!(
 1863            editor.selections.display_ranges(cx),
 1864            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1865        );
 1866
 1867        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1868        assert_eq!(
 1869            editor.selections.display_ranges(cx),
 1870            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1871        );
 1872
 1873        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1877        );
 1878
 1879        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1880        assert_eq!(
 1881            editor.selections.display_ranges(cx),
 1882            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1883        );
 1884
 1885        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1886        assert_eq!(
 1887            editor.selections.display_ranges(cx),
 1888            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1889        );
 1890
 1891        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1892        assert_eq!(
 1893            editor.selections.display_ranges(cx),
 1894            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1895        );
 1896    });
 1897}
 1898
 1899#[gpui::test]
 1900async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1901    init_test(cx, |_| {});
 1902    let mut cx = EditorTestContext::new(cx).await;
 1903
 1904    let line_height = cx.editor(|editor, window, _| {
 1905        editor
 1906            .style()
 1907            .unwrap()
 1908            .text
 1909            .line_height_in_pixels(window.rem_size())
 1910    });
 1911    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1912
 1913    cx.set_state(
 1914        &r#"ˇone
 1915        two
 1916
 1917        three
 1918        fourˇ
 1919        five
 1920
 1921        six"#
 1922            .unindent(),
 1923    );
 1924
 1925    cx.update_editor(|editor, window, cx| {
 1926        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1927    });
 1928    cx.assert_editor_state(
 1929        &r#"one
 1930        two
 1931        ˇ
 1932        three
 1933        four
 1934        five
 1935        ˇ
 1936        six"#
 1937            .unindent(),
 1938    );
 1939
 1940    cx.update_editor(|editor, window, cx| {
 1941        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1942    });
 1943    cx.assert_editor_state(
 1944        &r#"one
 1945        two
 1946
 1947        three
 1948        four
 1949        five
 1950        ˇ
 1951        sixˇ"#
 1952            .unindent(),
 1953    );
 1954
 1955    cx.update_editor(|editor, window, cx| {
 1956        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1957    });
 1958    cx.assert_editor_state(
 1959        &r#"one
 1960        two
 1961
 1962        three
 1963        four
 1964        five
 1965
 1966        sixˇ"#
 1967            .unindent(),
 1968    );
 1969
 1970    cx.update_editor(|editor, window, cx| {
 1971        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1972    });
 1973    cx.assert_editor_state(
 1974        &r#"one
 1975        two
 1976
 1977        three
 1978        four
 1979        five
 1980        ˇ
 1981        six"#
 1982            .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, window, cx| {
 1986        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1987    });
 1988    cx.assert_editor_state(
 1989        &r#"one
 1990        two
 1991        ˇ
 1992        three
 1993        four
 1994        five
 1995
 1996        six"#
 1997            .unindent(),
 1998    );
 1999
 2000    cx.update_editor(|editor, window, cx| {
 2001        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2002    });
 2003    cx.assert_editor_state(
 2004        &r#"ˇone
 2005        two
 2006
 2007        three
 2008        four
 2009        five
 2010
 2011        six"#
 2012            .unindent(),
 2013    );
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    let window = cx.window;
 2028    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2029
 2030    cx.set_state(
 2031        r#"ˇone
 2032        two
 2033        three
 2034        four
 2035        five
 2036        six
 2037        seven
 2038        eight
 2039        nine
 2040        ten
 2041        "#,
 2042    );
 2043
 2044    cx.update_editor(|editor, window, cx| {
 2045        assert_eq!(
 2046            editor.snapshot(window, cx).scroll_position(),
 2047            gpui::Point::new(0., 0.)
 2048        );
 2049        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2050        assert_eq!(
 2051            editor.snapshot(window, cx).scroll_position(),
 2052            gpui::Point::new(0., 3.)
 2053        );
 2054        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2055        assert_eq!(
 2056            editor.snapshot(window, cx).scroll_position(),
 2057            gpui::Point::new(0., 6.)
 2058        );
 2059        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2060        assert_eq!(
 2061            editor.snapshot(window, cx).scroll_position(),
 2062            gpui::Point::new(0., 3.)
 2063        );
 2064
 2065        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2066        assert_eq!(
 2067            editor.snapshot(window, cx).scroll_position(),
 2068            gpui::Point::new(0., 1.)
 2069        );
 2070        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2071        assert_eq!(
 2072            editor.snapshot(window, cx).scroll_position(),
 2073            gpui::Point::new(0., 3.)
 2074        );
 2075    });
 2076}
 2077
 2078#[gpui::test]
 2079async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2080    init_test(cx, |_| {});
 2081    let mut cx = EditorTestContext::new(cx).await;
 2082
 2083    let line_height = cx.update_editor(|editor, window, cx| {
 2084        editor.set_vertical_scroll_margin(2, cx);
 2085        editor
 2086            .style()
 2087            .unwrap()
 2088            .text
 2089            .line_height_in_pixels(window.rem_size())
 2090    });
 2091    let window = cx.window;
 2092    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2093
 2094    cx.set_state(
 2095        r#"ˇone
 2096            two
 2097            three
 2098            four
 2099            five
 2100            six
 2101            seven
 2102            eight
 2103            nine
 2104            ten
 2105        "#,
 2106    );
 2107    cx.update_editor(|editor, window, cx| {
 2108        assert_eq!(
 2109            editor.snapshot(window, cx).scroll_position(),
 2110            gpui::Point::new(0., 0.0)
 2111        );
 2112    });
 2113
 2114    // Add a cursor below the visible area. Since both cursors cannot fit
 2115    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2116    // allows the vertical scroll margin below that cursor.
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2119            selections.select_ranges([
 2120                Point::new(0, 0)..Point::new(0, 0),
 2121                Point::new(6, 0)..Point::new(6, 0),
 2122            ]);
 2123        })
 2124    });
 2125    cx.update_editor(|editor, window, cx| {
 2126        assert_eq!(
 2127            editor.snapshot(window, cx).scroll_position(),
 2128            gpui::Point::new(0., 3.0)
 2129        );
 2130    });
 2131
 2132    // Move down. The editor cursor scrolls down to track the newest cursor.
 2133    cx.update_editor(|editor, window, cx| {
 2134        editor.move_down(&Default::default(), window, cx);
 2135    });
 2136    cx.update_editor(|editor, window, cx| {
 2137        assert_eq!(
 2138            editor.snapshot(window, cx).scroll_position(),
 2139            gpui::Point::new(0., 4.0)
 2140        );
 2141    });
 2142
 2143    // Add a cursor above the visible area. Since both cursors fit on screen,
 2144    // the editor scrolls to show both.
 2145    cx.update_editor(|editor, window, cx| {
 2146        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2147            selections.select_ranges([
 2148                Point::new(1, 0)..Point::new(1, 0),
 2149                Point::new(6, 0)..Point::new(6, 0),
 2150            ]);
 2151        })
 2152    });
 2153    cx.update_editor(|editor, window, cx| {
 2154        assert_eq!(
 2155            editor.snapshot(window, cx).scroll_position(),
 2156            gpui::Point::new(0., 1.0)
 2157        );
 2158    });
 2159}
 2160
 2161#[gpui::test]
 2162async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2163    init_test(cx, |_| {});
 2164    let mut cx = EditorTestContext::new(cx).await;
 2165
 2166    let line_height = cx.editor(|editor, window, _cx| {
 2167        editor
 2168            .style()
 2169            .unwrap()
 2170            .text
 2171            .line_height_in_pixels(window.rem_size())
 2172    });
 2173    let window = cx.window;
 2174    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2175    cx.set_state(
 2176        &r#"
 2177        ˇone
 2178        two
 2179        threeˇ
 2180        four
 2181        five
 2182        six
 2183        seven
 2184        eight
 2185        nine
 2186        ten
 2187        "#
 2188        .unindent(),
 2189    );
 2190
 2191    cx.update_editor(|editor, window, cx| {
 2192        editor.move_page_down(&MovePageDown::default(), window, cx)
 2193    });
 2194    cx.assert_editor_state(
 2195        &r#"
 2196        one
 2197        two
 2198        three
 2199        ˇfour
 2200        five
 2201        sixˇ
 2202        seven
 2203        eight
 2204        nine
 2205        ten
 2206        "#
 2207        .unindent(),
 2208    );
 2209
 2210    cx.update_editor(|editor, window, cx| {
 2211        editor.move_page_down(&MovePageDown::default(), window, cx)
 2212    });
 2213    cx.assert_editor_state(
 2214        &r#"
 2215        one
 2216        two
 2217        three
 2218        four
 2219        five
 2220        six
 2221        ˇseven
 2222        eight
 2223        nineˇ
 2224        ten
 2225        "#
 2226        .unindent(),
 2227    );
 2228
 2229    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2230    cx.assert_editor_state(
 2231        &r#"
 2232        one
 2233        two
 2234        three
 2235        ˇfour
 2236        five
 2237        sixˇ
 2238        seven
 2239        eight
 2240        nine
 2241        ten
 2242        "#
 2243        .unindent(),
 2244    );
 2245
 2246    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2247    cx.assert_editor_state(
 2248        &r#"
 2249        ˇone
 2250        two
 2251        threeˇ
 2252        four
 2253        five
 2254        six
 2255        seven
 2256        eight
 2257        nine
 2258        ten
 2259        "#
 2260        .unindent(),
 2261    );
 2262
 2263    // Test select collapsing
 2264    cx.update_editor(|editor, window, cx| {
 2265        editor.move_page_down(&MovePageDown::default(), window, cx);
 2266        editor.move_page_down(&MovePageDown::default(), window, cx);
 2267        editor.move_page_down(&MovePageDown::default(), window, cx);
 2268    });
 2269    cx.assert_editor_state(
 2270        &r#"
 2271        one
 2272        two
 2273        three
 2274        four
 2275        five
 2276        six
 2277        seven
 2278        eight
 2279        nine
 2280        ˇten
 2281        ˇ"#
 2282        .unindent(),
 2283    );
 2284}
 2285
 2286#[gpui::test]
 2287async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2288    init_test(cx, |_| {});
 2289    let mut cx = EditorTestContext::new(cx).await;
 2290    cx.set_state("one «two threeˇ» four");
 2291    cx.update_editor(|editor, window, cx| {
 2292        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2293        assert_eq!(editor.text(cx), " four");
 2294    });
 2295}
 2296
 2297#[gpui::test]
 2298fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2299    init_test(cx, |_| {});
 2300
 2301    let editor = cx.add_window(|window, cx| {
 2302        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2303        build_editor(buffer.clone(), window, cx)
 2304    });
 2305
 2306    _ = editor.update(cx, |editor, window, cx| {
 2307        editor.change_selections(None, window, cx, |s| {
 2308            s.select_display_ranges([
 2309                // an empty selection - the preceding word fragment is deleted
 2310                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2311                // characters selected - they are deleted
 2312                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2313            ])
 2314        });
 2315        editor.delete_to_previous_word_start(
 2316            &DeleteToPreviousWordStart {
 2317                ignore_newlines: false,
 2318            },
 2319            window,
 2320            cx,
 2321        );
 2322        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2323    });
 2324
 2325    _ = editor.update(cx, |editor, window, cx| {
 2326        editor.change_selections(None, window, cx, |s| {
 2327            s.select_display_ranges([
 2328                // an empty selection - the following word fragment is deleted
 2329                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2330                // characters selected - they are deleted
 2331                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2332            ])
 2333        });
 2334        editor.delete_to_next_word_end(
 2335            &DeleteToNextWordEnd {
 2336                ignore_newlines: false,
 2337            },
 2338            window,
 2339            cx,
 2340        );
 2341        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2342    });
 2343}
 2344
 2345#[gpui::test]
 2346fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2347    init_test(cx, |_| {});
 2348
 2349    let editor = cx.add_window(|window, cx| {
 2350        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2351        build_editor(buffer.clone(), window, cx)
 2352    });
 2353    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2354        ignore_newlines: false,
 2355    };
 2356    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2357        ignore_newlines: true,
 2358    };
 2359
 2360    _ = editor.update(cx, |editor, window, cx| {
 2361        editor.change_selections(None, window, cx, |s| {
 2362            s.select_display_ranges([
 2363                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2364            ])
 2365        });
 2366        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2367        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2368        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2369        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2370        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2371        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2372        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2373        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2374        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2375        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2376        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2377        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2378    });
 2379}
 2380
 2381#[gpui::test]
 2382fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2383    init_test(cx, |_| {});
 2384
 2385    let editor = cx.add_window(|window, cx| {
 2386        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2387        build_editor(buffer.clone(), window, cx)
 2388    });
 2389    let del_to_next_word_end = DeleteToNextWordEnd {
 2390        ignore_newlines: false,
 2391    };
 2392    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2393        ignore_newlines: true,
 2394    };
 2395
 2396    _ = editor.update(cx, |editor, window, cx| {
 2397        editor.change_selections(None, window, cx, |s| {
 2398            s.select_display_ranges([
 2399                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2400            ])
 2401        });
 2402        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2403        assert_eq!(
 2404            editor.buffer.read(cx).read(cx).text(),
 2405            "one\n   two\nthree\n   four"
 2406        );
 2407        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2408        assert_eq!(
 2409            editor.buffer.read(cx).read(cx).text(),
 2410            "\n   two\nthree\n   four"
 2411        );
 2412        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2413        assert_eq!(
 2414            editor.buffer.read(cx).read(cx).text(),
 2415            "two\nthree\n   four"
 2416        );
 2417        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2418        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2419        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2420        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2421        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2422        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2423    });
 2424}
 2425
 2426#[gpui::test]
 2427fn test_newline(cx: &mut TestAppContext) {
 2428    init_test(cx, |_| {});
 2429
 2430    let editor = cx.add_window(|window, cx| {
 2431        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2432        build_editor(buffer.clone(), window, cx)
 2433    });
 2434
 2435    _ = editor.update(cx, |editor, window, cx| {
 2436        editor.change_selections(None, window, cx, |s| {
 2437            s.select_display_ranges([
 2438                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2439                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2440                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2441            ])
 2442        });
 2443
 2444        editor.newline(&Newline, window, cx);
 2445        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2446    });
 2447}
 2448
 2449#[gpui::test]
 2450fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2451    init_test(cx, |_| {});
 2452
 2453    let editor = cx.add_window(|window, cx| {
 2454        let buffer = MultiBuffer::build_simple(
 2455            "
 2456                a
 2457                b(
 2458                    X
 2459                )
 2460                c(
 2461                    X
 2462                )
 2463            "
 2464            .unindent()
 2465            .as_str(),
 2466            cx,
 2467        );
 2468        let mut editor = build_editor(buffer.clone(), window, cx);
 2469        editor.change_selections(None, window, cx, |s| {
 2470            s.select_ranges([
 2471                Point::new(2, 4)..Point::new(2, 5),
 2472                Point::new(5, 4)..Point::new(5, 5),
 2473            ])
 2474        });
 2475        editor
 2476    });
 2477
 2478    _ = editor.update(cx, |editor, window, cx| {
 2479        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2480        editor.buffer.update(cx, |buffer, cx| {
 2481            buffer.edit(
 2482                [
 2483                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2484                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2485                ],
 2486                None,
 2487                cx,
 2488            );
 2489            assert_eq!(
 2490                buffer.read(cx).text(),
 2491                "
 2492                    a
 2493                    b()
 2494                    c()
 2495                "
 2496                .unindent()
 2497            );
 2498        });
 2499        assert_eq!(
 2500            editor.selections.ranges(cx),
 2501            &[
 2502                Point::new(1, 2)..Point::new(1, 2),
 2503                Point::new(2, 2)..Point::new(2, 2),
 2504            ],
 2505        );
 2506
 2507        editor.newline(&Newline, window, cx);
 2508        assert_eq!(
 2509            editor.text(cx),
 2510            "
 2511                a
 2512                b(
 2513                )
 2514                c(
 2515                )
 2516            "
 2517            .unindent()
 2518        );
 2519
 2520        // The selections are moved after the inserted newlines
 2521        assert_eq!(
 2522            editor.selections.ranges(cx),
 2523            &[
 2524                Point::new(2, 0)..Point::new(2, 0),
 2525                Point::new(4, 0)..Point::new(4, 0),
 2526            ],
 2527        );
 2528    });
 2529}
 2530
 2531#[gpui::test]
 2532async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2533    init_test(cx, |settings| {
 2534        settings.defaults.tab_size = NonZeroU32::new(4)
 2535    });
 2536
 2537    let language = Arc::new(
 2538        Language::new(
 2539            LanguageConfig::default(),
 2540            Some(tree_sitter_rust::LANGUAGE.into()),
 2541        )
 2542        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2543        .unwrap(),
 2544    );
 2545
 2546    let mut cx = EditorTestContext::new(cx).await;
 2547    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2548    cx.set_state(indoc! {"
 2549        const a: ˇA = (
 2550 2551                «const_functionˇ»(ˇ),
 2552                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2553 2554        ˇ);ˇ
 2555    "});
 2556
 2557    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2558    cx.assert_editor_state(indoc! {"
 2559        ˇ
 2560        const a: A = (
 2561            ˇ
 2562            (
 2563                ˇ
 2564                ˇ
 2565                const_function(),
 2566                ˇ
 2567                ˇ
 2568                ˇ
 2569                ˇ
 2570                something_else,
 2571                ˇ
 2572            )
 2573            ˇ
 2574            ˇ
 2575        );
 2576    "});
 2577}
 2578
 2579#[gpui::test]
 2580async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2581    init_test(cx, |settings| {
 2582        settings.defaults.tab_size = NonZeroU32::new(4)
 2583    });
 2584
 2585    let language = Arc::new(
 2586        Language::new(
 2587            LanguageConfig::default(),
 2588            Some(tree_sitter_rust::LANGUAGE.into()),
 2589        )
 2590        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2591        .unwrap(),
 2592    );
 2593
 2594    let mut cx = EditorTestContext::new(cx).await;
 2595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2596    cx.set_state(indoc! {"
 2597        const a: ˇA = (
 2598 2599                «const_functionˇ»(ˇ),
 2600                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2601 2602        ˇ);ˇ
 2603    "});
 2604
 2605    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2606    cx.assert_editor_state(indoc! {"
 2607        const a: A = (
 2608            ˇ
 2609            (
 2610                ˇ
 2611                const_function(),
 2612                ˇ
 2613                ˇ
 2614                something_else,
 2615                ˇ
 2616                ˇ
 2617                ˇ
 2618                ˇ
 2619            )
 2620            ˇ
 2621        );
 2622        ˇ
 2623        ˇ
 2624    "});
 2625}
 2626
 2627#[gpui::test]
 2628async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2629    init_test(cx, |settings| {
 2630        settings.defaults.tab_size = NonZeroU32::new(4)
 2631    });
 2632
 2633    let language = Arc::new(Language::new(
 2634        LanguageConfig {
 2635            line_comments: vec!["//".into()],
 2636            ..LanguageConfig::default()
 2637        },
 2638        None,
 2639    ));
 2640    {
 2641        let mut cx = EditorTestContext::new(cx).await;
 2642        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2643        cx.set_state(indoc! {"
 2644        // Fooˇ
 2645    "});
 2646
 2647        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2648        cx.assert_editor_state(indoc! {"
 2649        // Foo
 2650        //ˇ
 2651    "});
 2652        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2653        cx.set_state(indoc! {"
 2654        ˇ// Foo
 2655    "});
 2656        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2657        cx.assert_editor_state(indoc! {"
 2658
 2659        ˇ// Foo
 2660    "});
 2661    }
 2662    // Ensure that comment continuations can be disabled.
 2663    update_test_language_settings(cx, |settings| {
 2664        settings.defaults.extend_comment_on_newline = Some(false);
 2665    });
 2666    let mut cx = EditorTestContext::new(cx).await;
 2667    cx.set_state(indoc! {"
 2668        // Fooˇ
 2669    "});
 2670    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2671    cx.assert_editor_state(indoc! {"
 2672        // Foo
 2673        ˇ
 2674    "});
 2675}
 2676
 2677#[gpui::test]
 2678fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2679    init_test(cx, |_| {});
 2680
 2681    let editor = cx.add_window(|window, cx| {
 2682        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2683        let mut editor = build_editor(buffer.clone(), window, cx);
 2684        editor.change_selections(None, window, cx, |s| {
 2685            s.select_ranges([3..4, 11..12, 19..20])
 2686        });
 2687        editor
 2688    });
 2689
 2690    _ = editor.update(cx, |editor, window, cx| {
 2691        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2692        editor.buffer.update(cx, |buffer, cx| {
 2693            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2694            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2695        });
 2696        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2697
 2698        editor.insert("Z", window, cx);
 2699        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2700
 2701        // The selections are moved after the inserted characters
 2702        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2703    });
 2704}
 2705
 2706#[gpui::test]
 2707async fn test_tab(cx: &mut gpui::TestAppContext) {
 2708    init_test(cx, |settings| {
 2709        settings.defaults.tab_size = NonZeroU32::new(3)
 2710    });
 2711
 2712    let mut cx = EditorTestContext::new(cx).await;
 2713    cx.set_state(indoc! {"
 2714        ˇabˇc
 2715        ˇ🏀ˇ🏀ˇefg
 2716 2717    "});
 2718    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2719    cx.assert_editor_state(indoc! {"
 2720           ˇab ˇc
 2721           ˇ🏀  ˇ🏀  ˇefg
 2722        d  ˇ
 2723    "});
 2724
 2725    cx.set_state(indoc! {"
 2726        a
 2727        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2728    "});
 2729    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        a
 2732           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2733    "});
 2734}
 2735
 2736#[gpui::test]
 2737async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2738    init_test(cx, |_| {});
 2739
 2740    let mut cx = EditorTestContext::new(cx).await;
 2741    let language = Arc::new(
 2742        Language::new(
 2743            LanguageConfig::default(),
 2744            Some(tree_sitter_rust::LANGUAGE.into()),
 2745        )
 2746        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2747        .unwrap(),
 2748    );
 2749    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2750
 2751    // cursors that are already at the suggested indent level insert
 2752    // a soft tab. cursors that are to the left of the suggested indent
 2753    // auto-indent their line.
 2754    cx.set_state(indoc! {"
 2755        ˇ
 2756        const a: B = (
 2757            c(
 2758                d(
 2759        ˇ
 2760                )
 2761        ˇ
 2762        ˇ    )
 2763        );
 2764    "});
 2765    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2766    cx.assert_editor_state(indoc! {"
 2767            ˇ
 2768        const a: B = (
 2769            c(
 2770                d(
 2771                    ˇ
 2772                )
 2773                ˇ
 2774            ˇ)
 2775        );
 2776    "});
 2777
 2778    // handle auto-indent when there are multiple cursors on the same line
 2779    cx.set_state(indoc! {"
 2780        const a: B = (
 2781            c(
 2782        ˇ    ˇ
 2783        ˇ    )
 2784        );
 2785    "});
 2786    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2787    cx.assert_editor_state(indoc! {"
 2788        const a: B = (
 2789            c(
 2790                ˇ
 2791            ˇ)
 2792        );
 2793    "});
 2794}
 2795
 2796#[gpui::test]
 2797async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2798    init_test(cx, |settings| {
 2799        settings.defaults.tab_size = NonZeroU32::new(4)
 2800    });
 2801
 2802    let language = Arc::new(
 2803        Language::new(
 2804            LanguageConfig::default(),
 2805            Some(tree_sitter_rust::LANGUAGE.into()),
 2806        )
 2807        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2808        .unwrap(),
 2809    );
 2810
 2811    let mut cx = EditorTestContext::new(cx).await;
 2812    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2813    cx.set_state(indoc! {"
 2814        fn a() {
 2815            if b {
 2816        \t ˇc
 2817            }
 2818        }
 2819    "});
 2820
 2821    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        fn a() {
 2824            if b {
 2825                ˇc
 2826            }
 2827        }
 2828    "});
 2829}
 2830
 2831#[gpui::test]
 2832async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2833    init_test(cx, |settings| {
 2834        settings.defaults.tab_size = NonZeroU32::new(4);
 2835    });
 2836
 2837    let mut cx = EditorTestContext::new(cx).await;
 2838
 2839    cx.set_state(indoc! {"
 2840          «oneˇ» «twoˇ»
 2841        three
 2842         four
 2843    "});
 2844    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2845    cx.assert_editor_state(indoc! {"
 2846            «oneˇ» «twoˇ»
 2847        three
 2848         four
 2849    "});
 2850
 2851    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        «oneˇ» «twoˇ»
 2854        three
 2855         four
 2856    "});
 2857
 2858    // select across line ending
 2859    cx.set_state(indoc! {"
 2860        one two
 2861        t«hree
 2862        ˇ» four
 2863    "});
 2864    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2865    cx.assert_editor_state(indoc! {"
 2866        one two
 2867            t«hree
 2868        ˇ» four
 2869    "});
 2870
 2871    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2872    cx.assert_editor_state(indoc! {"
 2873        one two
 2874        t«hree
 2875        ˇ» four
 2876    "});
 2877
 2878    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2879    cx.set_state(indoc! {"
 2880        one two
 2881        ˇthree
 2882            four
 2883    "});
 2884    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2885    cx.assert_editor_state(indoc! {"
 2886        one two
 2887            ˇthree
 2888            four
 2889    "});
 2890
 2891    cx.set_state(indoc! {"
 2892        one two
 2893        ˇ    three
 2894            four
 2895    "});
 2896    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2897    cx.assert_editor_state(indoc! {"
 2898        one two
 2899        ˇthree
 2900            four
 2901    "});
 2902}
 2903
 2904#[gpui::test]
 2905async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2906    init_test(cx, |settings| {
 2907        settings.defaults.hard_tabs = Some(true);
 2908    });
 2909
 2910    let mut cx = EditorTestContext::new(cx).await;
 2911
 2912    // select two ranges on one line
 2913    cx.set_state(indoc! {"
 2914        «oneˇ» «twoˇ»
 2915        three
 2916        four
 2917    "});
 2918    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2919    cx.assert_editor_state(indoc! {"
 2920        \t«oneˇ» «twoˇ»
 2921        three
 2922        four
 2923    "});
 2924    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        \t\t«oneˇ» «twoˇ»
 2927        three
 2928        four
 2929    "});
 2930    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2931    cx.assert_editor_state(indoc! {"
 2932        \t«oneˇ» «twoˇ»
 2933        three
 2934        four
 2935    "});
 2936    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2937    cx.assert_editor_state(indoc! {"
 2938        «oneˇ» «twoˇ»
 2939        three
 2940        four
 2941    "});
 2942
 2943    // select across a line ending
 2944    cx.set_state(indoc! {"
 2945        one two
 2946        t«hree
 2947        ˇ»four
 2948    "});
 2949    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2950    cx.assert_editor_state(indoc! {"
 2951        one two
 2952        \tt«hree
 2953        ˇ»four
 2954    "});
 2955    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        one two
 2958        \t\tt«hree
 2959        ˇ»four
 2960    "});
 2961    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2962    cx.assert_editor_state(indoc! {"
 2963        one two
 2964        \tt«hree
 2965        ˇ»four
 2966    "});
 2967    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2968    cx.assert_editor_state(indoc! {"
 2969        one two
 2970        t«hree
 2971        ˇ»four
 2972    "});
 2973
 2974    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2975    cx.set_state(indoc! {"
 2976        one two
 2977        ˇthree
 2978        four
 2979    "});
 2980    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2981    cx.assert_editor_state(indoc! {"
 2982        one two
 2983        ˇthree
 2984        four
 2985    "});
 2986    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2987    cx.assert_editor_state(indoc! {"
 2988        one two
 2989        \tˇthree
 2990        four
 2991    "});
 2992    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        one two
 2995        ˇthree
 2996        four
 2997    "});
 2998}
 2999
 3000#[gpui::test]
 3001fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3002    init_test(cx, |settings| {
 3003        settings.languages.extend([
 3004            (
 3005                "TOML".into(),
 3006                LanguageSettingsContent {
 3007                    tab_size: NonZeroU32::new(2),
 3008                    ..Default::default()
 3009                },
 3010            ),
 3011            (
 3012                "Rust".into(),
 3013                LanguageSettingsContent {
 3014                    tab_size: NonZeroU32::new(4),
 3015                    ..Default::default()
 3016                },
 3017            ),
 3018        ]);
 3019    });
 3020
 3021    let toml_language = Arc::new(Language::new(
 3022        LanguageConfig {
 3023            name: "TOML".into(),
 3024            ..Default::default()
 3025        },
 3026        None,
 3027    ));
 3028    let rust_language = Arc::new(Language::new(
 3029        LanguageConfig {
 3030            name: "Rust".into(),
 3031            ..Default::default()
 3032        },
 3033        None,
 3034    ));
 3035
 3036    let toml_buffer =
 3037        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3038    let rust_buffer =
 3039        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3040    let multibuffer = cx.new(|cx| {
 3041        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3042        multibuffer.push_excerpts(
 3043            toml_buffer.clone(),
 3044            [ExcerptRange {
 3045                context: Point::new(0, 0)..Point::new(2, 0),
 3046                primary: None,
 3047            }],
 3048            cx,
 3049        );
 3050        multibuffer.push_excerpts(
 3051            rust_buffer.clone(),
 3052            [ExcerptRange {
 3053                context: Point::new(0, 0)..Point::new(1, 0),
 3054                primary: None,
 3055            }],
 3056            cx,
 3057        );
 3058        multibuffer
 3059    });
 3060
 3061    cx.add_window(|window, cx| {
 3062        let mut editor = build_editor(multibuffer, window, cx);
 3063
 3064        assert_eq!(
 3065            editor.text(cx),
 3066            indoc! {"
 3067                a = 1
 3068                b = 2
 3069
 3070                const c: usize = 3;
 3071            "}
 3072        );
 3073
 3074        select_ranges(
 3075            &mut editor,
 3076            indoc! {"
 3077                «aˇ» = 1
 3078                b = 2
 3079
 3080                «const c:ˇ» usize = 3;
 3081            "},
 3082            window,
 3083            cx,
 3084        );
 3085
 3086        editor.tab(&Tab, window, cx);
 3087        assert_text_with_selections(
 3088            &mut editor,
 3089            indoc! {"
 3090                  «aˇ» = 1
 3091                b = 2
 3092
 3093                    «const c:ˇ» usize = 3;
 3094            "},
 3095            cx,
 3096        );
 3097        editor.tab_prev(&TabPrev, window, cx);
 3098        assert_text_with_selections(
 3099            &mut editor,
 3100            indoc! {"
 3101                «aˇ» = 1
 3102                b = 2
 3103
 3104                «const c:ˇ» usize = 3;
 3105            "},
 3106            cx,
 3107        );
 3108
 3109        editor
 3110    });
 3111}
 3112
 3113#[gpui::test]
 3114async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3115    init_test(cx, |_| {});
 3116
 3117    let mut cx = EditorTestContext::new(cx).await;
 3118
 3119    // Basic backspace
 3120    cx.set_state(indoc! {"
 3121        onˇe two three
 3122        fou«rˇ» five six
 3123        seven «ˇeight nine
 3124        »ten
 3125    "});
 3126    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3127    cx.assert_editor_state(indoc! {"
 3128        oˇe two three
 3129        fouˇ five six
 3130        seven ˇten
 3131    "});
 3132
 3133    // Test backspace inside and around indents
 3134    cx.set_state(indoc! {"
 3135        zero
 3136            ˇone
 3137                ˇtwo
 3138            ˇ ˇ ˇ  three
 3139        ˇ  ˇ  four
 3140    "});
 3141    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3142    cx.assert_editor_state(indoc! {"
 3143        zero
 3144        ˇone
 3145            ˇtwo
 3146        ˇ  threeˇ  four
 3147    "});
 3148
 3149    // Test backspace with line_mode set to true
 3150    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3151    cx.set_state(indoc! {"
 3152        The ˇquick ˇbrown
 3153        fox jumps over
 3154        the lazy dog
 3155        ˇThe qu«ick bˇ»rown"});
 3156    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3157    cx.assert_editor_state(indoc! {"
 3158        ˇfox jumps over
 3159        the lazy dogˇ"});
 3160}
 3161
 3162#[gpui::test]
 3163async fn test_delete(cx: &mut gpui::TestAppContext) {
 3164    init_test(cx, |_| {});
 3165
 3166    let mut cx = EditorTestContext::new(cx).await;
 3167    cx.set_state(indoc! {"
 3168        onˇe two three
 3169        fou«rˇ» five six
 3170        seven «ˇeight nine
 3171        »ten
 3172    "});
 3173    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3174    cx.assert_editor_state(indoc! {"
 3175        onˇ two three
 3176        fouˇ five six
 3177        seven ˇten
 3178    "});
 3179
 3180    // Test backspace with line_mode set to true
 3181    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3182    cx.set_state(indoc! {"
 3183        The ˇquick ˇbrown
 3184        fox «ˇjum»ps over
 3185        the lazy dog
 3186        ˇThe qu«ick bˇ»rown"});
 3187    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3188    cx.assert_editor_state("ˇthe lazy dogˇ");
 3189}
 3190
 3191#[gpui::test]
 3192fn test_delete_line(cx: &mut TestAppContext) {
 3193    init_test(cx, |_| {});
 3194
 3195    let editor = cx.add_window(|window, cx| {
 3196        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3197        build_editor(buffer, window, cx)
 3198    });
 3199    _ = editor.update(cx, |editor, window, cx| {
 3200        editor.change_selections(None, window, cx, |s| {
 3201            s.select_display_ranges([
 3202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3203                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3204                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3205            ])
 3206        });
 3207        editor.delete_line(&DeleteLine, window, cx);
 3208        assert_eq!(editor.display_text(cx), "ghi");
 3209        assert_eq!(
 3210            editor.selections.display_ranges(cx),
 3211            vec![
 3212                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3213                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3214            ]
 3215        );
 3216    });
 3217
 3218    let editor = cx.add_window(|window, cx| {
 3219        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3220        build_editor(buffer, window, cx)
 3221    });
 3222    _ = editor.update(cx, |editor, window, cx| {
 3223        editor.change_selections(None, window, cx, |s| {
 3224            s.select_display_ranges([
 3225                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3226            ])
 3227        });
 3228        editor.delete_line(&DeleteLine, window, cx);
 3229        assert_eq!(editor.display_text(cx), "ghi\n");
 3230        assert_eq!(
 3231            editor.selections.display_ranges(cx),
 3232            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3233        );
 3234    });
 3235}
 3236
 3237#[gpui::test]
 3238fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3239    init_test(cx, |_| {});
 3240
 3241    cx.add_window(|window, cx| {
 3242        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3243        let mut editor = build_editor(buffer.clone(), window, cx);
 3244        let buffer = buffer.read(cx).as_singleton().unwrap();
 3245
 3246        assert_eq!(
 3247            editor.selections.ranges::<Point>(cx),
 3248            &[Point::new(0, 0)..Point::new(0, 0)]
 3249        );
 3250
 3251        // When on single line, replace newline at end by space
 3252        editor.join_lines(&JoinLines, window, cx);
 3253        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3254        assert_eq!(
 3255            editor.selections.ranges::<Point>(cx),
 3256            &[Point::new(0, 3)..Point::new(0, 3)]
 3257        );
 3258
 3259        // When multiple lines are selected, remove newlines that are spanned by the selection
 3260        editor.change_selections(None, window, cx, |s| {
 3261            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3262        });
 3263        editor.join_lines(&JoinLines, window, cx);
 3264        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3265        assert_eq!(
 3266            editor.selections.ranges::<Point>(cx),
 3267            &[Point::new(0, 11)..Point::new(0, 11)]
 3268        );
 3269
 3270        // Undo should be transactional
 3271        editor.undo(&Undo, window, cx);
 3272        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3273        assert_eq!(
 3274            editor.selections.ranges::<Point>(cx),
 3275            &[Point::new(0, 5)..Point::new(2, 2)]
 3276        );
 3277
 3278        // When joining an empty line don't insert a space
 3279        editor.change_selections(None, window, cx, |s| {
 3280            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3281        });
 3282        editor.join_lines(&JoinLines, window, cx);
 3283        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3284        assert_eq!(
 3285            editor.selections.ranges::<Point>(cx),
 3286            [Point::new(2, 3)..Point::new(2, 3)]
 3287        );
 3288
 3289        // We can remove trailing newlines
 3290        editor.join_lines(&JoinLines, window, cx);
 3291        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3292        assert_eq!(
 3293            editor.selections.ranges::<Point>(cx),
 3294            [Point::new(2, 3)..Point::new(2, 3)]
 3295        );
 3296
 3297        // We don't blow up on the last line
 3298        editor.join_lines(&JoinLines, window, cx);
 3299        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3300        assert_eq!(
 3301            editor.selections.ranges::<Point>(cx),
 3302            [Point::new(2, 3)..Point::new(2, 3)]
 3303        );
 3304
 3305        // reset to test indentation
 3306        editor.buffer.update(cx, |buffer, cx| {
 3307            buffer.edit(
 3308                [
 3309                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3310                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3311                ],
 3312                None,
 3313                cx,
 3314            )
 3315        });
 3316
 3317        // We remove any leading spaces
 3318        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3321        });
 3322        editor.join_lines(&JoinLines, window, cx);
 3323        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3324
 3325        // We don't insert a space for a line containing only spaces
 3326        editor.join_lines(&JoinLines, window, cx);
 3327        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3328
 3329        // We ignore any leading tabs
 3330        editor.join_lines(&JoinLines, window, cx);
 3331        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3332
 3333        editor
 3334    });
 3335}
 3336
 3337#[gpui::test]
 3338fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3339    init_test(cx, |_| {});
 3340
 3341    cx.add_window(|window, cx| {
 3342        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3343        let mut editor = build_editor(buffer.clone(), window, cx);
 3344        let buffer = buffer.read(cx).as_singleton().unwrap();
 3345
 3346        editor.change_selections(None, window, cx, |s| {
 3347            s.select_ranges([
 3348                Point::new(0, 2)..Point::new(1, 1),
 3349                Point::new(1, 2)..Point::new(1, 2),
 3350                Point::new(3, 1)..Point::new(3, 2),
 3351            ])
 3352        });
 3353
 3354        editor.join_lines(&JoinLines, window, cx);
 3355        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3356
 3357        assert_eq!(
 3358            editor.selections.ranges::<Point>(cx),
 3359            [
 3360                Point::new(0, 7)..Point::new(0, 7),
 3361                Point::new(1, 3)..Point::new(1, 3)
 3362            ]
 3363        );
 3364        editor
 3365    });
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_join_lines_with_git_diff_base(
 3370    executor: BackgroundExecutor,
 3371    cx: &mut gpui::TestAppContext,
 3372) {
 3373    init_test(cx, |_| {});
 3374
 3375    let mut cx = EditorTestContext::new(cx).await;
 3376
 3377    let diff_base = r#"
 3378        Line 0
 3379        Line 1
 3380        Line 2
 3381        Line 3
 3382        "#
 3383    .unindent();
 3384
 3385    cx.set_state(
 3386        &r#"
 3387        ˇLine 0
 3388        Line 1
 3389        Line 2
 3390        Line 3
 3391        "#
 3392        .unindent(),
 3393    );
 3394
 3395    cx.set_diff_base(&diff_base);
 3396    executor.run_until_parked();
 3397
 3398    // Join lines
 3399    cx.update_editor(|editor, window, cx| {
 3400        editor.join_lines(&JoinLines, window, cx);
 3401    });
 3402    executor.run_until_parked();
 3403
 3404    cx.assert_editor_state(
 3405        &r#"
 3406        Line 0ˇ Line 1
 3407        Line 2
 3408        Line 3
 3409        "#
 3410        .unindent(),
 3411    );
 3412    // Join again
 3413    cx.update_editor(|editor, window, cx| {
 3414        editor.join_lines(&JoinLines, window, cx);
 3415    });
 3416    executor.run_until_parked();
 3417
 3418    cx.assert_editor_state(
 3419        &r#"
 3420        Line 0 Line 1ˇ Line 2
 3421        Line 3
 3422        "#
 3423        .unindent(),
 3424    );
 3425}
 3426
 3427#[gpui::test]
 3428async fn test_custom_newlines_cause_no_false_positive_diffs(
 3429    executor: BackgroundExecutor,
 3430    cx: &mut gpui::TestAppContext,
 3431) {
 3432    init_test(cx, |_| {});
 3433    let mut cx = EditorTestContext::new(cx).await;
 3434    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3435    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3436    executor.run_until_parked();
 3437
 3438    cx.update_editor(|editor, window, cx| {
 3439        let snapshot = editor.snapshot(window, cx);
 3440        assert_eq!(
 3441            snapshot
 3442                .buffer_snapshot
 3443                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3444                .collect::<Vec<_>>(),
 3445            Vec::new(),
 3446            "Should not have any diffs for files with custom newlines"
 3447        );
 3448    });
 3449}
 3450
 3451#[gpui::test]
 3452async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3453    init_test(cx, |_| {});
 3454
 3455    let mut cx = EditorTestContext::new(cx).await;
 3456
 3457    // Test sort_lines_case_insensitive()
 3458    cx.set_state(indoc! {"
 3459        «z
 3460        y
 3461        x
 3462        Z
 3463        Y
 3464        Xˇ»
 3465    "});
 3466    cx.update_editor(|e, window, cx| {
 3467        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3468    });
 3469    cx.assert_editor_state(indoc! {"
 3470        «x
 3471        X
 3472        y
 3473        Y
 3474        z
 3475        Zˇ»
 3476    "});
 3477
 3478    // Test reverse_lines()
 3479    cx.set_state(indoc! {"
 3480        «5
 3481        4
 3482        3
 3483        2
 3484        1ˇ»
 3485    "});
 3486    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «1
 3489        2
 3490        3
 3491        4
 3492        5ˇ»
 3493    "});
 3494
 3495    // Skip testing shuffle_line()
 3496
 3497    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3498    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3499
 3500    // Don't manipulate when cursor is on single line, but expand the selection
 3501    cx.set_state(indoc! {"
 3502        ddˇdd
 3503        ccc
 3504        bb
 3505        a
 3506    "});
 3507    cx.update_editor(|e, window, cx| {
 3508        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3509    });
 3510    cx.assert_editor_state(indoc! {"
 3511        «ddddˇ»
 3512        ccc
 3513        bb
 3514        a
 3515    "});
 3516
 3517    // Basic manipulate case
 3518    // Start selection moves to column 0
 3519    // End of selection shrinks to fit shorter line
 3520    cx.set_state(indoc! {"
 3521        dd«d
 3522        ccc
 3523        bb
 3524        aaaaaˇ»
 3525    "});
 3526    cx.update_editor(|e, window, cx| {
 3527        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3528    });
 3529    cx.assert_editor_state(indoc! {"
 3530        «aaaaa
 3531        bb
 3532        ccc
 3533        dddˇ»
 3534    "});
 3535
 3536    // Manipulate case with newlines
 3537    cx.set_state(indoc! {"
 3538        dd«d
 3539        ccc
 3540
 3541        bb
 3542        aaaaa
 3543
 3544        ˇ»
 3545    "});
 3546    cx.update_editor(|e, window, cx| {
 3547        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3548    });
 3549    cx.assert_editor_state(indoc! {"
 3550        «
 3551
 3552        aaaaa
 3553        bb
 3554        ccc
 3555        dddˇ»
 3556
 3557    "});
 3558
 3559    // Adding new line
 3560    cx.set_state(indoc! {"
 3561        aa«a
 3562        bbˇ»b
 3563    "});
 3564    cx.update_editor(|e, window, cx| {
 3565        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3566    });
 3567    cx.assert_editor_state(indoc! {"
 3568        «aaa
 3569        bbb
 3570        added_lineˇ»
 3571    "});
 3572
 3573    // Removing line
 3574    cx.set_state(indoc! {"
 3575        aa«a
 3576        bbbˇ»
 3577    "});
 3578    cx.update_editor(|e, window, cx| {
 3579        e.manipulate_lines(window, cx, |lines| {
 3580            lines.pop();
 3581        })
 3582    });
 3583    cx.assert_editor_state(indoc! {"
 3584        «aaaˇ»
 3585    "});
 3586
 3587    // Removing all lines
 3588    cx.set_state(indoc! {"
 3589        aa«a
 3590        bbbˇ»
 3591    "});
 3592    cx.update_editor(|e, window, cx| {
 3593        e.manipulate_lines(window, cx, |lines| {
 3594            lines.drain(..);
 3595        })
 3596    });
 3597    cx.assert_editor_state(indoc! {"
 3598        ˇ
 3599    "});
 3600}
 3601
 3602#[gpui::test]
 3603async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3604    init_test(cx, |_| {});
 3605
 3606    let mut cx = EditorTestContext::new(cx).await;
 3607
 3608    // Consider continuous selection as single selection
 3609    cx.set_state(indoc! {"
 3610        Aaa«aa
 3611        cˇ»c«c
 3612        bb
 3613        aaaˇ»aa
 3614    "});
 3615    cx.update_editor(|e, window, cx| {
 3616        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3617    });
 3618    cx.assert_editor_state(indoc! {"
 3619        «Aaaaa
 3620        ccc
 3621        bb
 3622        aaaaaˇ»
 3623    "});
 3624
 3625    cx.set_state(indoc! {"
 3626        Aaa«aa
 3627        cˇ»c«c
 3628        bb
 3629        aaaˇ»aa
 3630    "});
 3631    cx.update_editor(|e, window, cx| {
 3632        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3633    });
 3634    cx.assert_editor_state(indoc! {"
 3635        «Aaaaa
 3636        ccc
 3637        bbˇ»
 3638    "});
 3639
 3640    // Consider non continuous selection as distinct dedup operations
 3641    cx.set_state(indoc! {"
 3642        «aaaaa
 3643        bb
 3644        aaaaa
 3645        aaaaaˇ»
 3646
 3647        aaa«aaˇ»
 3648    "});
 3649    cx.update_editor(|e, window, cx| {
 3650        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3651    });
 3652    cx.assert_editor_state(indoc! {"
 3653        «aaaaa
 3654        bbˇ»
 3655
 3656        «aaaaaˇ»
 3657    "});
 3658}
 3659
 3660#[gpui::test]
 3661async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3662    init_test(cx, |_| {});
 3663
 3664    let mut cx = EditorTestContext::new(cx).await;
 3665
 3666    cx.set_state(indoc! {"
 3667        «Aaa
 3668        aAa
 3669        Aaaˇ»
 3670    "});
 3671    cx.update_editor(|e, window, cx| {
 3672        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3673    });
 3674    cx.assert_editor_state(indoc! {"
 3675        «Aaa
 3676        aAaˇ»
 3677    "});
 3678
 3679    cx.set_state(indoc! {"
 3680        «Aaa
 3681        aAa
 3682        aaAˇ»
 3683    "});
 3684    cx.update_editor(|e, window, cx| {
 3685        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3686    });
 3687    cx.assert_editor_state(indoc! {"
 3688        «Aaaˇ»
 3689    "});
 3690}
 3691
 3692#[gpui::test]
 3693async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3694    init_test(cx, |_| {});
 3695
 3696    let mut cx = EditorTestContext::new(cx).await;
 3697
 3698    // Manipulate with multiple selections on a single line
 3699    cx.set_state(indoc! {"
 3700        dd«dd
 3701        cˇ»c«c
 3702        bb
 3703        aaaˇ»aa
 3704    "});
 3705    cx.update_editor(|e, window, cx| {
 3706        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3707    });
 3708    cx.assert_editor_state(indoc! {"
 3709        «aaaaa
 3710        bb
 3711        ccc
 3712        ddddˇ»
 3713    "});
 3714
 3715    // Manipulate with multiple disjoin selections
 3716    cx.set_state(indoc! {"
 3717 3718        4
 3719        3
 3720        2
 3721        1ˇ»
 3722
 3723        dd«dd
 3724        ccc
 3725        bb
 3726        aaaˇ»aa
 3727    "});
 3728    cx.update_editor(|e, window, cx| {
 3729        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3730    });
 3731    cx.assert_editor_state(indoc! {"
 3732        «1
 3733        2
 3734        3
 3735        4
 3736        5ˇ»
 3737
 3738        «aaaaa
 3739        bb
 3740        ccc
 3741        ddddˇ»
 3742    "});
 3743
 3744    // Adding lines on each selection
 3745    cx.set_state(indoc! {"
 3746 3747        1ˇ»
 3748
 3749        bb«bb
 3750        aaaˇ»aa
 3751    "});
 3752    cx.update_editor(|e, window, cx| {
 3753        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3754    });
 3755    cx.assert_editor_state(indoc! {"
 3756        «2
 3757        1
 3758        added lineˇ»
 3759
 3760        «bbbb
 3761        aaaaa
 3762        added lineˇ»
 3763    "});
 3764
 3765    // Removing lines on each selection
 3766    cx.set_state(indoc! {"
 3767 3768        1ˇ»
 3769
 3770        bb«bb
 3771        aaaˇ»aa
 3772    "});
 3773    cx.update_editor(|e, window, cx| {
 3774        e.manipulate_lines(window, cx, |lines| {
 3775            lines.pop();
 3776        })
 3777    });
 3778    cx.assert_editor_state(indoc! {"
 3779        «2ˇ»
 3780
 3781        «bbbbˇ»
 3782    "});
 3783}
 3784
 3785#[gpui::test]
 3786async fn test_manipulate_text(cx: &mut TestAppContext) {
 3787    init_test(cx, |_| {});
 3788
 3789    let mut cx = EditorTestContext::new(cx).await;
 3790
 3791    // Test convert_to_upper_case()
 3792    cx.set_state(indoc! {"
 3793        «hello worldˇ»
 3794    "});
 3795    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3796    cx.assert_editor_state(indoc! {"
 3797        «HELLO WORLDˇ»
 3798    "});
 3799
 3800    // Test convert_to_lower_case()
 3801    cx.set_state(indoc! {"
 3802        «HELLO WORLDˇ»
 3803    "});
 3804    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3805    cx.assert_editor_state(indoc! {"
 3806        «hello worldˇ»
 3807    "});
 3808
 3809    // Test multiple line, single selection case
 3810    cx.set_state(indoc! {"
 3811        «The quick brown
 3812        fox jumps over
 3813        the lazy dogˇ»
 3814    "});
 3815    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3816    cx.assert_editor_state(indoc! {"
 3817        «The Quick Brown
 3818        Fox Jumps Over
 3819        The Lazy Dogˇ»
 3820    "});
 3821
 3822    // Test multiple line, single selection case
 3823    cx.set_state(indoc! {"
 3824        «The quick brown
 3825        fox jumps over
 3826        the lazy dogˇ»
 3827    "});
 3828    cx.update_editor(|e, window, cx| {
 3829        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3830    });
 3831    cx.assert_editor_state(indoc! {"
 3832        «TheQuickBrown
 3833        FoxJumpsOver
 3834        TheLazyDogˇ»
 3835    "});
 3836
 3837    // From here on out, test more complex cases of manipulate_text()
 3838
 3839    // Test no selection case - should affect words cursors are in
 3840    // Cursor at beginning, middle, and end of word
 3841    cx.set_state(indoc! {"
 3842        ˇhello big beauˇtiful worldˇ
 3843    "});
 3844    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3845    cx.assert_editor_state(indoc! {"
 3846        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3847    "});
 3848
 3849    // Test multiple selections on a single line and across multiple lines
 3850    cx.set_state(indoc! {"
 3851        «Theˇ» quick «brown
 3852        foxˇ» jumps «overˇ»
 3853        the «lazyˇ» dog
 3854    "});
 3855    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3856    cx.assert_editor_state(indoc! {"
 3857        «THEˇ» quick «BROWN
 3858        FOXˇ» jumps «OVERˇ»
 3859        the «LAZYˇ» dog
 3860    "});
 3861
 3862    // Test case where text length grows
 3863    cx.set_state(indoc! {"
 3864        «tschüߡ»
 3865    "});
 3866    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3867    cx.assert_editor_state(indoc! {"
 3868        «TSCHÜSSˇ»
 3869    "});
 3870
 3871    // Test to make sure we don't crash when text shrinks
 3872    cx.set_state(indoc! {"
 3873        aaa_bbbˇ
 3874    "});
 3875    cx.update_editor(|e, window, cx| {
 3876        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3877    });
 3878    cx.assert_editor_state(indoc! {"
 3879        «aaaBbbˇ»
 3880    "});
 3881
 3882    // Test to make sure we all aware of the fact that each word can grow and shrink
 3883    // Final selections should be aware of this fact
 3884    cx.set_state(indoc! {"
 3885        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3886    "});
 3887    cx.update_editor(|e, window, cx| {
 3888        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3889    });
 3890    cx.assert_editor_state(indoc! {"
 3891        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3892    "});
 3893
 3894    cx.set_state(indoc! {"
 3895        «hElLo, WoRld!ˇ»
 3896    "});
 3897    cx.update_editor(|e, window, cx| {
 3898        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3899    });
 3900    cx.assert_editor_state(indoc! {"
 3901        «HeLlO, wOrLD!ˇ»
 3902    "});
 3903}
 3904
 3905#[gpui::test]
 3906fn test_duplicate_line(cx: &mut TestAppContext) {
 3907    init_test(cx, |_| {});
 3908
 3909    let editor = cx.add_window(|window, cx| {
 3910        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3911        build_editor(buffer, window, cx)
 3912    });
 3913    _ = editor.update(cx, |editor, window, cx| {
 3914        editor.change_selections(None, window, cx, |s| {
 3915            s.select_display_ranges([
 3916                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3917                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3918                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3919                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3920            ])
 3921        });
 3922        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3923        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3924        assert_eq!(
 3925            editor.selections.display_ranges(cx),
 3926            vec![
 3927                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3928                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3929                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3930                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3931            ]
 3932        );
 3933    });
 3934
 3935    let editor = cx.add_window(|window, cx| {
 3936        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3937        build_editor(buffer, window, cx)
 3938    });
 3939    _ = editor.update(cx, |editor, window, cx| {
 3940        editor.change_selections(None, window, cx, |s| {
 3941            s.select_display_ranges([
 3942                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3943                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3944            ])
 3945        });
 3946        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3947        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3948        assert_eq!(
 3949            editor.selections.display_ranges(cx),
 3950            vec![
 3951                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3952                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3953            ]
 3954        );
 3955    });
 3956
 3957    // With `move_upwards` the selections stay in place, except for
 3958    // the lines inserted above them
 3959    let editor = cx.add_window(|window, cx| {
 3960        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3961        build_editor(buffer, window, cx)
 3962    });
 3963    _ = editor.update(cx, |editor, window, cx| {
 3964        editor.change_selections(None, window, cx, |s| {
 3965            s.select_display_ranges([
 3966                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3967                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3968                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3969                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3970            ])
 3971        });
 3972        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3973        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3974        assert_eq!(
 3975            editor.selections.display_ranges(cx),
 3976            vec![
 3977                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3978                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3979                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3980                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3981            ]
 3982        );
 3983    });
 3984
 3985    let editor = cx.add_window(|window, cx| {
 3986        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3987        build_editor(buffer, window, cx)
 3988    });
 3989    _ = editor.update(cx, |editor, window, cx| {
 3990        editor.change_selections(None, window, cx, |s| {
 3991            s.select_display_ranges([
 3992                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3993                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3994            ])
 3995        });
 3996        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3997        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3998        assert_eq!(
 3999            editor.selections.display_ranges(cx),
 4000            vec![
 4001                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4002                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4003            ]
 4004        );
 4005    });
 4006
 4007    let editor = cx.add_window(|window, cx| {
 4008        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4009        build_editor(buffer, window, cx)
 4010    });
 4011    _ = editor.update(cx, |editor, window, cx| {
 4012        editor.change_selections(None, window, cx, |s| {
 4013            s.select_display_ranges([
 4014                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4015                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4016            ])
 4017        });
 4018        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4019        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4020        assert_eq!(
 4021            editor.selections.display_ranges(cx),
 4022            vec![
 4023                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4024                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4025            ]
 4026        );
 4027    });
 4028}
 4029
 4030#[gpui::test]
 4031fn test_move_line_up_down(cx: &mut TestAppContext) {
 4032    init_test(cx, |_| {});
 4033
 4034    let editor = cx.add_window(|window, cx| {
 4035        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4036        build_editor(buffer, window, cx)
 4037    });
 4038    _ = editor.update(cx, |editor, window, cx| {
 4039        editor.fold_creases(
 4040            vec![
 4041                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4042                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4043                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4044            ],
 4045            true,
 4046            window,
 4047            cx,
 4048        );
 4049        editor.change_selections(None, window, cx, |s| {
 4050            s.select_display_ranges([
 4051                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4052                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4053                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4054                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4055            ])
 4056        });
 4057        assert_eq!(
 4058            editor.display_text(cx),
 4059            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4060        );
 4061
 4062        editor.move_line_up(&MoveLineUp, window, cx);
 4063        assert_eq!(
 4064            editor.display_text(cx),
 4065            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4066        );
 4067        assert_eq!(
 4068            editor.selections.display_ranges(cx),
 4069            vec![
 4070                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4071                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4072                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4073                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4074            ]
 4075        );
 4076    });
 4077
 4078    _ = editor.update(cx, |editor, window, cx| {
 4079        editor.move_line_down(&MoveLineDown, window, cx);
 4080        assert_eq!(
 4081            editor.display_text(cx),
 4082            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4083        );
 4084        assert_eq!(
 4085            editor.selections.display_ranges(cx),
 4086            vec![
 4087                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4088                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4089                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4090                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4091            ]
 4092        );
 4093    });
 4094
 4095    _ = editor.update(cx, |editor, window, cx| {
 4096        editor.move_line_down(&MoveLineDown, window, cx);
 4097        assert_eq!(
 4098            editor.display_text(cx),
 4099            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4100        );
 4101        assert_eq!(
 4102            editor.selections.display_ranges(cx),
 4103            vec![
 4104                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4105                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4106                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4107                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4108            ]
 4109        );
 4110    });
 4111
 4112    _ = editor.update(cx, |editor, window, cx| {
 4113        editor.move_line_up(&MoveLineUp, window, cx);
 4114        assert_eq!(
 4115            editor.display_text(cx),
 4116            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4117        );
 4118        assert_eq!(
 4119            editor.selections.display_ranges(cx),
 4120            vec![
 4121                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4122                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4123                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4124                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4125            ]
 4126        );
 4127    });
 4128}
 4129
 4130#[gpui::test]
 4131fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4132    init_test(cx, |_| {});
 4133
 4134    let editor = cx.add_window(|window, cx| {
 4135        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4136        build_editor(buffer, window, cx)
 4137    });
 4138    _ = editor.update(cx, |editor, window, cx| {
 4139        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4140        editor.insert_blocks(
 4141            [BlockProperties {
 4142                style: BlockStyle::Fixed,
 4143                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4144                height: 1,
 4145                render: Arc::new(|_| div().into_any()),
 4146                priority: 0,
 4147            }],
 4148            Some(Autoscroll::fit()),
 4149            cx,
 4150        );
 4151        editor.change_selections(None, window, cx, |s| {
 4152            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4153        });
 4154        editor.move_line_down(&MoveLineDown, window, cx);
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let mut cx = EditorTestContext::new(cx).await;
 4163    cx.set_state(
 4164        &"
 4165            ˇzero
 4166            one
 4167            two
 4168            three
 4169            four
 4170            five
 4171        "
 4172        .unindent(),
 4173    );
 4174
 4175    // Create a four-line block that replaces three lines of text.
 4176    cx.update_editor(|editor, window, cx| {
 4177        let snapshot = editor.snapshot(window, cx);
 4178        let snapshot = &snapshot.buffer_snapshot;
 4179        let placement = BlockPlacement::Replace(
 4180            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4181        );
 4182        editor.insert_blocks(
 4183            [BlockProperties {
 4184                placement,
 4185                height: 4,
 4186                style: BlockStyle::Sticky,
 4187                render: Arc::new(|_| gpui::div().into_any_element()),
 4188                priority: 0,
 4189            }],
 4190            None,
 4191            cx,
 4192        );
 4193    });
 4194
 4195    // Move down so that the cursor touches the block.
 4196    cx.update_editor(|editor, window, cx| {
 4197        editor.move_down(&Default::default(), window, cx);
 4198    });
 4199    cx.assert_editor_state(
 4200        &"
 4201            zero
 4202            «one
 4203            two
 4204            threeˇ»
 4205            four
 4206            five
 4207        "
 4208        .unindent(),
 4209    );
 4210
 4211    // Move down past the block.
 4212    cx.update_editor(|editor, window, cx| {
 4213        editor.move_down(&Default::default(), window, cx);
 4214    });
 4215    cx.assert_editor_state(
 4216        &"
 4217            zero
 4218            one
 4219            two
 4220            three
 4221            ˇfour
 4222            five
 4223        "
 4224        .unindent(),
 4225    );
 4226}
 4227
 4228#[gpui::test]
 4229fn test_transpose(cx: &mut TestAppContext) {
 4230    init_test(cx, |_| {});
 4231
 4232    _ = cx.add_window(|window, cx| {
 4233        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4234        editor.set_style(EditorStyle::default(), window, cx);
 4235        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4236        editor.transpose(&Default::default(), window, cx);
 4237        assert_eq!(editor.text(cx), "bac");
 4238        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4239
 4240        editor.transpose(&Default::default(), window, cx);
 4241        assert_eq!(editor.text(cx), "bca");
 4242        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4243
 4244        editor.transpose(&Default::default(), window, cx);
 4245        assert_eq!(editor.text(cx), "bac");
 4246        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4247
 4248        editor
 4249    });
 4250
 4251    _ = cx.add_window(|window, cx| {
 4252        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4253        editor.set_style(EditorStyle::default(), window, cx);
 4254        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4255        editor.transpose(&Default::default(), window, cx);
 4256        assert_eq!(editor.text(cx), "acb\nde");
 4257        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4258
 4259        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4260        editor.transpose(&Default::default(), window, cx);
 4261        assert_eq!(editor.text(cx), "acbd\ne");
 4262        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4263
 4264        editor.transpose(&Default::default(), window, cx);
 4265        assert_eq!(editor.text(cx), "acbde\n");
 4266        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4267
 4268        editor.transpose(&Default::default(), window, cx);
 4269        assert_eq!(editor.text(cx), "acbd\ne");
 4270        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4271
 4272        editor
 4273    });
 4274
 4275    _ = cx.add_window(|window, cx| {
 4276        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4277        editor.set_style(EditorStyle::default(), window, cx);
 4278        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4279        editor.transpose(&Default::default(), window, cx);
 4280        assert_eq!(editor.text(cx), "bacd\ne");
 4281        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4282
 4283        editor.transpose(&Default::default(), window, cx);
 4284        assert_eq!(editor.text(cx), "bcade\n");
 4285        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4286
 4287        editor.transpose(&Default::default(), window, cx);
 4288        assert_eq!(editor.text(cx), "bcda\ne");
 4289        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4290
 4291        editor.transpose(&Default::default(), window, cx);
 4292        assert_eq!(editor.text(cx), "bcade\n");
 4293        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4294
 4295        editor.transpose(&Default::default(), window, cx);
 4296        assert_eq!(editor.text(cx), "bcaed\n");
 4297        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4298
 4299        editor
 4300    });
 4301
 4302    _ = cx.add_window(|window, cx| {
 4303        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4304        editor.set_style(EditorStyle::default(), window, cx);
 4305        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4306        editor.transpose(&Default::default(), window, cx);
 4307        assert_eq!(editor.text(cx), "🏀🍐✋");
 4308        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4309
 4310        editor.transpose(&Default::default(), window, cx);
 4311        assert_eq!(editor.text(cx), "🏀✋🍐");
 4312        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4313
 4314        editor.transpose(&Default::default(), window, cx);
 4315        assert_eq!(editor.text(cx), "🏀🍐✋");
 4316        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4317
 4318        editor
 4319    });
 4320}
 4321
 4322#[gpui::test]
 4323async fn test_rewrap(cx: &mut TestAppContext) {
 4324    init_test(cx, |_| {});
 4325
 4326    let mut cx = EditorTestContext::new(cx).await;
 4327
 4328    let language_with_c_comments = Arc::new(Language::new(
 4329        LanguageConfig {
 4330            line_comments: vec!["// ".into()],
 4331            ..LanguageConfig::default()
 4332        },
 4333        None,
 4334    ));
 4335    let language_with_pound_comments = Arc::new(Language::new(
 4336        LanguageConfig {
 4337            line_comments: vec!["# ".into()],
 4338            ..LanguageConfig::default()
 4339        },
 4340        None,
 4341    ));
 4342    let markdown_language = Arc::new(Language::new(
 4343        LanguageConfig {
 4344            name: "Markdown".into(),
 4345            ..LanguageConfig::default()
 4346        },
 4347        None,
 4348    ));
 4349    let language_with_doc_comments = Arc::new(Language::new(
 4350        LanguageConfig {
 4351            line_comments: vec!["// ".into(), "/// ".into()],
 4352            ..LanguageConfig::default()
 4353        },
 4354        Some(tree_sitter_rust::LANGUAGE.into()),
 4355    ));
 4356
 4357    let plaintext_language = Arc::new(Language::new(
 4358        LanguageConfig {
 4359            name: "Plain Text".into(),
 4360            ..LanguageConfig::default()
 4361        },
 4362        None,
 4363    ));
 4364
 4365    assert_rewrap(
 4366        indoc! {"
 4367            // ˇ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.
 4368        "},
 4369        indoc! {"
 4370            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4371            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4372            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4373            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4374            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4375            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4376            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4377            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4378            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4379            // porttitor id. Aliquam id accumsan eros.
 4380        "},
 4381        language_with_c_comments.clone(),
 4382        &mut cx,
 4383    );
 4384
 4385    // Test that rewrapping works inside of a selection
 4386    assert_rewrap(
 4387        indoc! {"
 4388            «// 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.ˇ»
 4389        "},
 4390        indoc! {"
 4391            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4392            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4393            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4394            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4395            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4396            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4397            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4398            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4399            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4400            // porttitor id. Aliquam id accumsan eros.ˇ»
 4401        "},
 4402        language_with_c_comments.clone(),
 4403        &mut cx,
 4404    );
 4405
 4406    // Test that cursors that expand to the same region are collapsed.
 4407    assert_rewrap(
 4408        indoc! {"
 4409            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4410            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4411            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4412            // ˇ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.
 4413        "},
 4414        indoc! {"
 4415            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4416            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4417            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4418            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4419            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4420            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4421            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4422            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4423            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4424            // porttitor id. Aliquam id accumsan eros.
 4425        "},
 4426        language_with_c_comments.clone(),
 4427        &mut cx,
 4428    );
 4429
 4430    // Test that non-contiguous selections are treated separately.
 4431    assert_rewrap(
 4432        indoc! {"
 4433            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4434            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4435            //
 4436            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4437            // ˇ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.
 4438        "},
 4439        indoc! {"
 4440            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4441            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4442            // auctor, eu lacinia sapien scelerisque.
 4443            //
 4444            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4445            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4446            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4447            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4448            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4449            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4450            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4451        "},
 4452        language_with_c_comments.clone(),
 4453        &mut cx,
 4454    );
 4455
 4456    // Test that different comment prefixes are supported.
 4457    assert_rewrap(
 4458        indoc! {"
 4459            # ˇ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.
 4460        "},
 4461        indoc! {"
 4462            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4463            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4464            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4465            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4466            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4467            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4468            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4469            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4470            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4471            # accumsan eros.
 4472        "},
 4473        language_with_pound_comments.clone(),
 4474        &mut cx,
 4475    );
 4476
 4477    // Test that rewrapping is ignored outside of comments in most languages.
 4478    assert_rewrap(
 4479        indoc! {"
 4480            /// Adds two numbers.
 4481            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4482            fn add(a: u32, b: u32) -> u32 {
 4483                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ˇ
 4484            }
 4485        "},
 4486        indoc! {"
 4487            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4488            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4489            fn add(a: u32, b: u32) -> u32 {
 4490                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ˇ
 4491            }
 4492        "},
 4493        language_with_doc_comments.clone(),
 4494        &mut cx,
 4495    );
 4496
 4497    // Test that rewrapping works in Markdown and Plain Text languages.
 4498    assert_rewrap(
 4499        indoc! {"
 4500            # Hello
 4501
 4502            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.
 4503        "},
 4504        indoc! {"
 4505            # Hello
 4506
 4507            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4508            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4509            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4510            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4511            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4512            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4513            Integer sit amet scelerisque nisi.
 4514        "},
 4515        markdown_language,
 4516        &mut cx,
 4517    );
 4518
 4519    assert_rewrap(
 4520        indoc! {"
 4521            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.
 4522        "},
 4523        indoc! {"
 4524            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4525            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4526            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4527            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4528            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4529            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4530            Integer sit amet scelerisque nisi.
 4531        "},
 4532        plaintext_language,
 4533        &mut cx,
 4534    );
 4535
 4536    // Test rewrapping unaligned comments in a selection.
 4537    assert_rewrap(
 4538        indoc! {"
 4539            fn foo() {
 4540                if true {
 4541            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4542            // Praesent semper egestas tellus id dignissim.ˇ»
 4543                    do_something();
 4544                } else {
 4545                    //
 4546                }
 4547            }
 4548        "},
 4549        indoc! {"
 4550            fn foo() {
 4551                if true {
 4552            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4553                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4554                    // egestas tellus id dignissim.ˇ»
 4555                    do_something();
 4556                } else {
 4557                    //
 4558                }
 4559            }
 4560        "},
 4561        language_with_doc_comments.clone(),
 4562        &mut cx,
 4563    );
 4564
 4565    assert_rewrap(
 4566        indoc! {"
 4567            fn foo() {
 4568                if true {
 4569            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4570            // Praesent semper egestas tellus id dignissim.»
 4571                    do_something();
 4572                } else {
 4573                    //
 4574                }
 4575
 4576            }
 4577        "},
 4578        indoc! {"
 4579            fn foo() {
 4580                if true {
 4581            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4582                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4583                    // egestas tellus id dignissim.»
 4584                    do_something();
 4585                } else {
 4586                    //
 4587                }
 4588
 4589            }
 4590        "},
 4591        language_with_doc_comments.clone(),
 4592        &mut cx,
 4593    );
 4594
 4595    #[track_caller]
 4596    fn assert_rewrap(
 4597        unwrapped_text: &str,
 4598        wrapped_text: &str,
 4599        language: Arc<Language>,
 4600        cx: &mut EditorTestContext,
 4601    ) {
 4602        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4603        cx.set_state(unwrapped_text);
 4604        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4605        cx.assert_editor_state(wrapped_text);
 4606    }
 4607}
 4608
 4609#[gpui::test]
 4610async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4611    init_test(cx, |_| {});
 4612
 4613    let mut cx = EditorTestContext::new(cx).await;
 4614
 4615    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4616    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4617    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4618
 4619    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4620    cx.set_state("two ˇfour ˇsix ˇ");
 4621    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4622    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4623
 4624    // Paste again but with only two cursors. Since the number of cursors doesn't
 4625    // match the number of slices in the clipboard, the entire clipboard text
 4626    // is pasted at each cursor.
 4627    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4628    cx.update_editor(|e, window, cx| {
 4629        e.handle_input("( ", window, cx);
 4630        e.paste(&Paste, window, cx);
 4631        e.handle_input(") ", window, cx);
 4632    });
 4633    cx.assert_editor_state(
 4634        &([
 4635            "( one✅ ",
 4636            "three ",
 4637            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4638            "three ",
 4639            "five ) ˇ",
 4640        ]
 4641        .join("\n")),
 4642    );
 4643
 4644    // Cut with three selections, one of which is full-line.
 4645    cx.set_state(indoc! {"
 4646        1«2ˇ»3
 4647        4ˇ567
 4648        «8ˇ»9"});
 4649    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4650    cx.assert_editor_state(indoc! {"
 4651        1ˇ3
 4652        ˇ9"});
 4653
 4654    // Paste with three selections, noticing how the copied selection that was full-line
 4655    // gets inserted before the second cursor.
 4656    cx.set_state(indoc! {"
 4657        1ˇ3
 4658 4659        «oˇ»ne"});
 4660    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4661    cx.assert_editor_state(indoc! {"
 4662        12ˇ3
 4663        4567
 4664 4665        8ˇne"});
 4666
 4667    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4668    cx.set_state(indoc! {"
 4669        The quick brown
 4670        fox juˇmps over
 4671        the lazy dog"});
 4672    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4673    assert_eq!(
 4674        cx.read_from_clipboard()
 4675            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4676        Some("fox jumps over\n".to_string())
 4677    );
 4678
 4679    // Paste with three selections, noticing how the copied full-line selection is inserted
 4680    // before the empty selections but replaces the selection that is non-empty.
 4681    cx.set_state(indoc! {"
 4682        Tˇhe quick brown
 4683        «foˇ»x jumps over
 4684        tˇhe lazy dog"});
 4685    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4686    cx.assert_editor_state(indoc! {"
 4687        fox jumps over
 4688        Tˇhe quick brown
 4689        fox jumps over
 4690        ˇx jumps over
 4691        fox jumps over
 4692        tˇhe lazy dog"});
 4693}
 4694
 4695#[gpui::test]
 4696async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4697    init_test(cx, |_| {});
 4698
 4699    let mut cx = EditorTestContext::new(cx).await;
 4700    let language = Arc::new(Language::new(
 4701        LanguageConfig::default(),
 4702        Some(tree_sitter_rust::LANGUAGE.into()),
 4703    ));
 4704    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4705
 4706    // Cut an indented block, without the leading whitespace.
 4707    cx.set_state(indoc! {"
 4708        const a: B = (
 4709            c(),
 4710            «d(
 4711                e,
 4712                f
 4713            )ˇ»
 4714        );
 4715    "});
 4716    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4717    cx.assert_editor_state(indoc! {"
 4718        const a: B = (
 4719            c(),
 4720            ˇ
 4721        );
 4722    "});
 4723
 4724    // Paste it at the same position.
 4725    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4726    cx.assert_editor_state(indoc! {"
 4727        const a: B = (
 4728            c(),
 4729            d(
 4730                e,
 4731                f
 4732 4733        );
 4734    "});
 4735
 4736    // Paste it at a line with a lower indent level.
 4737    cx.set_state(indoc! {"
 4738        ˇ
 4739        const a: B = (
 4740            c(),
 4741        );
 4742    "});
 4743    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4744    cx.assert_editor_state(indoc! {"
 4745        d(
 4746            e,
 4747            f
 4748 4749        const a: B = (
 4750            c(),
 4751        );
 4752    "});
 4753
 4754    // Cut an indented block, with the leading whitespace.
 4755    cx.set_state(indoc! {"
 4756        const a: B = (
 4757            c(),
 4758        «    d(
 4759                e,
 4760                f
 4761            )
 4762        ˇ»);
 4763    "});
 4764    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4765    cx.assert_editor_state(indoc! {"
 4766        const a: B = (
 4767            c(),
 4768        ˇ);
 4769    "});
 4770
 4771    // Paste it at the same position.
 4772    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4773    cx.assert_editor_state(indoc! {"
 4774        const a: B = (
 4775            c(),
 4776            d(
 4777                e,
 4778                f
 4779            )
 4780        ˇ);
 4781    "});
 4782
 4783    // Paste it at a line with a higher indent level.
 4784    cx.set_state(indoc! {"
 4785        const a: B = (
 4786            c(),
 4787            d(
 4788                e,
 4789 4790            )
 4791        );
 4792    "});
 4793    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4794    cx.assert_editor_state(indoc! {"
 4795        const a: B = (
 4796            c(),
 4797            d(
 4798                e,
 4799                f    d(
 4800                    e,
 4801                    f
 4802                )
 4803        ˇ
 4804            )
 4805        );
 4806    "});
 4807}
 4808
 4809#[gpui::test]
 4810fn test_select_all(cx: &mut TestAppContext) {
 4811    init_test(cx, |_| {});
 4812
 4813    let editor = cx.add_window(|window, cx| {
 4814        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4815        build_editor(buffer, window, cx)
 4816    });
 4817    _ = editor.update(cx, |editor, window, cx| {
 4818        editor.select_all(&SelectAll, window, cx);
 4819        assert_eq!(
 4820            editor.selections.display_ranges(cx),
 4821            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4822        );
 4823    });
 4824}
 4825
 4826#[gpui::test]
 4827fn test_select_line(cx: &mut TestAppContext) {
 4828    init_test(cx, |_| {});
 4829
 4830    let editor = cx.add_window(|window, cx| {
 4831        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4832        build_editor(buffer, window, cx)
 4833    });
 4834    _ = editor.update(cx, |editor, window, cx| {
 4835        editor.change_selections(None, window, cx, |s| {
 4836            s.select_display_ranges([
 4837                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4838                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4839                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4840                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4841            ])
 4842        });
 4843        editor.select_line(&SelectLine, window, cx);
 4844        assert_eq!(
 4845            editor.selections.display_ranges(cx),
 4846            vec![
 4847                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4848                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4849            ]
 4850        );
 4851    });
 4852
 4853    _ = editor.update(cx, |editor, window, cx| {
 4854        editor.select_line(&SelectLine, window, cx);
 4855        assert_eq!(
 4856            editor.selections.display_ranges(cx),
 4857            vec![
 4858                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4859                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4860            ]
 4861        );
 4862    });
 4863
 4864    _ = editor.update(cx, |editor, window, cx| {
 4865        editor.select_line(&SelectLine, window, cx);
 4866        assert_eq!(
 4867            editor.selections.display_ranges(cx),
 4868            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4869        );
 4870    });
 4871}
 4872
 4873#[gpui::test]
 4874async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4875    init_test(cx, |_| {});
 4876    let mut cx = EditorTestContext::new(cx).await;
 4877
 4878    #[track_caller]
 4879    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4880        cx.set_state(initial_state);
 4881        cx.update_editor(|e, window, cx| {
 4882            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4883        });
 4884        cx.assert_editor_state(expected_state);
 4885    }
 4886
 4887    // Selection starts and ends at the middle of lines, left-to-right
 4888    test(
 4889        &mut cx,
 4890        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4891        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4892    );
 4893    // Same thing, right-to-left
 4894    test(
 4895        &mut cx,
 4896        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4897        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4898    );
 4899
 4900    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4901    test(
 4902        &mut cx,
 4903        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4904        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4905    );
 4906    // Same thing, right-to-left
 4907    test(
 4908        &mut cx,
 4909        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4910        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4911    );
 4912
 4913    // Whole buffer, left-to-right, last line ends with newline
 4914    test(
 4915        &mut cx,
 4916        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4917        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4918    );
 4919    // Same thing, right-to-left
 4920    test(
 4921        &mut cx,
 4922        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4923        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4924    );
 4925
 4926    // Starts at the end of a line, ends at the start of another
 4927    test(
 4928        &mut cx,
 4929        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4930        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4931    );
 4932}
 4933
 4934#[gpui::test]
 4935async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 4936    init_test(cx, |_| {});
 4937
 4938    let editor = cx.add_window(|window, cx| {
 4939        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4940        build_editor(buffer, window, cx)
 4941    });
 4942
 4943    // setup
 4944    _ = editor.update(cx, |editor, window, cx| {
 4945        editor.fold_creases(
 4946            vec![
 4947                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4948                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4949                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4950            ],
 4951            true,
 4952            window,
 4953            cx,
 4954        );
 4955        assert_eq!(
 4956            editor.display_text(cx),
 4957            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4958        );
 4959    });
 4960
 4961    _ = editor.update(cx, |editor, window, cx| {
 4962        editor.change_selections(None, window, cx, |s| {
 4963            s.select_display_ranges([
 4964                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4965                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4966                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4967                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4968            ])
 4969        });
 4970        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4971        assert_eq!(
 4972            editor.display_text(cx),
 4973            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4974        );
 4975    });
 4976    EditorTestContext::for_editor(editor, cx)
 4977        .await
 4978        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 4979
 4980    _ = editor.update(cx, |editor, window, cx| {
 4981        editor.change_selections(None, window, cx, |s| {
 4982            s.select_display_ranges([
 4983                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4984            ])
 4985        });
 4986        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4987        assert_eq!(
 4988            editor.display_text(cx),
 4989            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4990        );
 4991        assert_eq!(
 4992            editor.selections.display_ranges(cx),
 4993            [
 4994                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4995                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4996                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4997                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4998                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4999                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5000                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5001            ]
 5002        );
 5003    });
 5004    EditorTestContext::for_editor(editor, cx)
 5005        .await
 5006        .assert_editor_state(
 5007            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5008        );
 5009}
 5010
 5011#[gpui::test]
 5012async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5013    init_test(cx, |_| {});
 5014
 5015    let mut cx = EditorTestContext::new(cx).await;
 5016
 5017    cx.set_state(indoc!(
 5018        r#"abc
 5019           defˇghi
 5020
 5021           jk
 5022           nlmo
 5023           "#
 5024    ));
 5025
 5026    cx.update_editor(|editor, window, cx| {
 5027        editor.add_selection_above(&Default::default(), window, cx);
 5028    });
 5029
 5030    cx.assert_editor_state(indoc!(
 5031        r#"abcˇ
 5032           defˇghi
 5033
 5034           jk
 5035           nlmo
 5036           "#
 5037    ));
 5038
 5039    cx.update_editor(|editor, window, cx| {
 5040        editor.add_selection_above(&Default::default(), window, cx);
 5041    });
 5042
 5043    cx.assert_editor_state(indoc!(
 5044        r#"abcˇ
 5045            defˇghi
 5046
 5047            jk
 5048            nlmo
 5049            "#
 5050    ));
 5051
 5052    cx.update_editor(|editor, window, cx| {
 5053        editor.add_selection_below(&Default::default(), window, cx);
 5054    });
 5055
 5056    cx.assert_editor_state(indoc!(
 5057        r#"abc
 5058           defˇghi
 5059
 5060           jk
 5061           nlmo
 5062           "#
 5063    ));
 5064
 5065    cx.update_editor(|editor, window, cx| {
 5066        editor.undo_selection(&Default::default(), window, cx);
 5067    });
 5068
 5069    cx.assert_editor_state(indoc!(
 5070        r#"abcˇ
 5071           defˇghi
 5072
 5073           jk
 5074           nlmo
 5075           "#
 5076    ));
 5077
 5078    cx.update_editor(|editor, window, cx| {
 5079        editor.redo_selection(&Default::default(), window, cx);
 5080    });
 5081
 5082    cx.assert_editor_state(indoc!(
 5083        r#"abc
 5084           defˇghi
 5085
 5086           jk
 5087           nlmo
 5088           "#
 5089    ));
 5090
 5091    cx.update_editor(|editor, window, cx| {
 5092        editor.add_selection_below(&Default::default(), window, cx);
 5093    });
 5094
 5095    cx.assert_editor_state(indoc!(
 5096        r#"abc
 5097           defˇghi
 5098
 5099           jk
 5100           nlmˇo
 5101           "#
 5102    ));
 5103
 5104    cx.update_editor(|editor, window, cx| {
 5105        editor.add_selection_below(&Default::default(), window, cx);
 5106    });
 5107
 5108    cx.assert_editor_state(indoc!(
 5109        r#"abc
 5110           defˇghi
 5111
 5112           jk
 5113           nlmˇo
 5114           "#
 5115    ));
 5116
 5117    // change selections
 5118    cx.set_state(indoc!(
 5119        r#"abc
 5120           def«ˇg»hi
 5121
 5122           jk
 5123           nlmo
 5124           "#
 5125    ));
 5126
 5127    cx.update_editor(|editor, window, cx| {
 5128        editor.add_selection_below(&Default::default(), window, cx);
 5129    });
 5130
 5131    cx.assert_editor_state(indoc!(
 5132        r#"abc
 5133           def«ˇg»hi
 5134
 5135           jk
 5136           nlm«ˇo»
 5137           "#
 5138    ));
 5139
 5140    cx.update_editor(|editor, window, cx| {
 5141        editor.add_selection_below(&Default::default(), window, cx);
 5142    });
 5143
 5144    cx.assert_editor_state(indoc!(
 5145        r#"abc
 5146           def«ˇg»hi
 5147
 5148           jk
 5149           nlm«ˇo»
 5150           "#
 5151    ));
 5152
 5153    cx.update_editor(|editor, window, cx| {
 5154        editor.add_selection_above(&Default::default(), window, cx);
 5155    });
 5156
 5157    cx.assert_editor_state(indoc!(
 5158        r#"abc
 5159           def«ˇg»hi
 5160
 5161           jk
 5162           nlmo
 5163           "#
 5164    ));
 5165
 5166    cx.update_editor(|editor, window, cx| {
 5167        editor.add_selection_above(&Default::default(), window, cx);
 5168    });
 5169
 5170    cx.assert_editor_state(indoc!(
 5171        r#"abc
 5172           def«ˇg»hi
 5173
 5174           jk
 5175           nlmo
 5176           "#
 5177    ));
 5178
 5179    // Change selections again
 5180    cx.set_state(indoc!(
 5181        r#"a«bc
 5182           defgˇ»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#"a«bcˇ»
 5195           d«efgˇ»hi
 5196
 5197           j«kˇ»
 5198           nlmo
 5199           "#
 5200    ));
 5201
 5202    cx.update_editor(|editor, window, cx| {
 5203        editor.add_selection_below(&Default::default(), window, cx);
 5204    });
 5205    cx.assert_editor_state(indoc!(
 5206        r#"a«bcˇ»
 5207           d«efgˇ»hi
 5208
 5209           j«kˇ»
 5210           n«lmoˇ»
 5211           "#
 5212    ));
 5213    cx.update_editor(|editor, window, cx| {
 5214        editor.add_selection_above(&Default::default(), window, cx);
 5215    });
 5216
 5217    cx.assert_editor_state(indoc!(
 5218        r#"a«bcˇ»
 5219           d«efgˇ»hi
 5220
 5221           j«kˇ»
 5222           nlmo
 5223           "#
 5224    ));
 5225
 5226    // Change selections again
 5227    cx.set_state(indoc!(
 5228        r#"abc
 5229           d«ˇefghi
 5230
 5231           jk
 5232           nlm»o
 5233           "#
 5234    ));
 5235
 5236    cx.update_editor(|editor, window, cx| {
 5237        editor.add_selection_above(&Default::default(), window, cx);
 5238    });
 5239
 5240    cx.assert_editor_state(indoc!(
 5241        r#"a«ˇbc»
 5242           d«ˇef»ghi
 5243
 5244           j«ˇk»
 5245           n«ˇlm»o
 5246           "#
 5247    ));
 5248
 5249    cx.update_editor(|editor, window, cx| {
 5250        editor.add_selection_below(&Default::default(), window, cx);
 5251    });
 5252
 5253    cx.assert_editor_state(indoc!(
 5254        r#"abc
 5255           d«ˇef»ghi
 5256
 5257           j«ˇk»
 5258           n«ˇlm»o
 5259           "#
 5260    ));
 5261}
 5262
 5263#[gpui::test]
 5264async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5265    init_test(cx, |_| {});
 5266
 5267    let mut cx = EditorTestContext::new(cx).await;
 5268    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5269
 5270    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5271        .unwrap();
 5272    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5273
 5274    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5275        .unwrap();
 5276    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5277
 5278    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5279    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5280
 5281    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5282    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5283
 5284    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5285        .unwrap();
 5286    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5287
 5288    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5289        .unwrap();
 5290    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5291}
 5292
 5293#[gpui::test]
 5294async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5295    init_test(cx, |_| {});
 5296
 5297    let mut cx = EditorTestContext::new(cx).await;
 5298
 5299    // Test caret-only selections
 5300    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5301    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5302        .unwrap();
 5303    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5304
 5305    // Test left-to-right selections
 5306    cx.set_state("abc\n«abcˇ»\nabc");
 5307    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5308        .unwrap();
 5309    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5310
 5311    // Test right-to-left selections
 5312    cx.set_state("abc\n«ˇabc»\nabc");
 5313    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5314        .unwrap();
 5315    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5316
 5317    // Test selecting whitespace with caret selection
 5318    cx.set_state("abc\nˇ   abc\nabc");
 5319    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5320        .unwrap();
 5321    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5322
 5323    // Test selecting whitespace with left-to-right selection
 5324    cx.set_state("abc\n«ˇ  »abc\nabc");
 5325    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5326        .unwrap();
 5327    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5328
 5329    // Test no matches with right-to-left selection
 5330    cx.set_state("abc\n«  ˇ»abc\nabc");
 5331    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5332        .unwrap();
 5333    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5334}
 5335
 5336#[gpui::test]
 5337async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5338    init_test(cx, |_| {});
 5339
 5340    let mut cx = EditorTestContext::new(cx).await;
 5341    cx.set_state(
 5342        r#"let foo = 2;
 5343lˇet foo = 2;
 5344let fooˇ = 2;
 5345let foo = 2;
 5346let foo = ˇ2;"#,
 5347    );
 5348
 5349    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5350        .unwrap();
 5351    cx.assert_editor_state(
 5352        r#"let foo = 2;
 5353«letˇ» foo = 2;
 5354let «fooˇ» = 2;
 5355let foo = 2;
 5356let foo = «2ˇ»;"#,
 5357    );
 5358
 5359    // noop for multiple selections with different contents
 5360    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5361        .unwrap();
 5362    cx.assert_editor_state(
 5363        r#"let foo = 2;
 5364«letˇ» foo = 2;
 5365let «fooˇ» = 2;
 5366let foo = 2;
 5367let foo = «2ˇ»;"#,
 5368    );
 5369}
 5370
 5371#[gpui::test]
 5372async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5373    init_test(cx, |_| {});
 5374
 5375    let mut cx =
 5376        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5377
 5378    cx.assert_editor_state(indoc! {"
 5379        ˇbbb
 5380        ccc
 5381
 5382        bbb
 5383        ccc
 5384        "});
 5385    cx.dispatch_action(SelectPrevious::default());
 5386    cx.assert_editor_state(indoc! {"
 5387                «bbbˇ»
 5388                ccc
 5389
 5390                bbb
 5391                ccc
 5392                "});
 5393    cx.dispatch_action(SelectPrevious::default());
 5394    cx.assert_editor_state(indoc! {"
 5395                «bbbˇ»
 5396                ccc
 5397
 5398                «bbbˇ»
 5399                ccc
 5400                "});
 5401}
 5402
 5403#[gpui::test]
 5404async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5405    init_test(cx, |_| {});
 5406
 5407    let mut cx = EditorTestContext::new(cx).await;
 5408    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5409
 5410    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5411        .unwrap();
 5412    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5413
 5414    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5415        .unwrap();
 5416    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5417
 5418    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5419    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5420
 5421    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5422    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5423
 5424    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5425        .unwrap();
 5426    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5427
 5428    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5429        .unwrap();
 5430    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5431
 5432    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5433        .unwrap();
 5434    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5435}
 5436
 5437#[gpui::test]
 5438async fn test_select_previous_empty_buffer(cx: &mut gpui::TestAppContext) {
 5439    init_test(cx, |_| {});
 5440
 5441    let mut cx = EditorTestContext::new(cx).await;
 5442    cx.set_state("");
 5443
 5444    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5445        .unwrap();
 5446    cx.assert_editor_state("«aˇ»");
 5447    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5448        .unwrap();
 5449    cx.assert_editor_state("«aˇ»");
 5450}
 5451
 5452#[gpui::test]
 5453async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5454    init_test(cx, |_| {});
 5455
 5456    let mut cx = EditorTestContext::new(cx).await;
 5457    cx.set_state(
 5458        r#"let foo = 2;
 5459lˇet foo = 2;
 5460let fooˇ = 2;
 5461let foo = 2;
 5462let foo = ˇ2;"#,
 5463    );
 5464
 5465    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5466        .unwrap();
 5467    cx.assert_editor_state(
 5468        r#"let foo = 2;
 5469«letˇ» foo = 2;
 5470let «fooˇ» = 2;
 5471let foo = 2;
 5472let foo = «2ˇ»;"#,
 5473    );
 5474
 5475    // noop for multiple selections with different contents
 5476    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5477        .unwrap();
 5478    cx.assert_editor_state(
 5479        r#"let foo = 2;
 5480«letˇ» foo = 2;
 5481let «fooˇ» = 2;
 5482let foo = 2;
 5483let foo = «2ˇ»;"#,
 5484    );
 5485}
 5486
 5487#[gpui::test]
 5488async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5489    init_test(cx, |_| {});
 5490
 5491    let mut cx = EditorTestContext::new(cx).await;
 5492    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 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\ndefabc\nabc");
 5497
 5498    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5499        .unwrap();
 5500    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5501
 5502    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5503    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5504
 5505    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5506    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5507
 5508    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5509        .unwrap();
 5510    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5511
 5512    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5513        .unwrap();
 5514    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5515}
 5516
 5517#[gpui::test]
 5518async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5519    init_test(cx, |_| {});
 5520
 5521    let language = Arc::new(Language::new(
 5522        LanguageConfig::default(),
 5523        Some(tree_sitter_rust::LANGUAGE.into()),
 5524    ));
 5525
 5526    let text = r#"
 5527        use mod1::mod2::{mod3, mod4};
 5528
 5529        fn fn_1(param1: bool, param2: &str) {
 5530            let var1 = "text";
 5531        }
 5532    "#
 5533    .unindent();
 5534
 5535    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5536    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5537    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5538
 5539    editor
 5540        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5541        .await;
 5542
 5543    editor.update_in(cx, |editor, window, cx| {
 5544        editor.change_selections(None, window, cx, |s| {
 5545            s.select_display_ranges([
 5546                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5547                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5548                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5549            ]);
 5550        });
 5551        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5552    });
 5553    editor.update(cx, |editor, cx| {
 5554        assert_text_with_selections(
 5555            editor,
 5556            indoc! {r#"
 5557                use mod1::mod2::{mod3, «mod4ˇ»};
 5558
 5559                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5560                    let var1 = "«textˇ»";
 5561                }
 5562            "#},
 5563            cx,
 5564        );
 5565    });
 5566
 5567    editor.update_in(cx, |editor, window, cx| {
 5568        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5569    });
 5570    editor.update(cx, |editor, cx| {
 5571        assert_text_with_selections(
 5572            editor,
 5573            indoc! {r#"
 5574                use mod1::mod2::«{mod3, mod4}ˇ»;
 5575
 5576                «ˇfn fn_1(param1: bool, param2: &str) {
 5577                    let var1 = "text";
 5578 5579            "#},
 5580            cx,
 5581        );
 5582    });
 5583
 5584    editor.update_in(cx, |editor, window, cx| {
 5585        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5586    });
 5587    assert_eq!(
 5588        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5589        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5590    );
 5591
 5592    // Trying to expand the selected syntax node one more time has no effect.
 5593    editor.update_in(cx, |editor, window, cx| {
 5594        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5595    });
 5596    assert_eq!(
 5597        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5598        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5599    );
 5600
 5601    editor.update_in(cx, |editor, window, cx| {
 5602        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5603    });
 5604    editor.update(cx, |editor, cx| {
 5605        assert_text_with_selections(
 5606            editor,
 5607            indoc! {r#"
 5608                use mod1::mod2::«{mod3, mod4}ˇ»;
 5609
 5610                «ˇfn fn_1(param1: bool, param2: &str) {
 5611                    let var1 = "text";
 5612 5613            "#},
 5614            cx,
 5615        );
 5616    });
 5617
 5618    editor.update_in(cx, |editor, window, cx| {
 5619        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5620    });
 5621    editor.update(cx, |editor, cx| {
 5622        assert_text_with_selections(
 5623            editor,
 5624            indoc! {r#"
 5625                use mod1::mod2::{mod3, «mod4ˇ»};
 5626
 5627                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5628                    let var1 = "«textˇ»";
 5629                }
 5630            "#},
 5631            cx,
 5632        );
 5633    });
 5634
 5635    editor.update_in(cx, |editor, window, cx| {
 5636        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5637    });
 5638    editor.update(cx, |editor, cx| {
 5639        assert_text_with_selections(
 5640            editor,
 5641            indoc! {r#"
 5642                use mod1::mod2::{mod3, mo«ˇ»d4};
 5643
 5644                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5645                    let var1 = "te«ˇ»xt";
 5646                }
 5647            "#},
 5648            cx,
 5649        );
 5650    });
 5651
 5652    // Trying to shrink the selected syntax node one more time has no effect.
 5653    editor.update_in(cx, |editor, window, cx| {
 5654        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5655    });
 5656    editor.update_in(cx, |editor, _, cx| {
 5657        assert_text_with_selections(
 5658            editor,
 5659            indoc! {r#"
 5660                use mod1::mod2::{mod3, mo«ˇ»d4};
 5661
 5662                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5663                    let var1 = "te«ˇ»xt";
 5664                }
 5665            "#},
 5666            cx,
 5667        );
 5668    });
 5669
 5670    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5671    // a fold.
 5672    editor.update_in(cx, |editor, window, cx| {
 5673        editor.fold_creases(
 5674            vec![
 5675                Crease::simple(
 5676                    Point::new(0, 21)..Point::new(0, 24),
 5677                    FoldPlaceholder::test(),
 5678                ),
 5679                Crease::simple(
 5680                    Point::new(3, 20)..Point::new(3, 22),
 5681                    FoldPlaceholder::test(),
 5682                ),
 5683            ],
 5684            true,
 5685            window,
 5686            cx,
 5687        );
 5688        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5689    });
 5690    editor.update(cx, |editor, cx| {
 5691        assert_text_with_selections(
 5692            editor,
 5693            indoc! {r#"
 5694                use mod1::mod2::«{mod3, mod4}ˇ»;
 5695
 5696                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5697                    «let var1 = "text";ˇ»
 5698                }
 5699            "#},
 5700            cx,
 5701        );
 5702    });
 5703}
 5704
 5705#[gpui::test]
 5706async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5707    init_test(cx, |_| {});
 5708
 5709    let base_text = r#"
 5710        impl A {
 5711            // this is an uncommitted comment
 5712
 5713            fn b() {
 5714                c();
 5715            }
 5716
 5717            // this is another uncommitted comment
 5718
 5719            fn d() {
 5720                // e
 5721                // f
 5722            }
 5723        }
 5724
 5725        fn g() {
 5726            // h
 5727        }
 5728    "#
 5729    .unindent();
 5730
 5731    let text = r#"
 5732        ˇimpl A {
 5733
 5734            fn b() {
 5735                c();
 5736            }
 5737
 5738            fn d() {
 5739                // e
 5740                // f
 5741            }
 5742        }
 5743
 5744        fn g() {
 5745            // h
 5746        }
 5747    "#
 5748    .unindent();
 5749
 5750    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5751    cx.set_state(&text);
 5752    cx.set_diff_base(&base_text);
 5753    cx.update_editor(|editor, window, cx| {
 5754        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5755    });
 5756
 5757    cx.assert_state_with_diff(
 5758        "
 5759        ˇimpl A {
 5760      -     // this is an uncommitted comment
 5761
 5762            fn b() {
 5763                c();
 5764            }
 5765
 5766      -     // this is another uncommitted comment
 5767      -
 5768            fn d() {
 5769                // e
 5770                // f
 5771            }
 5772        }
 5773
 5774        fn g() {
 5775            // h
 5776        }
 5777    "
 5778        .unindent(),
 5779    );
 5780
 5781    let expected_display_text = "
 5782        impl A {
 5783            // this is an uncommitted comment
 5784
 5785            fn b() {
 5786 5787            }
 5788
 5789            // this is another uncommitted comment
 5790
 5791            fn d() {
 5792 5793            }
 5794        }
 5795
 5796        fn g() {
 5797 5798        }
 5799        "
 5800    .unindent();
 5801
 5802    cx.update_editor(|editor, window, cx| {
 5803        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5804        assert_eq!(editor.display_text(cx), expected_display_text);
 5805    });
 5806}
 5807
 5808#[gpui::test]
 5809async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5810    init_test(cx, |_| {});
 5811
 5812    let language = Arc::new(
 5813        Language::new(
 5814            LanguageConfig {
 5815                brackets: BracketPairConfig {
 5816                    pairs: vec![
 5817                        BracketPair {
 5818                            start: "{".to_string(),
 5819                            end: "}".to_string(),
 5820                            close: false,
 5821                            surround: false,
 5822                            newline: true,
 5823                        },
 5824                        BracketPair {
 5825                            start: "(".to_string(),
 5826                            end: ")".to_string(),
 5827                            close: false,
 5828                            surround: false,
 5829                            newline: true,
 5830                        },
 5831                    ],
 5832                    ..Default::default()
 5833                },
 5834                ..Default::default()
 5835            },
 5836            Some(tree_sitter_rust::LANGUAGE.into()),
 5837        )
 5838        .with_indents_query(
 5839            r#"
 5840                (_ "(" ")" @end) @indent
 5841                (_ "{" "}" @end) @indent
 5842            "#,
 5843        )
 5844        .unwrap(),
 5845    );
 5846
 5847    let text = "fn a() {}";
 5848
 5849    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5850    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5851    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5852    editor
 5853        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5854        .await;
 5855
 5856    editor.update_in(cx, |editor, window, cx| {
 5857        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5858        editor.newline(&Newline, window, cx);
 5859        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5860        assert_eq!(
 5861            editor.selections.ranges(cx),
 5862            &[
 5863                Point::new(1, 4)..Point::new(1, 4),
 5864                Point::new(3, 4)..Point::new(3, 4),
 5865                Point::new(5, 0)..Point::new(5, 0)
 5866            ]
 5867        );
 5868    });
 5869}
 5870
 5871#[gpui::test]
 5872async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5873    init_test(cx, |_| {});
 5874
 5875    {
 5876        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5877        cx.set_state(indoc! {"
 5878            impl A {
 5879
 5880                fn b() {}
 5881
 5882            «fn c() {
 5883
 5884            }ˇ»
 5885            }
 5886        "});
 5887
 5888        cx.update_editor(|editor, window, cx| {
 5889            editor.autoindent(&Default::default(), window, cx);
 5890        });
 5891
 5892        cx.assert_editor_state(indoc! {"
 5893            impl A {
 5894
 5895                fn b() {}
 5896
 5897                «fn c() {
 5898
 5899                }ˇ»
 5900            }
 5901        "});
 5902    }
 5903
 5904    {
 5905        let mut cx = EditorTestContext::new_multibuffer(
 5906            cx,
 5907            [indoc! { "
 5908                impl A {
 5909                «
 5910                // a
 5911                fn b(){}
 5912                »
 5913                «
 5914                    }
 5915                    fn c(){}
 5916                »
 5917            "}],
 5918        );
 5919
 5920        let buffer = cx.update_editor(|editor, _, cx| {
 5921            let buffer = editor.buffer().update(cx, |buffer, _| {
 5922                buffer.all_buffers().iter().next().unwrap().clone()
 5923            });
 5924            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5925            buffer
 5926        });
 5927
 5928        cx.run_until_parked();
 5929        cx.update_editor(|editor, window, cx| {
 5930            editor.select_all(&Default::default(), window, cx);
 5931            editor.autoindent(&Default::default(), window, cx)
 5932        });
 5933        cx.run_until_parked();
 5934
 5935        cx.update(|_, cx| {
 5936            pretty_assertions::assert_eq!(
 5937                buffer.read(cx).text(),
 5938                indoc! { "
 5939                    impl A {
 5940
 5941                        // a
 5942                        fn b(){}
 5943
 5944
 5945                    }
 5946                    fn c(){}
 5947
 5948                " }
 5949            )
 5950        });
 5951    }
 5952}
 5953
 5954#[gpui::test]
 5955async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5956    init_test(cx, |_| {});
 5957
 5958    let mut cx = EditorTestContext::new(cx).await;
 5959
 5960    let language = Arc::new(Language::new(
 5961        LanguageConfig {
 5962            brackets: BracketPairConfig {
 5963                pairs: vec![
 5964                    BracketPair {
 5965                        start: "{".to_string(),
 5966                        end: "}".to_string(),
 5967                        close: true,
 5968                        surround: true,
 5969                        newline: true,
 5970                    },
 5971                    BracketPair {
 5972                        start: "(".to_string(),
 5973                        end: ")".to_string(),
 5974                        close: true,
 5975                        surround: true,
 5976                        newline: true,
 5977                    },
 5978                    BracketPair {
 5979                        start: "/*".to_string(),
 5980                        end: " */".to_string(),
 5981                        close: true,
 5982                        surround: true,
 5983                        newline: true,
 5984                    },
 5985                    BracketPair {
 5986                        start: "[".to_string(),
 5987                        end: "]".to_string(),
 5988                        close: false,
 5989                        surround: false,
 5990                        newline: true,
 5991                    },
 5992                    BracketPair {
 5993                        start: "\"".to_string(),
 5994                        end: "\"".to_string(),
 5995                        close: true,
 5996                        surround: true,
 5997                        newline: false,
 5998                    },
 5999                    BracketPair {
 6000                        start: "<".to_string(),
 6001                        end: ">".to_string(),
 6002                        close: false,
 6003                        surround: true,
 6004                        newline: true,
 6005                    },
 6006                ],
 6007                ..Default::default()
 6008            },
 6009            autoclose_before: "})]".to_string(),
 6010            ..Default::default()
 6011        },
 6012        Some(tree_sitter_rust::LANGUAGE.into()),
 6013    ));
 6014
 6015    cx.language_registry().add(language.clone());
 6016    cx.update_buffer(|buffer, cx| {
 6017        buffer.set_language(Some(language), cx);
 6018    });
 6019
 6020    cx.set_state(
 6021        &r#"
 6022            🏀ˇ
 6023            εˇ
 6024            ❤️ˇ
 6025        "#
 6026        .unindent(),
 6027    );
 6028
 6029    // autoclose multiple nested brackets at multiple cursors
 6030    cx.update_editor(|editor, window, cx| {
 6031        editor.handle_input("{", window, cx);
 6032        editor.handle_input("{", window, cx);
 6033        editor.handle_input("{", window, cx);
 6034    });
 6035    cx.assert_editor_state(
 6036        &"
 6037            🏀{{{ˇ}}}
 6038            ε{{{ˇ}}}
 6039            ❤️{{{ˇ}}}
 6040        "
 6041        .unindent(),
 6042    );
 6043
 6044    // insert a different closing bracket
 6045    cx.update_editor(|editor, window, cx| {
 6046        editor.handle_input(")", window, cx);
 6047    });
 6048    cx.assert_editor_state(
 6049        &"
 6050            🏀{{{)ˇ}}}
 6051            ε{{{)ˇ}}}
 6052            ❤️{{{)ˇ}}}
 6053        "
 6054        .unindent(),
 6055    );
 6056
 6057    // skip over the auto-closed brackets when typing a closing bracket
 6058    cx.update_editor(|editor, window, cx| {
 6059        editor.move_right(&MoveRight, window, cx);
 6060        editor.handle_input("}", window, cx);
 6061        editor.handle_input("}", window, cx);
 6062        editor.handle_input("}", window, cx);
 6063    });
 6064    cx.assert_editor_state(
 6065        &"
 6066            🏀{{{)}}}}ˇ
 6067            ε{{{)}}}}ˇ
 6068            ❤️{{{)}}}}ˇ
 6069        "
 6070        .unindent(),
 6071    );
 6072
 6073    // autoclose multi-character pairs
 6074    cx.set_state(
 6075        &"
 6076            ˇ
 6077            ˇ
 6078        "
 6079        .unindent(),
 6080    );
 6081    cx.update_editor(|editor, window, cx| {
 6082        editor.handle_input("/", window, cx);
 6083        editor.handle_input("*", window, cx);
 6084    });
 6085    cx.assert_editor_state(
 6086        &"
 6087            /*ˇ */
 6088            /*ˇ */
 6089        "
 6090        .unindent(),
 6091    );
 6092
 6093    // one cursor autocloses a multi-character pair, one cursor
 6094    // does not autoclose.
 6095    cx.set_state(
 6096        &"
 6097 6098            ˇ
 6099        "
 6100        .unindent(),
 6101    );
 6102    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6103    cx.assert_editor_state(
 6104        &"
 6105            /*ˇ */
 6106 6107        "
 6108        .unindent(),
 6109    );
 6110
 6111    // Don't autoclose if the next character isn't whitespace and isn't
 6112    // listed in the language's "autoclose_before" section.
 6113    cx.set_state("ˇa b");
 6114    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6115    cx.assert_editor_state("{ˇa b");
 6116
 6117    // Don't autoclose if `close` is false for the bracket pair
 6118    cx.set_state("ˇ");
 6119    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6120    cx.assert_editor_state("");
 6121
 6122    // Surround with brackets if text is selected
 6123    cx.set_state("«aˇ» b");
 6124    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6125    cx.assert_editor_state("{«aˇ»} b");
 6126
 6127    // Autclose pair where the start and end characters are the same
 6128    cx.set_state("");
 6129    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6130    cx.assert_editor_state("a\"ˇ\"");
 6131    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6132    cx.assert_editor_state("a\"\"ˇ");
 6133
 6134    // Don't autoclose pair if autoclose is disabled
 6135    cx.set_state("ˇ");
 6136    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6137    cx.assert_editor_state("");
 6138
 6139    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6140    cx.set_state("«aˇ» b");
 6141    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6142    cx.assert_editor_state("<«aˇ»> b");
 6143}
 6144
 6145#[gpui::test]
 6146async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6147    init_test(cx, |settings| {
 6148        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6149    });
 6150
 6151    let mut cx = EditorTestContext::new(cx).await;
 6152
 6153    let language = Arc::new(Language::new(
 6154        LanguageConfig {
 6155            brackets: BracketPairConfig {
 6156                pairs: vec![
 6157                    BracketPair {
 6158                        start: "{".to_string(),
 6159                        end: "}".to_string(),
 6160                        close: true,
 6161                        surround: true,
 6162                        newline: true,
 6163                    },
 6164                    BracketPair {
 6165                        start: "(".to_string(),
 6166                        end: ")".to_string(),
 6167                        close: true,
 6168                        surround: true,
 6169                        newline: true,
 6170                    },
 6171                    BracketPair {
 6172                        start: "[".to_string(),
 6173                        end: "]".to_string(),
 6174                        close: false,
 6175                        surround: false,
 6176                        newline: true,
 6177                    },
 6178                ],
 6179                ..Default::default()
 6180            },
 6181            autoclose_before: "})]".to_string(),
 6182            ..Default::default()
 6183        },
 6184        Some(tree_sitter_rust::LANGUAGE.into()),
 6185    ));
 6186
 6187    cx.language_registry().add(language.clone());
 6188    cx.update_buffer(|buffer, cx| {
 6189        buffer.set_language(Some(language), cx);
 6190    });
 6191
 6192    cx.set_state(
 6193        &"
 6194            ˇ
 6195            ˇ
 6196            ˇ
 6197        "
 6198        .unindent(),
 6199    );
 6200
 6201    // ensure only matching closing brackets are skipped over
 6202    cx.update_editor(|editor, window, cx| {
 6203        editor.handle_input("}", window, cx);
 6204        editor.move_left(&MoveLeft, window, cx);
 6205        editor.handle_input(")", window, cx);
 6206        editor.move_left(&MoveLeft, window, cx);
 6207    });
 6208    cx.assert_editor_state(
 6209        &"
 6210            ˇ)}
 6211            ˇ)}
 6212            ˇ)}
 6213        "
 6214        .unindent(),
 6215    );
 6216
 6217    // skip-over closing brackets at multiple cursors
 6218    cx.update_editor(|editor, window, cx| {
 6219        editor.handle_input(")", window, cx);
 6220        editor.handle_input("}", window, cx);
 6221    });
 6222    cx.assert_editor_state(
 6223        &"
 6224            )}ˇ
 6225            )}ˇ
 6226            )}ˇ
 6227        "
 6228        .unindent(),
 6229    );
 6230
 6231    // ignore non-close brackets
 6232    cx.update_editor(|editor, window, cx| {
 6233        editor.handle_input("]", window, cx);
 6234        editor.move_left(&MoveLeft, window, cx);
 6235        editor.handle_input("]", window, cx);
 6236    });
 6237    cx.assert_editor_state(
 6238        &"
 6239            )}]ˇ]
 6240            )}]ˇ]
 6241            )}]ˇ]
 6242        "
 6243        .unindent(),
 6244    );
 6245}
 6246
 6247#[gpui::test]
 6248async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6249    init_test(cx, |_| {});
 6250
 6251    let mut cx = EditorTestContext::new(cx).await;
 6252
 6253    let html_language = Arc::new(
 6254        Language::new(
 6255            LanguageConfig {
 6256                name: "HTML".into(),
 6257                brackets: BracketPairConfig {
 6258                    pairs: vec![
 6259                        BracketPair {
 6260                            start: "<".into(),
 6261                            end: ">".into(),
 6262                            close: true,
 6263                            ..Default::default()
 6264                        },
 6265                        BracketPair {
 6266                            start: "{".into(),
 6267                            end: "}".into(),
 6268                            close: true,
 6269                            ..Default::default()
 6270                        },
 6271                        BracketPair {
 6272                            start: "(".into(),
 6273                            end: ")".into(),
 6274                            close: true,
 6275                            ..Default::default()
 6276                        },
 6277                    ],
 6278                    ..Default::default()
 6279                },
 6280                autoclose_before: "})]>".into(),
 6281                ..Default::default()
 6282            },
 6283            Some(tree_sitter_html::LANGUAGE.into()),
 6284        )
 6285        .with_injection_query(
 6286            r#"
 6287            (script_element
 6288                (raw_text) @injection.content
 6289                (#set! injection.language "javascript"))
 6290            "#,
 6291        )
 6292        .unwrap(),
 6293    );
 6294
 6295    let javascript_language = Arc::new(Language::new(
 6296        LanguageConfig {
 6297            name: "JavaScript".into(),
 6298            brackets: BracketPairConfig {
 6299                pairs: vec![
 6300                    BracketPair {
 6301                        start: "/*".into(),
 6302                        end: " */".into(),
 6303                        close: true,
 6304                        ..Default::default()
 6305                    },
 6306                    BracketPair {
 6307                        start: "{".into(),
 6308                        end: "}".into(),
 6309                        close: true,
 6310                        ..Default::default()
 6311                    },
 6312                    BracketPair {
 6313                        start: "(".into(),
 6314                        end: ")".into(),
 6315                        close: true,
 6316                        ..Default::default()
 6317                    },
 6318                ],
 6319                ..Default::default()
 6320            },
 6321            autoclose_before: "})]>".into(),
 6322            ..Default::default()
 6323        },
 6324        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6325    ));
 6326
 6327    cx.language_registry().add(html_language.clone());
 6328    cx.language_registry().add(javascript_language.clone());
 6329
 6330    cx.update_buffer(|buffer, cx| {
 6331        buffer.set_language(Some(html_language), cx);
 6332    });
 6333
 6334    cx.set_state(
 6335        &r#"
 6336            <body>ˇ
 6337                <script>
 6338                    var x = 1;ˇ
 6339                </script>
 6340            </body>ˇ
 6341        "#
 6342        .unindent(),
 6343    );
 6344
 6345    // Precondition: different languages are active at different locations.
 6346    cx.update_editor(|editor, window, cx| {
 6347        let snapshot = editor.snapshot(window, cx);
 6348        let cursors = editor.selections.ranges::<usize>(cx);
 6349        let languages = cursors
 6350            .iter()
 6351            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6352            .collect::<Vec<_>>();
 6353        assert_eq!(
 6354            languages,
 6355            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6356        );
 6357    });
 6358
 6359    // Angle brackets autoclose in HTML, but not JavaScript.
 6360    cx.update_editor(|editor, window, cx| {
 6361        editor.handle_input("<", window, cx);
 6362        editor.handle_input("a", window, cx);
 6363    });
 6364    cx.assert_editor_state(
 6365        &r#"
 6366            <body><aˇ>
 6367                <script>
 6368                    var x = 1;<aˇ
 6369                </script>
 6370            </body><aˇ>
 6371        "#
 6372        .unindent(),
 6373    );
 6374
 6375    // Curly braces and parens autoclose in both HTML and JavaScript.
 6376    cx.update_editor(|editor, window, cx| {
 6377        editor.handle_input(" b=", window, cx);
 6378        editor.handle_input("{", window, cx);
 6379        editor.handle_input("c", window, cx);
 6380        editor.handle_input("(", window, cx);
 6381    });
 6382    cx.assert_editor_state(
 6383        &r#"
 6384            <body><a b={c(ˇ)}>
 6385                <script>
 6386                    var x = 1;<a b={c(ˇ)}
 6387                </script>
 6388            </body><a b={c(ˇ)}>
 6389        "#
 6390        .unindent(),
 6391    );
 6392
 6393    // Brackets that were already autoclosed are skipped.
 6394    cx.update_editor(|editor, window, cx| {
 6395        editor.handle_input(")", window, cx);
 6396        editor.handle_input("d", window, cx);
 6397        editor.handle_input("}", window, cx);
 6398    });
 6399    cx.assert_editor_state(
 6400        &r#"
 6401            <body><a b={c()d}ˇ>
 6402                <script>
 6403                    var x = 1;<a b={c()d}ˇ
 6404                </script>
 6405            </body><a b={c()d}ˇ>
 6406        "#
 6407        .unindent(),
 6408    );
 6409    cx.update_editor(|editor, window, cx| {
 6410        editor.handle_input(">", window, cx);
 6411    });
 6412    cx.assert_editor_state(
 6413        &r#"
 6414            <body><a b={c()d}>ˇ
 6415                <script>
 6416                    var x = 1;<a b={c()d}>ˇ
 6417                </script>
 6418            </body><a b={c()d}>ˇ
 6419        "#
 6420        .unindent(),
 6421    );
 6422
 6423    // Reset
 6424    cx.set_state(
 6425        &r#"
 6426            <body>ˇ
 6427                <script>
 6428                    var x = 1;ˇ
 6429                </script>
 6430            </body>ˇ
 6431        "#
 6432        .unindent(),
 6433    );
 6434
 6435    cx.update_editor(|editor, window, cx| {
 6436        editor.handle_input("<", window, cx);
 6437    });
 6438    cx.assert_editor_state(
 6439        &r#"
 6440            <body><ˇ>
 6441                <script>
 6442                    var x = 1;<ˇ
 6443                </script>
 6444            </body><ˇ>
 6445        "#
 6446        .unindent(),
 6447    );
 6448
 6449    // When backspacing, the closing angle brackets are removed.
 6450    cx.update_editor(|editor, window, cx| {
 6451        editor.backspace(&Backspace, window, cx);
 6452    });
 6453    cx.assert_editor_state(
 6454        &r#"
 6455            <body>ˇ
 6456                <script>
 6457                    var x = 1;ˇ
 6458                </script>
 6459            </body>ˇ
 6460        "#
 6461        .unindent(),
 6462    );
 6463
 6464    // Block comments autoclose in JavaScript, but not HTML.
 6465    cx.update_editor(|editor, window, cx| {
 6466        editor.handle_input("/", window, cx);
 6467        editor.handle_input("*", window, cx);
 6468    });
 6469    cx.assert_editor_state(
 6470        &r#"
 6471            <body>/*ˇ
 6472                <script>
 6473                    var x = 1;/*ˇ */
 6474                </script>
 6475            </body>/*ˇ
 6476        "#
 6477        .unindent(),
 6478    );
 6479}
 6480
 6481#[gpui::test]
 6482async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6483    init_test(cx, |_| {});
 6484
 6485    let mut cx = EditorTestContext::new(cx).await;
 6486
 6487    let rust_language = Arc::new(
 6488        Language::new(
 6489            LanguageConfig {
 6490                name: "Rust".into(),
 6491                brackets: serde_json::from_value(json!([
 6492                    { "start": "{", "end": "}", "close": true, "newline": true },
 6493                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6494                ]))
 6495                .unwrap(),
 6496                autoclose_before: "})]>".into(),
 6497                ..Default::default()
 6498            },
 6499            Some(tree_sitter_rust::LANGUAGE.into()),
 6500        )
 6501        .with_override_query("(string_literal) @string")
 6502        .unwrap(),
 6503    );
 6504
 6505    cx.language_registry().add(rust_language.clone());
 6506    cx.update_buffer(|buffer, cx| {
 6507        buffer.set_language(Some(rust_language), cx);
 6508    });
 6509
 6510    cx.set_state(
 6511        &r#"
 6512            let x = ˇ
 6513        "#
 6514        .unindent(),
 6515    );
 6516
 6517    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6518    cx.update_editor(|editor, window, cx| {
 6519        editor.handle_input("\"", window, cx);
 6520    });
 6521    cx.assert_editor_state(
 6522        &r#"
 6523            let x = "ˇ"
 6524        "#
 6525        .unindent(),
 6526    );
 6527
 6528    // Inserting another quotation mark. The cursor moves across the existing
 6529    // automatically-inserted quotation mark.
 6530    cx.update_editor(|editor, window, cx| {
 6531        editor.handle_input("\"", window, cx);
 6532    });
 6533    cx.assert_editor_state(
 6534        &r#"
 6535            let x = ""ˇ
 6536        "#
 6537        .unindent(),
 6538    );
 6539
 6540    // Reset
 6541    cx.set_state(
 6542        &r#"
 6543            let x = ˇ
 6544        "#
 6545        .unindent(),
 6546    );
 6547
 6548    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6549    cx.update_editor(|editor, window, cx| {
 6550        editor.handle_input("\"", window, cx);
 6551        editor.handle_input(" ", window, cx);
 6552        editor.move_left(&Default::default(), window, cx);
 6553        editor.handle_input("\\", window, cx);
 6554        editor.handle_input("\"", window, cx);
 6555    });
 6556    cx.assert_editor_state(
 6557        &r#"
 6558            let x = "\"ˇ "
 6559        "#
 6560        .unindent(),
 6561    );
 6562
 6563    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6564    // mark. Nothing is inserted.
 6565    cx.update_editor(|editor, window, cx| {
 6566        editor.move_right(&Default::default(), window, cx);
 6567        editor.handle_input("\"", window, cx);
 6568    });
 6569    cx.assert_editor_state(
 6570        &r#"
 6571            let x = "\" "ˇ
 6572        "#
 6573        .unindent(),
 6574    );
 6575}
 6576
 6577#[gpui::test]
 6578async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6579    init_test(cx, |_| {});
 6580
 6581    let language = Arc::new(Language::new(
 6582        LanguageConfig {
 6583            brackets: BracketPairConfig {
 6584                pairs: vec![
 6585                    BracketPair {
 6586                        start: "{".to_string(),
 6587                        end: "}".to_string(),
 6588                        close: true,
 6589                        surround: true,
 6590                        newline: true,
 6591                    },
 6592                    BracketPair {
 6593                        start: "/* ".to_string(),
 6594                        end: "*/".to_string(),
 6595                        close: true,
 6596                        surround: true,
 6597                        ..Default::default()
 6598                    },
 6599                ],
 6600                ..Default::default()
 6601            },
 6602            ..Default::default()
 6603        },
 6604        Some(tree_sitter_rust::LANGUAGE.into()),
 6605    ));
 6606
 6607    let text = r#"
 6608        a
 6609        b
 6610        c
 6611    "#
 6612    .unindent();
 6613
 6614    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6615    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6616    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6617    editor
 6618        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6619        .await;
 6620
 6621    editor.update_in(cx, |editor, window, cx| {
 6622        editor.change_selections(None, window, cx, |s| {
 6623            s.select_display_ranges([
 6624                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6625                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6626                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6627            ])
 6628        });
 6629
 6630        editor.handle_input("{", window, cx);
 6631        editor.handle_input("{", window, cx);
 6632        editor.handle_input("{", window, cx);
 6633        assert_eq!(
 6634            editor.text(cx),
 6635            "
 6636                {{{a}}}
 6637                {{{b}}}
 6638                {{{c}}}
 6639            "
 6640            .unindent()
 6641        );
 6642        assert_eq!(
 6643            editor.selections.display_ranges(cx),
 6644            [
 6645                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6646                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6647                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6648            ]
 6649        );
 6650
 6651        editor.undo(&Undo, window, cx);
 6652        editor.undo(&Undo, window, cx);
 6653        editor.undo(&Undo, window, cx);
 6654        assert_eq!(
 6655            editor.text(cx),
 6656            "
 6657                a
 6658                b
 6659                c
 6660            "
 6661            .unindent()
 6662        );
 6663        assert_eq!(
 6664            editor.selections.display_ranges(cx),
 6665            [
 6666                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6667                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6668                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6669            ]
 6670        );
 6671
 6672        // Ensure inserting the first character of a multi-byte bracket pair
 6673        // doesn't surround the selections with the bracket.
 6674        editor.handle_input("/", window, cx);
 6675        assert_eq!(
 6676            editor.text(cx),
 6677            "
 6678                /
 6679                /
 6680                /
 6681            "
 6682            .unindent()
 6683        );
 6684        assert_eq!(
 6685            editor.selections.display_ranges(cx),
 6686            [
 6687                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6688                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6689                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6690            ]
 6691        );
 6692
 6693        editor.undo(&Undo, window, cx);
 6694        assert_eq!(
 6695            editor.text(cx),
 6696            "
 6697                a
 6698                b
 6699                c
 6700            "
 6701            .unindent()
 6702        );
 6703        assert_eq!(
 6704            editor.selections.display_ranges(cx),
 6705            [
 6706                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6707                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6708                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6709            ]
 6710        );
 6711
 6712        // Ensure inserting the last character of a multi-byte bracket pair
 6713        // doesn't surround the selections with the bracket.
 6714        editor.handle_input("*", window, cx);
 6715        assert_eq!(
 6716            editor.text(cx),
 6717            "
 6718                *
 6719                *
 6720                *
 6721            "
 6722            .unindent()
 6723        );
 6724        assert_eq!(
 6725            editor.selections.display_ranges(cx),
 6726            [
 6727                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6728                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6729                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6730            ]
 6731        );
 6732    });
 6733}
 6734
 6735#[gpui::test]
 6736async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6737    init_test(cx, |_| {});
 6738
 6739    let language = Arc::new(Language::new(
 6740        LanguageConfig {
 6741            brackets: BracketPairConfig {
 6742                pairs: vec![BracketPair {
 6743                    start: "{".to_string(),
 6744                    end: "}".to_string(),
 6745                    close: true,
 6746                    surround: true,
 6747                    newline: true,
 6748                }],
 6749                ..Default::default()
 6750            },
 6751            autoclose_before: "}".to_string(),
 6752            ..Default::default()
 6753        },
 6754        Some(tree_sitter_rust::LANGUAGE.into()),
 6755    ));
 6756
 6757    let text = r#"
 6758        a
 6759        b
 6760        c
 6761    "#
 6762    .unindent();
 6763
 6764    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6765    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6766    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6767    editor
 6768        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6769        .await;
 6770
 6771    editor.update_in(cx, |editor, window, cx| {
 6772        editor.change_selections(None, window, cx, |s| {
 6773            s.select_ranges([
 6774                Point::new(0, 1)..Point::new(0, 1),
 6775                Point::new(1, 1)..Point::new(1, 1),
 6776                Point::new(2, 1)..Point::new(2, 1),
 6777            ])
 6778        });
 6779
 6780        editor.handle_input("{", window, cx);
 6781        editor.handle_input("{", window, cx);
 6782        editor.handle_input("_", window, cx);
 6783        assert_eq!(
 6784            editor.text(cx),
 6785            "
 6786                a{{_}}
 6787                b{{_}}
 6788                c{{_}}
 6789            "
 6790            .unindent()
 6791        );
 6792        assert_eq!(
 6793            editor.selections.ranges::<Point>(cx),
 6794            [
 6795                Point::new(0, 4)..Point::new(0, 4),
 6796                Point::new(1, 4)..Point::new(1, 4),
 6797                Point::new(2, 4)..Point::new(2, 4)
 6798            ]
 6799        );
 6800
 6801        editor.backspace(&Default::default(), window, cx);
 6802        editor.backspace(&Default::default(), window, cx);
 6803        assert_eq!(
 6804            editor.text(cx),
 6805            "
 6806                a{}
 6807                b{}
 6808                c{}
 6809            "
 6810            .unindent()
 6811        );
 6812        assert_eq!(
 6813            editor.selections.ranges::<Point>(cx),
 6814            [
 6815                Point::new(0, 2)..Point::new(0, 2),
 6816                Point::new(1, 2)..Point::new(1, 2),
 6817                Point::new(2, 2)..Point::new(2, 2)
 6818            ]
 6819        );
 6820
 6821        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6822        assert_eq!(
 6823            editor.text(cx),
 6824            "
 6825                a
 6826                b
 6827                c
 6828            "
 6829            .unindent()
 6830        );
 6831        assert_eq!(
 6832            editor.selections.ranges::<Point>(cx),
 6833            [
 6834                Point::new(0, 1)..Point::new(0, 1),
 6835                Point::new(1, 1)..Point::new(1, 1),
 6836                Point::new(2, 1)..Point::new(2, 1)
 6837            ]
 6838        );
 6839    });
 6840}
 6841
 6842#[gpui::test]
 6843async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6844    init_test(cx, |settings| {
 6845        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6846    });
 6847
 6848    let mut cx = EditorTestContext::new(cx).await;
 6849
 6850    let language = Arc::new(Language::new(
 6851        LanguageConfig {
 6852            brackets: BracketPairConfig {
 6853                pairs: vec![
 6854                    BracketPair {
 6855                        start: "{".to_string(),
 6856                        end: "}".to_string(),
 6857                        close: true,
 6858                        surround: true,
 6859                        newline: true,
 6860                    },
 6861                    BracketPair {
 6862                        start: "(".to_string(),
 6863                        end: ")".to_string(),
 6864                        close: true,
 6865                        surround: true,
 6866                        newline: true,
 6867                    },
 6868                    BracketPair {
 6869                        start: "[".to_string(),
 6870                        end: "]".to_string(),
 6871                        close: false,
 6872                        surround: true,
 6873                        newline: true,
 6874                    },
 6875                ],
 6876                ..Default::default()
 6877            },
 6878            autoclose_before: "})]".to_string(),
 6879            ..Default::default()
 6880        },
 6881        Some(tree_sitter_rust::LANGUAGE.into()),
 6882    ));
 6883
 6884    cx.language_registry().add(language.clone());
 6885    cx.update_buffer(|buffer, cx| {
 6886        buffer.set_language(Some(language), cx);
 6887    });
 6888
 6889    cx.set_state(
 6890        &"
 6891            {(ˇ)}
 6892            [[ˇ]]
 6893            {(ˇ)}
 6894        "
 6895        .unindent(),
 6896    );
 6897
 6898    cx.update_editor(|editor, window, cx| {
 6899        editor.backspace(&Default::default(), window, cx);
 6900        editor.backspace(&Default::default(), window, cx);
 6901    });
 6902
 6903    cx.assert_editor_state(
 6904        &"
 6905            ˇ
 6906            ˇ]]
 6907            ˇ
 6908        "
 6909        .unindent(),
 6910    );
 6911
 6912    cx.update_editor(|editor, window, cx| {
 6913        editor.handle_input("{", window, cx);
 6914        editor.handle_input("{", window, cx);
 6915        editor.move_right(&MoveRight, window, cx);
 6916        editor.move_right(&MoveRight, window, cx);
 6917        editor.move_left(&MoveLeft, window, cx);
 6918        editor.move_left(&MoveLeft, window, cx);
 6919        editor.backspace(&Default::default(), window, cx);
 6920    });
 6921
 6922    cx.assert_editor_state(
 6923        &"
 6924            {ˇ}
 6925            {ˇ}]]
 6926            {ˇ}
 6927        "
 6928        .unindent(),
 6929    );
 6930
 6931    cx.update_editor(|editor, window, cx| {
 6932        editor.backspace(&Default::default(), window, cx);
 6933    });
 6934
 6935    cx.assert_editor_state(
 6936        &"
 6937            ˇ
 6938            ˇ]]
 6939            ˇ
 6940        "
 6941        .unindent(),
 6942    );
 6943}
 6944
 6945#[gpui::test]
 6946async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6947    init_test(cx, |_| {});
 6948
 6949    let language = Arc::new(Language::new(
 6950        LanguageConfig::default(),
 6951        Some(tree_sitter_rust::LANGUAGE.into()),
 6952    ));
 6953
 6954    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6955    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6956    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6957    editor
 6958        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6959        .await;
 6960
 6961    editor.update_in(cx, |editor, window, cx| {
 6962        editor.set_auto_replace_emoji_shortcode(true);
 6963
 6964        editor.handle_input("Hello ", window, cx);
 6965        editor.handle_input(":wave", window, cx);
 6966        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6967
 6968        editor.handle_input(":", window, cx);
 6969        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6970
 6971        editor.handle_input(" :smile", window, cx);
 6972        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6973
 6974        editor.handle_input(":", window, cx);
 6975        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6976
 6977        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6978        editor.handle_input(":wave", window, cx);
 6979        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6980
 6981        editor.handle_input(":", window, cx);
 6982        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6983
 6984        editor.handle_input(":1", window, cx);
 6985        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6986
 6987        editor.handle_input(":", window, cx);
 6988        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6989
 6990        // Ensure shortcode does not get replaced when it is part of a word
 6991        editor.handle_input(" Test:wave", window, cx);
 6992        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6993
 6994        editor.handle_input(":", window, cx);
 6995        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6996
 6997        editor.set_auto_replace_emoji_shortcode(false);
 6998
 6999        // Ensure shortcode does not get replaced when auto replace is off
 7000        editor.handle_input(" :wave", window, cx);
 7001        assert_eq!(
 7002            editor.text(cx),
 7003            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7004        );
 7005
 7006        editor.handle_input(":", window, cx);
 7007        assert_eq!(
 7008            editor.text(cx),
 7009            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7010        );
 7011    });
 7012}
 7013
 7014#[gpui::test]
 7015async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 7016    init_test(cx, |_| {});
 7017
 7018    let (text, insertion_ranges) = marked_text_ranges(
 7019        indoc! {"
 7020            ˇ
 7021        "},
 7022        false,
 7023    );
 7024
 7025    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7026    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7027
 7028    _ = editor.update_in(cx, |editor, window, cx| {
 7029        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7030
 7031        editor
 7032            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7033            .unwrap();
 7034
 7035        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7036            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7037            assert_eq!(editor.text(cx), expected_text);
 7038            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7039        }
 7040
 7041        assert(
 7042            editor,
 7043            cx,
 7044            indoc! {"
 7045            type «» =•
 7046            "},
 7047        );
 7048
 7049        assert!(editor.context_menu_visible(), "There should be a matches");
 7050    });
 7051}
 7052
 7053#[gpui::test]
 7054async fn test_snippets(cx: &mut gpui::TestAppContext) {
 7055    init_test(cx, |_| {});
 7056
 7057    let (text, insertion_ranges) = marked_text_ranges(
 7058        indoc! {"
 7059            a.ˇ b
 7060            a.ˇ b
 7061            a.ˇ b
 7062        "},
 7063        false,
 7064    );
 7065
 7066    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7067    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7068
 7069    editor.update_in(cx, |editor, window, cx| {
 7070        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7071
 7072        editor
 7073            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7074            .unwrap();
 7075
 7076        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7077            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7078            assert_eq!(editor.text(cx), expected_text);
 7079            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7080        }
 7081
 7082        assert(
 7083            editor,
 7084            cx,
 7085            indoc! {"
 7086                a.f(«one», two, «three») b
 7087                a.f(«one», two, «three») b
 7088                a.f(«one», two, «three») b
 7089            "},
 7090        );
 7091
 7092        // Can't move earlier than the first tab stop
 7093        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7094        assert(
 7095            editor,
 7096            cx,
 7097            indoc! {"
 7098                a.f(«one», two, «three») b
 7099                a.f(«one», two, «three») b
 7100                a.f(«one», two, «three») b
 7101            "},
 7102        );
 7103
 7104        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7105        assert(
 7106            editor,
 7107            cx,
 7108            indoc! {"
 7109                a.f(one, «two», three) b
 7110                a.f(one, «two», three) b
 7111                a.f(one, «two», three) b
 7112            "},
 7113        );
 7114
 7115        editor.move_to_prev_snippet_tabstop(window, cx);
 7116        assert(
 7117            editor,
 7118            cx,
 7119            indoc! {"
 7120                a.f(«one», two, «three») b
 7121                a.f(«one», two, «three») b
 7122                a.f(«one», two, «three») b
 7123            "},
 7124        );
 7125
 7126        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7127        assert(
 7128            editor,
 7129            cx,
 7130            indoc! {"
 7131                a.f(one, «two», three) b
 7132                a.f(one, «two», three) b
 7133                a.f(one, «two», three) b
 7134            "},
 7135        );
 7136        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7137        assert(
 7138            editor,
 7139            cx,
 7140            indoc! {"
 7141                a.f(one, two, three)ˇ b
 7142                a.f(one, two, three)ˇ b
 7143                a.f(one, two, three)ˇ b
 7144            "},
 7145        );
 7146
 7147        // As soon as the last tab stop is reached, snippet state is gone
 7148        editor.move_to_prev_snippet_tabstop(window, cx);
 7149        assert(
 7150            editor,
 7151            cx,
 7152            indoc! {"
 7153                a.f(one, two, three)ˇ b
 7154                a.f(one, two, three)ˇ b
 7155                a.f(one, two, three)ˇ b
 7156            "},
 7157        );
 7158    });
 7159}
 7160
 7161#[gpui::test]
 7162async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7163    init_test(cx, |_| {});
 7164
 7165    let fs = FakeFs::new(cx.executor());
 7166    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7167
 7168    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7169
 7170    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7171    language_registry.add(rust_lang());
 7172    let mut fake_servers = language_registry.register_fake_lsp(
 7173        "Rust",
 7174        FakeLspAdapter {
 7175            capabilities: lsp::ServerCapabilities {
 7176                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7177                ..Default::default()
 7178            },
 7179            ..Default::default()
 7180        },
 7181    );
 7182
 7183    let buffer = project
 7184        .update(cx, |project, cx| {
 7185            project.open_local_buffer(path!("/file.rs"), cx)
 7186        })
 7187        .await
 7188        .unwrap();
 7189
 7190    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7191    let (editor, cx) = cx.add_window_view(|window, cx| {
 7192        build_editor_with_project(project.clone(), buffer, window, cx)
 7193    });
 7194    editor.update_in(cx, |editor, window, cx| {
 7195        editor.set_text("one\ntwo\nthree\n", window, cx)
 7196    });
 7197    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7198
 7199    cx.executor().start_waiting();
 7200    let fake_server = fake_servers.next().await.unwrap();
 7201
 7202    let save = editor
 7203        .update_in(cx, |editor, window, cx| {
 7204            editor.save(true, project.clone(), window, cx)
 7205        })
 7206        .unwrap();
 7207    fake_server
 7208        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7209            assert_eq!(
 7210                params.text_document.uri,
 7211                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7212            );
 7213            assert_eq!(params.options.tab_size, 4);
 7214            Ok(Some(vec![lsp::TextEdit::new(
 7215                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7216                ", ".to_string(),
 7217            )]))
 7218        })
 7219        .next()
 7220        .await;
 7221    cx.executor().start_waiting();
 7222    save.await;
 7223
 7224    assert_eq!(
 7225        editor.update(cx, |editor, cx| editor.text(cx)),
 7226        "one, two\nthree\n"
 7227    );
 7228    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7229
 7230    editor.update_in(cx, |editor, window, cx| {
 7231        editor.set_text("one\ntwo\nthree\n", window, cx)
 7232    });
 7233    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7234
 7235    // Ensure we can still save even if formatting hangs.
 7236    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7237        assert_eq!(
 7238            params.text_document.uri,
 7239            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7240        );
 7241        futures::future::pending::<()>().await;
 7242        unreachable!()
 7243    });
 7244    let save = editor
 7245        .update_in(cx, |editor, window, cx| {
 7246            editor.save(true, project.clone(), window, cx)
 7247        })
 7248        .unwrap();
 7249    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7250    cx.executor().start_waiting();
 7251    save.await;
 7252    assert_eq!(
 7253        editor.update(cx, |editor, cx| editor.text(cx)),
 7254        "one\ntwo\nthree\n"
 7255    );
 7256    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7257
 7258    // For non-dirty buffer, no formatting request should be sent
 7259    let save = editor
 7260        .update_in(cx, |editor, window, cx| {
 7261            editor.save(true, project.clone(), window, cx)
 7262        })
 7263        .unwrap();
 7264    let _pending_format_request = fake_server
 7265        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7266            panic!("Should not be invoked on non-dirty buffer");
 7267        })
 7268        .next();
 7269    cx.executor().start_waiting();
 7270    save.await;
 7271
 7272    // Set rust language override and assert overridden tabsize is sent to language server
 7273    update_test_language_settings(cx, |settings| {
 7274        settings.languages.insert(
 7275            "Rust".into(),
 7276            LanguageSettingsContent {
 7277                tab_size: NonZeroU32::new(8),
 7278                ..Default::default()
 7279            },
 7280        );
 7281    });
 7282
 7283    editor.update_in(cx, |editor, window, cx| {
 7284        editor.set_text("somehting_new\n", window, cx)
 7285    });
 7286    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7287    let save = editor
 7288        .update_in(cx, |editor, window, cx| {
 7289            editor.save(true, project.clone(), window, cx)
 7290        })
 7291        .unwrap();
 7292    fake_server
 7293        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7294            assert_eq!(
 7295                params.text_document.uri,
 7296                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7297            );
 7298            assert_eq!(params.options.tab_size, 8);
 7299            Ok(Some(vec![]))
 7300        })
 7301        .next()
 7302        .await;
 7303    cx.executor().start_waiting();
 7304    save.await;
 7305}
 7306
 7307#[gpui::test]
 7308async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7309    init_test(cx, |_| {});
 7310
 7311    let cols = 4;
 7312    let rows = 10;
 7313    let sample_text_1 = sample_text(rows, cols, 'a');
 7314    assert_eq!(
 7315        sample_text_1,
 7316        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7317    );
 7318    let sample_text_2 = sample_text(rows, cols, 'l');
 7319    assert_eq!(
 7320        sample_text_2,
 7321        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7322    );
 7323    let sample_text_3 = sample_text(rows, cols, 'v');
 7324    assert_eq!(
 7325        sample_text_3,
 7326        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7327    );
 7328
 7329    let fs = FakeFs::new(cx.executor());
 7330    fs.insert_tree(
 7331        path!("/a"),
 7332        json!({
 7333            "main.rs": sample_text_1,
 7334            "other.rs": sample_text_2,
 7335            "lib.rs": sample_text_3,
 7336        }),
 7337    )
 7338    .await;
 7339
 7340    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7341    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7342    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7343
 7344    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7345    language_registry.add(rust_lang());
 7346    let mut fake_servers = language_registry.register_fake_lsp(
 7347        "Rust",
 7348        FakeLspAdapter {
 7349            capabilities: lsp::ServerCapabilities {
 7350                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7351                ..Default::default()
 7352            },
 7353            ..Default::default()
 7354        },
 7355    );
 7356
 7357    let worktree = project.update(cx, |project, cx| {
 7358        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7359        assert_eq!(worktrees.len(), 1);
 7360        worktrees.pop().unwrap()
 7361    });
 7362    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7363
 7364    let buffer_1 = project
 7365        .update(cx, |project, cx| {
 7366            project.open_buffer((worktree_id, "main.rs"), cx)
 7367        })
 7368        .await
 7369        .unwrap();
 7370    let buffer_2 = project
 7371        .update(cx, |project, cx| {
 7372            project.open_buffer((worktree_id, "other.rs"), cx)
 7373        })
 7374        .await
 7375        .unwrap();
 7376    let buffer_3 = project
 7377        .update(cx, |project, cx| {
 7378            project.open_buffer((worktree_id, "lib.rs"), cx)
 7379        })
 7380        .await
 7381        .unwrap();
 7382
 7383    let multi_buffer = cx.new(|cx| {
 7384        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7385        multi_buffer.push_excerpts(
 7386            buffer_1.clone(),
 7387            [
 7388                ExcerptRange {
 7389                    context: Point::new(0, 0)..Point::new(3, 0),
 7390                    primary: None,
 7391                },
 7392                ExcerptRange {
 7393                    context: Point::new(5, 0)..Point::new(7, 0),
 7394                    primary: None,
 7395                },
 7396                ExcerptRange {
 7397                    context: Point::new(9, 0)..Point::new(10, 4),
 7398                    primary: None,
 7399                },
 7400            ],
 7401            cx,
 7402        );
 7403        multi_buffer.push_excerpts(
 7404            buffer_2.clone(),
 7405            [
 7406                ExcerptRange {
 7407                    context: Point::new(0, 0)..Point::new(3, 0),
 7408                    primary: None,
 7409                },
 7410                ExcerptRange {
 7411                    context: Point::new(5, 0)..Point::new(7, 0),
 7412                    primary: None,
 7413                },
 7414                ExcerptRange {
 7415                    context: Point::new(9, 0)..Point::new(10, 4),
 7416                    primary: None,
 7417                },
 7418            ],
 7419            cx,
 7420        );
 7421        multi_buffer.push_excerpts(
 7422            buffer_3.clone(),
 7423            [
 7424                ExcerptRange {
 7425                    context: Point::new(0, 0)..Point::new(3, 0),
 7426                    primary: None,
 7427                },
 7428                ExcerptRange {
 7429                    context: Point::new(5, 0)..Point::new(7, 0),
 7430                    primary: None,
 7431                },
 7432                ExcerptRange {
 7433                    context: Point::new(9, 0)..Point::new(10, 4),
 7434                    primary: None,
 7435                },
 7436            ],
 7437            cx,
 7438        );
 7439        multi_buffer
 7440    });
 7441    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7442        Editor::new(
 7443            EditorMode::Full,
 7444            multi_buffer,
 7445            Some(project.clone()),
 7446            true,
 7447            window,
 7448            cx,
 7449        )
 7450    });
 7451
 7452    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7453        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7454            s.select_ranges(Some(1..2))
 7455        });
 7456        editor.insert("|one|two|three|", window, cx);
 7457    });
 7458    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7459    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7460        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7461            s.select_ranges(Some(60..70))
 7462        });
 7463        editor.insert("|four|five|six|", window, cx);
 7464    });
 7465    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7466
 7467    // First two buffers should be edited, but not the third one.
 7468    assert_eq!(
 7469        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7470        "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}",
 7471    );
 7472    buffer_1.update(cx, |buffer, _| {
 7473        assert!(buffer.is_dirty());
 7474        assert_eq!(
 7475            buffer.text(),
 7476            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7477        )
 7478    });
 7479    buffer_2.update(cx, |buffer, _| {
 7480        assert!(buffer.is_dirty());
 7481        assert_eq!(
 7482            buffer.text(),
 7483            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7484        )
 7485    });
 7486    buffer_3.update(cx, |buffer, _| {
 7487        assert!(!buffer.is_dirty());
 7488        assert_eq!(buffer.text(), sample_text_3,)
 7489    });
 7490    cx.executor().run_until_parked();
 7491
 7492    cx.executor().start_waiting();
 7493    let save = multi_buffer_editor
 7494        .update_in(cx, |editor, window, cx| {
 7495            editor.save(true, project.clone(), window, cx)
 7496        })
 7497        .unwrap();
 7498
 7499    let fake_server = fake_servers.next().await.unwrap();
 7500    fake_server
 7501        .server
 7502        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7503            Ok(Some(vec![lsp::TextEdit::new(
 7504                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7505                format!("[{} formatted]", params.text_document.uri),
 7506            )]))
 7507        })
 7508        .detach();
 7509    save.await;
 7510
 7511    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7512    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7513    assert_eq!(
 7514        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7515        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}"),
 7516    );
 7517    buffer_1.update(cx, |buffer, _| {
 7518        assert!(!buffer.is_dirty());
 7519        assert_eq!(
 7520            buffer.text(),
 7521            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7522        )
 7523    });
 7524    buffer_2.update(cx, |buffer, _| {
 7525        assert!(!buffer.is_dirty());
 7526        assert_eq!(
 7527            buffer.text(),
 7528            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7529        )
 7530    });
 7531    buffer_3.update(cx, |buffer, _| {
 7532        assert!(!buffer.is_dirty());
 7533        assert_eq!(buffer.text(), sample_text_3,)
 7534    });
 7535}
 7536
 7537#[gpui::test]
 7538async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7539    init_test(cx, |_| {});
 7540
 7541    let fs = FakeFs::new(cx.executor());
 7542    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7543
 7544    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7545
 7546    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7547    language_registry.add(rust_lang());
 7548    let mut fake_servers = language_registry.register_fake_lsp(
 7549        "Rust",
 7550        FakeLspAdapter {
 7551            capabilities: lsp::ServerCapabilities {
 7552                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7553                ..Default::default()
 7554            },
 7555            ..Default::default()
 7556        },
 7557    );
 7558
 7559    let buffer = project
 7560        .update(cx, |project, cx| {
 7561            project.open_local_buffer(path!("/file.rs"), cx)
 7562        })
 7563        .await
 7564        .unwrap();
 7565
 7566    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7567    let (editor, cx) = cx.add_window_view(|window, cx| {
 7568        build_editor_with_project(project.clone(), buffer, window, cx)
 7569    });
 7570    editor.update_in(cx, |editor, window, cx| {
 7571        editor.set_text("one\ntwo\nthree\n", window, cx)
 7572    });
 7573    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7574
 7575    cx.executor().start_waiting();
 7576    let fake_server = fake_servers.next().await.unwrap();
 7577
 7578    let save = editor
 7579        .update_in(cx, |editor, window, cx| {
 7580            editor.save(true, project.clone(), window, cx)
 7581        })
 7582        .unwrap();
 7583    fake_server
 7584        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7585            assert_eq!(
 7586                params.text_document.uri,
 7587                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7588            );
 7589            assert_eq!(params.options.tab_size, 4);
 7590            Ok(Some(vec![lsp::TextEdit::new(
 7591                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7592                ", ".to_string(),
 7593            )]))
 7594        })
 7595        .next()
 7596        .await;
 7597    cx.executor().start_waiting();
 7598    save.await;
 7599    assert_eq!(
 7600        editor.update(cx, |editor, cx| editor.text(cx)),
 7601        "one, two\nthree\n"
 7602    );
 7603    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7604
 7605    editor.update_in(cx, |editor, window, cx| {
 7606        editor.set_text("one\ntwo\nthree\n", window, cx)
 7607    });
 7608    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7609
 7610    // Ensure we can still save even if formatting hangs.
 7611    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7612        move |params, _| async move {
 7613            assert_eq!(
 7614                params.text_document.uri,
 7615                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7616            );
 7617            futures::future::pending::<()>().await;
 7618            unreachable!()
 7619        },
 7620    );
 7621    let save = editor
 7622        .update_in(cx, |editor, window, cx| {
 7623            editor.save(true, project.clone(), window, cx)
 7624        })
 7625        .unwrap();
 7626    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7627    cx.executor().start_waiting();
 7628    save.await;
 7629    assert_eq!(
 7630        editor.update(cx, |editor, cx| editor.text(cx)),
 7631        "one\ntwo\nthree\n"
 7632    );
 7633    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7634
 7635    // For non-dirty buffer, no formatting request should be sent
 7636    let save = editor
 7637        .update_in(cx, |editor, window, cx| {
 7638            editor.save(true, project.clone(), window, cx)
 7639        })
 7640        .unwrap();
 7641    let _pending_format_request = fake_server
 7642        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7643            panic!("Should not be invoked on non-dirty buffer");
 7644        })
 7645        .next();
 7646    cx.executor().start_waiting();
 7647    save.await;
 7648
 7649    // Set Rust language override and assert overridden tabsize is sent to language server
 7650    update_test_language_settings(cx, |settings| {
 7651        settings.languages.insert(
 7652            "Rust".into(),
 7653            LanguageSettingsContent {
 7654                tab_size: NonZeroU32::new(8),
 7655                ..Default::default()
 7656            },
 7657        );
 7658    });
 7659
 7660    editor.update_in(cx, |editor, window, cx| {
 7661        editor.set_text("somehting_new\n", window, cx)
 7662    });
 7663    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7664    let save = editor
 7665        .update_in(cx, |editor, window, cx| {
 7666            editor.save(true, project.clone(), window, cx)
 7667        })
 7668        .unwrap();
 7669    fake_server
 7670        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7671            assert_eq!(
 7672                params.text_document.uri,
 7673                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7674            );
 7675            assert_eq!(params.options.tab_size, 8);
 7676            Ok(Some(vec![]))
 7677        })
 7678        .next()
 7679        .await;
 7680    cx.executor().start_waiting();
 7681    save.await;
 7682}
 7683
 7684#[gpui::test]
 7685async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7686    init_test(cx, |settings| {
 7687        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7688            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7689        ))
 7690    });
 7691
 7692    let fs = FakeFs::new(cx.executor());
 7693    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7694
 7695    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7696
 7697    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7698    language_registry.add(Arc::new(Language::new(
 7699        LanguageConfig {
 7700            name: "Rust".into(),
 7701            matcher: LanguageMatcher {
 7702                path_suffixes: vec!["rs".to_string()],
 7703                ..Default::default()
 7704            },
 7705            ..LanguageConfig::default()
 7706        },
 7707        Some(tree_sitter_rust::LANGUAGE.into()),
 7708    )));
 7709    update_test_language_settings(cx, |settings| {
 7710        // Enable Prettier formatting for the same buffer, and ensure
 7711        // LSP is called instead of Prettier.
 7712        settings.defaults.prettier = Some(PrettierSettings {
 7713            allowed: true,
 7714            ..PrettierSettings::default()
 7715        });
 7716    });
 7717    let mut fake_servers = language_registry.register_fake_lsp(
 7718        "Rust",
 7719        FakeLspAdapter {
 7720            capabilities: lsp::ServerCapabilities {
 7721                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7722                ..Default::default()
 7723            },
 7724            ..Default::default()
 7725        },
 7726    );
 7727
 7728    let buffer = project
 7729        .update(cx, |project, cx| {
 7730            project.open_local_buffer(path!("/file.rs"), cx)
 7731        })
 7732        .await
 7733        .unwrap();
 7734
 7735    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7736    let (editor, cx) = cx.add_window_view(|window, cx| {
 7737        build_editor_with_project(project.clone(), buffer, window, cx)
 7738    });
 7739    editor.update_in(cx, |editor, window, cx| {
 7740        editor.set_text("one\ntwo\nthree\n", window, cx)
 7741    });
 7742
 7743    cx.executor().start_waiting();
 7744    let fake_server = fake_servers.next().await.unwrap();
 7745
 7746    let format = editor
 7747        .update_in(cx, |editor, window, cx| {
 7748            editor.perform_format(
 7749                project.clone(),
 7750                FormatTrigger::Manual,
 7751                FormatTarget::Buffers,
 7752                window,
 7753                cx,
 7754            )
 7755        })
 7756        .unwrap();
 7757    fake_server
 7758        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7759            assert_eq!(
 7760                params.text_document.uri,
 7761                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7762            );
 7763            assert_eq!(params.options.tab_size, 4);
 7764            Ok(Some(vec![lsp::TextEdit::new(
 7765                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7766                ", ".to_string(),
 7767            )]))
 7768        })
 7769        .next()
 7770        .await;
 7771    cx.executor().start_waiting();
 7772    format.await;
 7773    assert_eq!(
 7774        editor.update(cx, |editor, cx| editor.text(cx)),
 7775        "one, two\nthree\n"
 7776    );
 7777
 7778    editor.update_in(cx, |editor, window, cx| {
 7779        editor.set_text("one\ntwo\nthree\n", window, cx)
 7780    });
 7781    // Ensure we don't lock if formatting hangs.
 7782    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7783        assert_eq!(
 7784            params.text_document.uri,
 7785            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7786        );
 7787        futures::future::pending::<()>().await;
 7788        unreachable!()
 7789    });
 7790    let format = editor
 7791        .update_in(cx, |editor, window, cx| {
 7792            editor.perform_format(
 7793                project,
 7794                FormatTrigger::Manual,
 7795                FormatTarget::Buffers,
 7796                window,
 7797                cx,
 7798            )
 7799        })
 7800        .unwrap();
 7801    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7802    cx.executor().start_waiting();
 7803    format.await;
 7804    assert_eq!(
 7805        editor.update(cx, |editor, cx| editor.text(cx)),
 7806        "one\ntwo\nthree\n"
 7807    );
 7808}
 7809
 7810#[gpui::test]
 7811async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7812    init_test(cx, |_| {});
 7813
 7814    let mut cx = EditorLspTestContext::new_rust(
 7815        lsp::ServerCapabilities {
 7816            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7817            ..Default::default()
 7818        },
 7819        cx,
 7820    )
 7821    .await;
 7822
 7823    cx.set_state(indoc! {"
 7824        one.twoˇ
 7825    "});
 7826
 7827    // The format request takes a long time. When it completes, it inserts
 7828    // a newline and an indent before the `.`
 7829    cx.lsp
 7830        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7831            let executor = cx.background_executor().clone();
 7832            async move {
 7833                executor.timer(Duration::from_millis(100)).await;
 7834                Ok(Some(vec![lsp::TextEdit {
 7835                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7836                    new_text: "\n    ".into(),
 7837                }]))
 7838            }
 7839        });
 7840
 7841    // Submit a format request.
 7842    let format_1 = cx
 7843        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7844        .unwrap();
 7845    cx.executor().run_until_parked();
 7846
 7847    // Submit a second format request.
 7848    let format_2 = cx
 7849        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7850        .unwrap();
 7851    cx.executor().run_until_parked();
 7852
 7853    // Wait for both format requests to complete
 7854    cx.executor().advance_clock(Duration::from_millis(200));
 7855    cx.executor().start_waiting();
 7856    format_1.await.unwrap();
 7857    cx.executor().start_waiting();
 7858    format_2.await.unwrap();
 7859
 7860    // The formatting edits only happens once.
 7861    cx.assert_editor_state(indoc! {"
 7862        one
 7863            .twoˇ
 7864    "});
 7865}
 7866
 7867#[gpui::test]
 7868async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7869    init_test(cx, |settings| {
 7870        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7871    });
 7872
 7873    let mut cx = EditorLspTestContext::new_rust(
 7874        lsp::ServerCapabilities {
 7875            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7876            ..Default::default()
 7877        },
 7878        cx,
 7879    )
 7880    .await;
 7881
 7882    // Set up a buffer white some trailing whitespace and no trailing newline.
 7883    cx.set_state(
 7884        &[
 7885            "one ",   //
 7886            "twoˇ",   //
 7887            "three ", //
 7888            "four",   //
 7889        ]
 7890        .join("\n"),
 7891    );
 7892
 7893    // Submit a format request.
 7894    let format = cx
 7895        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7896        .unwrap();
 7897
 7898    // Record which buffer changes have been sent to the language server
 7899    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7900    cx.lsp
 7901        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7902            let buffer_changes = buffer_changes.clone();
 7903            move |params, _| {
 7904                buffer_changes.lock().extend(
 7905                    params
 7906                        .content_changes
 7907                        .into_iter()
 7908                        .map(|e| (e.range.unwrap(), e.text)),
 7909                );
 7910            }
 7911        });
 7912
 7913    // Handle formatting requests to the language server.
 7914    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7915        let buffer_changes = buffer_changes.clone();
 7916        move |_, _| {
 7917            // When formatting is requested, trailing whitespace has already been stripped,
 7918            // and the trailing newline has already been added.
 7919            assert_eq!(
 7920                &buffer_changes.lock()[1..],
 7921                &[
 7922                    (
 7923                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7924                        "".into()
 7925                    ),
 7926                    (
 7927                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7928                        "".into()
 7929                    ),
 7930                    (
 7931                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7932                        "\n".into()
 7933                    ),
 7934                ]
 7935            );
 7936
 7937            // Insert blank lines between each line of the buffer.
 7938            async move {
 7939                Ok(Some(vec![
 7940                    lsp::TextEdit {
 7941                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7942                        new_text: "\n".into(),
 7943                    },
 7944                    lsp::TextEdit {
 7945                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7946                        new_text: "\n".into(),
 7947                    },
 7948                ]))
 7949            }
 7950        }
 7951    });
 7952
 7953    // After formatting the buffer, the trailing whitespace is stripped,
 7954    // a newline is appended, and the edits provided by the language server
 7955    // have been applied.
 7956    format.await.unwrap();
 7957    cx.assert_editor_state(
 7958        &[
 7959            "one",   //
 7960            "",      //
 7961            "twoˇ",  //
 7962            "",      //
 7963            "three", //
 7964            "four",  //
 7965            "",      //
 7966        ]
 7967        .join("\n"),
 7968    );
 7969
 7970    // Undoing the formatting undoes the trailing whitespace removal, the
 7971    // trailing newline, and the LSP edits.
 7972    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7973    cx.assert_editor_state(
 7974        &[
 7975            "one ",   //
 7976            "twoˇ",   //
 7977            "three ", //
 7978            "four",   //
 7979        ]
 7980        .join("\n"),
 7981    );
 7982}
 7983
 7984#[gpui::test]
 7985async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7986    cx: &mut gpui::TestAppContext,
 7987) {
 7988    init_test(cx, |_| {});
 7989
 7990    cx.update(|cx| {
 7991        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7992            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7993                settings.auto_signature_help = Some(true);
 7994            });
 7995        });
 7996    });
 7997
 7998    let mut cx = EditorLspTestContext::new_rust(
 7999        lsp::ServerCapabilities {
 8000            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8001                ..Default::default()
 8002            }),
 8003            ..Default::default()
 8004        },
 8005        cx,
 8006    )
 8007    .await;
 8008
 8009    let language = Language::new(
 8010        LanguageConfig {
 8011            name: "Rust".into(),
 8012            brackets: BracketPairConfig {
 8013                pairs: vec![
 8014                    BracketPair {
 8015                        start: "{".to_string(),
 8016                        end: "}".to_string(),
 8017                        close: true,
 8018                        surround: true,
 8019                        newline: true,
 8020                    },
 8021                    BracketPair {
 8022                        start: "(".to_string(),
 8023                        end: ")".to_string(),
 8024                        close: true,
 8025                        surround: true,
 8026                        newline: true,
 8027                    },
 8028                    BracketPair {
 8029                        start: "/*".to_string(),
 8030                        end: " */".to_string(),
 8031                        close: true,
 8032                        surround: true,
 8033                        newline: true,
 8034                    },
 8035                    BracketPair {
 8036                        start: "[".to_string(),
 8037                        end: "]".to_string(),
 8038                        close: false,
 8039                        surround: false,
 8040                        newline: true,
 8041                    },
 8042                    BracketPair {
 8043                        start: "\"".to_string(),
 8044                        end: "\"".to_string(),
 8045                        close: true,
 8046                        surround: true,
 8047                        newline: false,
 8048                    },
 8049                    BracketPair {
 8050                        start: "<".to_string(),
 8051                        end: ">".to_string(),
 8052                        close: false,
 8053                        surround: true,
 8054                        newline: true,
 8055                    },
 8056                ],
 8057                ..Default::default()
 8058            },
 8059            autoclose_before: "})]".to_string(),
 8060            ..Default::default()
 8061        },
 8062        Some(tree_sitter_rust::LANGUAGE.into()),
 8063    );
 8064    let language = Arc::new(language);
 8065
 8066    cx.language_registry().add(language.clone());
 8067    cx.update_buffer(|buffer, cx| {
 8068        buffer.set_language(Some(language), cx);
 8069    });
 8070
 8071    cx.set_state(
 8072        &r#"
 8073            fn main() {
 8074                sampleˇ
 8075            }
 8076        "#
 8077        .unindent(),
 8078    );
 8079
 8080    cx.update_editor(|editor, window, cx| {
 8081        editor.handle_input("(", window, cx);
 8082    });
 8083    cx.assert_editor_state(
 8084        &"
 8085            fn main() {
 8086                sample(ˇ)
 8087            }
 8088        "
 8089        .unindent(),
 8090    );
 8091
 8092    let mocked_response = lsp::SignatureHelp {
 8093        signatures: vec![lsp::SignatureInformation {
 8094            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8095            documentation: None,
 8096            parameters: Some(vec![
 8097                lsp::ParameterInformation {
 8098                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8099                    documentation: None,
 8100                },
 8101                lsp::ParameterInformation {
 8102                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8103                    documentation: None,
 8104                },
 8105            ]),
 8106            active_parameter: None,
 8107        }],
 8108        active_signature: Some(0),
 8109        active_parameter: Some(0),
 8110    };
 8111    handle_signature_help_request(&mut cx, mocked_response).await;
 8112
 8113    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8114        .await;
 8115
 8116    cx.editor(|editor, _, _| {
 8117        let signature_help_state = editor.signature_help_state.popover().cloned();
 8118        assert_eq!(
 8119            signature_help_state.unwrap().label,
 8120            "param1: u8, param2: u8"
 8121        );
 8122    });
 8123}
 8124
 8125#[gpui::test]
 8126async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8127    init_test(cx, |_| {});
 8128
 8129    cx.update(|cx| {
 8130        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8131            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8132                settings.auto_signature_help = Some(false);
 8133                settings.show_signature_help_after_edits = Some(false);
 8134            });
 8135        });
 8136    });
 8137
 8138    let mut cx = EditorLspTestContext::new_rust(
 8139        lsp::ServerCapabilities {
 8140            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8141                ..Default::default()
 8142            }),
 8143            ..Default::default()
 8144        },
 8145        cx,
 8146    )
 8147    .await;
 8148
 8149    let language = Language::new(
 8150        LanguageConfig {
 8151            name: "Rust".into(),
 8152            brackets: BracketPairConfig {
 8153                pairs: vec![
 8154                    BracketPair {
 8155                        start: "{".to_string(),
 8156                        end: "}".to_string(),
 8157                        close: true,
 8158                        surround: true,
 8159                        newline: true,
 8160                    },
 8161                    BracketPair {
 8162                        start: "(".to_string(),
 8163                        end: ")".to_string(),
 8164                        close: true,
 8165                        surround: true,
 8166                        newline: true,
 8167                    },
 8168                    BracketPair {
 8169                        start: "/*".to_string(),
 8170                        end: " */".to_string(),
 8171                        close: true,
 8172                        surround: true,
 8173                        newline: true,
 8174                    },
 8175                    BracketPair {
 8176                        start: "[".to_string(),
 8177                        end: "]".to_string(),
 8178                        close: false,
 8179                        surround: false,
 8180                        newline: true,
 8181                    },
 8182                    BracketPair {
 8183                        start: "\"".to_string(),
 8184                        end: "\"".to_string(),
 8185                        close: true,
 8186                        surround: true,
 8187                        newline: false,
 8188                    },
 8189                    BracketPair {
 8190                        start: "<".to_string(),
 8191                        end: ">".to_string(),
 8192                        close: false,
 8193                        surround: true,
 8194                        newline: true,
 8195                    },
 8196                ],
 8197                ..Default::default()
 8198            },
 8199            autoclose_before: "})]".to_string(),
 8200            ..Default::default()
 8201        },
 8202        Some(tree_sitter_rust::LANGUAGE.into()),
 8203    );
 8204    let language = Arc::new(language);
 8205
 8206    cx.language_registry().add(language.clone());
 8207    cx.update_buffer(|buffer, cx| {
 8208        buffer.set_language(Some(language), cx);
 8209    });
 8210
 8211    // Ensure that signature_help is not called when no signature help is enabled.
 8212    cx.set_state(
 8213        &r#"
 8214            fn main() {
 8215                sampleˇ
 8216            }
 8217        "#
 8218        .unindent(),
 8219    );
 8220    cx.update_editor(|editor, window, cx| {
 8221        editor.handle_input("(", window, cx);
 8222    });
 8223    cx.assert_editor_state(
 8224        &"
 8225            fn main() {
 8226                sample(ˇ)
 8227            }
 8228        "
 8229        .unindent(),
 8230    );
 8231    cx.editor(|editor, _, _| {
 8232        assert!(editor.signature_help_state.task().is_none());
 8233    });
 8234
 8235    let mocked_response = lsp::SignatureHelp {
 8236        signatures: vec![lsp::SignatureInformation {
 8237            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8238            documentation: None,
 8239            parameters: Some(vec![
 8240                lsp::ParameterInformation {
 8241                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8242                    documentation: None,
 8243                },
 8244                lsp::ParameterInformation {
 8245                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8246                    documentation: None,
 8247                },
 8248            ]),
 8249            active_parameter: None,
 8250        }],
 8251        active_signature: Some(0),
 8252        active_parameter: Some(0),
 8253    };
 8254
 8255    // Ensure that signature_help is called when enabled afte edits
 8256    cx.update(|_, cx| {
 8257        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8258            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8259                settings.auto_signature_help = Some(false);
 8260                settings.show_signature_help_after_edits = Some(true);
 8261            });
 8262        });
 8263    });
 8264    cx.set_state(
 8265        &r#"
 8266            fn main() {
 8267                sampleˇ
 8268            }
 8269        "#
 8270        .unindent(),
 8271    );
 8272    cx.update_editor(|editor, window, cx| {
 8273        editor.handle_input("(", window, cx);
 8274    });
 8275    cx.assert_editor_state(
 8276        &"
 8277            fn main() {
 8278                sample(ˇ)
 8279            }
 8280        "
 8281        .unindent(),
 8282    );
 8283    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8284    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8285        .await;
 8286    cx.update_editor(|editor, _, _| {
 8287        let signature_help_state = editor.signature_help_state.popover().cloned();
 8288        assert!(signature_help_state.is_some());
 8289        assert_eq!(
 8290            signature_help_state.unwrap().label,
 8291            "param1: u8, param2: u8"
 8292        );
 8293        editor.signature_help_state = SignatureHelpState::default();
 8294    });
 8295
 8296    // Ensure that signature_help is called when auto signature help override is enabled
 8297    cx.update(|_, cx| {
 8298        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8299            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8300                settings.auto_signature_help = Some(true);
 8301                settings.show_signature_help_after_edits = Some(false);
 8302            });
 8303        });
 8304    });
 8305    cx.set_state(
 8306        &r#"
 8307            fn main() {
 8308                sampleˇ
 8309            }
 8310        "#
 8311        .unindent(),
 8312    );
 8313    cx.update_editor(|editor, window, cx| {
 8314        editor.handle_input("(", window, cx);
 8315    });
 8316    cx.assert_editor_state(
 8317        &"
 8318            fn main() {
 8319                sample(ˇ)
 8320            }
 8321        "
 8322        .unindent(),
 8323    );
 8324    handle_signature_help_request(&mut cx, mocked_response).await;
 8325    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8326        .await;
 8327    cx.editor(|editor, _, _| {
 8328        let signature_help_state = editor.signature_help_state.popover().cloned();
 8329        assert!(signature_help_state.is_some());
 8330        assert_eq!(
 8331            signature_help_state.unwrap().label,
 8332            "param1: u8, param2: u8"
 8333        );
 8334    });
 8335}
 8336
 8337#[gpui::test]
 8338async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8339    init_test(cx, |_| {});
 8340    cx.update(|cx| {
 8341        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8342            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8343                settings.auto_signature_help = Some(true);
 8344            });
 8345        });
 8346    });
 8347
 8348    let mut cx = EditorLspTestContext::new_rust(
 8349        lsp::ServerCapabilities {
 8350            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8351                ..Default::default()
 8352            }),
 8353            ..Default::default()
 8354        },
 8355        cx,
 8356    )
 8357    .await;
 8358
 8359    // A test that directly calls `show_signature_help`
 8360    cx.update_editor(|editor, window, cx| {
 8361        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8362    });
 8363
 8364    let mocked_response = lsp::SignatureHelp {
 8365        signatures: vec![lsp::SignatureInformation {
 8366            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8367            documentation: None,
 8368            parameters: Some(vec![
 8369                lsp::ParameterInformation {
 8370                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8371                    documentation: None,
 8372                },
 8373                lsp::ParameterInformation {
 8374                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8375                    documentation: None,
 8376                },
 8377            ]),
 8378            active_parameter: None,
 8379        }],
 8380        active_signature: Some(0),
 8381        active_parameter: Some(0),
 8382    };
 8383    handle_signature_help_request(&mut cx, mocked_response).await;
 8384
 8385    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8386        .await;
 8387
 8388    cx.editor(|editor, _, _| {
 8389        let signature_help_state = editor.signature_help_state.popover().cloned();
 8390        assert!(signature_help_state.is_some());
 8391        assert_eq!(
 8392            signature_help_state.unwrap().label,
 8393            "param1: u8, param2: u8"
 8394        );
 8395    });
 8396
 8397    // When exiting outside from inside the brackets, `signature_help` is closed.
 8398    cx.set_state(indoc! {"
 8399        fn main() {
 8400            sample(ˇ);
 8401        }
 8402
 8403        fn sample(param1: u8, param2: u8) {}
 8404    "});
 8405
 8406    cx.update_editor(|editor, window, cx| {
 8407        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8408    });
 8409
 8410    let mocked_response = lsp::SignatureHelp {
 8411        signatures: Vec::new(),
 8412        active_signature: None,
 8413        active_parameter: None,
 8414    };
 8415    handle_signature_help_request(&mut cx, mocked_response).await;
 8416
 8417    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8418        .await;
 8419
 8420    cx.editor(|editor, _, _| {
 8421        assert!(!editor.signature_help_state.is_shown());
 8422    });
 8423
 8424    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8425    cx.set_state(indoc! {"
 8426        fn main() {
 8427            sample(ˇ);
 8428        }
 8429
 8430        fn sample(param1: u8, param2: u8) {}
 8431    "});
 8432
 8433    let mocked_response = lsp::SignatureHelp {
 8434        signatures: vec![lsp::SignatureInformation {
 8435            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8436            documentation: None,
 8437            parameters: Some(vec![
 8438                lsp::ParameterInformation {
 8439                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8440                    documentation: None,
 8441                },
 8442                lsp::ParameterInformation {
 8443                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8444                    documentation: None,
 8445                },
 8446            ]),
 8447            active_parameter: None,
 8448        }],
 8449        active_signature: Some(0),
 8450        active_parameter: Some(0),
 8451    };
 8452    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8453    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8454        .await;
 8455    cx.editor(|editor, _, _| {
 8456        assert!(editor.signature_help_state.is_shown());
 8457    });
 8458
 8459    // Restore the popover with more parameter input
 8460    cx.set_state(indoc! {"
 8461        fn main() {
 8462            sample(param1, param2ˇ);
 8463        }
 8464
 8465        fn sample(param1: u8, param2: u8) {}
 8466    "});
 8467
 8468    let mocked_response = lsp::SignatureHelp {
 8469        signatures: vec![lsp::SignatureInformation {
 8470            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8471            documentation: None,
 8472            parameters: Some(vec![
 8473                lsp::ParameterInformation {
 8474                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8475                    documentation: None,
 8476                },
 8477                lsp::ParameterInformation {
 8478                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8479                    documentation: None,
 8480                },
 8481            ]),
 8482            active_parameter: None,
 8483        }],
 8484        active_signature: Some(0),
 8485        active_parameter: Some(1),
 8486    };
 8487    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8488    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8489        .await;
 8490
 8491    // When selecting a range, the popover is gone.
 8492    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8493    cx.update_editor(|editor, window, cx| {
 8494        editor.change_selections(None, window, cx, |s| {
 8495            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8496        })
 8497    });
 8498    cx.assert_editor_state(indoc! {"
 8499        fn main() {
 8500            sample(param1, «ˇparam2»);
 8501        }
 8502
 8503        fn sample(param1: u8, param2: u8) {}
 8504    "});
 8505    cx.editor(|editor, _, _| {
 8506        assert!(!editor.signature_help_state.is_shown());
 8507    });
 8508
 8509    // When unselecting again, the popover is back if within the brackets.
 8510    cx.update_editor(|editor, window, cx| {
 8511        editor.change_selections(None, window, cx, |s| {
 8512            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8513        })
 8514    });
 8515    cx.assert_editor_state(indoc! {"
 8516        fn main() {
 8517            sample(param1, ˇparam2);
 8518        }
 8519
 8520        fn sample(param1: u8, param2: u8) {}
 8521    "});
 8522    handle_signature_help_request(&mut cx, mocked_response).await;
 8523    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8524        .await;
 8525    cx.editor(|editor, _, _| {
 8526        assert!(editor.signature_help_state.is_shown());
 8527    });
 8528
 8529    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8530    cx.update_editor(|editor, window, cx| {
 8531        editor.change_selections(None, window, cx, |s| {
 8532            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8533            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8534        })
 8535    });
 8536    cx.assert_editor_state(indoc! {"
 8537        fn main() {
 8538            sample(param1, ˇparam2);
 8539        }
 8540
 8541        fn sample(param1: u8, param2: u8) {}
 8542    "});
 8543
 8544    let mocked_response = lsp::SignatureHelp {
 8545        signatures: vec![lsp::SignatureInformation {
 8546            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8547            documentation: None,
 8548            parameters: Some(vec![
 8549                lsp::ParameterInformation {
 8550                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8551                    documentation: None,
 8552                },
 8553                lsp::ParameterInformation {
 8554                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8555                    documentation: None,
 8556                },
 8557            ]),
 8558            active_parameter: None,
 8559        }],
 8560        active_signature: Some(0),
 8561        active_parameter: Some(1),
 8562    };
 8563    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8564    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8565        .await;
 8566    cx.update_editor(|editor, _, cx| {
 8567        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8568    });
 8569    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8570        .await;
 8571    cx.update_editor(|editor, window, cx| {
 8572        editor.change_selections(None, window, cx, |s| {
 8573            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8574        })
 8575    });
 8576    cx.assert_editor_state(indoc! {"
 8577        fn main() {
 8578            sample(param1, «ˇparam2»);
 8579        }
 8580
 8581        fn sample(param1: u8, param2: u8) {}
 8582    "});
 8583    cx.update_editor(|editor, window, cx| {
 8584        editor.change_selections(None, window, cx, |s| {
 8585            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8586        })
 8587    });
 8588    cx.assert_editor_state(indoc! {"
 8589        fn main() {
 8590            sample(param1, ˇparam2);
 8591        }
 8592
 8593        fn sample(param1: u8, param2: u8) {}
 8594    "});
 8595    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8596        .await;
 8597}
 8598
 8599#[gpui::test]
 8600async fn test_completion(cx: &mut gpui::TestAppContext) {
 8601    init_test(cx, |_| {});
 8602
 8603    let mut cx = EditorLspTestContext::new_rust(
 8604        lsp::ServerCapabilities {
 8605            completion_provider: Some(lsp::CompletionOptions {
 8606                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8607                resolve_provider: Some(true),
 8608                ..Default::default()
 8609            }),
 8610            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8611            ..Default::default()
 8612        },
 8613        cx,
 8614    )
 8615    .await;
 8616    let counter = Arc::new(AtomicUsize::new(0));
 8617
 8618    cx.set_state(indoc! {"
 8619        oneˇ
 8620        two
 8621        three
 8622    "});
 8623    cx.simulate_keystroke(".");
 8624    handle_completion_request(
 8625        &mut cx,
 8626        indoc! {"
 8627            one.|<>
 8628            two
 8629            three
 8630        "},
 8631        vec!["first_completion", "second_completion"],
 8632        counter.clone(),
 8633    )
 8634    .await;
 8635    cx.condition(|editor, _| editor.context_menu_visible())
 8636        .await;
 8637    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8638
 8639    let _handler = handle_signature_help_request(
 8640        &mut cx,
 8641        lsp::SignatureHelp {
 8642            signatures: vec![lsp::SignatureInformation {
 8643                label: "test signature".to_string(),
 8644                documentation: None,
 8645                parameters: Some(vec![lsp::ParameterInformation {
 8646                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8647                    documentation: None,
 8648                }]),
 8649                active_parameter: None,
 8650            }],
 8651            active_signature: None,
 8652            active_parameter: None,
 8653        },
 8654    );
 8655    cx.update_editor(|editor, window, cx| {
 8656        assert!(
 8657            !editor.signature_help_state.is_shown(),
 8658            "No signature help was called for"
 8659        );
 8660        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8661    });
 8662    cx.run_until_parked();
 8663    cx.update_editor(|editor, _, _| {
 8664        assert!(
 8665            !editor.signature_help_state.is_shown(),
 8666            "No signature help should be shown when completions menu is open"
 8667        );
 8668    });
 8669
 8670    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8671        editor.context_menu_next(&Default::default(), window, cx);
 8672        editor
 8673            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8674            .unwrap()
 8675    });
 8676    cx.assert_editor_state(indoc! {"
 8677        one.second_completionˇ
 8678        two
 8679        three
 8680    "});
 8681
 8682    handle_resolve_completion_request(
 8683        &mut cx,
 8684        Some(vec![
 8685            (
 8686                //This overlaps with the primary completion edit which is
 8687                //misbehavior from the LSP spec, test that we filter it out
 8688                indoc! {"
 8689                    one.second_ˇcompletion
 8690                    two
 8691                    threeˇ
 8692                "},
 8693                "overlapping additional edit",
 8694            ),
 8695            (
 8696                indoc! {"
 8697                    one.second_completion
 8698                    two
 8699                    threeˇ
 8700                "},
 8701                "\nadditional edit",
 8702            ),
 8703        ]),
 8704    )
 8705    .await;
 8706    apply_additional_edits.await.unwrap();
 8707    cx.assert_editor_state(indoc! {"
 8708        one.second_completionˇ
 8709        two
 8710        three
 8711        additional edit
 8712    "});
 8713
 8714    cx.set_state(indoc! {"
 8715        one.second_completion
 8716        twoˇ
 8717        threeˇ
 8718        additional edit
 8719    "});
 8720    cx.simulate_keystroke(" ");
 8721    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8722    cx.simulate_keystroke("s");
 8723    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8724
 8725    cx.assert_editor_state(indoc! {"
 8726        one.second_completion
 8727        two sˇ
 8728        three sˇ
 8729        additional edit
 8730    "});
 8731    handle_completion_request(
 8732        &mut cx,
 8733        indoc! {"
 8734            one.second_completion
 8735            two s
 8736            three <s|>
 8737            additional edit
 8738        "},
 8739        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8740        counter.clone(),
 8741    )
 8742    .await;
 8743    cx.condition(|editor, _| editor.context_menu_visible())
 8744        .await;
 8745    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8746
 8747    cx.simulate_keystroke("i");
 8748
 8749    handle_completion_request(
 8750        &mut cx,
 8751        indoc! {"
 8752            one.second_completion
 8753            two si
 8754            three <si|>
 8755            additional edit
 8756        "},
 8757        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8758        counter.clone(),
 8759    )
 8760    .await;
 8761    cx.condition(|editor, _| editor.context_menu_visible())
 8762        .await;
 8763    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8764
 8765    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8766        editor
 8767            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8768            .unwrap()
 8769    });
 8770    cx.assert_editor_state(indoc! {"
 8771        one.second_completion
 8772        two sixth_completionˇ
 8773        three sixth_completionˇ
 8774        additional edit
 8775    "});
 8776
 8777    apply_additional_edits.await.unwrap();
 8778
 8779    update_test_language_settings(&mut cx, |settings| {
 8780        settings.defaults.show_completions_on_input = Some(false);
 8781    });
 8782    cx.set_state("editorˇ");
 8783    cx.simulate_keystroke(".");
 8784    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8785    cx.simulate_keystroke("c");
 8786    cx.simulate_keystroke("l");
 8787    cx.simulate_keystroke("o");
 8788    cx.assert_editor_state("editor.cloˇ");
 8789    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8790    cx.update_editor(|editor, window, cx| {
 8791        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8792    });
 8793    handle_completion_request(
 8794        &mut cx,
 8795        "editor.<clo|>",
 8796        vec!["close", "clobber"],
 8797        counter.clone(),
 8798    )
 8799    .await;
 8800    cx.condition(|editor, _| editor.context_menu_visible())
 8801        .await;
 8802    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8803
 8804    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8805        editor
 8806            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8807            .unwrap()
 8808    });
 8809    cx.assert_editor_state("editor.closeˇ");
 8810    handle_resolve_completion_request(&mut cx, None).await;
 8811    apply_additional_edits.await.unwrap();
 8812}
 8813
 8814#[gpui::test]
 8815async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8816    init_test(cx, |_| {});
 8817
 8818    let fs = FakeFs::new(cx.executor());
 8819    fs.insert_tree(
 8820        path!("/a"),
 8821        json!({
 8822            "main.ts": "a",
 8823        }),
 8824    )
 8825    .await;
 8826
 8827    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8828    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8829    let typescript_language = Arc::new(Language::new(
 8830        LanguageConfig {
 8831            name: "TypeScript".into(),
 8832            matcher: LanguageMatcher {
 8833                path_suffixes: vec!["ts".to_string()],
 8834                ..LanguageMatcher::default()
 8835            },
 8836            line_comments: vec!["// ".into()],
 8837            ..LanguageConfig::default()
 8838        },
 8839        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8840    ));
 8841    language_registry.add(typescript_language.clone());
 8842    let mut fake_servers = language_registry.register_fake_lsp(
 8843        "TypeScript",
 8844        FakeLspAdapter {
 8845            capabilities: lsp::ServerCapabilities {
 8846                completion_provider: Some(lsp::CompletionOptions {
 8847                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8848                    ..lsp::CompletionOptions::default()
 8849                }),
 8850                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8851                ..lsp::ServerCapabilities::default()
 8852            },
 8853            // Emulate vtsls label generation
 8854            label_for_completion: Some(Box::new(|item, _| {
 8855                let text = if let Some(description) = item
 8856                    .label_details
 8857                    .as_ref()
 8858                    .and_then(|label_details| label_details.description.as_ref())
 8859                {
 8860                    format!("{} {}", item.label, description)
 8861                } else if let Some(detail) = &item.detail {
 8862                    format!("{} {}", item.label, detail)
 8863                } else {
 8864                    item.label.clone()
 8865                };
 8866                let len = text.len();
 8867                Some(language::CodeLabel {
 8868                    text,
 8869                    runs: Vec::new(),
 8870                    filter_range: 0..len,
 8871                })
 8872            })),
 8873            ..FakeLspAdapter::default()
 8874        },
 8875    );
 8876    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8877    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8878    let worktree_id = workspace
 8879        .update(cx, |workspace, _window, cx| {
 8880            workspace.project().update(cx, |project, cx| {
 8881                project.worktrees(cx).next().unwrap().read(cx).id()
 8882            })
 8883        })
 8884        .unwrap();
 8885    let _buffer = project
 8886        .update(cx, |project, cx| {
 8887            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8888        })
 8889        .await
 8890        .unwrap();
 8891    let editor = workspace
 8892        .update(cx, |workspace, window, cx| {
 8893            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8894        })
 8895        .unwrap()
 8896        .await
 8897        .unwrap()
 8898        .downcast::<Editor>()
 8899        .unwrap();
 8900    let fake_server = fake_servers.next().await.unwrap();
 8901
 8902    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8903    let multiline_label_2 = "a\nb\nc\n";
 8904    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8905    let multiline_description = "d\ne\nf\n";
 8906    let multiline_detail_2 = "g\nh\ni\n";
 8907
 8908    let mut completion_handle =
 8909        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8910            Ok(Some(lsp::CompletionResponse::Array(vec![
 8911                lsp::CompletionItem {
 8912                    label: multiline_label.to_string(),
 8913                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8914                        range: lsp::Range {
 8915                            start: lsp::Position {
 8916                                line: params.text_document_position.position.line,
 8917                                character: params.text_document_position.position.character,
 8918                            },
 8919                            end: lsp::Position {
 8920                                line: params.text_document_position.position.line,
 8921                                character: params.text_document_position.position.character,
 8922                            },
 8923                        },
 8924                        new_text: "new_text_1".to_string(),
 8925                    })),
 8926                    ..lsp::CompletionItem::default()
 8927                },
 8928                lsp::CompletionItem {
 8929                    label: "single line label 1".to_string(),
 8930                    detail: Some(multiline_detail.to_string()),
 8931                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8932                        range: lsp::Range {
 8933                            start: lsp::Position {
 8934                                line: params.text_document_position.position.line,
 8935                                character: params.text_document_position.position.character,
 8936                            },
 8937                            end: lsp::Position {
 8938                                line: params.text_document_position.position.line,
 8939                                character: params.text_document_position.position.character,
 8940                            },
 8941                        },
 8942                        new_text: "new_text_2".to_string(),
 8943                    })),
 8944                    ..lsp::CompletionItem::default()
 8945                },
 8946                lsp::CompletionItem {
 8947                    label: "single line label 2".to_string(),
 8948                    label_details: Some(lsp::CompletionItemLabelDetails {
 8949                        description: Some(multiline_description.to_string()),
 8950                        detail: None,
 8951                    }),
 8952                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8953                        range: lsp::Range {
 8954                            start: lsp::Position {
 8955                                line: params.text_document_position.position.line,
 8956                                character: params.text_document_position.position.character,
 8957                            },
 8958                            end: lsp::Position {
 8959                                line: params.text_document_position.position.line,
 8960                                character: params.text_document_position.position.character,
 8961                            },
 8962                        },
 8963                        new_text: "new_text_2".to_string(),
 8964                    })),
 8965                    ..lsp::CompletionItem::default()
 8966                },
 8967                lsp::CompletionItem {
 8968                    label: multiline_label_2.to_string(),
 8969                    detail: Some(multiline_detail_2.to_string()),
 8970                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8971                        range: lsp::Range {
 8972                            start: lsp::Position {
 8973                                line: params.text_document_position.position.line,
 8974                                character: params.text_document_position.position.character,
 8975                            },
 8976                            end: lsp::Position {
 8977                                line: params.text_document_position.position.line,
 8978                                character: params.text_document_position.position.character,
 8979                            },
 8980                        },
 8981                        new_text: "new_text_3".to_string(),
 8982                    })),
 8983                    ..lsp::CompletionItem::default()
 8984                },
 8985                lsp::CompletionItem {
 8986                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8987                    detail: Some(
 8988                        "Details with many     spaces and \t but without newlines".to_string(),
 8989                    ),
 8990                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8991                        range: lsp::Range {
 8992                            start: lsp::Position {
 8993                                line: params.text_document_position.position.line,
 8994                                character: params.text_document_position.position.character,
 8995                            },
 8996                            end: lsp::Position {
 8997                                line: params.text_document_position.position.line,
 8998                                character: params.text_document_position.position.character,
 8999                            },
 9000                        },
 9001                        new_text: "new_text_4".to_string(),
 9002                    })),
 9003                    ..lsp::CompletionItem::default()
 9004                },
 9005            ])))
 9006        });
 9007
 9008    editor.update_in(cx, |editor, window, cx| {
 9009        cx.focus_self(window);
 9010        editor.move_to_end(&MoveToEnd, window, cx);
 9011        editor.handle_input(".", window, cx);
 9012    });
 9013    cx.run_until_parked();
 9014    completion_handle.next().await.unwrap();
 9015
 9016    editor.update(cx, |editor, _| {
 9017        assert!(editor.context_menu_visible());
 9018        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9019        {
 9020            let completion_labels = menu
 9021                .completions
 9022                .borrow()
 9023                .iter()
 9024                .map(|c| c.label.text.clone())
 9025                .collect::<Vec<_>>();
 9026            assert_eq!(
 9027                completion_labels,
 9028                &[
 9029                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9030                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9031                    "single line label 2 d e f ",
 9032                    "a b c g h i ",
 9033                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9034                ],
 9035                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9036            );
 9037
 9038            for completion in menu
 9039                .completions
 9040                .borrow()
 9041                .iter() {
 9042                    assert_eq!(
 9043                        completion.label.filter_range,
 9044                        0..completion.label.text.len(),
 9045                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9046                    );
 9047                }
 9048
 9049        } else {
 9050            panic!("expected completion menu to be open");
 9051        }
 9052    });
 9053}
 9054
 9055#[gpui::test]
 9056async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 9057    init_test(cx, |_| {});
 9058    let mut cx = EditorLspTestContext::new_rust(
 9059        lsp::ServerCapabilities {
 9060            completion_provider: Some(lsp::CompletionOptions {
 9061                trigger_characters: Some(vec![".".to_string()]),
 9062                ..Default::default()
 9063            }),
 9064            ..Default::default()
 9065        },
 9066        cx,
 9067    )
 9068    .await;
 9069    cx.lsp
 9070        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9071            Ok(Some(lsp::CompletionResponse::Array(vec![
 9072                lsp::CompletionItem {
 9073                    label: "first".into(),
 9074                    ..Default::default()
 9075                },
 9076                lsp::CompletionItem {
 9077                    label: "last".into(),
 9078                    ..Default::default()
 9079                },
 9080            ])))
 9081        });
 9082    cx.set_state("variableˇ");
 9083    cx.simulate_keystroke(".");
 9084    cx.executor().run_until_parked();
 9085
 9086    cx.update_editor(|editor, _, _| {
 9087        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9088        {
 9089            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9090        } else {
 9091            panic!("expected completion menu to be open");
 9092        }
 9093    });
 9094
 9095    cx.update_editor(|editor, window, cx| {
 9096        editor.move_page_down(&MovePageDown::default(), window, cx);
 9097        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9098        {
 9099            assert!(
 9100                menu.selected_item == 1,
 9101                "expected PageDown to select the last item from the context menu"
 9102            );
 9103        } else {
 9104            panic!("expected completion menu to stay open after PageDown");
 9105        }
 9106    });
 9107
 9108    cx.update_editor(|editor, window, cx| {
 9109        editor.move_page_up(&MovePageUp::default(), window, cx);
 9110        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9111        {
 9112            assert!(
 9113                menu.selected_item == 0,
 9114                "expected PageUp to select the first item from the context menu"
 9115            );
 9116        } else {
 9117            panic!("expected completion menu to stay open after PageUp");
 9118        }
 9119    });
 9120}
 9121
 9122#[gpui::test]
 9123async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9124    init_test(cx, |_| {});
 9125    let mut cx = EditorLspTestContext::new_rust(
 9126        lsp::ServerCapabilities {
 9127            completion_provider: Some(lsp::CompletionOptions {
 9128                trigger_characters: Some(vec![".".to_string()]),
 9129                ..Default::default()
 9130            }),
 9131            ..Default::default()
 9132        },
 9133        cx,
 9134    )
 9135    .await;
 9136    cx.lsp
 9137        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9138            Ok(Some(lsp::CompletionResponse::Array(vec![
 9139                lsp::CompletionItem {
 9140                    label: "Range".into(),
 9141                    sort_text: Some("a".into()),
 9142                    ..Default::default()
 9143                },
 9144                lsp::CompletionItem {
 9145                    label: "r".into(),
 9146                    sort_text: Some("b".into()),
 9147                    ..Default::default()
 9148                },
 9149                lsp::CompletionItem {
 9150                    label: "ret".into(),
 9151                    sort_text: Some("c".into()),
 9152                    ..Default::default()
 9153                },
 9154                lsp::CompletionItem {
 9155                    label: "return".into(),
 9156                    sort_text: Some("d".into()),
 9157                    ..Default::default()
 9158                },
 9159                lsp::CompletionItem {
 9160                    label: "slice".into(),
 9161                    sort_text: Some("d".into()),
 9162                    ..Default::default()
 9163                },
 9164            ])))
 9165        });
 9166    cx.set_state("");
 9167    cx.executor().run_until_parked();
 9168    cx.update_editor(|editor, window, cx| {
 9169        editor.show_completions(
 9170            &ShowCompletions {
 9171                trigger: Some("r".into()),
 9172            },
 9173            window,
 9174            cx,
 9175        );
 9176    });
 9177    cx.executor().run_until_parked();
 9178
 9179    cx.update_editor(|editor, _, _| {
 9180        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9181        {
 9182            assert_eq!(
 9183                completion_menu_entries(&menu),
 9184                &["r", "ret", "Range", "return"]
 9185            );
 9186        } else {
 9187            panic!("expected completion menu to be open");
 9188        }
 9189    });
 9190}
 9191
 9192#[gpui::test]
 9193async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9194    init_test(cx, |_| {});
 9195
 9196    let mut cx = EditorLspTestContext::new_rust(
 9197        lsp::ServerCapabilities {
 9198            completion_provider: Some(lsp::CompletionOptions {
 9199                trigger_characters: Some(vec![".".to_string()]),
 9200                resolve_provider: Some(true),
 9201                ..Default::default()
 9202            }),
 9203            ..Default::default()
 9204        },
 9205        cx,
 9206    )
 9207    .await;
 9208
 9209    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9210    cx.simulate_keystroke(".");
 9211    let completion_item = lsp::CompletionItem {
 9212        label: "Some".into(),
 9213        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9214        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9215        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9216            kind: lsp::MarkupKind::Markdown,
 9217            value: "```rust\nSome(2)\n```".to_string(),
 9218        })),
 9219        deprecated: Some(false),
 9220        sort_text: Some("Some".to_string()),
 9221        filter_text: Some("Some".to_string()),
 9222        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9223        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9224            range: lsp::Range {
 9225                start: lsp::Position {
 9226                    line: 0,
 9227                    character: 22,
 9228                },
 9229                end: lsp::Position {
 9230                    line: 0,
 9231                    character: 22,
 9232                },
 9233            },
 9234            new_text: "Some(2)".to_string(),
 9235        })),
 9236        additional_text_edits: Some(vec![lsp::TextEdit {
 9237            range: lsp::Range {
 9238                start: lsp::Position {
 9239                    line: 0,
 9240                    character: 20,
 9241                },
 9242                end: lsp::Position {
 9243                    line: 0,
 9244                    character: 22,
 9245                },
 9246            },
 9247            new_text: "".to_string(),
 9248        }]),
 9249        ..Default::default()
 9250    };
 9251
 9252    let closure_completion_item = completion_item.clone();
 9253    let counter = Arc::new(AtomicUsize::new(0));
 9254    let counter_clone = counter.clone();
 9255    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9256        let task_completion_item = closure_completion_item.clone();
 9257        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9258        async move {
 9259            Ok(Some(lsp::CompletionResponse::Array(vec![
 9260                task_completion_item,
 9261            ])))
 9262        }
 9263    });
 9264
 9265    cx.condition(|editor, _| editor.context_menu_visible())
 9266        .await;
 9267    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9268    assert!(request.next().await.is_some());
 9269    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9270
 9271    cx.simulate_keystroke("S");
 9272    cx.simulate_keystroke("o");
 9273    cx.simulate_keystroke("m");
 9274    cx.condition(|editor, _| editor.context_menu_visible())
 9275        .await;
 9276    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9277    assert!(request.next().await.is_some());
 9278    assert!(request.next().await.is_some());
 9279    assert!(request.next().await.is_some());
 9280    request.close();
 9281    assert!(request.next().await.is_none());
 9282    assert_eq!(
 9283        counter.load(atomic::Ordering::Acquire),
 9284        4,
 9285        "With the completions menu open, only one LSP request should happen per input"
 9286    );
 9287}
 9288
 9289#[gpui::test]
 9290async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9291    init_test(cx, |_| {});
 9292    let mut cx = EditorTestContext::new(cx).await;
 9293    let language = Arc::new(Language::new(
 9294        LanguageConfig {
 9295            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9296            ..Default::default()
 9297        },
 9298        Some(tree_sitter_rust::LANGUAGE.into()),
 9299    ));
 9300    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9301
 9302    // If multiple selections intersect a line, the line is only toggled once.
 9303    cx.set_state(indoc! {"
 9304        fn a() {
 9305            «//b();
 9306            ˇ»// «c();
 9307            //ˇ»  d();
 9308        }
 9309    "});
 9310
 9311    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9312
 9313    cx.assert_editor_state(indoc! {"
 9314        fn a() {
 9315            «b();
 9316            c();
 9317            ˇ» d();
 9318        }
 9319    "});
 9320
 9321    // The comment prefix is inserted at the same column for every line in a
 9322    // selection.
 9323    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9324
 9325    cx.assert_editor_state(indoc! {"
 9326        fn a() {
 9327            // «b();
 9328            // c();
 9329            ˇ»//  d();
 9330        }
 9331    "});
 9332
 9333    // If a selection ends at the beginning of a line, that line is not toggled.
 9334    cx.set_selections_state(indoc! {"
 9335        fn a() {
 9336            // b();
 9337            «// c();
 9338        ˇ»    //  d();
 9339        }
 9340    "});
 9341
 9342    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9343
 9344    cx.assert_editor_state(indoc! {"
 9345        fn a() {
 9346            // b();
 9347            «c();
 9348        ˇ»    //  d();
 9349        }
 9350    "});
 9351
 9352    // If a selection span a single line and is empty, the line is toggled.
 9353    cx.set_state(indoc! {"
 9354        fn a() {
 9355            a();
 9356            b();
 9357        ˇ
 9358        }
 9359    "});
 9360
 9361    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9362
 9363    cx.assert_editor_state(indoc! {"
 9364        fn a() {
 9365            a();
 9366            b();
 9367        //•ˇ
 9368        }
 9369    "});
 9370
 9371    // If a selection span multiple lines, empty lines are not toggled.
 9372    cx.set_state(indoc! {"
 9373        fn a() {
 9374            «a();
 9375
 9376            c();ˇ»
 9377        }
 9378    "});
 9379
 9380    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9381
 9382    cx.assert_editor_state(indoc! {"
 9383        fn a() {
 9384            // «a();
 9385
 9386            // c();ˇ»
 9387        }
 9388    "});
 9389
 9390    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9391    cx.set_state(indoc! {"
 9392        fn a() {
 9393            «// a();
 9394            /// b();
 9395            //! c();ˇ»
 9396        }
 9397    "});
 9398
 9399    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9400
 9401    cx.assert_editor_state(indoc! {"
 9402        fn a() {
 9403            «a();
 9404            b();
 9405            c();ˇ»
 9406        }
 9407    "});
 9408}
 9409
 9410#[gpui::test]
 9411async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9412    init_test(cx, |_| {});
 9413    let mut cx = EditorTestContext::new(cx).await;
 9414    let language = Arc::new(Language::new(
 9415        LanguageConfig {
 9416            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9417            ..Default::default()
 9418        },
 9419        Some(tree_sitter_rust::LANGUAGE.into()),
 9420    ));
 9421    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9422
 9423    let toggle_comments = &ToggleComments {
 9424        advance_downwards: false,
 9425        ignore_indent: true,
 9426    };
 9427
 9428    // If multiple selections intersect a line, the line is only toggled once.
 9429    cx.set_state(indoc! {"
 9430        fn a() {
 9431        //    «b();
 9432        //    c();
 9433        //    ˇ» d();
 9434        }
 9435    "});
 9436
 9437    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9438
 9439    cx.assert_editor_state(indoc! {"
 9440        fn a() {
 9441            «b();
 9442            c();
 9443            ˇ» d();
 9444        }
 9445    "});
 9446
 9447    // The comment prefix is inserted at the beginning of each line
 9448    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9449
 9450    cx.assert_editor_state(indoc! {"
 9451        fn a() {
 9452        //    «b();
 9453        //    c();
 9454        //    ˇ» d();
 9455        }
 9456    "});
 9457
 9458    // If a selection ends at the beginning of a line, that line is not toggled.
 9459    cx.set_selections_state(indoc! {"
 9460        fn a() {
 9461        //    b();
 9462        //    «c();
 9463        ˇ»//     d();
 9464        }
 9465    "});
 9466
 9467    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9468
 9469    cx.assert_editor_state(indoc! {"
 9470        fn a() {
 9471        //    b();
 9472            «c();
 9473        ˇ»//     d();
 9474        }
 9475    "});
 9476
 9477    // If a selection span a single line and is empty, the line is toggled.
 9478    cx.set_state(indoc! {"
 9479        fn a() {
 9480            a();
 9481            b();
 9482        ˇ
 9483        }
 9484    "});
 9485
 9486    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9487
 9488    cx.assert_editor_state(indoc! {"
 9489        fn a() {
 9490            a();
 9491            b();
 9492        //ˇ
 9493        }
 9494    "});
 9495
 9496    // If a selection span multiple lines, empty lines are not toggled.
 9497    cx.set_state(indoc! {"
 9498        fn a() {
 9499            «a();
 9500
 9501            c();ˇ»
 9502        }
 9503    "});
 9504
 9505    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9506
 9507    cx.assert_editor_state(indoc! {"
 9508        fn a() {
 9509        //    «a();
 9510
 9511        //    c();ˇ»
 9512        }
 9513    "});
 9514
 9515    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9516    cx.set_state(indoc! {"
 9517        fn a() {
 9518        //    «a();
 9519        ///    b();
 9520        //!    c();ˇ»
 9521        }
 9522    "});
 9523
 9524    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9525
 9526    cx.assert_editor_state(indoc! {"
 9527        fn a() {
 9528            «a();
 9529            b();
 9530            c();ˇ»
 9531        }
 9532    "});
 9533}
 9534
 9535#[gpui::test]
 9536async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9537    init_test(cx, |_| {});
 9538
 9539    let language = Arc::new(Language::new(
 9540        LanguageConfig {
 9541            line_comments: vec!["// ".into()],
 9542            ..Default::default()
 9543        },
 9544        Some(tree_sitter_rust::LANGUAGE.into()),
 9545    ));
 9546
 9547    let mut cx = EditorTestContext::new(cx).await;
 9548
 9549    cx.language_registry().add(language.clone());
 9550    cx.update_buffer(|buffer, cx| {
 9551        buffer.set_language(Some(language), cx);
 9552    });
 9553
 9554    let toggle_comments = &ToggleComments {
 9555        advance_downwards: true,
 9556        ignore_indent: false,
 9557    };
 9558
 9559    // Single cursor on one line -> advance
 9560    // Cursor moves horizontally 3 characters as well on non-blank line
 9561    cx.set_state(indoc!(
 9562        "fn a() {
 9563             ˇdog();
 9564             cat();
 9565        }"
 9566    ));
 9567    cx.update_editor(|editor, window, cx| {
 9568        editor.toggle_comments(toggle_comments, window, cx);
 9569    });
 9570    cx.assert_editor_state(indoc!(
 9571        "fn a() {
 9572             // dog();
 9573             catˇ();
 9574        }"
 9575    ));
 9576
 9577    // Single selection on one line -> don't advance
 9578    cx.set_state(indoc!(
 9579        "fn a() {
 9580             «dog()ˇ»;
 9581             cat();
 9582        }"
 9583    ));
 9584    cx.update_editor(|editor, window, cx| {
 9585        editor.toggle_comments(toggle_comments, window, cx);
 9586    });
 9587    cx.assert_editor_state(indoc!(
 9588        "fn a() {
 9589             // «dog()ˇ»;
 9590             cat();
 9591        }"
 9592    ));
 9593
 9594    // Multiple cursors on one line -> advance
 9595    cx.set_state(indoc!(
 9596        "fn a() {
 9597             ˇdˇog();
 9598             cat();
 9599        }"
 9600    ));
 9601    cx.update_editor(|editor, window, cx| {
 9602        editor.toggle_comments(toggle_comments, window, cx);
 9603    });
 9604    cx.assert_editor_state(indoc!(
 9605        "fn a() {
 9606             // dog();
 9607             catˇ(ˇ);
 9608        }"
 9609    ));
 9610
 9611    // Multiple cursors on one line, with selection -> don't advance
 9612    cx.set_state(indoc!(
 9613        "fn a() {
 9614             ˇdˇog«()ˇ»;
 9615             cat();
 9616        }"
 9617    ));
 9618    cx.update_editor(|editor, window, cx| {
 9619        editor.toggle_comments(toggle_comments, window, cx);
 9620    });
 9621    cx.assert_editor_state(indoc!(
 9622        "fn a() {
 9623             // ˇdˇog«()ˇ»;
 9624             cat();
 9625        }"
 9626    ));
 9627
 9628    // Single cursor on one line -> advance
 9629    // Cursor moves to column 0 on blank line
 9630    cx.set_state(indoc!(
 9631        "fn a() {
 9632             ˇdog();
 9633
 9634             cat();
 9635        }"
 9636    ));
 9637    cx.update_editor(|editor, window, cx| {
 9638        editor.toggle_comments(toggle_comments, window, cx);
 9639    });
 9640    cx.assert_editor_state(indoc!(
 9641        "fn a() {
 9642             // dog();
 9643        ˇ
 9644             cat();
 9645        }"
 9646    ));
 9647
 9648    // Single cursor on one line -> advance
 9649    // Cursor starts and ends at column 0
 9650    cx.set_state(indoc!(
 9651        "fn a() {
 9652         ˇ    dog();
 9653             cat();
 9654        }"
 9655    ));
 9656    cx.update_editor(|editor, window, cx| {
 9657        editor.toggle_comments(toggle_comments, window, cx);
 9658    });
 9659    cx.assert_editor_state(indoc!(
 9660        "fn a() {
 9661             // dog();
 9662         ˇ    cat();
 9663        }"
 9664    ));
 9665}
 9666
 9667#[gpui::test]
 9668async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9669    init_test(cx, |_| {});
 9670
 9671    let mut cx = EditorTestContext::new(cx).await;
 9672
 9673    let html_language = Arc::new(
 9674        Language::new(
 9675            LanguageConfig {
 9676                name: "HTML".into(),
 9677                block_comment: Some(("<!-- ".into(), " -->".into())),
 9678                ..Default::default()
 9679            },
 9680            Some(tree_sitter_html::LANGUAGE.into()),
 9681        )
 9682        .with_injection_query(
 9683            r#"
 9684            (script_element
 9685                (raw_text) @injection.content
 9686                (#set! injection.language "javascript"))
 9687            "#,
 9688        )
 9689        .unwrap(),
 9690    );
 9691
 9692    let javascript_language = Arc::new(Language::new(
 9693        LanguageConfig {
 9694            name: "JavaScript".into(),
 9695            line_comments: vec!["// ".into()],
 9696            ..Default::default()
 9697        },
 9698        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9699    ));
 9700
 9701    cx.language_registry().add(html_language.clone());
 9702    cx.language_registry().add(javascript_language.clone());
 9703    cx.update_buffer(|buffer, cx| {
 9704        buffer.set_language(Some(html_language), cx);
 9705    });
 9706
 9707    // Toggle comments for empty selections
 9708    cx.set_state(
 9709        &r#"
 9710            <p>A</p>ˇ
 9711            <p>B</p>ˇ
 9712            <p>C</p>ˇ
 9713        "#
 9714        .unindent(),
 9715    );
 9716    cx.update_editor(|editor, window, cx| {
 9717        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9718    });
 9719    cx.assert_editor_state(
 9720        &r#"
 9721            <!-- <p>A</p>ˇ -->
 9722            <!-- <p>B</p>ˇ -->
 9723            <!-- <p>C</p>ˇ -->
 9724        "#
 9725        .unindent(),
 9726    );
 9727    cx.update_editor(|editor, window, cx| {
 9728        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9729    });
 9730    cx.assert_editor_state(
 9731        &r#"
 9732            <p>A</p>ˇ
 9733            <p>B</p>ˇ
 9734            <p>C</p>ˇ
 9735        "#
 9736        .unindent(),
 9737    );
 9738
 9739    // Toggle comments for mixture of empty and non-empty selections, where
 9740    // multiple selections occupy a given line.
 9741    cx.set_state(
 9742        &r#"
 9743            <p>A«</p>
 9744            <p>ˇ»B</p>ˇ
 9745            <p>C«</p>
 9746            <p>ˇ»D</p>ˇ
 9747        "#
 9748        .unindent(),
 9749    );
 9750
 9751    cx.update_editor(|editor, window, cx| {
 9752        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9753    });
 9754    cx.assert_editor_state(
 9755        &r#"
 9756            <!-- <p>A«</p>
 9757            <p>ˇ»B</p>ˇ -->
 9758            <!-- <p>C«</p>
 9759            <p>ˇ»D</p>ˇ -->
 9760        "#
 9761        .unindent(),
 9762    );
 9763    cx.update_editor(|editor, window, cx| {
 9764        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9765    });
 9766    cx.assert_editor_state(
 9767        &r#"
 9768            <p>A«</p>
 9769            <p>ˇ»B</p>ˇ
 9770            <p>C«</p>
 9771            <p>ˇ»D</p>ˇ
 9772        "#
 9773        .unindent(),
 9774    );
 9775
 9776    // Toggle comments when different languages are active for different
 9777    // selections.
 9778    cx.set_state(
 9779        &r#"
 9780            ˇ<script>
 9781                ˇvar x = new Y();
 9782            ˇ</script>
 9783        "#
 9784        .unindent(),
 9785    );
 9786    cx.executor().run_until_parked();
 9787    cx.update_editor(|editor, window, cx| {
 9788        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9789    });
 9790    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9791    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9792    cx.assert_editor_state(
 9793        &r#"
 9794            <!-- ˇ<script> -->
 9795                // ˇvar x = new Y();
 9796            <!-- ˇ</script> -->
 9797        "#
 9798        .unindent(),
 9799    );
 9800}
 9801
 9802#[gpui::test]
 9803fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9804    init_test(cx, |_| {});
 9805
 9806    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9807    let multibuffer = cx.new(|cx| {
 9808        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9809        multibuffer.push_excerpts(
 9810            buffer.clone(),
 9811            [
 9812                ExcerptRange {
 9813                    context: Point::new(0, 0)..Point::new(0, 4),
 9814                    primary: None,
 9815                },
 9816                ExcerptRange {
 9817                    context: Point::new(1, 0)..Point::new(1, 4),
 9818                    primary: None,
 9819                },
 9820            ],
 9821            cx,
 9822        );
 9823        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9824        multibuffer
 9825    });
 9826
 9827    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9828    editor.update_in(cx, |editor, window, cx| {
 9829        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9830        editor.change_selections(None, window, cx, |s| {
 9831            s.select_ranges([
 9832                Point::new(0, 0)..Point::new(0, 0),
 9833                Point::new(1, 0)..Point::new(1, 0),
 9834            ])
 9835        });
 9836
 9837        editor.handle_input("X", window, cx);
 9838        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9839        assert_eq!(
 9840            editor.selections.ranges(cx),
 9841            [
 9842                Point::new(0, 1)..Point::new(0, 1),
 9843                Point::new(1, 1)..Point::new(1, 1),
 9844            ]
 9845        );
 9846
 9847        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9848        editor.change_selections(None, window, cx, |s| {
 9849            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9850        });
 9851        editor.backspace(&Default::default(), window, cx);
 9852        assert_eq!(editor.text(cx), "Xa\nbbb");
 9853        assert_eq!(
 9854            editor.selections.ranges(cx),
 9855            [Point::new(1, 0)..Point::new(1, 0)]
 9856        );
 9857
 9858        editor.change_selections(None, window, cx, |s| {
 9859            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9860        });
 9861        editor.backspace(&Default::default(), window, cx);
 9862        assert_eq!(editor.text(cx), "X\nbb");
 9863        assert_eq!(
 9864            editor.selections.ranges(cx),
 9865            [Point::new(0, 1)..Point::new(0, 1)]
 9866        );
 9867    });
 9868}
 9869
 9870#[gpui::test]
 9871fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9872    init_test(cx, |_| {});
 9873
 9874    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9875    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9876        indoc! {"
 9877            [aaaa
 9878            (bbbb]
 9879            cccc)",
 9880        },
 9881        markers.clone(),
 9882    );
 9883    let excerpt_ranges = markers.into_iter().map(|marker| {
 9884        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9885        ExcerptRange {
 9886            context,
 9887            primary: None,
 9888        }
 9889    });
 9890    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9891    let multibuffer = cx.new(|cx| {
 9892        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9893        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9894        multibuffer
 9895    });
 9896
 9897    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9898    editor.update_in(cx, |editor, window, cx| {
 9899        let (expected_text, selection_ranges) = marked_text_ranges(
 9900            indoc! {"
 9901                aaaa
 9902                bˇbbb
 9903                bˇbbˇb
 9904                cccc"
 9905            },
 9906            true,
 9907        );
 9908        assert_eq!(editor.text(cx), expected_text);
 9909        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9910
 9911        editor.handle_input("X", window, cx);
 9912
 9913        let (expected_text, expected_selections) = marked_text_ranges(
 9914            indoc! {"
 9915                aaaa
 9916                bXˇbbXb
 9917                bXˇbbXˇb
 9918                cccc"
 9919            },
 9920            false,
 9921        );
 9922        assert_eq!(editor.text(cx), expected_text);
 9923        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9924
 9925        editor.newline(&Newline, window, cx);
 9926        let (expected_text, expected_selections) = marked_text_ranges(
 9927            indoc! {"
 9928                aaaa
 9929                bX
 9930                ˇbbX
 9931                b
 9932                bX
 9933                ˇbbX
 9934                ˇb
 9935                cccc"
 9936            },
 9937            false,
 9938        );
 9939        assert_eq!(editor.text(cx), expected_text);
 9940        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9941    });
 9942}
 9943
 9944#[gpui::test]
 9945fn test_refresh_selections(cx: &mut TestAppContext) {
 9946    init_test(cx, |_| {});
 9947
 9948    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9949    let mut excerpt1_id = None;
 9950    let multibuffer = cx.new(|cx| {
 9951        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9952        excerpt1_id = multibuffer
 9953            .push_excerpts(
 9954                buffer.clone(),
 9955                [
 9956                    ExcerptRange {
 9957                        context: Point::new(0, 0)..Point::new(1, 4),
 9958                        primary: None,
 9959                    },
 9960                    ExcerptRange {
 9961                        context: Point::new(1, 0)..Point::new(2, 4),
 9962                        primary: None,
 9963                    },
 9964                ],
 9965                cx,
 9966            )
 9967            .into_iter()
 9968            .next();
 9969        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9970        multibuffer
 9971    });
 9972
 9973    let editor = cx.add_window(|window, cx| {
 9974        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9975        let snapshot = editor.snapshot(window, cx);
 9976        editor.change_selections(None, window, cx, |s| {
 9977            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9978        });
 9979        editor.begin_selection(
 9980            Point::new(2, 1).to_display_point(&snapshot),
 9981            true,
 9982            1,
 9983            window,
 9984            cx,
 9985        );
 9986        assert_eq!(
 9987            editor.selections.ranges(cx),
 9988            [
 9989                Point::new(1, 3)..Point::new(1, 3),
 9990                Point::new(2, 1)..Point::new(2, 1),
 9991            ]
 9992        );
 9993        editor
 9994    });
 9995
 9996    // Refreshing selections is a no-op when excerpts haven't changed.
 9997    _ = editor.update(cx, |editor, window, cx| {
 9998        editor.change_selections(None, window, cx, |s| s.refresh());
 9999        assert_eq!(
10000            editor.selections.ranges(cx),
10001            [
10002                Point::new(1, 3)..Point::new(1, 3),
10003                Point::new(2, 1)..Point::new(2, 1),
10004            ]
10005        );
10006    });
10007
10008    multibuffer.update(cx, |multibuffer, cx| {
10009        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10010    });
10011    _ = editor.update(cx, |editor, window, cx| {
10012        // Removing an excerpt causes the first selection to become degenerate.
10013        assert_eq!(
10014            editor.selections.ranges(cx),
10015            [
10016                Point::new(0, 0)..Point::new(0, 0),
10017                Point::new(0, 1)..Point::new(0, 1)
10018            ]
10019        );
10020
10021        // Refreshing selections will relocate the first selection to the original buffer
10022        // location.
10023        editor.change_selections(None, window, cx, |s| s.refresh());
10024        assert_eq!(
10025            editor.selections.ranges(cx),
10026            [
10027                Point::new(0, 1)..Point::new(0, 1),
10028                Point::new(0, 3)..Point::new(0, 3)
10029            ]
10030        );
10031        assert!(editor.selections.pending_anchor().is_some());
10032    });
10033}
10034
10035#[gpui::test]
10036fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10037    init_test(cx, |_| {});
10038
10039    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10040    let mut excerpt1_id = None;
10041    let multibuffer = cx.new(|cx| {
10042        let mut multibuffer = MultiBuffer::new(ReadWrite);
10043        excerpt1_id = multibuffer
10044            .push_excerpts(
10045                buffer.clone(),
10046                [
10047                    ExcerptRange {
10048                        context: Point::new(0, 0)..Point::new(1, 4),
10049                        primary: None,
10050                    },
10051                    ExcerptRange {
10052                        context: Point::new(1, 0)..Point::new(2, 4),
10053                        primary: None,
10054                    },
10055                ],
10056                cx,
10057            )
10058            .into_iter()
10059            .next();
10060        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10061        multibuffer
10062    });
10063
10064    let editor = cx.add_window(|window, cx| {
10065        let mut editor = build_editor(multibuffer.clone(), window, cx);
10066        let snapshot = editor.snapshot(window, cx);
10067        editor.begin_selection(
10068            Point::new(1, 3).to_display_point(&snapshot),
10069            false,
10070            1,
10071            window,
10072            cx,
10073        );
10074        assert_eq!(
10075            editor.selections.ranges(cx),
10076            [Point::new(1, 3)..Point::new(1, 3)]
10077        );
10078        editor
10079    });
10080
10081    multibuffer.update(cx, |multibuffer, cx| {
10082        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10083    });
10084    _ = editor.update(cx, |editor, window, cx| {
10085        assert_eq!(
10086            editor.selections.ranges(cx),
10087            [Point::new(0, 0)..Point::new(0, 0)]
10088        );
10089
10090        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10091        editor.change_selections(None, window, cx, |s| s.refresh());
10092        assert_eq!(
10093            editor.selections.ranges(cx),
10094            [Point::new(0, 3)..Point::new(0, 3)]
10095        );
10096        assert!(editor.selections.pending_anchor().is_some());
10097    });
10098}
10099
10100#[gpui::test]
10101async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10102    init_test(cx, |_| {});
10103
10104    let language = Arc::new(
10105        Language::new(
10106            LanguageConfig {
10107                brackets: BracketPairConfig {
10108                    pairs: vec![
10109                        BracketPair {
10110                            start: "{".to_string(),
10111                            end: "}".to_string(),
10112                            close: true,
10113                            surround: true,
10114                            newline: true,
10115                        },
10116                        BracketPair {
10117                            start: "/* ".to_string(),
10118                            end: " */".to_string(),
10119                            close: true,
10120                            surround: true,
10121                            newline: true,
10122                        },
10123                    ],
10124                    ..Default::default()
10125                },
10126                ..Default::default()
10127            },
10128            Some(tree_sitter_rust::LANGUAGE.into()),
10129        )
10130        .with_indents_query("")
10131        .unwrap(),
10132    );
10133
10134    let text = concat!(
10135        "{   }\n",     //
10136        "  x\n",       //
10137        "  /*   */\n", //
10138        "x\n",         //
10139        "{{} }\n",     //
10140    );
10141
10142    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10143    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10144    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10145    editor
10146        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10147        .await;
10148
10149    editor.update_in(cx, |editor, window, cx| {
10150        editor.change_selections(None, window, cx, |s| {
10151            s.select_display_ranges([
10152                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10153                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10154                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10155            ])
10156        });
10157        editor.newline(&Newline, window, cx);
10158
10159        assert_eq!(
10160            editor.buffer().read(cx).read(cx).text(),
10161            concat!(
10162                "{ \n",    // Suppress rustfmt
10163                "\n",      //
10164                "}\n",     //
10165                "  x\n",   //
10166                "  /* \n", //
10167                "  \n",    //
10168                "  */\n",  //
10169                "x\n",     //
10170                "{{} \n",  //
10171                "}\n",     //
10172            )
10173        );
10174    });
10175}
10176
10177#[gpui::test]
10178fn test_highlighted_ranges(cx: &mut TestAppContext) {
10179    init_test(cx, |_| {});
10180
10181    let editor = cx.add_window(|window, cx| {
10182        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10183        build_editor(buffer.clone(), window, cx)
10184    });
10185
10186    _ = editor.update(cx, |editor, window, cx| {
10187        struct Type1;
10188        struct Type2;
10189
10190        let buffer = editor.buffer.read(cx).snapshot(cx);
10191
10192        let anchor_range =
10193            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10194
10195        editor.highlight_background::<Type1>(
10196            &[
10197                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10198                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10199                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10200                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10201            ],
10202            |_| Hsla::red(),
10203            cx,
10204        );
10205        editor.highlight_background::<Type2>(
10206            &[
10207                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10208                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10209                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10210                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10211            ],
10212            |_| Hsla::green(),
10213            cx,
10214        );
10215
10216        let snapshot = editor.snapshot(window, cx);
10217        let mut highlighted_ranges = editor.background_highlights_in_range(
10218            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10219            &snapshot,
10220            cx.theme().colors(),
10221        );
10222        // Enforce a consistent ordering based on color without relying on the ordering of the
10223        // highlight's `TypeId` which is non-executor.
10224        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10225        assert_eq!(
10226            highlighted_ranges,
10227            &[
10228                (
10229                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10230                    Hsla::red(),
10231                ),
10232                (
10233                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10234                    Hsla::red(),
10235                ),
10236                (
10237                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10238                    Hsla::green(),
10239                ),
10240                (
10241                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10242                    Hsla::green(),
10243                ),
10244            ]
10245        );
10246        assert_eq!(
10247            editor.background_highlights_in_range(
10248                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10249                &snapshot,
10250                cx.theme().colors(),
10251            ),
10252            &[(
10253                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10254                Hsla::red(),
10255            )]
10256        );
10257    });
10258}
10259
10260#[gpui::test]
10261async fn test_following(cx: &mut gpui::TestAppContext) {
10262    init_test(cx, |_| {});
10263
10264    let fs = FakeFs::new(cx.executor());
10265    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10266
10267    let buffer = project.update(cx, |project, cx| {
10268        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10269        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10270    });
10271    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10272    let follower = cx.update(|cx| {
10273        cx.open_window(
10274            WindowOptions {
10275                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10276                    gpui::Point::new(px(0.), px(0.)),
10277                    gpui::Point::new(px(10.), px(80.)),
10278                ))),
10279                ..Default::default()
10280            },
10281            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10282        )
10283        .unwrap()
10284    });
10285
10286    let is_still_following = Rc::new(RefCell::new(true));
10287    let follower_edit_event_count = Rc::new(RefCell::new(0));
10288    let pending_update = Rc::new(RefCell::new(None));
10289    let leader_entity = leader.root(cx).unwrap();
10290    let follower_entity = follower.root(cx).unwrap();
10291    _ = follower.update(cx, {
10292        let update = pending_update.clone();
10293        let is_still_following = is_still_following.clone();
10294        let follower_edit_event_count = follower_edit_event_count.clone();
10295        |_, window, cx| {
10296            cx.subscribe_in(
10297                &leader_entity,
10298                window,
10299                move |_, leader, event, window, cx| {
10300                    leader.read(cx).add_event_to_update_proto(
10301                        event,
10302                        &mut update.borrow_mut(),
10303                        window,
10304                        cx,
10305                    );
10306                },
10307            )
10308            .detach();
10309
10310            cx.subscribe_in(
10311                &follower_entity,
10312                window,
10313                move |_, _, event: &EditorEvent, _window, _cx| {
10314                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10315                        *is_still_following.borrow_mut() = false;
10316                    }
10317
10318                    if let EditorEvent::BufferEdited = event {
10319                        *follower_edit_event_count.borrow_mut() += 1;
10320                    }
10321                },
10322            )
10323            .detach();
10324        }
10325    });
10326
10327    // Update the selections only
10328    _ = leader.update(cx, |leader, window, cx| {
10329        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10330    });
10331    follower
10332        .update(cx, |follower, window, cx| {
10333            follower.apply_update_proto(
10334                &project,
10335                pending_update.borrow_mut().take().unwrap(),
10336                window,
10337                cx,
10338            )
10339        })
10340        .unwrap()
10341        .await
10342        .unwrap();
10343    _ = follower.update(cx, |follower, _, cx| {
10344        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10345    });
10346    assert!(*is_still_following.borrow());
10347    assert_eq!(*follower_edit_event_count.borrow(), 0);
10348
10349    // Update the scroll position only
10350    _ = leader.update(cx, |leader, window, cx| {
10351        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10352    });
10353    follower
10354        .update(cx, |follower, window, cx| {
10355            follower.apply_update_proto(
10356                &project,
10357                pending_update.borrow_mut().take().unwrap(),
10358                window,
10359                cx,
10360            )
10361        })
10362        .unwrap()
10363        .await
10364        .unwrap();
10365    assert_eq!(
10366        follower
10367            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10368            .unwrap(),
10369        gpui::Point::new(1.5, 3.5)
10370    );
10371    assert!(*is_still_following.borrow());
10372    assert_eq!(*follower_edit_event_count.borrow(), 0);
10373
10374    // Update the selections and scroll position. The follower's scroll position is updated
10375    // via autoscroll, not via the leader's exact scroll position.
10376    _ = leader.update(cx, |leader, window, cx| {
10377        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10378        leader.request_autoscroll(Autoscroll::newest(), cx);
10379        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10380    });
10381    follower
10382        .update(cx, |follower, window, cx| {
10383            follower.apply_update_proto(
10384                &project,
10385                pending_update.borrow_mut().take().unwrap(),
10386                window,
10387                cx,
10388            )
10389        })
10390        .unwrap()
10391        .await
10392        .unwrap();
10393    _ = follower.update(cx, |follower, _, cx| {
10394        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10395        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10396    });
10397    assert!(*is_still_following.borrow());
10398
10399    // Creating a pending selection that precedes another selection
10400    _ = leader.update(cx, |leader, window, cx| {
10401        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10402        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10403    });
10404    follower
10405        .update(cx, |follower, window, cx| {
10406            follower.apply_update_proto(
10407                &project,
10408                pending_update.borrow_mut().take().unwrap(),
10409                window,
10410                cx,
10411            )
10412        })
10413        .unwrap()
10414        .await
10415        .unwrap();
10416    _ = follower.update(cx, |follower, _, cx| {
10417        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10418    });
10419    assert!(*is_still_following.borrow());
10420
10421    // Extend the pending selection so that it surrounds another selection
10422    _ = leader.update(cx, |leader, window, cx| {
10423        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10424    });
10425    follower
10426        .update(cx, |follower, window, cx| {
10427            follower.apply_update_proto(
10428                &project,
10429                pending_update.borrow_mut().take().unwrap(),
10430                window,
10431                cx,
10432            )
10433        })
10434        .unwrap()
10435        .await
10436        .unwrap();
10437    _ = follower.update(cx, |follower, _, cx| {
10438        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10439    });
10440
10441    // Scrolling locally breaks the follow
10442    _ = follower.update(cx, |follower, window, cx| {
10443        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10444        follower.set_scroll_anchor(
10445            ScrollAnchor {
10446                anchor: top_anchor,
10447                offset: gpui::Point::new(0.0, 0.5),
10448            },
10449            window,
10450            cx,
10451        );
10452    });
10453    assert!(!(*is_still_following.borrow()));
10454}
10455
10456#[gpui::test]
10457async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10458    init_test(cx, |_| {});
10459
10460    let fs = FakeFs::new(cx.executor());
10461    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10462    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10463    let pane = workspace
10464        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10465        .unwrap();
10466
10467    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10468
10469    let leader = pane.update_in(cx, |_, window, cx| {
10470        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10471        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10472    });
10473
10474    // Start following the editor when it has no excerpts.
10475    let mut state_message =
10476        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10477    let workspace_entity = workspace.root(cx).unwrap();
10478    let follower_1 = cx
10479        .update_window(*workspace.deref(), |_, window, cx| {
10480            Editor::from_state_proto(
10481                workspace_entity,
10482                ViewId {
10483                    creator: Default::default(),
10484                    id: 0,
10485                },
10486                &mut state_message,
10487                window,
10488                cx,
10489            )
10490        })
10491        .unwrap()
10492        .unwrap()
10493        .await
10494        .unwrap();
10495
10496    let update_message = Rc::new(RefCell::new(None));
10497    follower_1.update_in(cx, {
10498        let update = update_message.clone();
10499        |_, window, cx| {
10500            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10501                leader.read(cx).add_event_to_update_proto(
10502                    event,
10503                    &mut update.borrow_mut(),
10504                    window,
10505                    cx,
10506                );
10507            })
10508            .detach();
10509        }
10510    });
10511
10512    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10513        (
10514            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10515            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10516        )
10517    });
10518
10519    // Insert some excerpts.
10520    leader.update(cx, |leader, cx| {
10521        leader.buffer.update(cx, |multibuffer, cx| {
10522            let excerpt_ids = multibuffer.push_excerpts(
10523                buffer_1.clone(),
10524                [
10525                    ExcerptRange {
10526                        context: 1..6,
10527                        primary: None,
10528                    },
10529                    ExcerptRange {
10530                        context: 12..15,
10531                        primary: None,
10532                    },
10533                    ExcerptRange {
10534                        context: 0..3,
10535                        primary: None,
10536                    },
10537                ],
10538                cx,
10539            );
10540            multibuffer.insert_excerpts_after(
10541                excerpt_ids[0],
10542                buffer_2.clone(),
10543                [
10544                    ExcerptRange {
10545                        context: 8..12,
10546                        primary: None,
10547                    },
10548                    ExcerptRange {
10549                        context: 0..6,
10550                        primary: None,
10551                    },
10552                ],
10553                cx,
10554            );
10555        });
10556    });
10557
10558    // Apply the update of adding the excerpts.
10559    follower_1
10560        .update_in(cx, |follower, window, cx| {
10561            follower.apply_update_proto(
10562                &project,
10563                update_message.borrow().clone().unwrap(),
10564                window,
10565                cx,
10566            )
10567        })
10568        .await
10569        .unwrap();
10570    assert_eq!(
10571        follower_1.update(cx, |editor, cx| editor.text(cx)),
10572        leader.update(cx, |editor, cx| editor.text(cx))
10573    );
10574    update_message.borrow_mut().take();
10575
10576    // Start following separately after it already has excerpts.
10577    let mut state_message =
10578        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10579    let workspace_entity = workspace.root(cx).unwrap();
10580    let follower_2 = cx
10581        .update_window(*workspace.deref(), |_, window, cx| {
10582            Editor::from_state_proto(
10583                workspace_entity,
10584                ViewId {
10585                    creator: Default::default(),
10586                    id: 0,
10587                },
10588                &mut state_message,
10589                window,
10590                cx,
10591            )
10592        })
10593        .unwrap()
10594        .unwrap()
10595        .await
10596        .unwrap();
10597    assert_eq!(
10598        follower_2.update(cx, |editor, cx| editor.text(cx)),
10599        leader.update(cx, |editor, cx| editor.text(cx))
10600    );
10601
10602    // Remove some excerpts.
10603    leader.update(cx, |leader, cx| {
10604        leader.buffer.update(cx, |multibuffer, cx| {
10605            let excerpt_ids = multibuffer.excerpt_ids();
10606            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10607            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10608        });
10609    });
10610
10611    // Apply the update of removing the excerpts.
10612    follower_1
10613        .update_in(cx, |follower, window, cx| {
10614            follower.apply_update_proto(
10615                &project,
10616                update_message.borrow().clone().unwrap(),
10617                window,
10618                cx,
10619            )
10620        })
10621        .await
10622        .unwrap();
10623    follower_2
10624        .update_in(cx, |follower, window, cx| {
10625            follower.apply_update_proto(
10626                &project,
10627                update_message.borrow().clone().unwrap(),
10628                window,
10629                cx,
10630            )
10631        })
10632        .await
10633        .unwrap();
10634    update_message.borrow_mut().take();
10635    assert_eq!(
10636        follower_1.update(cx, |editor, cx| editor.text(cx)),
10637        leader.update(cx, |editor, cx| editor.text(cx))
10638    );
10639}
10640
10641#[gpui::test]
10642async fn go_to_prev_overlapping_diagnostic(
10643    executor: BackgroundExecutor,
10644    cx: &mut gpui::TestAppContext,
10645) {
10646    init_test(cx, |_| {});
10647
10648    let mut cx = EditorTestContext::new(cx).await;
10649    let lsp_store =
10650        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10651
10652    cx.set_state(indoc! {"
10653        ˇfn func(abc def: i32) -> u32 {
10654        }
10655    "});
10656
10657    cx.update(|_, cx| {
10658        lsp_store.update(cx, |lsp_store, cx| {
10659            lsp_store
10660                .update_diagnostics(
10661                    LanguageServerId(0),
10662                    lsp::PublishDiagnosticsParams {
10663                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10664                        version: None,
10665                        diagnostics: vec![
10666                            lsp::Diagnostic {
10667                                range: lsp::Range::new(
10668                                    lsp::Position::new(0, 11),
10669                                    lsp::Position::new(0, 12),
10670                                ),
10671                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10672                                ..Default::default()
10673                            },
10674                            lsp::Diagnostic {
10675                                range: lsp::Range::new(
10676                                    lsp::Position::new(0, 12),
10677                                    lsp::Position::new(0, 15),
10678                                ),
10679                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10680                                ..Default::default()
10681                            },
10682                            lsp::Diagnostic {
10683                                range: lsp::Range::new(
10684                                    lsp::Position::new(0, 25),
10685                                    lsp::Position::new(0, 28),
10686                                ),
10687                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10688                                ..Default::default()
10689                            },
10690                        ],
10691                    },
10692                    &[],
10693                    cx,
10694                )
10695                .unwrap()
10696        });
10697    });
10698
10699    executor.run_until_parked();
10700
10701    cx.update_editor(|editor, window, cx| {
10702        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10703    });
10704
10705    cx.assert_editor_state(indoc! {"
10706        fn func(abc def: i32) -> ˇu32 {
10707        }
10708    "});
10709
10710    cx.update_editor(|editor, window, cx| {
10711        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10712    });
10713
10714    cx.assert_editor_state(indoc! {"
10715        fn func(abc ˇdef: i32) -> u32 {
10716        }
10717    "});
10718
10719    cx.update_editor(|editor, window, cx| {
10720        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10721    });
10722
10723    cx.assert_editor_state(indoc! {"
10724        fn func(abcˇ def: i32) -> u32 {
10725        }
10726    "});
10727
10728    cx.update_editor(|editor, window, cx| {
10729        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10730    });
10731
10732    cx.assert_editor_state(indoc! {"
10733        fn func(abc def: i32) -> ˇu32 {
10734        }
10735    "});
10736}
10737
10738#[gpui::test]
10739async fn cycle_through_same_place_diagnostics(
10740    executor: BackgroundExecutor,
10741    cx: &mut gpui::TestAppContext,
10742) {
10743    init_test(cx, |_| {});
10744
10745    let mut cx = EditorTestContext::new(cx).await;
10746    let lsp_store =
10747        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10748
10749    cx.set_state(indoc! {"
10750        ˇfn func(abc def: i32) -> u32 {
10751        }
10752    "});
10753
10754    cx.update(|_, cx| {
10755        lsp_store.update(cx, |lsp_store, cx| {
10756            lsp_store
10757                .update_diagnostics(
10758                    LanguageServerId(0),
10759                    lsp::PublishDiagnosticsParams {
10760                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10761                        version: None,
10762                        diagnostics: vec![
10763                            lsp::Diagnostic {
10764                                range: lsp::Range::new(
10765                                    lsp::Position::new(0, 11),
10766                                    lsp::Position::new(0, 12),
10767                                ),
10768                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10769                                ..Default::default()
10770                            },
10771                            lsp::Diagnostic {
10772                                range: lsp::Range::new(
10773                                    lsp::Position::new(0, 12),
10774                                    lsp::Position::new(0, 15),
10775                                ),
10776                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10777                                ..Default::default()
10778                            },
10779                            lsp::Diagnostic {
10780                                range: lsp::Range::new(
10781                                    lsp::Position::new(0, 12),
10782                                    lsp::Position::new(0, 15),
10783                                ),
10784                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10785                                ..Default::default()
10786                            },
10787                            lsp::Diagnostic {
10788                                range: lsp::Range::new(
10789                                    lsp::Position::new(0, 25),
10790                                    lsp::Position::new(0, 28),
10791                                ),
10792                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10793                                ..Default::default()
10794                            },
10795                        ],
10796                    },
10797                    &[],
10798                    cx,
10799                )
10800                .unwrap()
10801        });
10802    });
10803    executor.run_until_parked();
10804
10805    //// Backward
10806
10807    // Fourth diagnostic
10808    cx.update_editor(|editor, window, cx| {
10809        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10810    });
10811    cx.assert_editor_state(indoc! {"
10812        fn func(abc def: i32) -> ˇu32 {
10813        }
10814    "});
10815
10816    // Third diagnostic
10817    cx.update_editor(|editor, window, cx| {
10818        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10819    });
10820    cx.assert_editor_state(indoc! {"
10821        fn func(abc ˇdef: i32) -> u32 {
10822        }
10823    "});
10824
10825    // Second diagnostic, same place
10826    cx.update_editor(|editor, window, cx| {
10827        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10828    });
10829    cx.assert_editor_state(indoc! {"
10830        fn func(abc ˇdef: i32) -> u32 {
10831        }
10832    "});
10833
10834    // First diagnostic
10835    cx.update_editor(|editor, window, cx| {
10836        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10837    });
10838    cx.assert_editor_state(indoc! {"
10839        fn func(abcˇ def: i32) -> u32 {
10840        }
10841    "});
10842
10843    // Wrapped over, fourth diagnostic
10844    cx.update_editor(|editor, window, cx| {
10845        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10846    });
10847    cx.assert_editor_state(indoc! {"
10848        fn func(abc def: i32) -> ˇu32 {
10849        }
10850    "});
10851
10852    cx.update_editor(|editor, window, cx| {
10853        editor.move_to_beginning(&MoveToBeginning, window, cx);
10854    });
10855    cx.assert_editor_state(indoc! {"
10856        ˇfn func(abc def: i32) -> u32 {
10857        }
10858    "});
10859
10860    //// Forward
10861
10862    // First diagnostic
10863    cx.update_editor(|editor, window, cx| {
10864        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10865    });
10866    cx.assert_editor_state(indoc! {"
10867        fn func(abcˇ def: i32) -> u32 {
10868        }
10869    "});
10870
10871    // Second diagnostic
10872    cx.update_editor(|editor, window, cx| {
10873        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10874    });
10875    cx.assert_editor_state(indoc! {"
10876        fn func(abc ˇdef: i32) -> u32 {
10877        }
10878    "});
10879
10880    // Third diagnostic, same place
10881    cx.update_editor(|editor, window, cx| {
10882        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10883    });
10884    cx.assert_editor_state(indoc! {"
10885        fn func(abc ˇdef: i32) -> u32 {
10886        }
10887    "});
10888
10889    // Fourth diagnostic
10890    cx.update_editor(|editor, window, cx| {
10891        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10892    });
10893    cx.assert_editor_state(indoc! {"
10894        fn func(abc def: i32) -> ˇu32 {
10895        }
10896    "});
10897
10898    // Wrapped around, first diagnostic
10899    cx.update_editor(|editor, window, cx| {
10900        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10901    });
10902    cx.assert_editor_state(indoc! {"
10903        fn func(abcˇ def: i32) -> u32 {
10904        }
10905    "});
10906}
10907
10908#[gpui::test]
10909async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10910    init_test(cx, |_| {});
10911
10912    let mut cx = EditorTestContext::new(cx).await;
10913
10914    cx.set_state(indoc! {"
10915        fn func(abˇc def: i32) -> u32 {
10916        }
10917    "});
10918    let lsp_store =
10919        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10920
10921    cx.update(|_, cx| {
10922        lsp_store.update(cx, |lsp_store, cx| {
10923            lsp_store.update_diagnostics(
10924                LanguageServerId(0),
10925                lsp::PublishDiagnosticsParams {
10926                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10927                    version: None,
10928                    diagnostics: vec![lsp::Diagnostic {
10929                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10930                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10931                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10932                        ..Default::default()
10933                    }],
10934                },
10935                &[],
10936                cx,
10937            )
10938        })
10939    }).unwrap();
10940    cx.run_until_parked();
10941    cx.update_editor(|editor, window, cx| {
10942        hover_popover::hover(editor, &Default::default(), window, cx)
10943    });
10944    cx.run_until_parked();
10945    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10946}
10947
10948#[gpui::test]
10949async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10950    init_test(cx, |_| {});
10951
10952    let mut cx = EditorTestContext::new(cx).await;
10953
10954    let diff_base = r#"
10955        use some::mod;
10956
10957        const A: u32 = 42;
10958
10959        fn main() {
10960            println!("hello");
10961
10962            println!("world");
10963        }
10964        "#
10965    .unindent();
10966
10967    // Edits are modified, removed, modified, added
10968    cx.set_state(
10969        &r#"
10970        use some::modified;
10971
10972        ˇ
10973        fn main() {
10974            println!("hello there");
10975
10976            println!("around the");
10977            println!("world");
10978        }
10979        "#
10980        .unindent(),
10981    );
10982
10983    cx.set_diff_base(&diff_base);
10984    executor.run_until_parked();
10985
10986    cx.update_editor(|editor, window, cx| {
10987        //Wrap around the bottom of the buffer
10988        for _ in 0..3 {
10989            editor.go_to_next_hunk(&GoToHunk, window, cx);
10990        }
10991    });
10992
10993    cx.assert_editor_state(
10994        &r#"
10995        ˇuse some::modified;
10996
10997
10998        fn main() {
10999            println!("hello there");
11000
11001            println!("around the");
11002            println!("world");
11003        }
11004        "#
11005        .unindent(),
11006    );
11007
11008    cx.update_editor(|editor, window, cx| {
11009        //Wrap around the top of the buffer
11010        for _ in 0..2 {
11011            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11012        }
11013    });
11014
11015    cx.assert_editor_state(
11016        &r#"
11017        use some::modified;
11018
11019
11020        fn main() {
11021        ˇ    println!("hello there");
11022
11023            println!("around the");
11024            println!("world");
11025        }
11026        "#
11027        .unindent(),
11028    );
11029
11030    cx.update_editor(|editor, window, cx| {
11031        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11032    });
11033
11034    cx.assert_editor_state(
11035        &r#"
11036        use some::modified;
11037
11038        ˇ
11039        fn main() {
11040            println!("hello there");
11041
11042            println!("around the");
11043            println!("world");
11044        }
11045        "#
11046        .unindent(),
11047    );
11048
11049    cx.update_editor(|editor, window, cx| {
11050        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11051    });
11052
11053    cx.assert_editor_state(
11054        &r#"
11055        ˇuse some::modified;
11056
11057
11058        fn main() {
11059            println!("hello there");
11060
11061            println!("around the");
11062            println!("world");
11063        }
11064        "#
11065        .unindent(),
11066    );
11067
11068    cx.update_editor(|editor, window, cx| {
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.fold(&Fold, window, cx);
11091    });
11092
11093    cx.update_editor(|editor, window, cx| {
11094        editor.go_to_next_hunk(&GoToHunk, window, cx);
11095    });
11096
11097    cx.assert_editor_state(
11098        &r#"
11099        ˇuse some::modified;
11100
11101
11102        fn main() {
11103            println!("hello there");
11104
11105            println!("around the");
11106            println!("world");
11107        }
11108        "#
11109        .unindent(),
11110    );
11111}
11112
11113#[test]
11114fn test_split_words() {
11115    fn split(text: &str) -> Vec<&str> {
11116        split_words(text).collect()
11117    }
11118
11119    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11120    assert_eq!(split("hello_world"), &["hello_", "world"]);
11121    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11122    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11123    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11124    assert_eq!(split("helloworld"), &["helloworld"]);
11125
11126    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11127}
11128
11129#[gpui::test]
11130async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
11131    init_test(cx, |_| {});
11132
11133    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11134    let mut assert = |before, after| {
11135        let _state_context = cx.set_state(before);
11136        cx.run_until_parked();
11137        cx.update_editor(|editor, window, cx| {
11138            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11139        });
11140        cx.assert_editor_state(after);
11141    };
11142
11143    // Outside bracket jumps to outside of matching bracket
11144    assert("console.logˇ(var);", "console.log(var)ˇ;");
11145    assert("console.log(var)ˇ;", "console.logˇ(var);");
11146
11147    // Inside bracket jumps to inside of matching bracket
11148    assert("console.log(ˇvar);", "console.log(varˇ);");
11149    assert("console.log(varˇ);", "console.log(ˇvar);");
11150
11151    // When outside a bracket and inside, favor jumping to the inside bracket
11152    assert(
11153        "console.log('foo', [1, 2, 3]ˇ);",
11154        "console.log(ˇ'foo', [1, 2, 3]);",
11155    );
11156    assert(
11157        "console.log(ˇ'foo', [1, 2, 3]);",
11158        "console.log('foo', [1, 2, 3]ˇ);",
11159    );
11160
11161    // Bias forward if two options are equally likely
11162    assert(
11163        "let result = curried_fun()ˇ();",
11164        "let result = curried_fun()()ˇ;",
11165    );
11166
11167    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11168    assert(
11169        indoc! {"
11170            function test() {
11171                console.log('test')ˇ
11172            }"},
11173        indoc! {"
11174            function test() {
11175                console.logˇ('test')
11176            }"},
11177    );
11178}
11179
11180#[gpui::test]
11181async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
11182    init_test(cx, |_| {});
11183
11184    let fs = FakeFs::new(cx.executor());
11185    fs.insert_tree(
11186        path!("/a"),
11187        json!({
11188            "main.rs": "fn main() { let a = 5; }",
11189            "other.rs": "// Test file",
11190        }),
11191    )
11192    .await;
11193    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11194
11195    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11196    language_registry.add(Arc::new(Language::new(
11197        LanguageConfig {
11198            name: "Rust".into(),
11199            matcher: LanguageMatcher {
11200                path_suffixes: vec!["rs".to_string()],
11201                ..Default::default()
11202            },
11203            brackets: BracketPairConfig {
11204                pairs: vec![BracketPair {
11205                    start: "{".to_string(),
11206                    end: "}".to_string(),
11207                    close: true,
11208                    surround: true,
11209                    newline: true,
11210                }],
11211                disabled_scopes_by_bracket_ix: Vec::new(),
11212            },
11213            ..Default::default()
11214        },
11215        Some(tree_sitter_rust::LANGUAGE.into()),
11216    )));
11217    let mut fake_servers = language_registry.register_fake_lsp(
11218        "Rust",
11219        FakeLspAdapter {
11220            capabilities: lsp::ServerCapabilities {
11221                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11222                    first_trigger_character: "{".to_string(),
11223                    more_trigger_character: None,
11224                }),
11225                ..Default::default()
11226            },
11227            ..Default::default()
11228        },
11229    );
11230
11231    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11232
11233    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11234
11235    let worktree_id = workspace
11236        .update(cx, |workspace, _, cx| {
11237            workspace.project().update(cx, |project, cx| {
11238                project.worktrees(cx).next().unwrap().read(cx).id()
11239            })
11240        })
11241        .unwrap();
11242
11243    let buffer = project
11244        .update(cx, |project, cx| {
11245            project.open_local_buffer(path!("/a/main.rs"), cx)
11246        })
11247        .await
11248        .unwrap();
11249    let editor_handle = workspace
11250        .update(cx, |workspace, window, cx| {
11251            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11252        })
11253        .unwrap()
11254        .await
11255        .unwrap()
11256        .downcast::<Editor>()
11257        .unwrap();
11258
11259    cx.executor().start_waiting();
11260    let fake_server = fake_servers.next().await.unwrap();
11261
11262    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11263        assert_eq!(
11264            params.text_document_position.text_document.uri,
11265            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11266        );
11267        assert_eq!(
11268            params.text_document_position.position,
11269            lsp::Position::new(0, 21),
11270        );
11271
11272        Ok(Some(vec![lsp::TextEdit {
11273            new_text: "]".to_string(),
11274            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11275        }]))
11276    });
11277
11278    editor_handle.update_in(cx, |editor, window, cx| {
11279        window.focus(&editor.focus_handle(cx));
11280        editor.change_selections(None, window, cx, |s| {
11281            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11282        });
11283        editor.handle_input("{", window, cx);
11284    });
11285
11286    cx.executor().run_until_parked();
11287
11288    buffer.update(cx, |buffer, _| {
11289        assert_eq!(
11290            buffer.text(),
11291            "fn main() { let a = {5}; }",
11292            "No extra braces from on type formatting should appear in the buffer"
11293        )
11294    });
11295}
11296
11297#[gpui::test]
11298async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11299    init_test(cx, |_| {});
11300
11301    let fs = FakeFs::new(cx.executor());
11302    fs.insert_tree(
11303        path!("/a"),
11304        json!({
11305            "main.rs": "fn main() { let a = 5; }",
11306            "other.rs": "// Test file",
11307        }),
11308    )
11309    .await;
11310
11311    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11312
11313    let server_restarts = Arc::new(AtomicUsize::new(0));
11314    let closure_restarts = Arc::clone(&server_restarts);
11315    let language_server_name = "test language server";
11316    let language_name: LanguageName = "Rust".into();
11317
11318    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11319    language_registry.add(Arc::new(Language::new(
11320        LanguageConfig {
11321            name: language_name.clone(),
11322            matcher: LanguageMatcher {
11323                path_suffixes: vec!["rs".to_string()],
11324                ..Default::default()
11325            },
11326            ..Default::default()
11327        },
11328        Some(tree_sitter_rust::LANGUAGE.into()),
11329    )));
11330    let mut fake_servers = language_registry.register_fake_lsp(
11331        "Rust",
11332        FakeLspAdapter {
11333            name: language_server_name,
11334            initialization_options: Some(json!({
11335                "testOptionValue": true
11336            })),
11337            initializer: Some(Box::new(move |fake_server| {
11338                let task_restarts = Arc::clone(&closure_restarts);
11339                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11340                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11341                    futures::future::ready(Ok(()))
11342                });
11343            })),
11344            ..Default::default()
11345        },
11346    );
11347
11348    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11349    let _buffer = project
11350        .update(cx, |project, cx| {
11351            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11352        })
11353        .await
11354        .unwrap();
11355    let _fake_server = fake_servers.next().await.unwrap();
11356    update_test_language_settings(cx, |language_settings| {
11357        language_settings.languages.insert(
11358            language_name.clone(),
11359            LanguageSettingsContent {
11360                tab_size: NonZeroU32::new(8),
11361                ..Default::default()
11362            },
11363        );
11364    });
11365    cx.executor().run_until_parked();
11366    assert_eq!(
11367        server_restarts.load(atomic::Ordering::Acquire),
11368        0,
11369        "Should not restart LSP server on an unrelated change"
11370    );
11371
11372    update_test_project_settings(cx, |project_settings| {
11373        project_settings.lsp.insert(
11374            "Some other server name".into(),
11375            LspSettings {
11376                binary: None,
11377                settings: None,
11378                initialization_options: Some(json!({
11379                    "some other init value": false
11380                })),
11381            },
11382        );
11383    });
11384    cx.executor().run_until_parked();
11385    assert_eq!(
11386        server_restarts.load(atomic::Ordering::Acquire),
11387        0,
11388        "Should not restart LSP server on an unrelated LSP settings change"
11389    );
11390
11391    update_test_project_settings(cx, |project_settings| {
11392        project_settings.lsp.insert(
11393            language_server_name.into(),
11394            LspSettings {
11395                binary: None,
11396                settings: None,
11397                initialization_options: Some(json!({
11398                    "anotherInitValue": false
11399                })),
11400            },
11401        );
11402    });
11403    cx.executor().run_until_parked();
11404    assert_eq!(
11405        server_restarts.load(atomic::Ordering::Acquire),
11406        1,
11407        "Should restart LSP server on a related LSP settings change"
11408    );
11409
11410    update_test_project_settings(cx, |project_settings| {
11411        project_settings.lsp.insert(
11412            language_server_name.into(),
11413            LspSettings {
11414                binary: None,
11415                settings: None,
11416                initialization_options: Some(json!({
11417                    "anotherInitValue": false
11418                })),
11419            },
11420        );
11421    });
11422    cx.executor().run_until_parked();
11423    assert_eq!(
11424        server_restarts.load(atomic::Ordering::Acquire),
11425        1,
11426        "Should not restart LSP server on a related LSP settings change that is the same"
11427    );
11428
11429    update_test_project_settings(cx, |project_settings| {
11430        project_settings.lsp.insert(
11431            language_server_name.into(),
11432            LspSettings {
11433                binary: None,
11434                settings: None,
11435                initialization_options: None,
11436            },
11437        );
11438    });
11439    cx.executor().run_until_parked();
11440    assert_eq!(
11441        server_restarts.load(atomic::Ordering::Acquire),
11442        2,
11443        "Should restart LSP server on another related LSP settings change"
11444    );
11445}
11446
11447#[gpui::test]
11448async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11449    init_test(cx, |_| {});
11450
11451    let mut cx = EditorLspTestContext::new_rust(
11452        lsp::ServerCapabilities {
11453            completion_provider: Some(lsp::CompletionOptions {
11454                trigger_characters: Some(vec![".".to_string()]),
11455                resolve_provider: Some(true),
11456                ..Default::default()
11457            }),
11458            ..Default::default()
11459        },
11460        cx,
11461    )
11462    .await;
11463
11464    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11465    cx.simulate_keystroke(".");
11466    let completion_item = lsp::CompletionItem {
11467        label: "some".into(),
11468        kind: Some(lsp::CompletionItemKind::SNIPPET),
11469        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11470        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11471            kind: lsp::MarkupKind::Markdown,
11472            value: "```rust\nSome(2)\n```".to_string(),
11473        })),
11474        deprecated: Some(false),
11475        sort_text: Some("fffffff2".to_string()),
11476        filter_text: Some("some".to_string()),
11477        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11478        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11479            range: lsp::Range {
11480                start: lsp::Position {
11481                    line: 0,
11482                    character: 22,
11483                },
11484                end: lsp::Position {
11485                    line: 0,
11486                    character: 22,
11487                },
11488            },
11489            new_text: "Some(2)".to_string(),
11490        })),
11491        additional_text_edits: Some(vec![lsp::TextEdit {
11492            range: lsp::Range {
11493                start: lsp::Position {
11494                    line: 0,
11495                    character: 20,
11496                },
11497                end: lsp::Position {
11498                    line: 0,
11499                    character: 22,
11500                },
11501            },
11502            new_text: "".to_string(),
11503        }]),
11504        ..Default::default()
11505    };
11506
11507    let closure_completion_item = completion_item.clone();
11508    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11509        let task_completion_item = closure_completion_item.clone();
11510        async move {
11511            Ok(Some(lsp::CompletionResponse::Array(vec![
11512                task_completion_item,
11513            ])))
11514        }
11515    });
11516
11517    request.next().await;
11518
11519    cx.condition(|editor, _| editor.context_menu_visible())
11520        .await;
11521    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11522        editor
11523            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11524            .unwrap()
11525    });
11526    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11527
11528    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11529        let task_completion_item = completion_item.clone();
11530        async move { Ok(task_completion_item) }
11531    })
11532    .next()
11533    .await
11534    .unwrap();
11535    apply_additional_edits.await.unwrap();
11536    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11537}
11538
11539#[gpui::test]
11540async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11541    cx: &mut gpui::TestAppContext,
11542) {
11543    init_test(cx, |_| {});
11544
11545    let mut cx = EditorLspTestContext::new_rust(
11546        lsp::ServerCapabilities {
11547            completion_provider: Some(lsp::CompletionOptions {
11548                trigger_characters: Some(vec![".".to_string()]),
11549                resolve_provider: Some(true),
11550                ..Default::default()
11551            }),
11552            ..Default::default()
11553        },
11554        cx,
11555    )
11556    .await;
11557
11558    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11559    cx.simulate_keystroke(".");
11560
11561    let item1 = lsp::CompletionItem {
11562        label: "method id()".to_string(),
11563        filter_text: Some("id".to_string()),
11564        detail: None,
11565        documentation: None,
11566        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11567            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11568            new_text: ".id".to_string(),
11569        })),
11570        ..lsp::CompletionItem::default()
11571    };
11572
11573    let item2 = lsp::CompletionItem {
11574        label: "other".to_string(),
11575        filter_text: Some("other".to_string()),
11576        detail: None,
11577        documentation: None,
11578        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11579            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11580            new_text: ".other".to_string(),
11581        })),
11582        ..lsp::CompletionItem::default()
11583    };
11584
11585    let item1 = item1.clone();
11586    cx.handle_request::<lsp::request::Completion, _, _>({
11587        let item1 = item1.clone();
11588        move |_, _, _| {
11589            let item1 = item1.clone();
11590            let item2 = item2.clone();
11591            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11592        }
11593    })
11594    .next()
11595    .await;
11596
11597    cx.condition(|editor, _| editor.context_menu_visible())
11598        .await;
11599    cx.update_editor(|editor, _, _| {
11600        let context_menu = editor.context_menu.borrow_mut();
11601        let context_menu = context_menu
11602            .as_ref()
11603            .expect("Should have the context menu deployed");
11604        match context_menu {
11605            CodeContextMenu::Completions(completions_menu) => {
11606                let completions = completions_menu.completions.borrow_mut();
11607                assert_eq!(
11608                    completions
11609                        .iter()
11610                        .map(|completion| &completion.label.text)
11611                        .collect::<Vec<_>>(),
11612                    vec!["method id()", "other"]
11613                )
11614            }
11615            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11616        }
11617    });
11618
11619    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11620        let item1 = item1.clone();
11621        move |_, item_to_resolve, _| {
11622            let item1 = item1.clone();
11623            async move {
11624                if item1 == item_to_resolve {
11625                    Ok(lsp::CompletionItem {
11626                        label: "method id()".to_string(),
11627                        filter_text: Some("id".to_string()),
11628                        detail: Some("Now resolved!".to_string()),
11629                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11630                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11631                            range: lsp::Range::new(
11632                                lsp::Position::new(0, 22),
11633                                lsp::Position::new(0, 22),
11634                            ),
11635                            new_text: ".id".to_string(),
11636                        })),
11637                        ..lsp::CompletionItem::default()
11638                    })
11639                } else {
11640                    Ok(item_to_resolve)
11641                }
11642            }
11643        }
11644    })
11645    .next()
11646    .await
11647    .unwrap();
11648    cx.run_until_parked();
11649
11650    cx.update_editor(|editor, window, cx| {
11651        editor.context_menu_next(&Default::default(), window, cx);
11652    });
11653
11654    cx.update_editor(|editor, _, _| {
11655        let context_menu = editor.context_menu.borrow_mut();
11656        let context_menu = context_menu
11657            .as_ref()
11658            .expect("Should have the context menu deployed");
11659        match context_menu {
11660            CodeContextMenu::Completions(completions_menu) => {
11661                let completions = completions_menu.completions.borrow_mut();
11662                assert_eq!(
11663                    completions
11664                        .iter()
11665                        .map(|completion| &completion.label.text)
11666                        .collect::<Vec<_>>(),
11667                    vec!["method id() Now resolved!", "other"],
11668                    "Should update first completion label, but not second as the filter text did not match."
11669                );
11670            }
11671            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11672        }
11673    });
11674}
11675
11676#[gpui::test]
11677async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11678    init_test(cx, |_| {});
11679
11680    let mut cx = EditorLspTestContext::new_rust(
11681        lsp::ServerCapabilities {
11682            completion_provider: Some(lsp::CompletionOptions {
11683                trigger_characters: Some(vec![".".to_string()]),
11684                resolve_provider: Some(true),
11685                ..Default::default()
11686            }),
11687            ..Default::default()
11688        },
11689        cx,
11690    )
11691    .await;
11692
11693    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11694    cx.simulate_keystroke(".");
11695
11696    let unresolved_item_1 = lsp::CompletionItem {
11697        label: "id".to_string(),
11698        filter_text: Some("id".to_string()),
11699        detail: None,
11700        documentation: None,
11701        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11702            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11703            new_text: ".id".to_string(),
11704        })),
11705        ..lsp::CompletionItem::default()
11706    };
11707    let resolved_item_1 = lsp::CompletionItem {
11708        additional_text_edits: Some(vec![lsp::TextEdit {
11709            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11710            new_text: "!!".to_string(),
11711        }]),
11712        ..unresolved_item_1.clone()
11713    };
11714    let unresolved_item_2 = lsp::CompletionItem {
11715        label: "other".to_string(),
11716        filter_text: Some("other".to_string()),
11717        detail: None,
11718        documentation: None,
11719        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11720            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11721            new_text: ".other".to_string(),
11722        })),
11723        ..lsp::CompletionItem::default()
11724    };
11725    let resolved_item_2 = lsp::CompletionItem {
11726        additional_text_edits: Some(vec![lsp::TextEdit {
11727            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11728            new_text: "??".to_string(),
11729        }]),
11730        ..unresolved_item_2.clone()
11731    };
11732
11733    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11734    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11735    cx.lsp
11736        .server
11737        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11738            let unresolved_item_1 = unresolved_item_1.clone();
11739            let resolved_item_1 = resolved_item_1.clone();
11740            let unresolved_item_2 = unresolved_item_2.clone();
11741            let resolved_item_2 = resolved_item_2.clone();
11742            let resolve_requests_1 = resolve_requests_1.clone();
11743            let resolve_requests_2 = resolve_requests_2.clone();
11744            move |unresolved_request, _| {
11745                let unresolved_item_1 = unresolved_item_1.clone();
11746                let resolved_item_1 = resolved_item_1.clone();
11747                let unresolved_item_2 = unresolved_item_2.clone();
11748                let resolved_item_2 = resolved_item_2.clone();
11749                let resolve_requests_1 = resolve_requests_1.clone();
11750                let resolve_requests_2 = resolve_requests_2.clone();
11751                async move {
11752                    if unresolved_request == unresolved_item_1 {
11753                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11754                        Ok(resolved_item_1.clone())
11755                    } else if unresolved_request == unresolved_item_2 {
11756                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11757                        Ok(resolved_item_2.clone())
11758                    } else {
11759                        panic!("Unexpected completion item {unresolved_request:?}")
11760                    }
11761                }
11762            }
11763        })
11764        .detach();
11765
11766    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11767        let unresolved_item_1 = unresolved_item_1.clone();
11768        let unresolved_item_2 = unresolved_item_2.clone();
11769        async move {
11770            Ok(Some(lsp::CompletionResponse::Array(vec![
11771                unresolved_item_1,
11772                unresolved_item_2,
11773            ])))
11774        }
11775    })
11776    .next()
11777    .await;
11778
11779    cx.condition(|editor, _| editor.context_menu_visible())
11780        .await;
11781    cx.update_editor(|editor, _, _| {
11782        let context_menu = editor.context_menu.borrow_mut();
11783        let context_menu = context_menu
11784            .as_ref()
11785            .expect("Should have the context menu deployed");
11786        match context_menu {
11787            CodeContextMenu::Completions(completions_menu) => {
11788                let completions = completions_menu.completions.borrow_mut();
11789                assert_eq!(
11790                    completions
11791                        .iter()
11792                        .map(|completion| &completion.label.text)
11793                        .collect::<Vec<_>>(),
11794                    vec!["id", "other"]
11795                )
11796            }
11797            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11798        }
11799    });
11800    cx.run_until_parked();
11801
11802    cx.update_editor(|editor, window, cx| {
11803        editor.context_menu_next(&ContextMenuNext, window, cx);
11804    });
11805    cx.run_until_parked();
11806    cx.update_editor(|editor, window, cx| {
11807        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11808    });
11809    cx.run_until_parked();
11810    cx.update_editor(|editor, window, cx| {
11811        editor.context_menu_next(&ContextMenuNext, window, cx);
11812    });
11813    cx.run_until_parked();
11814    cx.update_editor(|editor, window, cx| {
11815        editor
11816            .compose_completion(&ComposeCompletion::default(), window, cx)
11817            .expect("No task returned")
11818    })
11819    .await
11820    .expect("Completion failed");
11821    cx.run_until_parked();
11822
11823    cx.update_editor(|editor, _, cx| {
11824        assert_eq!(
11825            resolve_requests_1.load(atomic::Ordering::Acquire),
11826            1,
11827            "Should always resolve once despite multiple selections"
11828        );
11829        assert_eq!(
11830            resolve_requests_2.load(atomic::Ordering::Acquire),
11831            1,
11832            "Should always resolve once after multiple selections and applying the completion"
11833        );
11834        assert_eq!(
11835            editor.text(cx),
11836            "fn main() { let a = ??.other; }",
11837            "Should use resolved data when applying the completion"
11838        );
11839    });
11840}
11841
11842#[gpui::test]
11843async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11844    init_test(cx, |_| {});
11845
11846    let item_0 = lsp::CompletionItem {
11847        label: "abs".into(),
11848        insert_text: Some("abs".into()),
11849        data: Some(json!({ "very": "special"})),
11850        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11851        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11852            lsp::InsertReplaceEdit {
11853                new_text: "abs".to_string(),
11854                insert: lsp::Range::default(),
11855                replace: lsp::Range::default(),
11856            },
11857        )),
11858        ..lsp::CompletionItem::default()
11859    };
11860    let items = iter::once(item_0.clone())
11861        .chain((11..51).map(|i| lsp::CompletionItem {
11862            label: format!("item_{}", i),
11863            insert_text: Some(format!("item_{}", i)),
11864            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11865            ..lsp::CompletionItem::default()
11866        }))
11867        .collect::<Vec<_>>();
11868
11869    let default_commit_characters = vec!["?".to_string()];
11870    let default_data = json!({ "default": "data"});
11871    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11872    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11873    let default_edit_range = lsp::Range {
11874        start: lsp::Position {
11875            line: 0,
11876            character: 5,
11877        },
11878        end: lsp::Position {
11879            line: 0,
11880            character: 5,
11881        },
11882    };
11883
11884    let item_0_out = lsp::CompletionItem {
11885        commit_characters: Some(default_commit_characters.clone()),
11886        insert_text_format: Some(default_insert_text_format),
11887        ..item_0
11888    };
11889    let items_out = iter::once(item_0_out)
11890        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11891            commit_characters: Some(default_commit_characters.clone()),
11892            data: Some(default_data.clone()),
11893            insert_text_mode: Some(default_insert_text_mode),
11894            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11895                range: default_edit_range,
11896                new_text: item.label.clone(),
11897            })),
11898            ..item.clone()
11899        }))
11900        .collect::<Vec<lsp::CompletionItem>>();
11901
11902    let mut cx = EditorLspTestContext::new_rust(
11903        lsp::ServerCapabilities {
11904            completion_provider: Some(lsp::CompletionOptions {
11905                trigger_characters: Some(vec![".".to_string()]),
11906                resolve_provider: Some(true),
11907                ..Default::default()
11908            }),
11909            ..Default::default()
11910        },
11911        cx,
11912    )
11913    .await;
11914
11915    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11916    cx.simulate_keystroke(".");
11917
11918    let completion_data = default_data.clone();
11919    let completion_characters = default_commit_characters.clone();
11920    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11921        let default_data = completion_data.clone();
11922        let default_commit_characters = completion_characters.clone();
11923        let items = items.clone();
11924        async move {
11925            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11926                items,
11927                item_defaults: Some(lsp::CompletionListItemDefaults {
11928                    data: Some(default_data.clone()),
11929                    commit_characters: Some(default_commit_characters.clone()),
11930                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11931                        default_edit_range,
11932                    )),
11933                    insert_text_format: Some(default_insert_text_format),
11934                    insert_text_mode: Some(default_insert_text_mode),
11935                }),
11936                ..lsp::CompletionList::default()
11937            })))
11938        }
11939    })
11940    .next()
11941    .await;
11942
11943    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11944    cx.lsp
11945        .server
11946        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11947            let closure_resolved_items = resolved_items.clone();
11948            move |item_to_resolve, _| {
11949                let closure_resolved_items = closure_resolved_items.clone();
11950                async move {
11951                    closure_resolved_items.lock().push(item_to_resolve.clone());
11952                    Ok(item_to_resolve)
11953                }
11954            }
11955        })
11956        .detach();
11957
11958    cx.condition(|editor, _| editor.context_menu_visible())
11959        .await;
11960    cx.run_until_parked();
11961    cx.update_editor(|editor, _, _| {
11962        let menu = editor.context_menu.borrow_mut();
11963        match menu.as_ref().expect("should have the completions menu") {
11964            CodeContextMenu::Completions(completions_menu) => {
11965                assert_eq!(
11966                    completions_menu
11967                        .entries
11968                        .borrow()
11969                        .iter()
11970                        .map(|mat| mat.string.clone())
11971                        .collect::<Vec<String>>(),
11972                    items_out
11973                        .iter()
11974                        .map(|completion| completion.label.clone())
11975                        .collect::<Vec<String>>()
11976                );
11977            }
11978            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11979        }
11980    });
11981    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11982    // with 4 from the end.
11983    assert_eq!(
11984        *resolved_items.lock(),
11985        [
11986            &items_out[0..16],
11987            &items_out[items_out.len() - 4..items_out.len()]
11988        ]
11989        .concat()
11990        .iter()
11991        .cloned()
11992        .collect::<Vec<lsp::CompletionItem>>()
11993    );
11994    resolved_items.lock().clear();
11995
11996    cx.update_editor(|editor, window, cx| {
11997        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11998    });
11999    cx.run_until_parked();
12000    // Completions that have already been resolved are skipped.
12001    assert_eq!(
12002        *resolved_items.lock(),
12003        items_out[items_out.len() - 16..items_out.len() - 4]
12004            .iter()
12005            .cloned()
12006            .collect::<Vec<lsp::CompletionItem>>()
12007    );
12008    resolved_items.lock().clear();
12009}
12010
12011#[gpui::test]
12012async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
12013    init_test(cx, |_| {});
12014
12015    let mut cx = EditorLspTestContext::new(
12016        Language::new(
12017            LanguageConfig {
12018                matcher: LanguageMatcher {
12019                    path_suffixes: vec!["jsx".into()],
12020                    ..Default::default()
12021                },
12022                overrides: [(
12023                    "element".into(),
12024                    LanguageConfigOverride {
12025                        word_characters: Override::Set(['-'].into_iter().collect()),
12026                        ..Default::default()
12027                    },
12028                )]
12029                .into_iter()
12030                .collect(),
12031                ..Default::default()
12032            },
12033            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12034        )
12035        .with_override_query("(jsx_self_closing_element) @element")
12036        .unwrap(),
12037        lsp::ServerCapabilities {
12038            completion_provider: Some(lsp::CompletionOptions {
12039                trigger_characters: Some(vec![":".to_string()]),
12040                ..Default::default()
12041            }),
12042            ..Default::default()
12043        },
12044        cx,
12045    )
12046    .await;
12047
12048    cx.lsp
12049        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12050            Ok(Some(lsp::CompletionResponse::Array(vec![
12051                lsp::CompletionItem {
12052                    label: "bg-blue".into(),
12053                    ..Default::default()
12054                },
12055                lsp::CompletionItem {
12056                    label: "bg-red".into(),
12057                    ..Default::default()
12058                },
12059                lsp::CompletionItem {
12060                    label: "bg-yellow".into(),
12061                    ..Default::default()
12062                },
12063            ])))
12064        });
12065
12066    cx.set_state(r#"<p class="bgˇ" />"#);
12067
12068    // Trigger completion when typing a dash, because the dash is an extra
12069    // word character in the 'element' scope, which contains the cursor.
12070    cx.simulate_keystroke("-");
12071    cx.executor().run_until_parked();
12072    cx.update_editor(|editor, _, _| {
12073        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12074        {
12075            assert_eq!(
12076                completion_menu_entries(&menu),
12077                &["bg-red", "bg-blue", "bg-yellow"]
12078            );
12079        } else {
12080            panic!("expected completion menu to be open");
12081        }
12082    });
12083
12084    cx.simulate_keystroke("l");
12085    cx.executor().run_until_parked();
12086    cx.update_editor(|editor, _, _| {
12087        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12088        {
12089            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12090        } else {
12091            panic!("expected completion menu to be open");
12092        }
12093    });
12094
12095    // When filtering completions, consider the character after the '-' to
12096    // be the start of a subword.
12097    cx.set_state(r#"<p class="yelˇ" />"#);
12098    cx.simulate_keystroke("l");
12099    cx.executor().run_until_parked();
12100    cx.update_editor(|editor, _, _| {
12101        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12102        {
12103            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12104        } else {
12105            panic!("expected completion menu to be open");
12106        }
12107    });
12108}
12109
12110fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12111    let entries = menu.entries.borrow();
12112    entries.iter().map(|mat| mat.string.clone()).collect()
12113}
12114
12115#[gpui::test]
12116async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
12117    init_test(cx, |settings| {
12118        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12119            FormatterList(vec![Formatter::Prettier].into()),
12120        ))
12121    });
12122
12123    let fs = FakeFs::new(cx.executor());
12124    fs.insert_file(path!("/file.ts"), Default::default()).await;
12125
12126    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12127    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12128
12129    language_registry.add(Arc::new(Language::new(
12130        LanguageConfig {
12131            name: "TypeScript".into(),
12132            matcher: LanguageMatcher {
12133                path_suffixes: vec!["ts".to_string()],
12134                ..Default::default()
12135            },
12136            ..Default::default()
12137        },
12138        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12139    )));
12140    update_test_language_settings(cx, |settings| {
12141        settings.defaults.prettier = Some(PrettierSettings {
12142            allowed: true,
12143            ..PrettierSettings::default()
12144        });
12145    });
12146
12147    let test_plugin = "test_plugin";
12148    let _ = language_registry.register_fake_lsp(
12149        "TypeScript",
12150        FakeLspAdapter {
12151            prettier_plugins: vec![test_plugin],
12152            ..Default::default()
12153        },
12154    );
12155
12156    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12157    let buffer = project
12158        .update(cx, |project, cx| {
12159            project.open_local_buffer(path!("/file.ts"), cx)
12160        })
12161        .await
12162        .unwrap();
12163
12164    let buffer_text = "one\ntwo\nthree\n";
12165    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12166    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12167    editor.update_in(cx, |editor, window, cx| {
12168        editor.set_text(buffer_text, window, cx)
12169    });
12170
12171    editor
12172        .update_in(cx, |editor, window, cx| {
12173            editor.perform_format(
12174                project.clone(),
12175                FormatTrigger::Manual,
12176                FormatTarget::Buffers,
12177                window,
12178                cx,
12179            )
12180        })
12181        .unwrap()
12182        .await;
12183    assert_eq!(
12184        editor.update(cx, |editor, cx| editor.text(cx)),
12185        buffer_text.to_string() + prettier_format_suffix,
12186        "Test prettier formatting was not applied to the original buffer text",
12187    );
12188
12189    update_test_language_settings(cx, |settings| {
12190        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12191    });
12192    let format = editor.update_in(cx, |editor, window, cx| {
12193        editor.perform_format(
12194            project.clone(),
12195            FormatTrigger::Manual,
12196            FormatTarget::Buffers,
12197            window,
12198            cx,
12199        )
12200    });
12201    format.await.unwrap();
12202    assert_eq!(
12203        editor.update(cx, |editor, cx| editor.text(cx)),
12204        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12205        "Autoformatting (via test prettier) was not applied to the original buffer text",
12206    );
12207}
12208
12209#[gpui::test]
12210async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
12211    init_test(cx, |_| {});
12212    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12213    let base_text = indoc! {r#"
12214        struct Row;
12215        struct Row1;
12216        struct Row2;
12217
12218        struct Row4;
12219        struct Row5;
12220        struct Row6;
12221
12222        struct Row8;
12223        struct Row9;
12224        struct Row10;"#};
12225
12226    // When addition hunks are not adjacent to carets, no hunk revert is performed
12227    assert_hunk_revert(
12228        indoc! {r#"struct Row;
12229                   struct Row1;
12230                   struct Row1.1;
12231                   struct Row1.2;
12232                   struct Row2;ˇ
12233
12234                   struct Row4;
12235                   struct Row5;
12236                   struct Row6;
12237
12238                   struct Row8;
12239                   ˇstruct Row9;
12240                   struct Row9.1;
12241                   struct Row9.2;
12242                   struct Row9.3;
12243                   struct Row10;"#},
12244        vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
12245        indoc! {r#"struct Row;
12246                   struct Row1;
12247                   struct Row1.1;
12248                   struct Row1.2;
12249                   struct Row2;ˇ
12250
12251                   struct Row4;
12252                   struct Row5;
12253                   struct Row6;
12254
12255                   struct Row8;
12256                   ˇstruct Row9;
12257                   struct Row9.1;
12258                   struct Row9.2;
12259                   struct Row9.3;
12260                   struct Row10;"#},
12261        base_text,
12262        &mut cx,
12263    );
12264    // Same for selections
12265    assert_hunk_revert(
12266        indoc! {r#"struct Row;
12267                   struct Row1;
12268                   struct Row2;
12269                   struct Row2.1;
12270                   struct Row2.2;
12271                   «ˇ
12272                   struct Row4;
12273                   struct» Row5;
12274                   «struct Row6;
12275                   ˇ»
12276                   struct Row9.1;
12277                   struct Row9.2;
12278                   struct Row9.3;
12279                   struct Row8;
12280                   struct Row9;
12281                   struct Row10;"#},
12282        vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
12283        indoc! {r#"struct Row;
12284                   struct Row1;
12285                   struct Row2;
12286                   struct Row2.1;
12287                   struct Row2.2;
12288                   «ˇ
12289                   struct Row4;
12290                   struct» Row5;
12291                   «struct Row6;
12292                   ˇ»
12293                   struct Row9.1;
12294                   struct Row9.2;
12295                   struct Row9.3;
12296                   struct Row8;
12297                   struct Row9;
12298                   struct Row10;"#},
12299        base_text,
12300        &mut cx,
12301    );
12302
12303    // When carets and selections intersect the addition hunks, those are reverted.
12304    // Adjacent carets got merged.
12305    assert_hunk_revert(
12306        indoc! {r#"struct Row;
12307                   ˇ// something on the top
12308                   struct Row1;
12309                   struct Row2;
12310                   struct Roˇw3.1;
12311                   struct Row2.2;
12312                   struct Row2.3;ˇ
12313
12314                   struct Row4;
12315                   struct ˇRow5.1;
12316                   struct Row5.2;
12317                   struct «Rowˇ»5.3;
12318                   struct Row5;
12319                   struct Row6;
12320                   ˇ
12321                   struct Row9.1;
12322                   struct «Rowˇ»9.2;
12323                   struct «ˇRow»9.3;
12324                   struct Row8;
12325                   struct Row9;
12326                   «ˇ// something on bottom»
12327                   struct Row10;"#},
12328        vec![
12329            DiffHunkStatus::added(),
12330            DiffHunkStatus::added(),
12331            DiffHunkStatus::added(),
12332            DiffHunkStatus::added(),
12333            DiffHunkStatus::added(),
12334        ],
12335        indoc! {r#"struct Row;
12336                   ˇstruct Row1;
12337                   struct Row2;
12338                   ˇ
12339                   struct Row4;
12340                   ˇstruct Row5;
12341                   struct Row6;
12342                   ˇ
12343                   ˇstruct Row8;
12344                   struct Row9;
12345                   ˇstruct Row10;"#},
12346        base_text,
12347        &mut cx,
12348    );
12349}
12350
12351#[gpui::test]
12352async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12353    init_test(cx, |_| {});
12354    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12355    let base_text = indoc! {r#"
12356        struct Row;
12357        struct Row1;
12358        struct Row2;
12359
12360        struct Row4;
12361        struct Row5;
12362        struct Row6;
12363
12364        struct Row8;
12365        struct Row9;
12366        struct Row10;"#};
12367
12368    // Modification hunks behave the same as the addition ones.
12369    assert_hunk_revert(
12370        indoc! {r#"struct Row;
12371                   struct Row1;
12372                   struct Row33;
12373                   ˇ
12374                   struct Row4;
12375                   struct Row5;
12376                   struct Row6;
12377                   ˇ
12378                   struct Row99;
12379                   struct Row9;
12380                   struct Row10;"#},
12381        vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
12382        indoc! {r#"struct Row;
12383                   struct Row1;
12384                   struct Row33;
12385                   ˇ
12386                   struct Row4;
12387                   struct Row5;
12388                   struct Row6;
12389                   ˇ
12390                   struct Row99;
12391                   struct Row9;
12392                   struct Row10;"#},
12393        base_text,
12394        &mut cx,
12395    );
12396    assert_hunk_revert(
12397        indoc! {r#"struct Row;
12398                   struct Row1;
12399                   struct Row33;
12400                   «ˇ
12401                   struct Row4;
12402                   struct» Row5;
12403                   «struct Row6;
12404                   ˇ»
12405                   struct Row99;
12406                   struct Row9;
12407                   struct Row10;"#},
12408        vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
12409        indoc! {r#"struct Row;
12410                   struct Row1;
12411                   struct Row33;
12412                   «ˇ
12413                   struct Row4;
12414                   struct» Row5;
12415                   «struct Row6;
12416                   ˇ»
12417                   struct Row99;
12418                   struct Row9;
12419                   struct Row10;"#},
12420        base_text,
12421        &mut cx,
12422    );
12423
12424    assert_hunk_revert(
12425        indoc! {r#"ˇstruct Row1.1;
12426                   struct Row1;
12427                   «ˇstr»uct Row22;
12428
12429                   struct ˇRow44;
12430                   struct Row5;
12431                   struct «Rˇ»ow66;ˇ
12432
12433                   «struˇ»ct Row88;
12434                   struct Row9;
12435                   struct Row1011;ˇ"#},
12436        vec![
12437            DiffHunkStatus::modified(),
12438            DiffHunkStatus::modified(),
12439            DiffHunkStatus::modified(),
12440            DiffHunkStatus::modified(),
12441            DiffHunkStatus::modified(),
12442            DiffHunkStatus::modified(),
12443        ],
12444        indoc! {r#"struct Row;
12445                   ˇstruct Row1;
12446                   struct Row2;
12447                   ˇ
12448                   struct Row4;
12449                   ˇstruct Row5;
12450                   struct Row6;
12451                   ˇ
12452                   struct Row8;
12453                   ˇstruct Row9;
12454                   struct Row10;ˇ"#},
12455        base_text,
12456        &mut cx,
12457    );
12458}
12459
12460#[gpui::test]
12461async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12462    init_test(cx, |_| {});
12463    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12464    let base_text = indoc! {r#"
12465        one
12466
12467        two
12468        three
12469        "#};
12470
12471    cx.set_diff_base(base_text);
12472    cx.set_state("\nˇ\n");
12473    cx.executor().run_until_parked();
12474    cx.update_editor(|editor, _window, cx| {
12475        editor.expand_selected_diff_hunks(cx);
12476    });
12477    cx.executor().run_until_parked();
12478    cx.update_editor(|editor, window, cx| {
12479        editor.backspace(&Default::default(), window, cx);
12480    });
12481    cx.run_until_parked();
12482    cx.assert_state_with_diff(
12483        indoc! {r#"
12484
12485        - two
12486        - threeˇ
12487        +
12488        "#}
12489        .to_string(),
12490    );
12491}
12492
12493#[gpui::test]
12494async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12495    init_test(cx, |_| {});
12496    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12497    let base_text = indoc! {r#"struct Row;
12498struct Row1;
12499struct Row2;
12500
12501struct Row4;
12502struct Row5;
12503struct Row6;
12504
12505struct Row8;
12506struct Row9;
12507struct Row10;"#};
12508
12509    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12510    assert_hunk_revert(
12511        indoc! {r#"struct Row;
12512                   struct Row2;
12513
12514                   ˇstruct Row4;
12515                   struct Row5;
12516                   struct Row6;
12517                   ˇ
12518                   struct Row8;
12519                   struct Row10;"#},
12520        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12521        indoc! {r#"struct Row;
12522                   struct Row2;
12523
12524                   ˇstruct Row4;
12525                   struct Row5;
12526                   struct Row6;
12527                   ˇ
12528                   struct Row8;
12529                   struct Row10;"#},
12530        base_text,
12531        &mut cx,
12532    );
12533    assert_hunk_revert(
12534        indoc! {r#"struct Row;
12535                   struct Row2;
12536
12537                   «ˇstruct Row4;
12538                   struct» Row5;
12539                   «struct Row6;
12540                   ˇ»
12541                   struct Row8;
12542                   struct Row10;"#},
12543        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12544        indoc! {r#"struct Row;
12545                   struct Row2;
12546
12547                   «ˇstruct Row4;
12548                   struct» Row5;
12549                   «struct Row6;
12550                   ˇ»
12551                   struct Row8;
12552                   struct Row10;"#},
12553        base_text,
12554        &mut cx,
12555    );
12556
12557    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12558    assert_hunk_revert(
12559        indoc! {r#"struct Row;
12560                   ˇstruct Row2;
12561
12562                   struct Row4;
12563                   struct Row5;
12564                   struct Row6;
12565
12566                   struct Row8;ˇ
12567                   struct Row10;"#},
12568        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12569        indoc! {r#"struct Row;
12570                   struct Row1;
12571                   ˇstruct Row2;
12572
12573                   struct Row4;
12574                   struct Row5;
12575                   struct Row6;
12576
12577                   struct Row8;ˇ
12578                   struct Row9;
12579                   struct Row10;"#},
12580        base_text,
12581        &mut cx,
12582    );
12583    assert_hunk_revert(
12584        indoc! {r#"struct Row;
12585                   struct Row2«ˇ;
12586                   struct Row4;
12587                   struct» Row5;
12588                   «struct Row6;
12589
12590                   struct Row8;ˇ»
12591                   struct Row10;"#},
12592        vec![
12593            DiffHunkStatus::removed(),
12594            DiffHunkStatus::removed(),
12595            DiffHunkStatus::removed(),
12596        ],
12597        indoc! {r#"struct Row;
12598                   struct Row1;
12599                   struct Row2«ˇ;
12600
12601                   struct Row4;
12602                   struct» Row5;
12603                   «struct Row6;
12604
12605                   struct Row8;ˇ»
12606                   struct Row9;
12607                   struct Row10;"#},
12608        base_text,
12609        &mut cx,
12610    );
12611}
12612
12613#[gpui::test]
12614async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12615    init_test(cx, |_| {});
12616
12617    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12618    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12619    let base_text_3 =
12620        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12621
12622    let text_1 = edit_first_char_of_every_line(base_text_1);
12623    let text_2 = edit_first_char_of_every_line(base_text_2);
12624    let text_3 = edit_first_char_of_every_line(base_text_3);
12625
12626    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12627    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12628    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12629
12630    let multibuffer = cx.new(|cx| {
12631        let mut multibuffer = MultiBuffer::new(ReadWrite);
12632        multibuffer.push_excerpts(
12633            buffer_1.clone(),
12634            [
12635                ExcerptRange {
12636                    context: Point::new(0, 0)..Point::new(3, 0),
12637                    primary: None,
12638                },
12639                ExcerptRange {
12640                    context: Point::new(5, 0)..Point::new(7, 0),
12641                    primary: None,
12642                },
12643                ExcerptRange {
12644                    context: Point::new(9, 0)..Point::new(10, 4),
12645                    primary: None,
12646                },
12647            ],
12648            cx,
12649        );
12650        multibuffer.push_excerpts(
12651            buffer_2.clone(),
12652            [
12653                ExcerptRange {
12654                    context: Point::new(0, 0)..Point::new(3, 0),
12655                    primary: None,
12656                },
12657                ExcerptRange {
12658                    context: Point::new(5, 0)..Point::new(7, 0),
12659                    primary: None,
12660                },
12661                ExcerptRange {
12662                    context: Point::new(9, 0)..Point::new(10, 4),
12663                    primary: None,
12664                },
12665            ],
12666            cx,
12667        );
12668        multibuffer.push_excerpts(
12669            buffer_3.clone(),
12670            [
12671                ExcerptRange {
12672                    context: Point::new(0, 0)..Point::new(3, 0),
12673                    primary: None,
12674                },
12675                ExcerptRange {
12676                    context: Point::new(5, 0)..Point::new(7, 0),
12677                    primary: None,
12678                },
12679                ExcerptRange {
12680                    context: Point::new(9, 0)..Point::new(10, 4),
12681                    primary: None,
12682                },
12683            ],
12684            cx,
12685        );
12686        multibuffer
12687    });
12688
12689    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12690    editor.update_in(cx, |editor, _window, cx| {
12691        for (buffer, diff_base) in [
12692            (buffer_1.clone(), base_text_1),
12693            (buffer_2.clone(), base_text_2),
12694            (buffer_3.clone(), base_text_3),
12695        ] {
12696            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12697            editor
12698                .buffer
12699                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12700        }
12701    });
12702    cx.executor().run_until_parked();
12703
12704    editor.update_in(cx, |editor, window, cx| {
12705        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}");
12706        editor.select_all(&SelectAll, window, cx);
12707        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12708    });
12709    cx.executor().run_until_parked();
12710
12711    // When all ranges are selected, all buffer hunks are reverted.
12712    editor.update(cx, |editor, cx| {
12713        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");
12714    });
12715    buffer_1.update(cx, |buffer, _| {
12716        assert_eq!(buffer.text(), base_text_1);
12717    });
12718    buffer_2.update(cx, |buffer, _| {
12719        assert_eq!(buffer.text(), base_text_2);
12720    });
12721    buffer_3.update(cx, |buffer, _| {
12722        assert_eq!(buffer.text(), base_text_3);
12723    });
12724
12725    editor.update_in(cx, |editor, window, cx| {
12726        editor.undo(&Default::default(), window, cx);
12727    });
12728
12729    editor.update_in(cx, |editor, window, cx| {
12730        editor.change_selections(None, window, cx, |s| {
12731            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12732        });
12733        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12734    });
12735
12736    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12737    // but not affect buffer_2 and its related excerpts.
12738    editor.update(cx, |editor, cx| {
12739        assert_eq!(
12740            editor.text(cx),
12741            "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}"
12742        );
12743    });
12744    buffer_1.update(cx, |buffer, _| {
12745        assert_eq!(buffer.text(), base_text_1);
12746    });
12747    buffer_2.update(cx, |buffer, _| {
12748        assert_eq!(
12749            buffer.text(),
12750            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12751        );
12752    });
12753    buffer_3.update(cx, |buffer, _| {
12754        assert_eq!(
12755            buffer.text(),
12756            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12757        );
12758    });
12759
12760    fn edit_first_char_of_every_line(text: &str) -> String {
12761        text.split('\n')
12762            .map(|line| format!("X{}", &line[1..]))
12763            .collect::<Vec<_>>()
12764            .join("\n")
12765    }
12766}
12767
12768#[gpui::test]
12769async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12770    init_test(cx, |_| {});
12771
12772    let cols = 4;
12773    let rows = 10;
12774    let sample_text_1 = sample_text(rows, cols, 'a');
12775    assert_eq!(
12776        sample_text_1,
12777        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12778    );
12779    let sample_text_2 = sample_text(rows, cols, 'l');
12780    assert_eq!(
12781        sample_text_2,
12782        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12783    );
12784    let sample_text_3 = sample_text(rows, cols, 'v');
12785    assert_eq!(
12786        sample_text_3,
12787        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12788    );
12789
12790    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12791    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12792    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12793
12794    let multi_buffer = cx.new(|cx| {
12795        let mut multibuffer = MultiBuffer::new(ReadWrite);
12796        multibuffer.push_excerpts(
12797            buffer_1.clone(),
12798            [
12799                ExcerptRange {
12800                    context: Point::new(0, 0)..Point::new(3, 0),
12801                    primary: None,
12802                },
12803                ExcerptRange {
12804                    context: Point::new(5, 0)..Point::new(7, 0),
12805                    primary: None,
12806                },
12807                ExcerptRange {
12808                    context: Point::new(9, 0)..Point::new(10, 4),
12809                    primary: None,
12810                },
12811            ],
12812            cx,
12813        );
12814        multibuffer.push_excerpts(
12815            buffer_2.clone(),
12816            [
12817                ExcerptRange {
12818                    context: Point::new(0, 0)..Point::new(3, 0),
12819                    primary: None,
12820                },
12821                ExcerptRange {
12822                    context: Point::new(5, 0)..Point::new(7, 0),
12823                    primary: None,
12824                },
12825                ExcerptRange {
12826                    context: Point::new(9, 0)..Point::new(10, 4),
12827                    primary: None,
12828                },
12829            ],
12830            cx,
12831        );
12832        multibuffer.push_excerpts(
12833            buffer_3.clone(),
12834            [
12835                ExcerptRange {
12836                    context: Point::new(0, 0)..Point::new(3, 0),
12837                    primary: None,
12838                },
12839                ExcerptRange {
12840                    context: Point::new(5, 0)..Point::new(7, 0),
12841                    primary: None,
12842                },
12843                ExcerptRange {
12844                    context: Point::new(9, 0)..Point::new(10, 4),
12845                    primary: None,
12846                },
12847            ],
12848            cx,
12849        );
12850        multibuffer
12851    });
12852
12853    let fs = FakeFs::new(cx.executor());
12854    fs.insert_tree(
12855        "/a",
12856        json!({
12857            "main.rs": sample_text_1,
12858            "other.rs": sample_text_2,
12859            "lib.rs": sample_text_3,
12860        }),
12861    )
12862    .await;
12863    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12864    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12865    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12866    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12867        Editor::new(
12868            EditorMode::Full,
12869            multi_buffer,
12870            Some(project.clone()),
12871            true,
12872            window,
12873            cx,
12874        )
12875    });
12876    let multibuffer_item_id = workspace
12877        .update(cx, |workspace, window, cx| {
12878            assert!(
12879                workspace.active_item(cx).is_none(),
12880                "active item should be None before the first item is added"
12881            );
12882            workspace.add_item_to_active_pane(
12883                Box::new(multi_buffer_editor.clone()),
12884                None,
12885                true,
12886                window,
12887                cx,
12888            );
12889            let active_item = workspace
12890                .active_item(cx)
12891                .expect("should have an active item after adding the multi buffer");
12892            assert!(
12893                !active_item.is_singleton(cx),
12894                "A multi buffer was expected to active after adding"
12895            );
12896            active_item.item_id()
12897        })
12898        .unwrap();
12899    cx.executor().run_until_parked();
12900
12901    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12902        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12903            s.select_ranges(Some(1..2))
12904        });
12905        editor.open_excerpts(&OpenExcerpts, window, cx);
12906    });
12907    cx.executor().run_until_parked();
12908    let first_item_id = workspace
12909        .update(cx, |workspace, window, cx| {
12910            let active_item = workspace
12911                .active_item(cx)
12912                .expect("should have an active item after navigating into the 1st buffer");
12913            let first_item_id = active_item.item_id();
12914            assert_ne!(
12915                first_item_id, multibuffer_item_id,
12916                "Should navigate into the 1st buffer and activate it"
12917            );
12918            assert!(
12919                active_item.is_singleton(cx),
12920                "New active item should be a singleton buffer"
12921            );
12922            assert_eq!(
12923                active_item
12924                    .act_as::<Editor>(cx)
12925                    .expect("should have navigated into an editor for the 1st buffer")
12926                    .read(cx)
12927                    .text(cx),
12928                sample_text_1
12929            );
12930
12931            workspace
12932                .go_back(workspace.active_pane().downgrade(), window, cx)
12933                .detach_and_log_err(cx);
12934
12935            first_item_id
12936        })
12937        .unwrap();
12938    cx.executor().run_until_parked();
12939    workspace
12940        .update(cx, |workspace, _, cx| {
12941            let active_item = workspace
12942                .active_item(cx)
12943                .expect("should have an active item after navigating back");
12944            assert_eq!(
12945                active_item.item_id(),
12946                multibuffer_item_id,
12947                "Should navigate back to the multi buffer"
12948            );
12949            assert!(!active_item.is_singleton(cx));
12950        })
12951        .unwrap();
12952
12953    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12954        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12955            s.select_ranges(Some(39..40))
12956        });
12957        editor.open_excerpts(&OpenExcerpts, window, cx);
12958    });
12959    cx.executor().run_until_parked();
12960    let second_item_id = workspace
12961        .update(cx, |workspace, window, cx| {
12962            let active_item = workspace
12963                .active_item(cx)
12964                .expect("should have an active item after navigating into the 2nd buffer");
12965            let second_item_id = active_item.item_id();
12966            assert_ne!(
12967                second_item_id, multibuffer_item_id,
12968                "Should navigate away from the multibuffer"
12969            );
12970            assert_ne!(
12971                second_item_id, first_item_id,
12972                "Should navigate into the 2nd buffer and activate it"
12973            );
12974            assert!(
12975                active_item.is_singleton(cx),
12976                "New active item should be a singleton buffer"
12977            );
12978            assert_eq!(
12979                active_item
12980                    .act_as::<Editor>(cx)
12981                    .expect("should have navigated into an editor")
12982                    .read(cx)
12983                    .text(cx),
12984                sample_text_2
12985            );
12986
12987            workspace
12988                .go_back(workspace.active_pane().downgrade(), window, cx)
12989                .detach_and_log_err(cx);
12990
12991            second_item_id
12992        })
12993        .unwrap();
12994    cx.executor().run_until_parked();
12995    workspace
12996        .update(cx, |workspace, _, cx| {
12997            let active_item = workspace
12998                .active_item(cx)
12999                .expect("should have an active item after navigating back from the 2nd buffer");
13000            assert_eq!(
13001                active_item.item_id(),
13002                multibuffer_item_id,
13003                "Should navigate back from the 2nd buffer to the multi buffer"
13004            );
13005            assert!(!active_item.is_singleton(cx));
13006        })
13007        .unwrap();
13008
13009    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13010        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13011            s.select_ranges(Some(70..70))
13012        });
13013        editor.open_excerpts(&OpenExcerpts, window, cx);
13014    });
13015    cx.executor().run_until_parked();
13016    workspace
13017        .update(cx, |workspace, window, cx| {
13018            let active_item = workspace
13019                .active_item(cx)
13020                .expect("should have an active item after navigating into the 3rd buffer");
13021            let third_item_id = active_item.item_id();
13022            assert_ne!(
13023                third_item_id, multibuffer_item_id,
13024                "Should navigate into the 3rd buffer and activate it"
13025            );
13026            assert_ne!(third_item_id, first_item_id);
13027            assert_ne!(third_item_id, second_item_id);
13028            assert!(
13029                active_item.is_singleton(cx),
13030                "New active item should be a singleton buffer"
13031            );
13032            assert_eq!(
13033                active_item
13034                    .act_as::<Editor>(cx)
13035                    .expect("should have navigated into an editor")
13036                    .read(cx)
13037                    .text(cx),
13038                sample_text_3
13039            );
13040
13041            workspace
13042                .go_back(workspace.active_pane().downgrade(), window, cx)
13043                .detach_and_log_err(cx);
13044        })
13045        .unwrap();
13046    cx.executor().run_until_parked();
13047    workspace
13048        .update(cx, |workspace, _, cx| {
13049            let active_item = workspace
13050                .active_item(cx)
13051                .expect("should have an active item after navigating back from the 3rd buffer");
13052            assert_eq!(
13053                active_item.item_id(),
13054                multibuffer_item_id,
13055                "Should navigate back from the 3rd buffer to the multi buffer"
13056            );
13057            assert!(!active_item.is_singleton(cx));
13058        })
13059        .unwrap();
13060}
13061
13062#[gpui::test]
13063async fn test_toggle_selected_diff_hunks(
13064    executor: BackgroundExecutor,
13065    cx: &mut gpui::TestAppContext,
13066) {
13067    init_test(cx, |_| {});
13068
13069    let mut cx = EditorTestContext::new(cx).await;
13070
13071    let diff_base = r#"
13072        use some::mod;
13073
13074        const A: u32 = 42;
13075
13076        fn main() {
13077            println!("hello");
13078
13079            println!("world");
13080        }
13081        "#
13082    .unindent();
13083
13084    cx.set_state(
13085        &r#"
13086        use some::modified;
13087
13088        ˇ
13089        fn main() {
13090            println!("hello there");
13091
13092            println!("around the");
13093            println!("world");
13094        }
13095        "#
13096        .unindent(),
13097    );
13098
13099    cx.set_diff_base(&diff_base);
13100    executor.run_until_parked();
13101
13102    cx.update_editor(|editor, window, cx| {
13103        editor.go_to_next_hunk(&GoToHunk, window, cx);
13104        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13105    });
13106    executor.run_until_parked();
13107    cx.assert_state_with_diff(
13108        r#"
13109          use some::modified;
13110
13111
13112          fn main() {
13113        -     println!("hello");
13114        + ˇ    println!("hello there");
13115
13116              println!("around the");
13117              println!("world");
13118          }
13119        "#
13120        .unindent(),
13121    );
13122
13123    cx.update_editor(|editor, window, cx| {
13124        for _ in 0..2 {
13125            editor.go_to_next_hunk(&GoToHunk, window, cx);
13126            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13127        }
13128    });
13129    executor.run_until_parked();
13130    cx.assert_state_with_diff(
13131        r#"
13132        - use some::mod;
13133        + ˇuse some::modified;
13134
13135
13136          fn main() {
13137        -     println!("hello");
13138        +     println!("hello there");
13139
13140        +     println!("around the");
13141              println!("world");
13142          }
13143        "#
13144        .unindent(),
13145    );
13146
13147    cx.update_editor(|editor, window, cx| {
13148        editor.go_to_next_hunk(&GoToHunk, window, cx);
13149        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13150    });
13151    executor.run_until_parked();
13152    cx.assert_state_with_diff(
13153        r#"
13154        - use some::mod;
13155        + use some::modified;
13156
13157        - const A: u32 = 42;
13158          ˇ
13159          fn main() {
13160        -     println!("hello");
13161        +     println!("hello there");
13162
13163        +     println!("around the");
13164              println!("world");
13165          }
13166        "#
13167        .unindent(),
13168    );
13169
13170    cx.update_editor(|editor, window, cx| {
13171        editor.cancel(&Cancel, window, cx);
13172    });
13173
13174    cx.assert_state_with_diff(
13175        r#"
13176          use some::modified;
13177
13178          ˇ
13179          fn main() {
13180              println!("hello there");
13181
13182              println!("around the");
13183              println!("world");
13184          }
13185        "#
13186        .unindent(),
13187    );
13188}
13189
13190#[gpui::test]
13191async fn test_diff_base_change_with_expanded_diff_hunks(
13192    executor: BackgroundExecutor,
13193    cx: &mut gpui::TestAppContext,
13194) {
13195    init_test(cx, |_| {});
13196
13197    let mut cx = EditorTestContext::new(cx).await;
13198
13199    let diff_base = r#"
13200        use some::mod1;
13201        use some::mod2;
13202
13203        const A: u32 = 42;
13204        const B: u32 = 42;
13205        const C: u32 = 42;
13206
13207        fn main() {
13208            println!("hello");
13209
13210            println!("world");
13211        }
13212        "#
13213    .unindent();
13214
13215    cx.set_state(
13216        &r#"
13217        use some::mod2;
13218
13219        const A: u32 = 42;
13220        const C: u32 = 42;
13221
13222        fn main(ˇ) {
13223            //println!("hello");
13224
13225            println!("world");
13226            //
13227            //
13228        }
13229        "#
13230        .unindent(),
13231    );
13232
13233    cx.set_diff_base(&diff_base);
13234    executor.run_until_parked();
13235
13236    cx.update_editor(|editor, window, cx| {
13237        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13238    });
13239    executor.run_until_parked();
13240    cx.assert_state_with_diff(
13241        r#"
13242        - use some::mod1;
13243          use some::mod2;
13244
13245          const A: u32 = 42;
13246        - const B: u32 = 42;
13247          const C: u32 = 42;
13248
13249          fn main(ˇ) {
13250        -     println!("hello");
13251        +     //println!("hello");
13252
13253              println!("world");
13254        +     //
13255        +     //
13256          }
13257        "#
13258        .unindent(),
13259    );
13260
13261    cx.set_diff_base("new diff base!");
13262    executor.run_until_parked();
13263    cx.assert_state_with_diff(
13264        r#"
13265        - new diff base!
13266        + use some::mod2;
13267        +
13268        + const A: u32 = 42;
13269        + const C: u32 = 42;
13270        +
13271        + fn main(ˇ) {
13272        +     //println!("hello");
13273        +
13274        +     println!("world");
13275        +     //
13276        +     //
13277        + }
13278        "#
13279        .unindent(),
13280    );
13281}
13282
13283#[gpui::test]
13284async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13285    init_test(cx, |_| {});
13286
13287    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13288    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13289    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13290    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13291    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13292    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13293
13294    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13295    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13296    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13297
13298    let multi_buffer = cx.new(|cx| {
13299        let mut multibuffer = MultiBuffer::new(ReadWrite);
13300        multibuffer.push_excerpts(
13301            buffer_1.clone(),
13302            [
13303                ExcerptRange {
13304                    context: Point::new(0, 0)..Point::new(3, 0),
13305                    primary: None,
13306                },
13307                ExcerptRange {
13308                    context: Point::new(5, 0)..Point::new(7, 0),
13309                    primary: None,
13310                },
13311                ExcerptRange {
13312                    context: Point::new(9, 0)..Point::new(10, 3),
13313                    primary: None,
13314                },
13315            ],
13316            cx,
13317        );
13318        multibuffer.push_excerpts(
13319            buffer_2.clone(),
13320            [
13321                ExcerptRange {
13322                    context: Point::new(0, 0)..Point::new(3, 0),
13323                    primary: None,
13324                },
13325                ExcerptRange {
13326                    context: Point::new(5, 0)..Point::new(7, 0),
13327                    primary: None,
13328                },
13329                ExcerptRange {
13330                    context: Point::new(9, 0)..Point::new(10, 3),
13331                    primary: None,
13332                },
13333            ],
13334            cx,
13335        );
13336        multibuffer.push_excerpts(
13337            buffer_3.clone(),
13338            [
13339                ExcerptRange {
13340                    context: Point::new(0, 0)..Point::new(3, 0),
13341                    primary: None,
13342                },
13343                ExcerptRange {
13344                    context: Point::new(5, 0)..Point::new(7, 0),
13345                    primary: None,
13346                },
13347                ExcerptRange {
13348                    context: Point::new(9, 0)..Point::new(10, 3),
13349                    primary: None,
13350                },
13351            ],
13352            cx,
13353        );
13354        multibuffer
13355    });
13356
13357    let editor = cx.add_window(|window, cx| {
13358        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13359    });
13360    editor
13361        .update(cx, |editor, _window, cx| {
13362            for (buffer, diff_base) in [
13363                (buffer_1.clone(), file_1_old),
13364                (buffer_2.clone(), file_2_old),
13365                (buffer_3.clone(), file_3_old),
13366            ] {
13367                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13368                editor
13369                    .buffer
13370                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13371            }
13372        })
13373        .unwrap();
13374
13375    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13376    cx.run_until_parked();
13377
13378    cx.assert_editor_state(
13379        &"
13380            ˇaaa
13381            ccc
13382            ddd
13383
13384            ggg
13385            hhh
13386
13387
13388            lll
13389            mmm
13390            NNN
13391
13392            qqq
13393            rrr
13394
13395            uuu
13396            111
13397            222
13398            333
13399
13400            666
13401            777
13402
13403            000
13404            !!!"
13405        .unindent(),
13406    );
13407
13408    cx.update_editor(|editor, window, cx| {
13409        editor.select_all(&SelectAll, window, cx);
13410        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13411    });
13412    cx.executor().run_until_parked();
13413
13414    cx.assert_state_with_diff(
13415        "
13416            «aaa
13417          - bbb
13418            ccc
13419            ddd
13420
13421            ggg
13422            hhh
13423
13424
13425            lll
13426            mmm
13427          - nnn
13428          + NNN
13429
13430            qqq
13431            rrr
13432
13433            uuu
13434            111
13435            222
13436            333
13437
13438          + 666
13439            777
13440
13441            000
13442            !!!ˇ»"
13443            .unindent(),
13444    );
13445}
13446
13447#[gpui::test]
13448async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13449    init_test(cx, |_| {});
13450
13451    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13452    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13453
13454    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13455    let multi_buffer = cx.new(|cx| {
13456        let mut multibuffer = MultiBuffer::new(ReadWrite);
13457        multibuffer.push_excerpts(
13458            buffer.clone(),
13459            [
13460                ExcerptRange {
13461                    context: Point::new(0, 0)..Point::new(2, 0),
13462                    primary: None,
13463                },
13464                ExcerptRange {
13465                    context: Point::new(4, 0)..Point::new(7, 0),
13466                    primary: None,
13467                },
13468                ExcerptRange {
13469                    context: Point::new(9, 0)..Point::new(10, 0),
13470                    primary: None,
13471                },
13472            ],
13473            cx,
13474        );
13475        multibuffer
13476    });
13477
13478    let editor = cx.add_window(|window, cx| {
13479        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13480    });
13481    editor
13482        .update(cx, |editor, _window, cx| {
13483            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13484            editor
13485                .buffer
13486                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13487        })
13488        .unwrap();
13489
13490    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13491    cx.run_until_parked();
13492
13493    cx.update_editor(|editor, window, cx| {
13494        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13495    });
13496    cx.executor().run_until_parked();
13497
13498    // When the start of a hunk coincides with the start of its excerpt,
13499    // the hunk is expanded. When the start of a a hunk is earlier than
13500    // the start of its excerpt, the hunk is not expanded.
13501    cx.assert_state_with_diff(
13502        "
13503            ˇaaa
13504          - bbb
13505          + BBB
13506
13507          - ddd
13508          - eee
13509          + DDD
13510          + EEE
13511            fff
13512
13513            iii
13514        "
13515        .unindent(),
13516    );
13517}
13518
13519#[gpui::test]
13520async fn test_edits_around_expanded_insertion_hunks(
13521    executor: BackgroundExecutor,
13522    cx: &mut gpui::TestAppContext,
13523) {
13524    init_test(cx, |_| {});
13525
13526    let mut cx = EditorTestContext::new(cx).await;
13527
13528    let diff_base = r#"
13529        use some::mod1;
13530        use some::mod2;
13531
13532        const A: u32 = 42;
13533
13534        fn main() {
13535            println!("hello");
13536
13537            println!("world");
13538        }
13539        "#
13540    .unindent();
13541    executor.run_until_parked();
13542    cx.set_state(
13543        &r#"
13544        use some::mod1;
13545        use some::mod2;
13546
13547        const A: u32 = 42;
13548        const B: u32 = 42;
13549        const C: u32 = 42;
13550        ˇ
13551
13552        fn main() {
13553            println!("hello");
13554
13555            println!("world");
13556        }
13557        "#
13558        .unindent(),
13559    );
13560
13561    cx.set_diff_base(&diff_base);
13562    executor.run_until_parked();
13563
13564    cx.update_editor(|editor, window, cx| {
13565        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13566    });
13567    executor.run_until_parked();
13568
13569    cx.assert_state_with_diff(
13570        r#"
13571        use some::mod1;
13572        use some::mod2;
13573
13574        const A: u32 = 42;
13575      + const B: u32 = 42;
13576      + const C: u32 = 42;
13577      + ˇ
13578
13579        fn main() {
13580            println!("hello");
13581
13582            println!("world");
13583        }
13584      "#
13585        .unindent(),
13586    );
13587
13588    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13589    executor.run_until_parked();
13590
13591    cx.assert_state_with_diff(
13592        r#"
13593        use some::mod1;
13594        use some::mod2;
13595
13596        const A: u32 = 42;
13597      + const B: u32 = 42;
13598      + const C: u32 = 42;
13599      + const D: u32 = 42;
13600      + ˇ
13601
13602        fn main() {
13603            println!("hello");
13604
13605            println!("world");
13606        }
13607      "#
13608        .unindent(),
13609    );
13610
13611    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13612    executor.run_until_parked();
13613
13614    cx.assert_state_with_diff(
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      + const D: u32 = 42;
13623      + const E: u32 = 42;
13624      + ˇ
13625
13626        fn main() {
13627            println!("hello");
13628
13629            println!("world");
13630        }
13631      "#
13632        .unindent(),
13633    );
13634
13635    cx.update_editor(|editor, window, cx| {
13636        editor.delete_line(&DeleteLine, window, cx);
13637    });
13638    executor.run_until_parked();
13639
13640    cx.assert_state_with_diff(
13641        r#"
13642        use some::mod1;
13643        use some::mod2;
13644
13645        const A: u32 = 42;
13646      + const B: u32 = 42;
13647      + const C: u32 = 42;
13648      + const D: u32 = 42;
13649      + const E: u32 = 42;
13650        ˇ
13651        fn main() {
13652            println!("hello");
13653
13654            println!("world");
13655        }
13656      "#
13657        .unindent(),
13658    );
13659
13660    cx.update_editor(|editor, window, cx| {
13661        editor.move_up(&MoveUp, window, cx);
13662        editor.delete_line(&DeleteLine, window, cx);
13663        editor.move_up(&MoveUp, window, cx);
13664        editor.delete_line(&DeleteLine, window, cx);
13665        editor.move_up(&MoveUp, window, cx);
13666        editor.delete_line(&DeleteLine, window, cx);
13667    });
13668    executor.run_until_parked();
13669    cx.assert_state_with_diff(
13670        r#"
13671        use some::mod1;
13672        use some::mod2;
13673
13674        const A: u32 = 42;
13675      + const B: u32 = 42;
13676        ˇ
13677        fn main() {
13678            println!("hello");
13679
13680            println!("world");
13681        }
13682      "#
13683        .unindent(),
13684    );
13685
13686    cx.update_editor(|editor, window, cx| {
13687        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13688        editor.delete_line(&DeleteLine, window, cx);
13689    });
13690    executor.run_until_parked();
13691    cx.assert_state_with_diff(
13692        r#"
13693        ˇ
13694        fn main() {
13695            println!("hello");
13696
13697            println!("world");
13698        }
13699      "#
13700        .unindent(),
13701    );
13702}
13703
13704#[gpui::test]
13705async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13706    init_test(cx, |_| {});
13707
13708    let mut cx = EditorTestContext::new(cx).await;
13709    cx.set_diff_base(indoc! { "
13710        one
13711        two
13712        three
13713        four
13714        five
13715        "
13716    });
13717    cx.set_state(indoc! { "
13718        one
13719        ˇthree
13720        five
13721    "});
13722    cx.run_until_parked();
13723    cx.update_editor(|editor, window, cx| {
13724        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13725    });
13726    cx.assert_state_with_diff(
13727        indoc! { "
13728        one
13729      - two
13730        ˇthree
13731      - four
13732        five
13733    "}
13734        .to_string(),
13735    );
13736    cx.update_editor(|editor, window, cx| {
13737        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13738    });
13739
13740    cx.assert_state_with_diff(
13741        indoc! { "
13742        one
13743        ˇthree
13744        five
13745    "}
13746        .to_string(),
13747    );
13748
13749    cx.set_state(indoc! { "
13750        one
13751        ˇTWO
13752        three
13753        four
13754        five
13755    "});
13756    cx.run_until_parked();
13757    cx.update_editor(|editor, window, cx| {
13758        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13759    });
13760
13761    cx.assert_state_with_diff(
13762        indoc! { "
13763            one
13764          - two
13765          + ˇTWO
13766            three
13767            four
13768            five
13769        "}
13770        .to_string(),
13771    );
13772    cx.update_editor(|editor, window, cx| {
13773        editor.move_up(&Default::default(), window, cx);
13774        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13775    });
13776    cx.assert_state_with_diff(
13777        indoc! { "
13778            one
13779            ˇTWO
13780            three
13781            four
13782            five
13783        "}
13784        .to_string(),
13785    );
13786}
13787
13788#[gpui::test]
13789async fn test_edits_around_expanded_deletion_hunks(
13790    executor: BackgroundExecutor,
13791    cx: &mut gpui::TestAppContext,
13792) {
13793    init_test(cx, |_| {});
13794
13795    let mut cx = EditorTestContext::new(cx).await;
13796
13797    let diff_base = r#"
13798        use some::mod1;
13799        use some::mod2;
13800
13801        const A: u32 = 42;
13802        const B: u32 = 42;
13803        const C: u32 = 42;
13804
13805
13806        fn main() {
13807            println!("hello");
13808
13809            println!("world");
13810        }
13811    "#
13812    .unindent();
13813    executor.run_until_parked();
13814    cx.set_state(
13815        &r#"
13816        use some::mod1;
13817        use some::mod2;
13818
13819        ˇconst B: u32 = 42;
13820        const C: u32 = 42;
13821
13822
13823        fn main() {
13824            println!("hello");
13825
13826            println!("world");
13827        }
13828        "#
13829        .unindent(),
13830    );
13831
13832    cx.set_diff_base(&diff_base);
13833    executor.run_until_parked();
13834
13835    cx.update_editor(|editor, window, cx| {
13836        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13837    });
13838    executor.run_until_parked();
13839
13840    cx.assert_state_with_diff(
13841        r#"
13842        use some::mod1;
13843        use some::mod2;
13844
13845      - const A: u32 = 42;
13846        ˇconst B: u32 = 42;
13847        const C: u32 = 42;
13848
13849
13850        fn main() {
13851            println!("hello");
13852
13853            println!("world");
13854        }
13855      "#
13856        .unindent(),
13857    );
13858
13859    cx.update_editor(|editor, window, cx| {
13860        editor.delete_line(&DeleteLine, window, cx);
13861    });
13862    executor.run_until_parked();
13863    cx.assert_state_with_diff(
13864        r#"
13865        use some::mod1;
13866        use some::mod2;
13867
13868      - const A: u32 = 42;
13869      - const B: u32 = 42;
13870        ˇconst C: u32 = 42;
13871
13872
13873        fn main() {
13874            println!("hello");
13875
13876            println!("world");
13877        }
13878      "#
13879        .unindent(),
13880    );
13881
13882    cx.update_editor(|editor, window, cx| {
13883        editor.delete_line(&DeleteLine, window, cx);
13884    });
13885    executor.run_until_parked();
13886    cx.assert_state_with_diff(
13887        r#"
13888        use some::mod1;
13889        use some::mod2;
13890
13891      - const A: u32 = 42;
13892      - const B: u32 = 42;
13893      - const C: u32 = 42;
13894        ˇ
13895
13896        fn main() {
13897            println!("hello");
13898
13899            println!("world");
13900        }
13901      "#
13902        .unindent(),
13903    );
13904
13905    cx.update_editor(|editor, window, cx| {
13906        editor.handle_input("replacement", window, cx);
13907    });
13908    executor.run_until_parked();
13909    cx.assert_state_with_diff(
13910        r#"
13911        use some::mod1;
13912        use some::mod2;
13913
13914      - const A: u32 = 42;
13915      - const B: u32 = 42;
13916      - const C: u32 = 42;
13917      -
13918      + replacementˇ
13919
13920        fn main() {
13921            println!("hello");
13922
13923            println!("world");
13924        }
13925      "#
13926        .unindent(),
13927    );
13928}
13929
13930#[gpui::test]
13931async fn test_backspace_after_deletion_hunk(
13932    executor: BackgroundExecutor,
13933    cx: &mut gpui::TestAppContext,
13934) {
13935    init_test(cx, |_| {});
13936
13937    let mut cx = EditorTestContext::new(cx).await;
13938
13939    let base_text = r#"
13940        one
13941        two
13942        three
13943        four
13944        five
13945    "#
13946    .unindent();
13947    executor.run_until_parked();
13948    cx.set_state(
13949        &r#"
13950        one
13951        two
13952        fˇour
13953        five
13954        "#
13955        .unindent(),
13956    );
13957
13958    cx.set_diff_base(&base_text);
13959    executor.run_until_parked();
13960
13961    cx.update_editor(|editor, window, cx| {
13962        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13963    });
13964    executor.run_until_parked();
13965
13966    cx.assert_state_with_diff(
13967        r#"
13968          one
13969          two
13970        - three
13971          fˇour
13972          five
13973        "#
13974        .unindent(),
13975    );
13976
13977    cx.update_editor(|editor, window, cx| {
13978        editor.backspace(&Backspace, window, cx);
13979        editor.backspace(&Backspace, window, cx);
13980    });
13981    executor.run_until_parked();
13982    cx.assert_state_with_diff(
13983        r#"
13984          one
13985          two
13986        - threeˇ
13987        - four
13988        + our
13989          five
13990        "#
13991        .unindent(),
13992    );
13993}
13994
13995#[gpui::test]
13996async fn test_edit_after_expanded_modification_hunk(
13997    executor: BackgroundExecutor,
13998    cx: &mut gpui::TestAppContext,
13999) {
14000    init_test(cx, |_| {});
14001
14002    let mut cx = EditorTestContext::new(cx).await;
14003
14004    let diff_base = r#"
14005        use some::mod1;
14006        use some::mod2;
14007
14008        const A: u32 = 42;
14009        const B: u32 = 42;
14010        const C: u32 = 42;
14011        const D: u32 = 42;
14012
14013
14014        fn main() {
14015            println!("hello");
14016
14017            println!("world");
14018        }"#
14019    .unindent();
14020
14021    cx.set_state(
14022        &r#"
14023        use some::mod1;
14024        use some::mod2;
14025
14026        const A: u32 = 42;
14027        const B: u32 = 42;
14028        const C: u32 = 43ˇ
14029        const D: u32 = 42;
14030
14031
14032        fn main() {
14033            println!("hello");
14034
14035            println!("world");
14036        }"#
14037        .unindent(),
14038    );
14039
14040    cx.set_diff_base(&diff_base);
14041    executor.run_until_parked();
14042    cx.update_editor(|editor, window, cx| {
14043        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
14044    });
14045    executor.run_until_parked();
14046
14047    cx.assert_state_with_diff(
14048        r#"
14049        use some::mod1;
14050        use some::mod2;
14051
14052        const A: u32 = 42;
14053        const B: u32 = 42;
14054      - const C: u32 = 42;
14055      + const C: u32 = 43ˇ
14056        const D: u32 = 42;
14057
14058
14059        fn main() {
14060            println!("hello");
14061
14062            println!("world");
14063        }"#
14064        .unindent(),
14065    );
14066
14067    cx.update_editor(|editor, window, cx| {
14068        editor.handle_input("\nnew_line\n", window, cx);
14069    });
14070    executor.run_until_parked();
14071
14072    cx.assert_state_with_diff(
14073        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 C: u32 = 43
14081      + new_line
14082      + ˇ
14083        const D: u32 = 42;
14084
14085
14086        fn main() {
14087            println!("hello");
14088
14089            println!("world");
14090        }"#
14091        .unindent(),
14092    );
14093}
14094
14095#[gpui::test]
14096async fn test_stage_and_unstage_added_file_hunk(
14097    executor: BackgroundExecutor,
14098    cx: &mut gpui::TestAppContext,
14099) {
14100    init_test(cx, |_| {});
14101
14102    let mut cx = EditorTestContext::new(cx).await;
14103    cx.update_editor(|editor, _, cx| {
14104        editor.set_expand_all_diff_hunks(cx);
14105    });
14106
14107    let working_copy = r#"
14108            ˇfn main() {
14109                println!("hello, world!");
14110            }
14111        "#
14112    .unindent();
14113
14114    cx.set_state(&working_copy);
14115    executor.run_until_parked();
14116
14117    cx.assert_state_with_diff(
14118        r#"
14119            + ˇfn main() {
14120            +     println!("hello, world!");
14121            + }
14122        "#
14123        .unindent(),
14124    );
14125    cx.assert_index_text(None);
14126
14127    cx.update_editor(|editor, window, cx| {
14128        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14129    });
14130    executor.run_until_parked();
14131    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14132    cx.assert_state_with_diff(
14133        r#"
14134            + ˇfn main() {
14135            +     println!("hello, world!");
14136            + }
14137        "#
14138        .unindent(),
14139    );
14140
14141    cx.update_editor(|editor, window, cx| {
14142        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14143    });
14144    executor.run_until_parked();
14145    cx.assert_index_text(None);
14146}
14147
14148async fn setup_indent_guides_editor(
14149    text: &str,
14150    cx: &mut gpui::TestAppContext,
14151) -> (BufferId, EditorTestContext) {
14152    init_test(cx, |_| {});
14153
14154    let mut cx = EditorTestContext::new(cx).await;
14155
14156    let buffer_id = cx.update_editor(|editor, window, cx| {
14157        editor.set_text(text, window, cx);
14158        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14159
14160        buffer_ids[0]
14161    });
14162
14163    (buffer_id, cx)
14164}
14165
14166fn assert_indent_guides(
14167    range: Range<u32>,
14168    expected: Vec<IndentGuide>,
14169    active_indices: Option<Vec<usize>>,
14170    cx: &mut EditorTestContext,
14171) {
14172    let indent_guides = cx.update_editor(|editor, window, cx| {
14173        let snapshot = editor.snapshot(window, cx).display_snapshot;
14174        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14175            editor,
14176            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14177            true,
14178            &snapshot,
14179            cx,
14180        );
14181
14182        indent_guides.sort_by(|a, b| {
14183            a.depth.cmp(&b.depth).then(
14184                a.start_row
14185                    .cmp(&b.start_row)
14186                    .then(a.end_row.cmp(&b.end_row)),
14187            )
14188        });
14189        indent_guides
14190    });
14191
14192    if let Some(expected) = active_indices {
14193        let active_indices = cx.update_editor(|editor, window, cx| {
14194            let snapshot = editor.snapshot(window, cx).display_snapshot;
14195            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14196        });
14197
14198        assert_eq!(
14199            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14200            expected,
14201            "Active indent guide indices do not match"
14202        );
14203    }
14204
14205    assert_eq!(indent_guides, expected, "Indent guides do not match");
14206}
14207
14208fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14209    IndentGuide {
14210        buffer_id,
14211        start_row: MultiBufferRow(start_row),
14212        end_row: MultiBufferRow(end_row),
14213        depth,
14214        tab_size: 4,
14215        settings: IndentGuideSettings {
14216            enabled: true,
14217            line_width: 1,
14218            active_line_width: 1,
14219            ..Default::default()
14220        },
14221    }
14222}
14223
14224#[gpui::test]
14225async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14226    let (buffer_id, mut cx) = setup_indent_guides_editor(
14227        &"
14228    fn main() {
14229        let a = 1;
14230    }"
14231        .unindent(),
14232        cx,
14233    )
14234    .await;
14235
14236    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14237}
14238
14239#[gpui::test]
14240async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
14241    let (buffer_id, mut cx) = setup_indent_guides_editor(
14242        &"
14243    fn main() {
14244        let a = 1;
14245        let b = 2;
14246    }"
14247        .unindent(),
14248        cx,
14249    )
14250    .await;
14251
14252    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14253}
14254
14255#[gpui::test]
14256async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
14257    let (buffer_id, mut cx) = setup_indent_guides_editor(
14258        &"
14259    fn main() {
14260        let a = 1;
14261        if a == 3 {
14262            let b = 2;
14263        } else {
14264            let c = 3;
14265        }
14266    }"
14267        .unindent(),
14268        cx,
14269    )
14270    .await;
14271
14272    assert_indent_guides(
14273        0..8,
14274        vec![
14275            indent_guide(buffer_id, 1, 6, 0),
14276            indent_guide(buffer_id, 3, 3, 1),
14277            indent_guide(buffer_id, 5, 5, 1),
14278        ],
14279        None,
14280        &mut cx,
14281    );
14282}
14283
14284#[gpui::test]
14285async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
14286    let (buffer_id, mut cx) = setup_indent_guides_editor(
14287        &"
14288    fn main() {
14289        let a = 1;
14290            let b = 2;
14291        let c = 3;
14292    }"
14293        .unindent(),
14294        cx,
14295    )
14296    .await;
14297
14298    assert_indent_guides(
14299        0..5,
14300        vec![
14301            indent_guide(buffer_id, 1, 3, 0),
14302            indent_guide(buffer_id, 2, 2, 1),
14303        ],
14304        None,
14305        &mut cx,
14306    );
14307}
14308
14309#[gpui::test]
14310async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14311    let (buffer_id, mut cx) = setup_indent_guides_editor(
14312        &"
14313        fn main() {
14314            let a = 1;
14315
14316            let c = 3;
14317        }"
14318        .unindent(),
14319        cx,
14320    )
14321    .await;
14322
14323    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14324}
14325
14326#[gpui::test]
14327async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14328    let (buffer_id, mut cx) = setup_indent_guides_editor(
14329        &"
14330        fn main() {
14331            let a = 1;
14332
14333            let c = 3;
14334
14335            if a == 3 {
14336                let b = 2;
14337            } else {
14338                let c = 3;
14339            }
14340        }"
14341        .unindent(),
14342        cx,
14343    )
14344    .await;
14345
14346    assert_indent_guides(
14347        0..11,
14348        vec![
14349            indent_guide(buffer_id, 1, 9, 0),
14350            indent_guide(buffer_id, 6, 6, 1),
14351            indent_guide(buffer_id, 8, 8, 1),
14352        ],
14353        None,
14354        &mut cx,
14355    );
14356}
14357
14358#[gpui::test]
14359async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14360    let (buffer_id, mut cx) = setup_indent_guides_editor(
14361        &"
14362        fn main() {
14363            let a = 1;
14364
14365            let c = 3;
14366
14367            if a == 3 {
14368                let b = 2;
14369            } else {
14370                let c = 3;
14371            }
14372        }"
14373        .unindent(),
14374        cx,
14375    )
14376    .await;
14377
14378    assert_indent_guides(
14379        1..11,
14380        vec![
14381            indent_guide(buffer_id, 1, 9, 0),
14382            indent_guide(buffer_id, 6, 6, 1),
14383            indent_guide(buffer_id, 8, 8, 1),
14384        ],
14385        None,
14386        &mut cx,
14387    );
14388}
14389
14390#[gpui::test]
14391async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14392    let (buffer_id, mut cx) = setup_indent_guides_editor(
14393        &"
14394        fn main() {
14395            let a = 1;
14396
14397            let c = 3;
14398
14399            if a == 3 {
14400                let b = 2;
14401            } else {
14402                let c = 3;
14403            }
14404        }"
14405        .unindent(),
14406        cx,
14407    )
14408    .await;
14409
14410    assert_indent_guides(
14411        1..10,
14412        vec![
14413            indent_guide(buffer_id, 1, 9, 0),
14414            indent_guide(buffer_id, 6, 6, 1),
14415            indent_guide(buffer_id, 8, 8, 1),
14416        ],
14417        None,
14418        &mut cx,
14419    );
14420}
14421
14422#[gpui::test]
14423async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14424    let (buffer_id, mut cx) = setup_indent_guides_editor(
14425        &"
14426        block1
14427            block2
14428                block3
14429                    block4
14430            block2
14431        block1
14432        block1"
14433            .unindent(),
14434        cx,
14435    )
14436    .await;
14437
14438    assert_indent_guides(
14439        1..10,
14440        vec![
14441            indent_guide(buffer_id, 1, 4, 0),
14442            indent_guide(buffer_id, 2, 3, 1),
14443            indent_guide(buffer_id, 3, 3, 2),
14444        ],
14445        None,
14446        &mut cx,
14447    );
14448}
14449
14450#[gpui::test]
14451async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14452    let (buffer_id, mut cx) = setup_indent_guides_editor(
14453        &"
14454        block1
14455            block2
14456                block3
14457
14458        block1
14459        block1"
14460            .unindent(),
14461        cx,
14462    )
14463    .await;
14464
14465    assert_indent_guides(
14466        0..6,
14467        vec![
14468            indent_guide(buffer_id, 1, 2, 0),
14469            indent_guide(buffer_id, 2, 2, 1),
14470        ],
14471        None,
14472        &mut cx,
14473    );
14474}
14475
14476#[gpui::test]
14477async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14478    let (buffer_id, mut cx) = setup_indent_guides_editor(
14479        &"
14480        block1
14481
14482
14483
14484            block2
14485        "
14486        .unindent(),
14487        cx,
14488    )
14489    .await;
14490
14491    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14492}
14493
14494#[gpui::test]
14495async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14496    let (buffer_id, mut cx) = setup_indent_guides_editor(
14497        &"
14498        def a:
14499        \tb = 3
14500        \tif True:
14501        \t\tc = 4
14502        \t\td = 5
14503        \tprint(b)
14504        "
14505        .unindent(),
14506        cx,
14507    )
14508    .await;
14509
14510    assert_indent_guides(
14511        0..6,
14512        vec![
14513            indent_guide(buffer_id, 1, 6, 0),
14514            indent_guide(buffer_id, 3, 4, 1),
14515        ],
14516        None,
14517        &mut cx,
14518    );
14519}
14520
14521#[gpui::test]
14522async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14523    let (buffer_id, mut cx) = setup_indent_guides_editor(
14524        &"
14525    fn main() {
14526        let a = 1;
14527    }"
14528        .unindent(),
14529        cx,
14530    )
14531    .await;
14532
14533    cx.update_editor(|editor, window, cx| {
14534        editor.change_selections(None, window, cx, |s| {
14535            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14536        });
14537    });
14538
14539    assert_indent_guides(
14540        0..3,
14541        vec![indent_guide(buffer_id, 1, 1, 0)],
14542        Some(vec![0]),
14543        &mut cx,
14544    );
14545}
14546
14547#[gpui::test]
14548async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14549    let (buffer_id, mut cx) = setup_indent_guides_editor(
14550        &"
14551    fn main() {
14552        if 1 == 2 {
14553            let a = 1;
14554        }
14555    }"
14556        .unindent(),
14557        cx,
14558    )
14559    .await;
14560
14561    cx.update_editor(|editor, window, cx| {
14562        editor.change_selections(None, window, cx, |s| {
14563            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14564        });
14565    });
14566
14567    assert_indent_guides(
14568        0..4,
14569        vec![
14570            indent_guide(buffer_id, 1, 3, 0),
14571            indent_guide(buffer_id, 2, 2, 1),
14572        ],
14573        Some(vec![1]),
14574        &mut cx,
14575    );
14576
14577    cx.update_editor(|editor, window, cx| {
14578        editor.change_selections(None, window, cx, |s| {
14579            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14580        });
14581    });
14582
14583    assert_indent_guides(
14584        0..4,
14585        vec![
14586            indent_guide(buffer_id, 1, 3, 0),
14587            indent_guide(buffer_id, 2, 2, 1),
14588        ],
14589        Some(vec![1]),
14590        &mut cx,
14591    );
14592
14593    cx.update_editor(|editor, window, cx| {
14594        editor.change_selections(None, window, cx, |s| {
14595            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14596        });
14597    });
14598
14599    assert_indent_guides(
14600        0..4,
14601        vec![
14602            indent_guide(buffer_id, 1, 3, 0),
14603            indent_guide(buffer_id, 2, 2, 1),
14604        ],
14605        Some(vec![0]),
14606        &mut cx,
14607    );
14608}
14609
14610#[gpui::test]
14611async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14612    let (buffer_id, mut cx) = setup_indent_guides_editor(
14613        &"
14614    fn main() {
14615        let a = 1;
14616
14617        let b = 2;
14618    }"
14619        .unindent(),
14620        cx,
14621    )
14622    .await;
14623
14624    cx.update_editor(|editor, window, cx| {
14625        editor.change_selections(None, window, cx, |s| {
14626            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14627        });
14628    });
14629
14630    assert_indent_guides(
14631        0..5,
14632        vec![indent_guide(buffer_id, 1, 3, 0)],
14633        Some(vec![0]),
14634        &mut cx,
14635    );
14636}
14637
14638#[gpui::test]
14639async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14640    let (buffer_id, mut cx) = setup_indent_guides_editor(
14641        &"
14642    def m:
14643        a = 1
14644        pass"
14645            .unindent(),
14646        cx,
14647    )
14648    .await;
14649
14650    cx.update_editor(|editor, window, cx| {
14651        editor.change_selections(None, window, cx, |s| {
14652            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14653        });
14654    });
14655
14656    assert_indent_guides(
14657        0..3,
14658        vec![indent_guide(buffer_id, 1, 2, 0)],
14659        Some(vec![0]),
14660        &mut cx,
14661    );
14662}
14663
14664#[gpui::test]
14665async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14666    init_test(cx, |_| {});
14667    let mut cx = EditorTestContext::new(cx).await;
14668    let text = indoc! {
14669        "
14670        impl A {
14671            fn b() {
14672                0;
14673                3;
14674                5;
14675                6;
14676                7;
14677            }
14678        }
14679        "
14680    };
14681    let base_text = indoc! {
14682        "
14683        impl A {
14684            fn b() {
14685                0;
14686                1;
14687                2;
14688                3;
14689                4;
14690            }
14691            fn c() {
14692                5;
14693                6;
14694                7;
14695            }
14696        }
14697        "
14698    };
14699
14700    cx.update_editor(|editor, window, cx| {
14701        editor.set_text(text, window, cx);
14702
14703        editor.buffer().update(cx, |multibuffer, cx| {
14704            let buffer = multibuffer.as_singleton().unwrap();
14705            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14706
14707            multibuffer.set_all_diff_hunks_expanded(cx);
14708            multibuffer.add_diff(diff, cx);
14709
14710            buffer.read(cx).remote_id()
14711        })
14712    });
14713    cx.run_until_parked();
14714
14715    cx.assert_state_with_diff(
14716        indoc! { "
14717          impl A {
14718              fn b() {
14719                  0;
14720        -         1;
14721        -         2;
14722                  3;
14723        -         4;
14724        -     }
14725        -     fn c() {
14726                  5;
14727                  6;
14728                  7;
14729              }
14730          }
14731          ˇ"
14732        }
14733        .to_string(),
14734    );
14735
14736    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14737        editor
14738            .snapshot(window, cx)
14739            .buffer_snapshot
14740            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14741            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14742            .collect::<Vec<_>>()
14743    });
14744    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14745    assert_eq!(
14746        actual_guides,
14747        vec![
14748            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14749            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14750            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14751        ]
14752    );
14753}
14754
14755#[gpui::test]
14756fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14757    init_test(cx, |_| {});
14758
14759    let editor = cx.add_window(|window, cx| {
14760        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14761        build_editor(buffer, window, cx)
14762    });
14763
14764    let render_args = Arc::new(Mutex::new(None));
14765    let snapshot = editor
14766        .update(cx, |editor, window, cx| {
14767            let snapshot = editor.buffer().read(cx).snapshot(cx);
14768            let range =
14769                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14770
14771            struct RenderArgs {
14772                row: MultiBufferRow,
14773                folded: bool,
14774                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14775            }
14776
14777            let crease = Crease::inline(
14778                range,
14779                FoldPlaceholder::test(),
14780                {
14781                    let toggle_callback = render_args.clone();
14782                    move |row, folded, callback, _window, _cx| {
14783                        *toggle_callback.lock() = Some(RenderArgs {
14784                            row,
14785                            folded,
14786                            callback,
14787                        });
14788                        div()
14789                    }
14790                },
14791                |_row, _folded, _window, _cx| div(),
14792            );
14793
14794            editor.insert_creases(Some(crease), cx);
14795            let snapshot = editor.snapshot(window, cx);
14796            let _div = snapshot.render_crease_toggle(
14797                MultiBufferRow(1),
14798                false,
14799                cx.entity().clone(),
14800                window,
14801                cx,
14802            );
14803            snapshot
14804        })
14805        .unwrap();
14806
14807    let render_args = render_args.lock().take().unwrap();
14808    assert_eq!(render_args.row, MultiBufferRow(1));
14809    assert!(!render_args.folded);
14810    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14811
14812    cx.update_window(*editor, |_, window, cx| {
14813        (render_args.callback)(true, window, cx)
14814    })
14815    .unwrap();
14816    let snapshot = editor
14817        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14818        .unwrap();
14819    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14820
14821    cx.update_window(*editor, |_, window, cx| {
14822        (render_args.callback)(false, window, cx)
14823    })
14824    .unwrap();
14825    let snapshot = editor
14826        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14827        .unwrap();
14828    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14829}
14830
14831#[gpui::test]
14832async fn test_input_text(cx: &mut gpui::TestAppContext) {
14833    init_test(cx, |_| {});
14834    let mut cx = EditorTestContext::new(cx).await;
14835
14836    cx.set_state(
14837        &r#"ˇone
14838        two
14839
14840        three
14841        fourˇ
14842        five
14843
14844        siˇx"#
14845            .unindent(),
14846    );
14847
14848    cx.dispatch_action(HandleInput(String::new()));
14849    cx.assert_editor_state(
14850        &r#"ˇone
14851        two
14852
14853        three
14854        fourˇ
14855        five
14856
14857        siˇx"#
14858            .unindent(),
14859    );
14860
14861    cx.dispatch_action(HandleInput("AAAA".to_string()));
14862    cx.assert_editor_state(
14863        &r#"AAAAˇone
14864        two
14865
14866        three
14867        fourAAAAˇ
14868        five
14869
14870        siAAAAˇx"#
14871            .unindent(),
14872    );
14873}
14874
14875#[gpui::test]
14876async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14877    init_test(cx, |_| {});
14878
14879    let mut cx = EditorTestContext::new(cx).await;
14880    cx.set_state(
14881        r#"let foo = 1;
14882let foo = 2;
14883let foo = 3;
14884let fooˇ = 4;
14885let foo = 5;
14886let foo = 6;
14887let foo = 7;
14888let foo = 8;
14889let foo = 9;
14890let foo = 10;
14891let foo = 11;
14892let foo = 12;
14893let foo = 13;
14894let foo = 14;
14895let foo = 15;"#,
14896    );
14897
14898    cx.update_editor(|e, window, cx| {
14899        assert_eq!(
14900            e.next_scroll_position,
14901            NextScrollCursorCenterTopBottom::Center,
14902            "Default next scroll direction is center",
14903        );
14904
14905        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14906        assert_eq!(
14907            e.next_scroll_position,
14908            NextScrollCursorCenterTopBottom::Top,
14909            "After center, next scroll direction should be top",
14910        );
14911
14912        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14913        assert_eq!(
14914            e.next_scroll_position,
14915            NextScrollCursorCenterTopBottom::Bottom,
14916            "After top, next scroll direction should be bottom",
14917        );
14918
14919        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14920        assert_eq!(
14921            e.next_scroll_position,
14922            NextScrollCursorCenterTopBottom::Center,
14923            "After bottom, scrolling should start over",
14924        );
14925
14926        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14927        assert_eq!(
14928            e.next_scroll_position,
14929            NextScrollCursorCenterTopBottom::Top,
14930            "Scrolling continues if retriggered fast enough"
14931        );
14932    });
14933
14934    cx.executor()
14935        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14936    cx.executor().run_until_parked();
14937    cx.update_editor(|e, _, _| {
14938        assert_eq!(
14939            e.next_scroll_position,
14940            NextScrollCursorCenterTopBottom::Center,
14941            "If scrolling is not triggered fast enough, it should reset"
14942        );
14943    });
14944}
14945
14946#[gpui::test]
14947async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14948    init_test(cx, |_| {});
14949    let mut cx = EditorLspTestContext::new_rust(
14950        lsp::ServerCapabilities {
14951            definition_provider: Some(lsp::OneOf::Left(true)),
14952            references_provider: Some(lsp::OneOf::Left(true)),
14953            ..lsp::ServerCapabilities::default()
14954        },
14955        cx,
14956    )
14957    .await;
14958
14959    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14960        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14961            move |params, _| async move {
14962                if empty_go_to_definition {
14963                    Ok(None)
14964                } else {
14965                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14966                        uri: params.text_document_position_params.text_document.uri,
14967                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14968                    })))
14969                }
14970            },
14971        );
14972        let references =
14973            cx.lsp
14974                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14975                    Ok(Some(vec![lsp::Location {
14976                        uri: params.text_document_position.text_document.uri,
14977                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14978                    }]))
14979                });
14980        (go_to_definition, references)
14981    };
14982
14983    cx.set_state(
14984        &r#"fn one() {
14985            let mut a = ˇtwo();
14986        }
14987
14988        fn two() {}"#
14989            .unindent(),
14990    );
14991    set_up_lsp_handlers(false, &mut cx);
14992    let navigated = cx
14993        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14994        .await
14995        .expect("Failed to navigate to definition");
14996    assert_eq!(
14997        navigated,
14998        Navigated::Yes,
14999        "Should have navigated to definition from the GetDefinition response"
15000    );
15001    cx.assert_editor_state(
15002        &r#"fn one() {
15003            let mut a = two();
15004        }
15005
15006        fn «twoˇ»() {}"#
15007            .unindent(),
15008    );
15009
15010    let editors = cx.update_workspace(|workspace, _, cx| {
15011        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15012    });
15013    cx.update_editor(|_, _, test_editor_cx| {
15014        assert_eq!(
15015            editors.len(),
15016            1,
15017            "Initially, only one, test, editor should be open in the workspace"
15018        );
15019        assert_eq!(
15020            test_editor_cx.entity(),
15021            editors.last().expect("Asserted len is 1").clone()
15022        );
15023    });
15024
15025    set_up_lsp_handlers(true, &mut cx);
15026    let navigated = cx
15027        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15028        .await
15029        .expect("Failed to navigate to lookup references");
15030    assert_eq!(
15031        navigated,
15032        Navigated::Yes,
15033        "Should have navigated to references as a fallback after empty GoToDefinition response"
15034    );
15035    // We should not change the selections in the existing file,
15036    // if opening another milti buffer with the references
15037    cx.assert_editor_state(
15038        &r#"fn one() {
15039            let mut a = two();
15040        }
15041
15042        fn «twoˇ»() {}"#
15043            .unindent(),
15044    );
15045    let editors = cx.update_workspace(|workspace, _, cx| {
15046        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15047    });
15048    cx.update_editor(|_, _, test_editor_cx| {
15049        assert_eq!(
15050            editors.len(),
15051            2,
15052            "After falling back to references search, we open a new editor with the results"
15053        );
15054        let references_fallback_text = editors
15055            .into_iter()
15056            .find(|new_editor| *new_editor != test_editor_cx.entity())
15057            .expect("Should have one non-test editor now")
15058            .read(test_editor_cx)
15059            .text(test_editor_cx);
15060        assert_eq!(
15061            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15062            "Should use the range from the references response and not the GoToDefinition one"
15063        );
15064    });
15065}
15066
15067#[gpui::test]
15068async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
15069    init_test(cx, |_| {});
15070
15071    let language = Arc::new(Language::new(
15072        LanguageConfig::default(),
15073        Some(tree_sitter_rust::LANGUAGE.into()),
15074    ));
15075
15076    let text = r#"
15077        #[cfg(test)]
15078        mod tests() {
15079            #[test]
15080            fn runnable_1() {
15081                let a = 1;
15082            }
15083
15084            #[test]
15085            fn runnable_2() {
15086                let a = 1;
15087                let b = 2;
15088            }
15089        }
15090    "#
15091    .unindent();
15092
15093    let fs = FakeFs::new(cx.executor());
15094    fs.insert_file("/file.rs", Default::default()).await;
15095
15096    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15097    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15098    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15099    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15100    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15101
15102    let editor = cx.new_window_entity(|window, cx| {
15103        Editor::new(
15104            EditorMode::Full,
15105            multi_buffer,
15106            Some(project.clone()),
15107            true,
15108            window,
15109            cx,
15110        )
15111    });
15112
15113    editor.update_in(cx, |editor, window, cx| {
15114        editor.tasks.insert(
15115            (buffer.read(cx).remote_id(), 3),
15116            RunnableTasks {
15117                templates: vec![],
15118                offset: MultiBufferOffset(43),
15119                column: 0,
15120                extra_variables: HashMap::default(),
15121                context_range: BufferOffset(43)..BufferOffset(85),
15122            },
15123        );
15124        editor.tasks.insert(
15125            (buffer.read(cx).remote_id(), 8),
15126            RunnableTasks {
15127                templates: vec![],
15128                offset: MultiBufferOffset(86),
15129                column: 0,
15130                extra_variables: HashMap::default(),
15131                context_range: BufferOffset(86)..BufferOffset(191),
15132            },
15133        );
15134
15135        // Test finding task when cursor is inside function body
15136        editor.change_selections(None, window, cx, |s| {
15137            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15138        });
15139        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15140        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15141
15142        // Test finding task when cursor is on function name
15143        editor.change_selections(None, window, cx, |s| {
15144            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15145        });
15146        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15147        assert_eq!(row, 8, "Should find task when cursor is on function name");
15148    });
15149}
15150
15151#[gpui::test]
15152async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
15153    init_test(cx, |_| {});
15154
15155    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15156    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15157    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15158
15159    let fs = FakeFs::new(cx.executor());
15160    fs.insert_tree(
15161        path!("/a"),
15162        json!({
15163            "first.rs": sample_text_1,
15164            "second.rs": sample_text_2,
15165            "third.rs": sample_text_3,
15166        }),
15167    )
15168    .await;
15169    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15170    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15171    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15172    let worktree = project.update(cx, |project, cx| {
15173        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15174        assert_eq!(worktrees.len(), 1);
15175        worktrees.pop().unwrap()
15176    });
15177    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15178
15179    let buffer_1 = project
15180        .update(cx, |project, cx| {
15181            project.open_buffer((worktree_id, "first.rs"), cx)
15182        })
15183        .await
15184        .unwrap();
15185    let buffer_2 = project
15186        .update(cx, |project, cx| {
15187            project.open_buffer((worktree_id, "second.rs"), cx)
15188        })
15189        .await
15190        .unwrap();
15191    let buffer_3 = project
15192        .update(cx, |project, cx| {
15193            project.open_buffer((worktree_id, "third.rs"), cx)
15194        })
15195        .await
15196        .unwrap();
15197
15198    let multi_buffer = cx.new(|cx| {
15199        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15200        multi_buffer.push_excerpts(
15201            buffer_1.clone(),
15202            [
15203                ExcerptRange {
15204                    context: Point::new(0, 0)..Point::new(3, 0),
15205                    primary: None,
15206                },
15207                ExcerptRange {
15208                    context: Point::new(5, 0)..Point::new(7, 0),
15209                    primary: None,
15210                },
15211                ExcerptRange {
15212                    context: Point::new(9, 0)..Point::new(10, 4),
15213                    primary: None,
15214                },
15215            ],
15216            cx,
15217        );
15218        multi_buffer.push_excerpts(
15219            buffer_2.clone(),
15220            [
15221                ExcerptRange {
15222                    context: Point::new(0, 0)..Point::new(3, 0),
15223                    primary: None,
15224                },
15225                ExcerptRange {
15226                    context: Point::new(5, 0)..Point::new(7, 0),
15227                    primary: None,
15228                },
15229                ExcerptRange {
15230                    context: Point::new(9, 0)..Point::new(10, 4),
15231                    primary: None,
15232                },
15233            ],
15234            cx,
15235        );
15236        multi_buffer.push_excerpts(
15237            buffer_3.clone(),
15238            [
15239                ExcerptRange {
15240                    context: Point::new(0, 0)..Point::new(3, 0),
15241                    primary: None,
15242                },
15243                ExcerptRange {
15244                    context: Point::new(5, 0)..Point::new(7, 0),
15245                    primary: None,
15246                },
15247                ExcerptRange {
15248                    context: Point::new(9, 0)..Point::new(10, 4),
15249                    primary: None,
15250                },
15251            ],
15252            cx,
15253        );
15254        multi_buffer
15255    });
15256    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15257        Editor::new(
15258            EditorMode::Full,
15259            multi_buffer,
15260            Some(project.clone()),
15261            true,
15262            window,
15263            cx,
15264        )
15265    });
15266
15267    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";
15268    assert_eq!(
15269        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15270        full_text,
15271    );
15272
15273    multi_buffer_editor.update(cx, |editor, cx| {
15274        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15275    });
15276    assert_eq!(
15277        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15278        "\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",
15279        "After folding the first buffer, its text should not be displayed"
15280    );
15281
15282    multi_buffer_editor.update(cx, |editor, cx| {
15283        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15284    });
15285    assert_eq!(
15286        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15287        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15288        "After folding the second buffer, its text should not be displayed"
15289    );
15290
15291    multi_buffer_editor.update(cx, |editor, cx| {
15292        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15293    });
15294    assert_eq!(
15295        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15296        "\n\n\n\n\n",
15297        "After folding the third buffer, its text should not be displayed"
15298    );
15299
15300    // Emulate selection inside the fold logic, that should work
15301    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15302        editor
15303            .snapshot(window, cx)
15304            .next_line_boundary(Point::new(0, 4));
15305    });
15306
15307    multi_buffer_editor.update(cx, |editor, cx| {
15308        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15309    });
15310    assert_eq!(
15311        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15312        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15313        "After unfolding the second buffer, its text should be displayed"
15314    );
15315
15316    multi_buffer_editor.update(cx, |editor, cx| {
15317        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15318    });
15319    assert_eq!(
15320        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15321        "\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",
15322        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15323    );
15324
15325    multi_buffer_editor.update(cx, |editor, cx| {
15326        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15327    });
15328    assert_eq!(
15329        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15330        full_text,
15331        "After unfolding the all buffers, all original text should be displayed"
15332    );
15333}
15334
15335#[gpui::test]
15336async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15337    init_test(cx, |_| {});
15338
15339    let sample_text_1 = "1111\n2222\n3333".to_string();
15340    let sample_text_2 = "4444\n5555\n6666".to_string();
15341    let sample_text_3 = "7777\n8888\n9999".to_string();
15342
15343    let fs = FakeFs::new(cx.executor());
15344    fs.insert_tree(
15345        path!("/a"),
15346        json!({
15347            "first.rs": sample_text_1,
15348            "second.rs": sample_text_2,
15349            "third.rs": sample_text_3,
15350        }),
15351    )
15352    .await;
15353    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15354    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15355    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15356    let worktree = project.update(cx, |project, cx| {
15357        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15358        assert_eq!(worktrees.len(), 1);
15359        worktrees.pop().unwrap()
15360    });
15361    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15362
15363    let buffer_1 = project
15364        .update(cx, |project, cx| {
15365            project.open_buffer((worktree_id, "first.rs"), cx)
15366        })
15367        .await
15368        .unwrap();
15369    let buffer_2 = project
15370        .update(cx, |project, cx| {
15371            project.open_buffer((worktree_id, "second.rs"), cx)
15372        })
15373        .await
15374        .unwrap();
15375    let buffer_3 = project
15376        .update(cx, |project, cx| {
15377            project.open_buffer((worktree_id, "third.rs"), cx)
15378        })
15379        .await
15380        .unwrap();
15381
15382    let multi_buffer = cx.new(|cx| {
15383        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15384        multi_buffer.push_excerpts(
15385            buffer_1.clone(),
15386            [ExcerptRange {
15387                context: Point::new(0, 0)..Point::new(3, 0),
15388                primary: None,
15389            }],
15390            cx,
15391        );
15392        multi_buffer.push_excerpts(
15393            buffer_2.clone(),
15394            [ExcerptRange {
15395                context: Point::new(0, 0)..Point::new(3, 0),
15396                primary: None,
15397            }],
15398            cx,
15399        );
15400        multi_buffer.push_excerpts(
15401            buffer_3.clone(),
15402            [ExcerptRange {
15403                context: Point::new(0, 0)..Point::new(3, 0),
15404                primary: None,
15405            }],
15406            cx,
15407        );
15408        multi_buffer
15409    });
15410
15411    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15412        Editor::new(
15413            EditorMode::Full,
15414            multi_buffer,
15415            Some(project.clone()),
15416            true,
15417            window,
15418            cx,
15419        )
15420    });
15421
15422    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15423    assert_eq!(
15424        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15425        full_text,
15426    );
15427
15428    multi_buffer_editor.update(cx, |editor, cx| {
15429        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15430    });
15431    assert_eq!(
15432        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15433        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15434        "After folding the first buffer, its text should not be displayed"
15435    );
15436
15437    multi_buffer_editor.update(cx, |editor, cx| {
15438        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15439    });
15440
15441    assert_eq!(
15442        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15443        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15444        "After folding the second buffer, its text should not be displayed"
15445    );
15446
15447    multi_buffer_editor.update(cx, |editor, cx| {
15448        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15449    });
15450    assert_eq!(
15451        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15452        "\n\n\n\n\n",
15453        "After folding the third buffer, its text should not be displayed"
15454    );
15455
15456    multi_buffer_editor.update(cx, |editor, cx| {
15457        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15458    });
15459    assert_eq!(
15460        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15461        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15462        "After unfolding the second buffer, its text should be displayed"
15463    );
15464
15465    multi_buffer_editor.update(cx, |editor, cx| {
15466        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15467    });
15468    assert_eq!(
15469        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15470        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15471        "After unfolding the first buffer, its text should be displayed"
15472    );
15473
15474    multi_buffer_editor.update(cx, |editor, cx| {
15475        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15476    });
15477    assert_eq!(
15478        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15479        full_text,
15480        "After unfolding all buffers, all original text should be displayed"
15481    );
15482}
15483
15484#[gpui::test]
15485async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15486    init_test(cx, |_| {});
15487
15488    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15489
15490    let fs = FakeFs::new(cx.executor());
15491    fs.insert_tree(
15492        path!("/a"),
15493        json!({
15494            "main.rs": sample_text,
15495        }),
15496    )
15497    .await;
15498    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15499    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15500    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15501    let worktree = project.update(cx, |project, cx| {
15502        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15503        assert_eq!(worktrees.len(), 1);
15504        worktrees.pop().unwrap()
15505    });
15506    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15507
15508    let buffer_1 = project
15509        .update(cx, |project, cx| {
15510            project.open_buffer((worktree_id, "main.rs"), cx)
15511        })
15512        .await
15513        .unwrap();
15514
15515    let multi_buffer = cx.new(|cx| {
15516        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15517        multi_buffer.push_excerpts(
15518            buffer_1.clone(),
15519            [ExcerptRange {
15520                context: Point::new(0, 0)
15521                    ..Point::new(
15522                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15523                        0,
15524                    ),
15525                primary: None,
15526            }],
15527            cx,
15528        );
15529        multi_buffer
15530    });
15531    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15532        Editor::new(
15533            EditorMode::Full,
15534            multi_buffer,
15535            Some(project.clone()),
15536            true,
15537            window,
15538            cx,
15539        )
15540    });
15541
15542    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15543    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15544        enum TestHighlight {}
15545        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15546        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15547        editor.highlight_text::<TestHighlight>(
15548            vec![highlight_range.clone()],
15549            HighlightStyle::color(Hsla::green()),
15550            cx,
15551        );
15552        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15553    });
15554
15555    let full_text = format!("\n\n\n{sample_text}\n");
15556    assert_eq!(
15557        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15558        full_text,
15559    );
15560}
15561
15562#[gpui::test]
15563async fn test_inline_completion_text(cx: &mut TestAppContext) {
15564    init_test(cx, |_| {});
15565
15566    // Simple insertion
15567    assert_highlighted_edits(
15568        "Hello, world!",
15569        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15570        true,
15571        cx,
15572        |highlighted_edits, cx| {
15573            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15574            assert_eq!(highlighted_edits.highlights.len(), 1);
15575            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15576            assert_eq!(
15577                highlighted_edits.highlights[0].1.background_color,
15578                Some(cx.theme().status().created_background)
15579            );
15580        },
15581    )
15582    .await;
15583
15584    // Replacement
15585    assert_highlighted_edits(
15586        "This is a test.",
15587        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15588        false,
15589        cx,
15590        |highlighted_edits, cx| {
15591            assert_eq!(highlighted_edits.text, "That is a test.");
15592            assert_eq!(highlighted_edits.highlights.len(), 1);
15593            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15594            assert_eq!(
15595                highlighted_edits.highlights[0].1.background_color,
15596                Some(cx.theme().status().created_background)
15597            );
15598        },
15599    )
15600    .await;
15601
15602    // Multiple edits
15603    assert_highlighted_edits(
15604        "Hello, world!",
15605        vec![
15606            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15607            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15608        ],
15609        false,
15610        cx,
15611        |highlighted_edits, cx| {
15612            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15613            assert_eq!(highlighted_edits.highlights.len(), 2);
15614            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15615            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15616            assert_eq!(
15617                highlighted_edits.highlights[0].1.background_color,
15618                Some(cx.theme().status().created_background)
15619            );
15620            assert_eq!(
15621                highlighted_edits.highlights[1].1.background_color,
15622                Some(cx.theme().status().created_background)
15623            );
15624        },
15625    )
15626    .await;
15627
15628    // Multiple lines with edits
15629    assert_highlighted_edits(
15630        "First line\nSecond line\nThird line\nFourth line",
15631        vec![
15632            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15633            (
15634                Point::new(2, 0)..Point::new(2, 10),
15635                "New third line".to_string(),
15636            ),
15637            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15638        ],
15639        false,
15640        cx,
15641        |highlighted_edits, cx| {
15642            assert_eq!(
15643                highlighted_edits.text,
15644                "Second modified\nNew third line\nFourth updated line"
15645            );
15646            assert_eq!(highlighted_edits.highlights.len(), 3);
15647            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15648            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15649            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15650            for highlight in &highlighted_edits.highlights {
15651                assert_eq!(
15652                    highlight.1.background_color,
15653                    Some(cx.theme().status().created_background)
15654                );
15655            }
15656        },
15657    )
15658    .await;
15659}
15660
15661#[gpui::test]
15662async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15663    init_test(cx, |_| {});
15664
15665    // Deletion
15666    assert_highlighted_edits(
15667        "Hello, world!",
15668        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15669        true,
15670        cx,
15671        |highlighted_edits, cx| {
15672            assert_eq!(highlighted_edits.text, "Hello, world!");
15673            assert_eq!(highlighted_edits.highlights.len(), 1);
15674            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15675            assert_eq!(
15676                highlighted_edits.highlights[0].1.background_color,
15677                Some(cx.theme().status().deleted_background)
15678            );
15679        },
15680    )
15681    .await;
15682
15683    // Insertion
15684    assert_highlighted_edits(
15685        "Hello, world!",
15686        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15687        true,
15688        cx,
15689        |highlighted_edits, cx| {
15690            assert_eq!(highlighted_edits.highlights.len(), 1);
15691            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15692            assert_eq!(
15693                highlighted_edits.highlights[0].1.background_color,
15694                Some(cx.theme().status().created_background)
15695            );
15696        },
15697    )
15698    .await;
15699}
15700
15701async fn assert_highlighted_edits(
15702    text: &str,
15703    edits: Vec<(Range<Point>, String)>,
15704    include_deletions: bool,
15705    cx: &mut TestAppContext,
15706    assertion_fn: impl Fn(HighlightedText, &App),
15707) {
15708    let window = cx.add_window(|window, cx| {
15709        let buffer = MultiBuffer::build_simple(text, cx);
15710        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15711    });
15712    let cx = &mut VisualTestContext::from_window(*window, cx);
15713
15714    let (buffer, snapshot) = window
15715        .update(cx, |editor, _window, cx| {
15716            (
15717                editor.buffer().clone(),
15718                editor.buffer().read(cx).snapshot(cx),
15719            )
15720        })
15721        .unwrap();
15722
15723    let edits = edits
15724        .into_iter()
15725        .map(|(range, edit)| {
15726            (
15727                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15728                edit,
15729            )
15730        })
15731        .collect::<Vec<_>>();
15732
15733    let text_anchor_edits = edits
15734        .clone()
15735        .into_iter()
15736        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15737        .collect::<Vec<_>>();
15738
15739    let edit_preview = window
15740        .update(cx, |_, _window, cx| {
15741            buffer
15742                .read(cx)
15743                .as_singleton()
15744                .unwrap()
15745                .read(cx)
15746                .preview_edits(text_anchor_edits.into(), cx)
15747        })
15748        .unwrap()
15749        .await;
15750
15751    cx.update(|_window, cx| {
15752        let highlighted_edits = inline_completion_edit_text(
15753            &snapshot.as_singleton().unwrap().2,
15754            &edits,
15755            &edit_preview,
15756            include_deletions,
15757            cx,
15758        );
15759        assertion_fn(highlighted_edits, cx)
15760    });
15761}
15762
15763#[gpui::test]
15764async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15765    init_test(cx, |_| {});
15766    let capabilities = lsp::ServerCapabilities {
15767        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15768            prepare_provider: Some(true),
15769            work_done_progress_options: Default::default(),
15770        })),
15771        ..Default::default()
15772    };
15773    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15774
15775    cx.set_state(indoc! {"
15776        struct Fˇoo {}
15777    "});
15778
15779    cx.update_editor(|editor, _, cx| {
15780        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15781        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15782        editor.highlight_background::<DocumentHighlightRead>(
15783            &[highlight_range],
15784            |c| c.editor_document_highlight_read_background,
15785            cx,
15786        );
15787    });
15788
15789    let mut prepare_rename_handler =
15790        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15791            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15792                start: lsp::Position {
15793                    line: 0,
15794                    character: 7,
15795                },
15796                end: lsp::Position {
15797                    line: 0,
15798                    character: 10,
15799                },
15800            })))
15801        });
15802    let prepare_rename_task = cx
15803        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15804        .expect("Prepare rename was not started");
15805    prepare_rename_handler.next().await.unwrap();
15806    prepare_rename_task.await.expect("Prepare rename failed");
15807
15808    let mut rename_handler =
15809        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15810            let edit = lsp::TextEdit {
15811                range: lsp::Range {
15812                    start: lsp::Position {
15813                        line: 0,
15814                        character: 7,
15815                    },
15816                    end: lsp::Position {
15817                        line: 0,
15818                        character: 10,
15819                    },
15820                },
15821                new_text: "FooRenamed".to_string(),
15822            };
15823            Ok(Some(lsp::WorkspaceEdit::new(
15824                // Specify the same edit twice
15825                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15826            )))
15827        });
15828    let rename_task = cx
15829        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15830        .expect("Confirm rename was not started");
15831    rename_handler.next().await.unwrap();
15832    rename_task.await.expect("Confirm rename failed");
15833    cx.run_until_parked();
15834
15835    // Despite two edits, only one is actually applied as those are identical
15836    cx.assert_editor_state(indoc! {"
15837        struct FooRenamedˇ {}
15838    "});
15839}
15840
15841#[gpui::test]
15842async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15843    init_test(cx, |_| {});
15844    // These capabilities indicate that the server does not support prepare rename.
15845    let capabilities = lsp::ServerCapabilities {
15846        rename_provider: Some(lsp::OneOf::Left(true)),
15847        ..Default::default()
15848    };
15849    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15850
15851    cx.set_state(indoc! {"
15852        struct Fˇoo {}
15853    "});
15854
15855    cx.update_editor(|editor, _window, cx| {
15856        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15857        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15858        editor.highlight_background::<DocumentHighlightRead>(
15859            &[highlight_range],
15860            |c| c.editor_document_highlight_read_background,
15861            cx,
15862        );
15863    });
15864
15865    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15866        .expect("Prepare rename was not started")
15867        .await
15868        .expect("Prepare rename failed");
15869
15870    let mut rename_handler =
15871        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15872            let edit = lsp::TextEdit {
15873                range: lsp::Range {
15874                    start: lsp::Position {
15875                        line: 0,
15876                        character: 7,
15877                    },
15878                    end: lsp::Position {
15879                        line: 0,
15880                        character: 10,
15881                    },
15882                },
15883                new_text: "FooRenamed".to_string(),
15884            };
15885            Ok(Some(lsp::WorkspaceEdit::new(
15886                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15887            )))
15888        });
15889    let rename_task = cx
15890        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15891        .expect("Confirm rename was not started");
15892    rename_handler.next().await.unwrap();
15893    rename_task.await.expect("Confirm rename failed");
15894    cx.run_until_parked();
15895
15896    // Correct range is renamed, as `surrounding_word` is used to find it.
15897    cx.assert_editor_state(indoc! {"
15898        struct FooRenamedˇ {}
15899    "});
15900}
15901
15902fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15903    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15904    point..point
15905}
15906
15907fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15908    let (text, ranges) = marked_text_ranges(marked_text, true);
15909    assert_eq!(editor.text(cx), text);
15910    assert_eq!(
15911        editor.selections.ranges(cx),
15912        ranges,
15913        "Assert selections are {}",
15914        marked_text
15915    );
15916}
15917
15918pub fn handle_signature_help_request(
15919    cx: &mut EditorLspTestContext,
15920    mocked_response: lsp::SignatureHelp,
15921) -> impl Future<Output = ()> {
15922    let mut request =
15923        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15924            let mocked_response = mocked_response.clone();
15925            async move { Ok(Some(mocked_response)) }
15926        });
15927
15928    async move {
15929        request.next().await;
15930    }
15931}
15932
15933/// Handle completion request passing a marked string specifying where the completion
15934/// should be triggered from using '|' character, what range should be replaced, and what completions
15935/// should be returned using '<' and '>' to delimit the range
15936pub fn handle_completion_request(
15937    cx: &mut EditorLspTestContext,
15938    marked_string: &str,
15939    completions: Vec<&'static str>,
15940    counter: Arc<AtomicUsize>,
15941) -> impl Future<Output = ()> {
15942    let complete_from_marker: TextRangeMarker = '|'.into();
15943    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15944    let (_, mut marked_ranges) = marked_text_ranges_by(
15945        marked_string,
15946        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15947    );
15948
15949    let complete_from_position =
15950        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15951    let replace_range =
15952        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15953
15954    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15955        let completions = completions.clone();
15956        counter.fetch_add(1, atomic::Ordering::Release);
15957        async move {
15958            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15959            assert_eq!(
15960                params.text_document_position.position,
15961                complete_from_position
15962            );
15963            Ok(Some(lsp::CompletionResponse::Array(
15964                completions
15965                    .iter()
15966                    .map(|completion_text| lsp::CompletionItem {
15967                        label: completion_text.to_string(),
15968                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15969                            range: replace_range,
15970                            new_text: completion_text.to_string(),
15971                        })),
15972                        ..Default::default()
15973                    })
15974                    .collect(),
15975            )))
15976        }
15977    });
15978
15979    async move {
15980        request.next().await;
15981    }
15982}
15983
15984fn handle_resolve_completion_request(
15985    cx: &mut EditorLspTestContext,
15986    edits: Option<Vec<(&'static str, &'static str)>>,
15987) -> impl Future<Output = ()> {
15988    let edits = edits.map(|edits| {
15989        edits
15990            .iter()
15991            .map(|(marked_string, new_text)| {
15992                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15993                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15994                lsp::TextEdit::new(replace_range, new_text.to_string())
15995            })
15996            .collect::<Vec<_>>()
15997    });
15998
15999    let mut request =
16000        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16001            let edits = edits.clone();
16002            async move {
16003                Ok(lsp::CompletionItem {
16004                    additional_text_edits: edits,
16005                    ..Default::default()
16006                })
16007            }
16008        });
16009
16010    async move {
16011        request.next().await;
16012    }
16013}
16014
16015pub(crate) fn update_test_language_settings(
16016    cx: &mut TestAppContext,
16017    f: impl Fn(&mut AllLanguageSettingsContent),
16018) {
16019    cx.update(|cx| {
16020        SettingsStore::update_global(cx, |store, cx| {
16021            store.update_user_settings::<AllLanguageSettings>(cx, f);
16022        });
16023    });
16024}
16025
16026pub(crate) fn update_test_project_settings(
16027    cx: &mut TestAppContext,
16028    f: impl Fn(&mut ProjectSettings),
16029) {
16030    cx.update(|cx| {
16031        SettingsStore::update_global(cx, |store, cx| {
16032            store.update_user_settings::<ProjectSettings>(cx, f);
16033        });
16034    });
16035}
16036
16037pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16038    cx.update(|cx| {
16039        assets::Assets.load_test_fonts(cx);
16040        let store = SettingsStore::test(cx);
16041        cx.set_global(store);
16042        theme::init(theme::LoadThemes::JustBase, cx);
16043        release_channel::init(SemanticVersion::default(), cx);
16044        client::init_settings(cx);
16045        language::init(cx);
16046        Project::init_settings(cx);
16047        workspace::init_settings(cx);
16048        crate::init(cx);
16049    });
16050
16051    update_test_language_settings(cx, f);
16052}
16053
16054#[track_caller]
16055fn assert_hunk_revert(
16056    not_reverted_text_with_selections: &str,
16057    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16058    expected_reverted_text_with_selections: &str,
16059    base_text: &str,
16060    cx: &mut EditorLspTestContext,
16061) {
16062    cx.set_state(not_reverted_text_with_selections);
16063    cx.set_diff_base(base_text);
16064    cx.executor().run_until_parked();
16065
16066    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16067        let snapshot = editor.snapshot(window, cx);
16068        let reverted_hunk_statuses = snapshot
16069            .buffer_snapshot
16070            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16071            .map(|hunk| hunk.status())
16072            .collect::<Vec<_>>();
16073
16074        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
16075        reverted_hunk_statuses
16076    });
16077    cx.executor().run_until_parked();
16078    cx.assert_editor_state(expected_reverted_text_with_selections);
16079    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16080}