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 futures::StreamExt;
   11use gpui::{
   12    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use project::FakeFs;
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic;
   35use std::sync::atomic::AtomicUsize;
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use unindent::Unindent;
   38use util::{
   39    assert_set_eq,
   40    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   41};
   42use workspace::{
   43    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   44    NavigationEntry, ViewId,
   45};
   46
   47#[gpui::test]
   48fn test_edit_events(cx: &mut TestAppContext) {
   49    init_test(cx, |_| {});
   50
   51    let buffer = cx.new_model(|cx| {
   52        let mut buffer = language::Buffer::local("123456", cx);
   53        buffer.set_group_interval(Duration::from_secs(1));
   54        buffer
   55    });
   56
   57    let events = Rc::new(RefCell::new(Vec::new()));
   58    let editor1 = cx.add_window({
   59        let events = events.clone();
   60        |cx| {
   61            let view = cx.view().clone();
   62            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   63                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   64                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   65                _ => {}
   66            })
   67            .detach();
   68            Editor::for_buffer(buffer.clone(), None, cx)
   69        }
   70    });
   71
   72    let editor2 = cx.add_window({
   73        let events = events.clone();
   74        |cx| {
   75            cx.subscribe(
   76                &cx.view().clone(),
   77                move |_, _, event: &EditorEvent, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor2", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, cx)
   87        }
   88    });
   89
   90    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   91
   92    // Mutating editor 1 will emit an `Edited` event only for that editor.
   93    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   94    assert_eq!(
   95        mem::take(&mut *events.borrow_mut()),
   96        [
   97            ("editor1", "edited"),
   98            ("editor1", "buffer edited"),
   99            ("editor2", "buffer edited"),
  100        ]
  101    );
  102
  103    // Mutating editor 2 will emit an `Edited` event only for that editor.
  104    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor2", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  137    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor2", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // No event is emitted when the mutation is a no-op.
  159    _ = editor2.update(cx, |editor, cx| {
  160        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  161
  162        editor.backspace(&Backspace, cx);
  163    });
  164    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  165}
  166
  167#[gpui::test]
  168fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  169    init_test(cx, |_| {});
  170
  171    let mut now = Instant::now();
  172    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  173    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  174    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  175    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  176
  177    _ = editor.update(cx, |editor, cx| {
  178        editor.start_transaction_at(now, cx);
  179        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  180
  181        editor.insert("cd", cx);
  182        editor.end_transaction_at(now, cx);
  183        assert_eq!(editor.text(cx), "12cd56");
  184        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  185
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  188        editor.insert("e", cx);
  189        editor.end_transaction_at(now, cx);
  190        assert_eq!(editor.text(cx), "12cde6");
  191        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  192
  193        now += group_interval + Duration::from_millis(1);
  194        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  195
  196        // Simulate an edit in another editor
  197        buffer.update(cx, |buffer, cx| {
  198            buffer.start_transaction_at(now, cx);
  199            buffer.edit([(0..1, "a")], None, cx);
  200            buffer.edit([(1..1, "b")], None, cx);
  201            buffer.end_transaction_at(now, cx);
  202        });
  203
  204        assert_eq!(editor.text(cx), "ab2cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  206
  207        // Last transaction happened past the group interval in a different editor.
  208        // Undo it individually and don't restore selections.
  209        editor.undo(&Undo, cx);
  210        assert_eq!(editor.text(cx), "12cde6");
  211        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  212
  213        // First two transactions happened within the group interval in this editor.
  214        // Undo them together and restore selections.
  215        editor.undo(&Undo, cx);
  216        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  217        assert_eq!(editor.text(cx), "123456");
  218        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  219
  220        // Redo the first two transactions together.
  221        editor.redo(&Redo, cx);
  222        assert_eq!(editor.text(cx), "12cde6");
  223        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  224
  225        // Redo the last transaction on its own.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  229
  230        // Test empty transactions.
  231        editor.start_transaction_at(now, cx);
  232        editor.end_transaction_at(now, cx);
  233        editor.undo(&Undo, cx);
  234        assert_eq!(editor.text(cx), "12cde6");
  235    });
  236}
  237
  238#[gpui::test]
  239fn test_ime_composition(cx: &mut TestAppContext) {
  240    init_test(cx, |_| {});
  241
  242    let buffer = cx.new_model(|cx| {
  243        let mut buffer = language::Buffer::local("abcde", cx);
  244        // Ensure automatic grouping doesn't occur.
  245        buffer.set_group_interval(Duration::ZERO);
  246        buffer
  247    });
  248
  249    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  250    cx.add_window(|cx| {
  251        let mut editor = build_editor(buffer.clone(), cx);
  252
  253        // Start a new IME composition.
  254        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  255        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  257        assert_eq!(editor.text(cx), "äbcde");
  258        assert_eq!(
  259            editor.marked_text_ranges(cx),
  260            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  261        );
  262
  263        // Finalize IME composition.
  264        editor.replace_text_in_range(None, "ā", cx);
  265        assert_eq!(editor.text(cx), "ābcde");
  266        assert_eq!(editor.marked_text_ranges(cx), None);
  267
  268        // IME composition edits are grouped and are undone/redone at once.
  269        editor.undo(&Default::default(), cx);
  270        assert_eq!(editor.text(cx), "abcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272        editor.redo(&Default::default(), cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  278        assert_eq!(
  279            editor.marked_text_ranges(cx),
  280            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  281        );
  282
  283        // Undoing during an IME composition cancels it.
  284        editor.undo(&Default::default(), cx);
  285        assert_eq!(editor.text(cx), "ābcde");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287
  288        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  289        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  290        assert_eq!(editor.text(cx), "ābcdè");
  291        assert_eq!(
  292            editor.marked_text_ranges(cx),
  293            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  294        );
  295
  296        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  297        editor.replace_text_in_range(Some(4..999), "ę", cx);
  298        assert_eq!(editor.text(cx), "ābcdę");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition with multiple cursors.
  302        editor.change_selections(None, cx, |s| {
  303            s.select_ranges([
  304                OffsetUtf16(1)..OffsetUtf16(1),
  305                OffsetUtf16(3)..OffsetUtf16(3),
  306                OffsetUtf16(5)..OffsetUtf16(5),
  307            ])
  308        });
  309        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  310        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  311        assert_eq!(
  312            editor.marked_text_ranges(cx),
  313            Some(vec![
  314                OffsetUtf16(0)..OffsetUtf16(3),
  315                OffsetUtf16(4)..OffsetUtf16(7),
  316                OffsetUtf16(8)..OffsetUtf16(11)
  317            ])
  318        );
  319
  320        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  321        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  322        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  323        assert_eq!(
  324            editor.marked_text_ranges(cx),
  325            Some(vec![
  326                OffsetUtf16(1)..OffsetUtf16(2),
  327                OffsetUtf16(5)..OffsetUtf16(6),
  328                OffsetUtf16(9)..OffsetUtf16(10)
  329            ])
  330        );
  331
  332        // Finalize IME composition with multiple cursors.
  333        editor.replace_text_in_range(Some(9..10), "2", cx);
  334        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  335        assert_eq!(editor.marked_text_ranges(cx), None);
  336
  337        editor
  338    });
  339}
  340
  341#[gpui::test]
  342fn test_selection_with_mouse(cx: &mut TestAppContext) {
  343    init_test(cx, |_| {});
  344
  345    let editor = cx.add_window(|cx| {
  346        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  347        build_editor(buffer, cx)
  348    });
  349
  350    _ = editor.update(cx, |view, cx| {
  351        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  352    });
  353    assert_eq!(
  354        editor
  355            .update(cx, |view, cx| view.selections.display_ranges(cx))
  356            .unwrap(),
  357        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  358    );
  359
  360    _ = editor.update(cx, |view, cx| {
  361        view.update_selection(
  362            DisplayPoint::new(DisplayRow(3), 3),
  363            0,
  364            gpui::Point::<f32>::default(),
  365            cx,
  366        );
  367    });
  368
  369    assert_eq!(
  370        editor
  371            .update(cx, |view, cx| view.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  374    );
  375
  376    _ = editor.update(cx, |view, cx| {
  377        view.update_selection(
  378            DisplayPoint::new(DisplayRow(1), 1),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            cx,
  382        );
  383    });
  384
  385    assert_eq!(
  386        editor
  387            .update(cx, |view, cx| view.selections.display_ranges(cx))
  388            .unwrap(),
  389        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  390    );
  391
  392    _ = editor.update(cx, |view, cx| {
  393        view.end_selection(cx);
  394        view.update_selection(
  395            DisplayPoint::new(DisplayRow(3), 3),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            cx,
  399        );
  400    });
  401
  402    assert_eq!(
  403        editor
  404            .update(cx, |view, cx| view.selections.display_ranges(cx))
  405            .unwrap(),
  406        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  407    );
  408
  409    _ = editor.update(cx, |view, cx| {
  410        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  411        view.update_selection(
  412            DisplayPoint::new(DisplayRow(0), 0),
  413            0,
  414            gpui::Point::<f32>::default(),
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |view, cx| view.selections.display_ranges(cx))
  422            .unwrap(),
  423        [
  424            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  425            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  426        ]
  427    );
  428
  429    _ = editor.update(cx, |view, cx| {
  430        view.end_selection(cx);
  431    });
  432
  433    assert_eq!(
  434        editor
  435            .update(cx, |view, cx| view.selections.display_ranges(cx))
  436            .unwrap(),
  437        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  438    );
  439}
  440
  441#[gpui::test]
  442fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  443    init_test(cx, |_| {});
  444
  445    let editor = cx.add_window(|cx| {
  446        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  447        build_editor(buffer, cx)
  448    });
  449
  450    _ = editor.update(cx, |view, cx| {
  451        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.end_selection(cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    assert_eq!(
  467        editor
  468            .update(cx, |view, cx| view.selections.display_ranges(cx))
  469            .unwrap(),
  470        [
  471            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  472            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  473        ]
  474    );
  475
  476    _ = editor.update(cx, |view, cx| {
  477        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  478    });
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.end_selection(cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |view, cx| view.selections.display_ranges(cx))
  487            .unwrap(),
  488        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  489    );
  490}
  491
  492#[gpui::test]
  493fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  494    init_test(cx, |_| {});
  495
  496    let view = cx.add_window(|cx| {
  497        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  498        build_editor(buffer, cx)
  499    });
  500
  501    _ = view.update(cx, |view, cx| {
  502        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  503        assert_eq!(
  504            view.selections.display_ranges(cx),
  505            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  506        );
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.update_selection(
  511            DisplayPoint::new(DisplayRow(3), 3),
  512            0,
  513            gpui::Point::<f32>::default(),
  514            cx,
  515        );
  516        assert_eq!(
  517            view.selections.display_ranges(cx),
  518            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  519        );
  520    });
  521
  522    _ = view.update(cx, |view, cx| {
  523        view.cancel(&Cancel, cx);
  524        view.update_selection(
  525            DisplayPoint::new(DisplayRow(1), 1),
  526            0,
  527            gpui::Point::<f32>::default(),
  528            cx,
  529        );
  530        assert_eq!(
  531            view.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  533        );
  534    });
  535}
  536
  537#[gpui::test]
  538fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  539    init_test(cx, |_| {});
  540
  541    let view = cx.add_window(|cx| {
  542        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  543        build_editor(buffer, cx)
  544    });
  545
  546    _ = view.update(cx, |view, cx| {
  547        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  548        assert_eq!(
  549            view.selections.display_ranges(cx),
  550            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  551        );
  552
  553        view.move_down(&Default::default(), cx);
  554        assert_eq!(
  555            view.selections.display_ranges(cx),
  556            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  557        );
  558
  559        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  560        assert_eq!(
  561            view.selections.display_ranges(cx),
  562            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  563        );
  564
  565        view.move_up(&Default::default(), cx);
  566        assert_eq!(
  567            view.selections.display_ranges(cx),
  568            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  569        );
  570    });
  571}
  572
  573#[gpui::test]
  574fn test_clone(cx: &mut TestAppContext) {
  575    init_test(cx, |_| {});
  576
  577    let (text, selection_ranges) = marked_text_ranges(
  578        indoc! {"
  579            one
  580            two
  581            threeˇ
  582            four
  583            fiveˇ
  584        "},
  585        true,
  586    );
  587
  588    let editor = cx.add_window(|cx| {
  589        let buffer = MultiBuffer::build_simple(&text, cx);
  590        build_editor(buffer, cx)
  591    });
  592
  593    _ = editor.update(cx, |editor, cx| {
  594        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  595        editor.fold_ranges(
  596            [
  597                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  598                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  599            ],
  600            true,
  601            cx,
  602        );
  603    });
  604
  605    let cloned_editor = editor
  606        .update(cx, |editor, cx| {
  607            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  608        })
  609        .unwrap()
  610        .unwrap();
  611
  612    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  613    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614
  615    assert_eq!(
  616        cloned_editor
  617            .update(cx, |e, cx| e.display_text(cx))
  618            .unwrap(),
  619        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  620    );
  621    assert_eq!(
  622        cloned_snapshot
  623            .folds_in_range(0..text.len())
  624            .collect::<Vec<_>>(),
  625        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  626    );
  627    assert_set_eq!(
  628        cloned_editor
  629            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  630            .unwrap(),
  631        editor
  632            .update(cx, |editor, cx| editor.selections.ranges(cx))
  633            .unwrap()
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |e, cx| e.selections.display_ranges(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |e, cx| e.selections.display_ranges(cx))
  641            .unwrap()
  642    );
  643}
  644
  645#[gpui::test]
  646async fn test_navigation_history(cx: &mut TestAppContext) {
  647    init_test(cx, |_| {});
  648
  649    use workspace::item::Item;
  650
  651    let fs = FakeFs::new(cx.executor());
  652    let project = Project::test(fs, [], cx).await;
  653    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  654    let pane = workspace
  655        .update(cx, |workspace, _| workspace.active_pane().clone())
  656        .unwrap();
  657
  658    _ = workspace.update(cx, |_v, cx| {
  659        cx.new_view(|cx| {
  660            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  661            let mut editor = build_editor(buffer.clone(), cx);
  662            let handle = cx.view();
  663            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  664
  665            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  666                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  667            }
  668
  669            // Move the cursor a small distance.
  670            // Nothing is added to the navigation history.
  671            editor.change_selections(None, cx, |s| {
  672                s.select_display_ranges([
  673                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  674                ])
  675            });
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  679                ])
  680            });
  681            assert!(pop_history(&mut editor, cx).is_none());
  682
  683            // Move the cursor a large distance.
  684            // The history can jump back to the previous position.
  685            editor.change_selections(None, cx, |s| {
  686                s.select_display_ranges([
  687                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  688                ])
  689            });
  690            let nav_entry = pop_history(&mut editor, cx).unwrap();
  691            editor.navigate(nav_entry.data.unwrap(), cx);
  692            assert_eq!(nav_entry.item.id(), cx.entity_id());
  693            assert_eq!(
  694                editor.selections.display_ranges(cx),
  695                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  696            );
  697            assert!(pop_history(&mut editor, cx).is_none());
  698
  699            // Move the cursor a small distance via the mouse.
  700            // Nothing is added to the navigation history.
  701            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  702            editor.end_selection(cx);
  703            assert_eq!(
  704                editor.selections.display_ranges(cx),
  705                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  706            );
  707            assert!(pop_history(&mut editor, cx).is_none());
  708
  709            // Move the cursor a large distance via the mouse.
  710            // The history can jump back to the previous position.
  711            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  712            editor.end_selection(cx);
  713            assert_eq!(
  714                editor.selections.display_ranges(cx),
  715                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  716            );
  717            let nav_entry = pop_history(&mut editor, cx).unwrap();
  718            editor.navigate(nav_entry.data.unwrap(), cx);
  719            assert_eq!(nav_entry.item.id(), cx.entity_id());
  720            assert_eq!(
  721                editor.selections.display_ranges(cx),
  722                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  723            );
  724            assert!(pop_history(&mut editor, cx).is_none());
  725
  726            // Set scroll position to check later
  727            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  728            let original_scroll_position = editor.scroll_manager.anchor();
  729
  730            // Jump to the end of the document and adjust scroll
  731            editor.move_to_end(&MoveToEnd, cx);
  732            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  733            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  734
  735            let nav_entry = pop_history(&mut editor, cx).unwrap();
  736            editor.navigate(nav_entry.data.unwrap(), cx);
  737            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  740            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  741            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  742            let invalid_point = Point::new(9999, 0);
  743            editor.navigate(
  744                Box::new(NavigationData {
  745                    cursor_anchor: invalid_anchor,
  746                    cursor_position: invalid_point,
  747                    scroll_anchor: ScrollAnchor {
  748                        anchor: invalid_anchor,
  749                        offset: Default::default(),
  750                    },
  751                    scroll_top_row: invalid_point.row,
  752                }),
  753                cx,
  754            );
  755            assert_eq!(
  756                editor.selections.display_ranges(cx),
  757                &[editor.max_point(cx)..editor.max_point(cx)]
  758            );
  759            assert_eq!(
  760                editor.scroll_position(cx),
  761                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  762            );
  763
  764            editor
  765        })
  766    });
  767}
  768
  769#[gpui::test]
  770fn test_cancel(cx: &mut TestAppContext) {
  771    init_test(cx, |_| {});
  772
  773    let view = cx.add_window(|cx| {
  774        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  775        build_editor(buffer, cx)
  776    });
  777
  778    _ = view.update(cx, |view, cx| {
  779        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  780        view.update_selection(
  781            DisplayPoint::new(DisplayRow(1), 1),
  782            0,
  783            gpui::Point::<f32>::default(),
  784            cx,
  785        );
  786        view.end_selection(cx);
  787
  788        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  789        view.update_selection(
  790            DisplayPoint::new(DisplayRow(0), 3),
  791            0,
  792            gpui::Point::<f32>::default(),
  793            cx,
  794        );
  795        view.end_selection(cx);
  796        assert_eq!(
  797            view.selections.display_ranges(cx),
  798            [
  799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  800                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  801            ]
  802        );
  803    });
  804
  805    _ = view.update(cx, |view, cx| {
  806        view.cancel(&Cancel, cx);
  807        assert_eq!(
  808            view.selections.display_ranges(cx),
  809            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820}
  821
  822#[gpui::test]
  823fn test_fold_action(cx: &mut TestAppContext) {
  824    init_test(cx, |_| {});
  825
  826    let view = cx.add_window(|cx| {
  827        let buffer = MultiBuffer::build_simple(
  828            &"
  829                impl Foo {
  830                    // Hello!
  831
  832                    fn a() {
  833                        1
  834                    }
  835
  836                    fn b() {
  837                        2
  838                    }
  839
  840                    fn c() {
  841                        3
  842                    }
  843                }
  844            "
  845            .unindent(),
  846            cx,
  847        );
  848        build_editor(buffer.clone(), cx)
  849    });
  850
  851    _ = view.update(cx, |view, cx| {
  852        view.change_selections(None, cx, |s| {
  853            s.select_display_ranges([
  854                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  855            ]);
  856        });
  857        view.fold(&Fold, cx);
  858        assert_eq!(
  859            view.display_text(cx),
  860            "
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {⋯
  869                    }
  870
  871                    fn c() {⋯
  872                    }
  873                }
  874            "
  875            .unindent(),
  876        );
  877
  878        view.fold(&Fold, cx);
  879        assert_eq!(
  880            view.display_text(cx),
  881            "
  882                impl Foo {⋯
  883                }
  884            "
  885            .unindent(),
  886        );
  887
  888        view.unfold_lines(&UnfoldLines, cx);
  889        assert_eq!(
  890            view.display_text(cx),
  891            "
  892                impl Foo {
  893                    // Hello!
  894
  895                    fn a() {
  896                        1
  897                    }
  898
  899                    fn b() {⋯
  900                    }
  901
  902                    fn c() {⋯
  903                    }
  904                }
  905            "
  906            .unindent(),
  907        );
  908
  909        view.unfold_lines(&UnfoldLines, cx);
  910        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  911    });
  912}
  913
  914#[gpui::test]
  915fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  916    init_test(cx, |_| {});
  917
  918    let view = cx.add_window(|cx| {
  919        let buffer = MultiBuffer::build_simple(
  920            &"
  921                class Foo:
  922                    # Hello!
  923
  924                    def a():
  925                        print(1)
  926
  927                    def b():
  928                        print(2)
  929
  930                    def c():
  931                        print(3)
  932            "
  933            .unindent(),
  934            cx,
  935        );
  936        build_editor(buffer.clone(), cx)
  937    });
  938
  939    _ = view.update(cx, |view, cx| {
  940        view.change_selections(None, cx, |s| {
  941            s.select_display_ranges([
  942                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  943            ]);
  944        });
  945        view.fold(&Fold, cx);
  946        assert_eq!(
  947            view.display_text(cx),
  948            "
  949                class Foo:
  950                    # Hello!
  951
  952                    def a():
  953                        print(1)
  954
  955                    def b():⋯
  956
  957                    def c():⋯
  958            "
  959            .unindent(),
  960        );
  961
  962        view.fold(&Fold, cx);
  963        assert_eq!(
  964            view.display_text(cx),
  965            "
  966                class Foo:⋯
  967            "
  968            .unindent(),
  969        );
  970
  971        view.unfold_lines(&UnfoldLines, cx);
  972        assert_eq!(
  973            view.display_text(cx),
  974            "
  975                class Foo:
  976                    # Hello!
  977
  978                    def a():
  979                        print(1)
  980
  981                    def b():⋯
  982
  983                    def c():⋯
  984            "
  985            .unindent(),
  986        );
  987
  988        view.unfold_lines(&UnfoldLines, cx);
  989        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  990    });
  991}
  992
  993#[gpui::test]
  994fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  995    init_test(cx, |_| {});
  996
  997    let view = cx.add_window(|cx| {
  998        let buffer = MultiBuffer::build_simple(
  999            &"
 1000                class Foo:
 1001                    # Hello!
 1002
 1003                    def a():
 1004                        print(1)
 1005
 1006                    def b():
 1007                        print(2)
 1008
 1009
 1010                    def c():
 1011                        print(3)
 1012
 1013
 1014            "
 1015            .unindent(),
 1016            cx,
 1017        );
 1018        build_editor(buffer.clone(), cx)
 1019    });
 1020
 1021    _ = view.update(cx, |view, cx| {
 1022        view.change_selections(None, cx, |s| {
 1023            s.select_display_ranges([
 1024                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1025            ]);
 1026        });
 1027        view.fold(&Fold, cx);
 1028        assert_eq!(
 1029            view.display_text(cx),
 1030            "
 1031                class Foo:
 1032                    # Hello!
 1033
 1034                    def a():
 1035                        print(1)
 1036
 1037                    def b():⋯
 1038
 1039
 1040                    def c():⋯
 1041
 1042
 1043            "
 1044            .unindent(),
 1045        );
 1046
 1047        view.fold(&Fold, cx);
 1048        assert_eq!(
 1049            view.display_text(cx),
 1050            "
 1051                class Foo:⋯
 1052
 1053
 1054            "
 1055            .unindent(),
 1056        );
 1057
 1058        view.unfold_lines(&UnfoldLines, cx);
 1059        assert_eq!(
 1060            view.display_text(cx),
 1061            "
 1062                class Foo:
 1063                    # Hello!
 1064
 1065                    def a():
 1066                        print(1)
 1067
 1068                    def b():⋯
 1069
 1070
 1071                    def c():⋯
 1072
 1073
 1074            "
 1075            .unindent(),
 1076        );
 1077
 1078        view.unfold_lines(&UnfoldLines, cx);
 1079        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1080    });
 1081}
 1082
 1083#[gpui::test]
 1084fn test_fold_at_level(cx: &mut TestAppContext) {
 1085    init_test(cx, |_| {});
 1086
 1087    let view = cx.add_window(|cx| {
 1088        let buffer = MultiBuffer::build_simple(
 1089            &"
 1090                class Foo:
 1091                    # Hello!
 1092
 1093                    def a():
 1094                        print(1)
 1095
 1096                    def b():
 1097                        print(2)
 1098
 1099
 1100                class Bar:
 1101                    # World!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():
 1107                        print(2)
 1108
 1109
 1110            "
 1111            .unindent(),
 1112            cx,
 1113        );
 1114        build_editor(buffer.clone(), cx)
 1115    });
 1116
 1117    _ = view.update(cx, |view, cx| {
 1118        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1119        assert_eq!(
 1120            view.display_text(cx),
 1121            "
 1122                class Foo:
 1123                    # Hello!
 1124
 1125                    def a():⋯
 1126
 1127                    def b():⋯
 1128
 1129
 1130                class Bar:
 1131                    # World!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138            "
 1139            .unindent(),
 1140        );
 1141
 1142        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1143        assert_eq!(
 1144            view.display_text(cx),
 1145            "
 1146                class Foo:⋯
 1147
 1148
 1149                class Bar:⋯
 1150
 1151
 1152            "
 1153            .unindent(),
 1154        );
 1155
 1156        view.unfold_all(&UnfoldAll, cx);
 1157        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1158        assert_eq!(
 1159            view.display_text(cx),
 1160            "
 1161                class Foo:
 1162                    # Hello!
 1163
 1164                    def a():
 1165                        print(1)
 1166
 1167                    def b():
 1168                        print(2)
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():
 1175                        print(1)
 1176
 1177                    def b():
 1178                        print(2)
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1186    });
 1187}
 1188
 1189#[gpui::test]
 1190fn test_move_cursor(cx: &mut TestAppContext) {
 1191    init_test(cx, |_| {});
 1192
 1193    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1194    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1195
 1196    buffer.update(cx, |buffer, cx| {
 1197        buffer.edit(
 1198            vec![
 1199                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1200                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1201            ],
 1202            None,
 1203            cx,
 1204        );
 1205    });
 1206    _ = view.update(cx, |view, cx| {
 1207        assert_eq!(
 1208            view.selections.display_ranges(cx),
 1209            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1210        );
 1211
 1212        view.move_down(&MoveDown, cx);
 1213        assert_eq!(
 1214            view.selections.display_ranges(cx),
 1215            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1216        );
 1217
 1218        view.move_right(&MoveRight, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1222        );
 1223
 1224        view.move_left(&MoveLeft, cx);
 1225        assert_eq!(
 1226            view.selections.display_ranges(cx),
 1227            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1228        );
 1229
 1230        view.move_up(&MoveUp, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1234        );
 1235
 1236        view.move_to_end(&MoveToEnd, cx);
 1237        assert_eq!(
 1238            view.selections.display_ranges(cx),
 1239            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1240        );
 1241
 1242        view.move_to_beginning(&MoveToBeginning, cx);
 1243        assert_eq!(
 1244            view.selections.display_ranges(cx),
 1245            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1246        );
 1247
 1248        view.change_selections(None, cx, |s| {
 1249            s.select_display_ranges([
 1250                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1251            ]);
 1252        });
 1253        view.select_to_beginning(&SelectToBeginning, cx);
 1254        assert_eq!(
 1255            view.selections.display_ranges(cx),
 1256            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1257        );
 1258
 1259        view.select_to_end(&SelectToEnd, cx);
 1260        assert_eq!(
 1261            view.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1263        );
 1264    });
 1265}
 1266
 1267// TODO: Re-enable this test
 1268#[cfg(target_os = "macos")]
 1269#[gpui::test]
 1270fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1271    init_test(cx, |_| {});
 1272
 1273    let view = cx.add_window(|cx| {
 1274        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1275        build_editor(buffer.clone(), cx)
 1276    });
 1277
 1278    assert_eq!('ⓐ'.len_utf8(), 3);
 1279    assert_eq!('α'.len_utf8(), 2);
 1280
 1281    _ = view.update(cx, |view, cx| {
 1282        view.fold_ranges(
 1283            vec![
 1284                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1285                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1286                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1287            ],
 1288            true,
 1289            cx,
 1290        );
 1291        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1292
 1293        view.move_right(&MoveRight, cx);
 1294        assert_eq!(
 1295            view.selections.display_ranges(cx),
 1296            &[empty_range(0, "".len())]
 1297        );
 1298        view.move_right(&MoveRight, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(0, "ⓐⓑ".len())]
 1302        );
 1303        view.move_right(&MoveRight, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(0, "ⓐⓑ⋯".len())]
 1307        );
 1308
 1309        view.move_down(&MoveDown, cx);
 1310        assert_eq!(
 1311            view.selections.display_ranges(cx),
 1312            &[empty_range(1, "ab⋯e".len())]
 1313        );
 1314        view.move_left(&MoveLeft, cx);
 1315        assert_eq!(
 1316            view.selections.display_ranges(cx),
 1317            &[empty_range(1, "ab⋯".len())]
 1318        );
 1319        view.move_left(&MoveLeft, cx);
 1320        assert_eq!(
 1321            view.selections.display_ranges(cx),
 1322            &[empty_range(1, "ab".len())]
 1323        );
 1324        view.move_left(&MoveLeft, cx);
 1325        assert_eq!(
 1326            view.selections.display_ranges(cx),
 1327            &[empty_range(1, "a".len())]
 1328        );
 1329
 1330        view.move_down(&MoveDown, cx);
 1331        assert_eq!(
 1332            view.selections.display_ranges(cx),
 1333            &[empty_range(2, "α".len())]
 1334        );
 1335        view.move_right(&MoveRight, cx);
 1336        assert_eq!(
 1337            view.selections.display_ranges(cx),
 1338            &[empty_range(2, "αβ".len())]
 1339        );
 1340        view.move_right(&MoveRight, cx);
 1341        assert_eq!(
 1342            view.selections.display_ranges(cx),
 1343            &[empty_range(2, "αβ⋯".len())]
 1344        );
 1345        view.move_right(&MoveRight, cx);
 1346        assert_eq!(
 1347            view.selections.display_ranges(cx),
 1348            &[empty_range(2, "αβ⋯ε".len())]
 1349        );
 1350
 1351        view.move_up(&MoveUp, cx);
 1352        assert_eq!(
 1353            view.selections.display_ranges(cx),
 1354            &[empty_range(1, "ab⋯e".len())]
 1355        );
 1356        view.move_down(&MoveDown, cx);
 1357        assert_eq!(
 1358            view.selections.display_ranges(cx),
 1359            &[empty_range(2, "αβ⋯ε".len())]
 1360        );
 1361        view.move_up(&MoveUp, cx);
 1362        assert_eq!(
 1363            view.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366
 1367        view.move_up(&MoveUp, cx);
 1368        assert_eq!(
 1369            view.selections.display_ranges(cx),
 1370            &[empty_range(0, "ⓐⓑ".len())]
 1371        );
 1372        view.move_left(&MoveLeft, cx);
 1373        assert_eq!(
 1374            view.selections.display_ranges(cx),
 1375            &[empty_range(0, "".len())]
 1376        );
 1377        view.move_left(&MoveLeft, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[empty_range(0, "".len())]
 1381        );
 1382    });
 1383}
 1384
 1385#[gpui::test]
 1386fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1387    init_test(cx, |_| {});
 1388
 1389    let view = cx.add_window(|cx| {
 1390        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1391        build_editor(buffer.clone(), cx)
 1392    });
 1393    _ = view.update(cx, |view, cx| {
 1394        view.change_selections(None, cx, |s| {
 1395            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1396        });
 1397        view.move_down(&MoveDown, cx);
 1398        assert_eq!(
 1399            view.selections.display_ranges(cx),
 1400            &[empty_range(1, "abcd".len())]
 1401        );
 1402
 1403        view.move_down(&MoveDown, cx);
 1404        assert_eq!(
 1405            view.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβγ".len())]
 1407        );
 1408
 1409        view.move_down(&MoveDown, cx);
 1410        assert_eq!(
 1411            view.selections.display_ranges(cx),
 1412            &[empty_range(3, "abcd".len())]
 1413        );
 1414
 1415        view.move_down(&MoveDown, cx);
 1416        assert_eq!(
 1417            view.selections.display_ranges(cx),
 1418            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1419        );
 1420
 1421        view.move_up(&MoveUp, cx);
 1422        assert_eq!(
 1423            view.selections.display_ranges(cx),
 1424            &[empty_range(3, "abcd".len())]
 1425        );
 1426
 1427        view.move_up(&MoveUp, cx);
 1428        assert_eq!(
 1429            view.selections.display_ranges(cx),
 1430            &[empty_range(2, "αβγ".len())]
 1431        );
 1432    });
 1433}
 1434
 1435#[gpui::test]
 1436fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1437    init_test(cx, |_| {});
 1438    let move_to_beg = MoveToBeginningOfLine {
 1439        stop_at_soft_wraps: true,
 1440    };
 1441
 1442    let move_to_end = MoveToEndOfLine {
 1443        stop_at_soft_wraps: true,
 1444    };
 1445
 1446    let view = cx.add_window(|cx| {
 1447        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1448        build_editor(buffer, cx)
 1449    });
 1450    _ = view.update(cx, |view, cx| {
 1451        view.change_selections(None, cx, |s| {
 1452            s.select_display_ranges([
 1453                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1455            ]);
 1456        });
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.move_to_beginning_of_line(&move_to_beg, cx);
 1461        assert_eq!(
 1462            view.selections.display_ranges(cx),
 1463            &[
 1464                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1465                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1466            ]
 1467        );
 1468    });
 1469
 1470    _ = view.update(cx, |view, cx| {
 1471        view.move_to_beginning_of_line(&move_to_beg, cx);
 1472        assert_eq!(
 1473            view.selections.display_ranges(cx),
 1474            &[
 1475                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1476                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1477            ]
 1478        );
 1479    });
 1480
 1481    _ = view.update(cx, |view, cx| {
 1482        view.move_to_beginning_of_line(&move_to_beg, cx);
 1483        assert_eq!(
 1484            view.selections.display_ranges(cx),
 1485            &[
 1486                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1487                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1488            ]
 1489        );
 1490    });
 1491
 1492    _ = view.update(cx, |view, cx| {
 1493        view.move_to_end_of_line(&move_to_end, cx);
 1494        assert_eq!(
 1495            view.selections.display_ranges(cx),
 1496            &[
 1497                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1498                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1499            ]
 1500        );
 1501    });
 1502
 1503    // Moving to the end of line again is a no-op.
 1504    _ = view.update(cx, |view, cx| {
 1505        view.move_to_end_of_line(&move_to_end, cx);
 1506        assert_eq!(
 1507            view.selections.display_ranges(cx),
 1508            &[
 1509                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1510                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1511            ]
 1512        );
 1513    });
 1514
 1515    _ = view.update(cx, |view, cx| {
 1516        view.move_left(&MoveLeft, cx);
 1517        view.select_to_beginning_of_line(
 1518            &SelectToBeginningOfLine {
 1519                stop_at_soft_wraps: true,
 1520            },
 1521            cx,
 1522        );
 1523        assert_eq!(
 1524            view.selections.display_ranges(cx),
 1525            &[
 1526                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1527                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1528            ]
 1529        );
 1530    });
 1531
 1532    _ = view.update(cx, |view, cx| {
 1533        view.select_to_beginning_of_line(
 1534            &SelectToBeginningOfLine {
 1535                stop_at_soft_wraps: true,
 1536            },
 1537            cx,
 1538        );
 1539        assert_eq!(
 1540            view.selections.display_ranges(cx),
 1541            &[
 1542                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1543                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1544            ]
 1545        );
 1546    });
 1547
 1548    _ = view.update(cx, |view, cx| {
 1549        view.select_to_beginning_of_line(
 1550            &SelectToBeginningOfLine {
 1551                stop_at_soft_wraps: true,
 1552            },
 1553            cx,
 1554        );
 1555        assert_eq!(
 1556            view.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = view.update(cx, |view, cx| {
 1565        view.select_to_end_of_line(
 1566            &SelectToEndOfLine {
 1567                stop_at_soft_wraps: true,
 1568            },
 1569            cx,
 1570        );
 1571        assert_eq!(
 1572            view.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1575                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = view.update(cx, |view, cx| {
 1581        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1582        assert_eq!(view.display_text(cx), "ab\n  de");
 1583        assert_eq!(
 1584            view.selections.display_ranges(cx),
 1585            &[
 1586                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1587                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1588            ]
 1589        );
 1590    });
 1591
 1592    _ = view.update(cx, |view, cx| {
 1593        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1594        assert_eq!(view.display_text(cx), "\n");
 1595        assert_eq!(
 1596            view.selections.display_ranges(cx),
 1597            &[
 1598                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1599                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1600            ]
 1601        );
 1602    });
 1603}
 1604
 1605#[gpui::test]
 1606fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1607    init_test(cx, |_| {});
 1608    let move_to_beg = MoveToBeginningOfLine {
 1609        stop_at_soft_wraps: false,
 1610    };
 1611
 1612    let move_to_end = MoveToEndOfLine {
 1613        stop_at_soft_wraps: false,
 1614    };
 1615
 1616    let view = cx.add_window(|cx| {
 1617        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1618        build_editor(buffer, cx)
 1619    });
 1620
 1621    _ = view.update(cx, |view, cx| {
 1622        view.set_wrap_width(Some(140.0.into()), cx);
 1623
 1624        // We expect the following lines after wrapping
 1625        // ```
 1626        // thequickbrownfox
 1627        // jumpedoverthelazydo
 1628        // gs
 1629        // ```
 1630        // The final `gs` was soft-wrapped onto a new line.
 1631        assert_eq!(
 1632            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1633            view.display_text(cx),
 1634        );
 1635
 1636        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1637        // Start the cursor at the `k` on the first line
 1638        view.change_selections(None, cx, |s| {
 1639            s.select_display_ranges([
 1640                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1641            ]);
 1642        });
 1643
 1644        // Moving to the beginning of the line should put us at the beginning of the line.
 1645        view.move_to_beginning_of_line(&move_to_beg, cx);
 1646        assert_eq!(
 1647            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1648            view.selections.display_ranges(cx)
 1649        );
 1650
 1651        // Moving to the end of the line should put us at the end of the line.
 1652        view.move_to_end_of_line(&move_to_end, cx);
 1653        assert_eq!(
 1654            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1655            view.selections.display_ranges(cx)
 1656        );
 1657
 1658        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1659        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1660        view.change_selections(None, cx, |s| {
 1661            s.select_display_ranges([
 1662                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1663            ]);
 1664        });
 1665
 1666        // Moving to the beginning of the line should put us at the start of the second line of
 1667        // display text, i.e., the `j`.
 1668        view.move_to_beginning_of_line(&move_to_beg, cx);
 1669        assert_eq!(
 1670            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1671            view.selections.display_ranges(cx)
 1672        );
 1673
 1674        // Moving to the beginning of the line again should be a no-op.
 1675        view.move_to_beginning_of_line(&move_to_beg, cx);
 1676        assert_eq!(
 1677            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1678            view.selections.display_ranges(cx)
 1679        );
 1680
 1681        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1682        // next display line.
 1683        view.move_to_end_of_line(&move_to_end, cx);
 1684        assert_eq!(
 1685            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1686            view.selections.display_ranges(cx)
 1687        );
 1688
 1689        // Moving to the end of the line again should be a no-op.
 1690        view.move_to_end_of_line(&move_to_end, cx);
 1691        assert_eq!(
 1692            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1693            view.selections.display_ranges(cx)
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701
 1702    let view = cx.add_window(|cx| {
 1703        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1704        build_editor(buffer, cx)
 1705    });
 1706    _ = view.update(cx, |view, cx| {
 1707        view.change_selections(None, cx, |s| {
 1708            s.select_display_ranges([
 1709                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1710                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1711            ])
 1712        });
 1713
 1714        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1715        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1716
 1717        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1718        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1719
 1720        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1721        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1722
 1723        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1724        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1725
 1726        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1727        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1728
 1729        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1730        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1731
 1732        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1733        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1734
 1735        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1736        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1737
 1738        view.move_right(&MoveRight, cx);
 1739        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1740        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1741
 1742        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1743        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1744
 1745        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1746        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1747    });
 1748}
 1749
 1750#[gpui::test]
 1751fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1752    init_test(cx, |_| {});
 1753
 1754    let view = cx.add_window(|cx| {
 1755        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1756        build_editor(buffer, cx)
 1757    });
 1758
 1759    _ = view.update(cx, |view, cx| {
 1760        view.set_wrap_width(Some(140.0.into()), cx);
 1761        assert_eq!(
 1762            view.display_text(cx),
 1763            "use one::{\n    two::three::\n    four::five\n};"
 1764        );
 1765
 1766        view.change_selections(None, cx, |s| {
 1767            s.select_display_ranges([
 1768                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1769            ]);
 1770        });
 1771
 1772        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1773        assert_eq!(
 1774            view.selections.display_ranges(cx),
 1775            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1776        );
 1777
 1778        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1779        assert_eq!(
 1780            view.selections.display_ranges(cx),
 1781            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1782        );
 1783
 1784        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1785        assert_eq!(
 1786            view.selections.display_ranges(cx),
 1787            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1788        );
 1789
 1790        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1791        assert_eq!(
 1792            view.selections.display_ranges(cx),
 1793            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1794        );
 1795
 1796        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1797        assert_eq!(
 1798            view.selections.display_ranges(cx),
 1799            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1800        );
 1801
 1802        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1803        assert_eq!(
 1804            view.selections.display_ranges(cx),
 1805            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1806        );
 1807    });
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814
 1815    let line_height = cx.editor(|editor, cx| {
 1816        editor
 1817            .style()
 1818            .unwrap()
 1819            .text
 1820            .line_height_in_pixels(cx.rem_size())
 1821    });
 1822    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827
 1828        three
 1829        fourˇ
 1830        five
 1831
 1832        six"#
 1833            .unindent(),
 1834    );
 1835
 1836    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1837    cx.assert_editor_state(
 1838        &r#"one
 1839        two
 1840        ˇ
 1841        three
 1842        four
 1843        five
 1844        ˇ
 1845        six"#
 1846            .unindent(),
 1847    );
 1848
 1849    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1850    cx.assert_editor_state(
 1851        &r#"one
 1852        two
 1853
 1854        three
 1855        four
 1856        five
 1857        ˇ
 1858        sixˇ"#
 1859            .unindent(),
 1860    );
 1861
 1862    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1863    cx.assert_editor_state(
 1864        &r#"one
 1865        two
 1866
 1867        three
 1868        four
 1869        five
 1870
 1871        sixˇ"#
 1872            .unindent(),
 1873    );
 1874
 1875    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1876    cx.assert_editor_state(
 1877        &r#"one
 1878        two
 1879
 1880        three
 1881        four
 1882        five
 1883        ˇ
 1884        six"#
 1885            .unindent(),
 1886    );
 1887
 1888    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1889    cx.assert_editor_state(
 1890        &r#"one
 1891        two
 1892        ˇ
 1893        three
 1894        four
 1895        five
 1896
 1897        six"#
 1898            .unindent(),
 1899    );
 1900
 1901    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1902    cx.assert_editor_state(
 1903        &r#"ˇone
 1904        two
 1905
 1906        three
 1907        four
 1908        five
 1909
 1910        six"#
 1911            .unindent(),
 1912    );
 1913}
 1914
 1915#[gpui::test]
 1916async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1917    init_test(cx, |_| {});
 1918    let mut cx = EditorTestContext::new(cx).await;
 1919    let line_height = cx.editor(|editor, cx| {
 1920        editor
 1921            .style()
 1922            .unwrap()
 1923            .text
 1924            .line_height_in_pixels(cx.rem_size())
 1925    });
 1926    let window = cx.window;
 1927    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1928
 1929    cx.set_state(
 1930        r#"ˇone
 1931        two
 1932        three
 1933        four
 1934        five
 1935        six
 1936        seven
 1937        eight
 1938        nine
 1939        ten
 1940        "#,
 1941    );
 1942
 1943    cx.update_editor(|editor, cx| {
 1944        assert_eq!(
 1945            editor.snapshot(cx).scroll_position(),
 1946            gpui::Point::new(0., 0.)
 1947        );
 1948        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1949        assert_eq!(
 1950            editor.snapshot(cx).scroll_position(),
 1951            gpui::Point::new(0., 3.)
 1952        );
 1953        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1954        assert_eq!(
 1955            editor.snapshot(cx).scroll_position(),
 1956            gpui::Point::new(0., 6.)
 1957        );
 1958        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1959        assert_eq!(
 1960            editor.snapshot(cx).scroll_position(),
 1961            gpui::Point::new(0., 3.)
 1962        );
 1963
 1964        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1965        assert_eq!(
 1966            editor.snapshot(cx).scroll_position(),
 1967            gpui::Point::new(0., 1.)
 1968        );
 1969        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1970        assert_eq!(
 1971            editor.snapshot(cx).scroll_position(),
 1972            gpui::Point::new(0., 3.)
 1973        );
 1974    });
 1975}
 1976
 1977#[gpui::test]
 1978async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1979    init_test(cx, |_| {});
 1980    let mut cx = EditorTestContext::new(cx).await;
 1981
 1982    let line_height = cx.update_editor(|editor, cx| {
 1983        editor.set_vertical_scroll_margin(2, cx);
 1984        editor
 1985            .style()
 1986            .unwrap()
 1987            .text
 1988            .line_height_in_pixels(cx.rem_size())
 1989    });
 1990    let window = cx.window;
 1991    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1992
 1993    cx.set_state(
 1994        r#"ˇone
 1995            two
 1996            three
 1997            four
 1998            five
 1999            six
 2000            seven
 2001            eight
 2002            nine
 2003            ten
 2004        "#,
 2005    );
 2006    cx.update_editor(|editor, cx| {
 2007        assert_eq!(
 2008            editor.snapshot(cx).scroll_position(),
 2009            gpui::Point::new(0., 0.0)
 2010        );
 2011    });
 2012
 2013    // Add a cursor below the visible area. Since both cursors cannot fit
 2014    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2015    // allows the vertical scroll margin below that cursor.
 2016    cx.update_editor(|editor, cx| {
 2017        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2018            selections.select_ranges([
 2019                Point::new(0, 0)..Point::new(0, 0),
 2020                Point::new(6, 0)..Point::new(6, 0),
 2021            ]);
 2022        })
 2023    });
 2024    cx.update_editor(|editor, cx| {
 2025        assert_eq!(
 2026            editor.snapshot(cx).scroll_position(),
 2027            gpui::Point::new(0., 3.0)
 2028        );
 2029    });
 2030
 2031    // Move down. The editor cursor scrolls down to track the newest cursor.
 2032    cx.update_editor(|editor, cx| {
 2033        editor.move_down(&Default::default(), cx);
 2034    });
 2035    cx.update_editor(|editor, cx| {
 2036        assert_eq!(
 2037            editor.snapshot(cx).scroll_position(),
 2038            gpui::Point::new(0., 4.0)
 2039        );
 2040    });
 2041
 2042    // Add a cursor above the visible area. Since both cursors fit on screen,
 2043    // the editor scrolls to show both.
 2044    cx.update_editor(|editor, cx| {
 2045        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2046            selections.select_ranges([
 2047                Point::new(1, 0)..Point::new(1, 0),
 2048                Point::new(6, 0)..Point::new(6, 0),
 2049            ]);
 2050        })
 2051    });
 2052    cx.update_editor(|editor, cx| {
 2053        assert_eq!(
 2054            editor.snapshot(cx).scroll_position(),
 2055            gpui::Point::new(0., 1.0)
 2056        );
 2057    });
 2058}
 2059
 2060#[gpui::test]
 2061async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2062    init_test(cx, |_| {});
 2063    let mut cx = EditorTestContext::new(cx).await;
 2064
 2065    let line_height = cx.editor(|editor, cx| {
 2066        editor
 2067            .style()
 2068            .unwrap()
 2069            .text
 2070            .line_height_in_pixels(cx.rem_size())
 2071    });
 2072    let window = cx.window;
 2073    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2074    cx.set_state(
 2075        &r#"
 2076        ˇone
 2077        two
 2078        threeˇ
 2079        four
 2080        five
 2081        six
 2082        seven
 2083        eight
 2084        nine
 2085        ten
 2086        "#
 2087        .unindent(),
 2088    );
 2089
 2090    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2091    cx.assert_editor_state(
 2092        &r#"
 2093        one
 2094        two
 2095        three
 2096        ˇfour
 2097        five
 2098        sixˇ
 2099        seven
 2100        eight
 2101        nine
 2102        ten
 2103        "#
 2104        .unindent(),
 2105    );
 2106
 2107    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2108    cx.assert_editor_state(
 2109        &r#"
 2110        one
 2111        two
 2112        three
 2113        four
 2114        five
 2115        six
 2116        ˇseven
 2117        eight
 2118        nineˇ
 2119        ten
 2120        "#
 2121        .unindent(),
 2122    );
 2123
 2124    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2125    cx.assert_editor_state(
 2126        &r#"
 2127        one
 2128        two
 2129        three
 2130        ˇfour
 2131        five
 2132        sixˇ
 2133        seven
 2134        eight
 2135        nine
 2136        ten
 2137        "#
 2138        .unindent(),
 2139    );
 2140
 2141    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2142    cx.assert_editor_state(
 2143        &r#"
 2144        ˇone
 2145        two
 2146        threeˇ
 2147        four
 2148        five
 2149        six
 2150        seven
 2151        eight
 2152        nine
 2153        ten
 2154        "#
 2155        .unindent(),
 2156    );
 2157
 2158    // Test select collapsing
 2159    cx.update_editor(|editor, cx| {
 2160        editor.move_page_down(&MovePageDown::default(), cx);
 2161        editor.move_page_down(&MovePageDown::default(), cx);
 2162        editor.move_page_down(&MovePageDown::default(), cx);
 2163    });
 2164    cx.assert_editor_state(
 2165        &r#"
 2166        one
 2167        two
 2168        three
 2169        four
 2170        five
 2171        six
 2172        seven
 2173        eight
 2174        nine
 2175        ˇten
 2176        ˇ"#
 2177        .unindent(),
 2178    );
 2179}
 2180
 2181#[gpui::test]
 2182async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2183    init_test(cx, |_| {});
 2184    let mut cx = EditorTestContext::new(cx).await;
 2185    cx.set_state("one «two threeˇ» four");
 2186    cx.update_editor(|editor, cx| {
 2187        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2188        assert_eq!(editor.text(cx), " four");
 2189    });
 2190}
 2191
 2192#[gpui::test]
 2193fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2194    init_test(cx, |_| {});
 2195
 2196    let view = cx.add_window(|cx| {
 2197        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2198        build_editor(buffer.clone(), cx)
 2199    });
 2200
 2201    _ = view.update(cx, |view, cx| {
 2202        view.change_selections(None, cx, |s| {
 2203            s.select_display_ranges([
 2204                // an empty selection - the preceding word fragment is deleted
 2205                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2206                // characters selected - they are deleted
 2207                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2208            ])
 2209        });
 2210        view.delete_to_previous_word_start(
 2211            &DeleteToPreviousWordStart {
 2212                ignore_newlines: false,
 2213            },
 2214            cx,
 2215        );
 2216        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2217    });
 2218
 2219    _ = view.update(cx, |view, cx| {
 2220        view.change_selections(None, cx, |s| {
 2221            s.select_display_ranges([
 2222                // an empty selection - the following word fragment is deleted
 2223                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2224                // characters selected - they are deleted
 2225                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2226            ])
 2227        });
 2228        view.delete_to_next_word_end(
 2229            &DeleteToNextWordEnd {
 2230                ignore_newlines: false,
 2231            },
 2232            cx,
 2233        );
 2234        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2235    });
 2236}
 2237
 2238#[gpui::test]
 2239fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2240    init_test(cx, |_| {});
 2241
 2242    let view = cx.add_window(|cx| {
 2243        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2244        build_editor(buffer.clone(), cx)
 2245    });
 2246    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2247        ignore_newlines: false,
 2248    };
 2249    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2250        ignore_newlines: true,
 2251    };
 2252
 2253    _ = view.update(cx, |view, cx| {
 2254        view.change_selections(None, cx, |s| {
 2255            s.select_display_ranges([
 2256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2257            ])
 2258        });
 2259        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2260        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2261        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2262        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2263        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2264        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2265        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2266        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2267        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2268        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2269        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_next_word_end = DeleteToNextWordEnd {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2286        ignore_newlines: true,
 2287    };
 2288
 2289    _ = view.update(cx, |view, cx| {
 2290        view.change_selections(None, cx, |s| {
 2291            s.select_display_ranges([
 2292                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2293            ])
 2294        });
 2295        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2296        assert_eq!(
 2297            view.buffer.read(cx).read(cx).text(),
 2298            "one\n   two\nthree\n   four"
 2299        );
 2300        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2301        assert_eq!(
 2302            view.buffer.read(cx).read(cx).text(),
 2303            "\n   two\nthree\n   four"
 2304        );
 2305        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2307        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2308        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2309        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2310        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2311        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2312        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317fn test_newline(cx: &mut TestAppContext) {
 2318    init_test(cx, |_| {});
 2319
 2320    let view = cx.add_window(|cx| {
 2321        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2322        build_editor(buffer.clone(), cx)
 2323    });
 2324
 2325    _ = view.update(cx, |view, cx| {
 2326        view.change_selections(None, cx, |s| {
 2327            s.select_display_ranges([
 2328                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2329                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2330                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2331            ])
 2332        });
 2333
 2334        view.newline(&Newline, cx);
 2335        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2336    });
 2337}
 2338
 2339#[gpui::test]
 2340fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2341    init_test(cx, |_| {});
 2342
 2343    let editor = cx.add_window(|cx| {
 2344        let buffer = MultiBuffer::build_simple(
 2345            "
 2346                a
 2347                b(
 2348                    X
 2349                )
 2350                c(
 2351                    X
 2352                )
 2353            "
 2354            .unindent()
 2355            .as_str(),
 2356            cx,
 2357        );
 2358        let mut editor = build_editor(buffer.clone(), cx);
 2359        editor.change_selections(None, cx, |s| {
 2360            s.select_ranges([
 2361                Point::new(2, 4)..Point::new(2, 5),
 2362                Point::new(5, 4)..Point::new(5, 5),
 2363            ])
 2364        });
 2365        editor
 2366    });
 2367
 2368    _ = editor.update(cx, |editor, cx| {
 2369        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2370        editor.buffer.update(cx, |buffer, cx| {
 2371            buffer.edit(
 2372                [
 2373                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2374                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2375                ],
 2376                None,
 2377                cx,
 2378            );
 2379            assert_eq!(
 2380                buffer.read(cx).text(),
 2381                "
 2382                    a
 2383                    b()
 2384                    c()
 2385                "
 2386                .unindent()
 2387            );
 2388        });
 2389        assert_eq!(
 2390            editor.selections.ranges(cx),
 2391            &[
 2392                Point::new(1, 2)..Point::new(1, 2),
 2393                Point::new(2, 2)..Point::new(2, 2),
 2394            ],
 2395        );
 2396
 2397        editor.newline(&Newline, cx);
 2398        assert_eq!(
 2399            editor.text(cx),
 2400            "
 2401                a
 2402                b(
 2403                )
 2404                c(
 2405                )
 2406            "
 2407            .unindent()
 2408        );
 2409
 2410        // The selections are moved after the inserted newlines
 2411        assert_eq!(
 2412            editor.selections.ranges(cx),
 2413            &[
 2414                Point::new(2, 0)..Point::new(2, 0),
 2415                Point::new(4, 0)..Point::new(4, 0),
 2416            ],
 2417        );
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2423    init_test(cx, |settings| {
 2424        settings.defaults.tab_size = NonZeroU32::new(4)
 2425    });
 2426
 2427    let language = Arc::new(
 2428        Language::new(
 2429            LanguageConfig::default(),
 2430            Some(tree_sitter_rust::LANGUAGE.into()),
 2431        )
 2432        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2433        .unwrap(),
 2434    );
 2435
 2436    let mut cx = EditorTestContext::new(cx).await;
 2437    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2438    cx.set_state(indoc! {"
 2439        const a: ˇA = (
 2440 2441                «const_functionˇ»(ˇ),
 2442                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2443 2444        ˇ);ˇ
 2445    "});
 2446
 2447    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2448    cx.assert_editor_state(indoc! {"
 2449        ˇ
 2450        const a: A = (
 2451            ˇ
 2452            (
 2453                ˇ
 2454                ˇ
 2455                const_function(),
 2456                ˇ
 2457                ˇ
 2458                ˇ
 2459                ˇ
 2460                something_else,
 2461                ˇ
 2462            )
 2463            ˇ
 2464            ˇ
 2465        );
 2466    "});
 2467}
 2468
 2469#[gpui::test]
 2470async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2471    init_test(cx, |settings| {
 2472        settings.defaults.tab_size = NonZeroU32::new(4)
 2473    });
 2474
 2475    let language = Arc::new(
 2476        Language::new(
 2477            LanguageConfig::default(),
 2478            Some(tree_sitter_rust::LANGUAGE.into()),
 2479        )
 2480        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2481        .unwrap(),
 2482    );
 2483
 2484    let mut cx = EditorTestContext::new(cx).await;
 2485    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2486    cx.set_state(indoc! {"
 2487        const a: ˇA = (
 2488 2489                «const_functionˇ»(ˇ),
 2490                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2491 2492        ˇ);ˇ
 2493    "});
 2494
 2495    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2496    cx.assert_editor_state(indoc! {"
 2497        const a: A = (
 2498            ˇ
 2499            (
 2500                ˇ
 2501                const_function(),
 2502                ˇ
 2503                ˇ
 2504                something_else,
 2505                ˇ
 2506                ˇ
 2507                ˇ
 2508                ˇ
 2509            )
 2510            ˇ
 2511        );
 2512        ˇ
 2513        ˇ
 2514    "});
 2515}
 2516
 2517#[gpui::test]
 2518async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2519    init_test(cx, |settings| {
 2520        settings.defaults.tab_size = NonZeroU32::new(4)
 2521    });
 2522
 2523    let language = Arc::new(Language::new(
 2524        LanguageConfig {
 2525            line_comments: vec!["//".into()],
 2526            ..LanguageConfig::default()
 2527        },
 2528        None,
 2529    ));
 2530    {
 2531        let mut cx = EditorTestContext::new(cx).await;
 2532        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533        cx.set_state(indoc! {"
 2534        // Fooˇ
 2535    "});
 2536
 2537        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2538        cx.assert_editor_state(indoc! {"
 2539        // Foo
 2540        //ˇ
 2541    "});
 2542        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2543        cx.set_state(indoc! {"
 2544        ˇ// Foo
 2545    "});
 2546        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2547        cx.assert_editor_state(indoc! {"
 2548
 2549        ˇ// Foo
 2550    "});
 2551    }
 2552    // Ensure that comment continuations can be disabled.
 2553    update_test_language_settings(cx, |settings| {
 2554        settings.defaults.extend_comment_on_newline = Some(false);
 2555    });
 2556    let mut cx = EditorTestContext::new(cx).await;
 2557    cx.set_state(indoc! {"
 2558        // Fooˇ
 2559    "});
 2560    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2561    cx.assert_editor_state(indoc! {"
 2562        // Foo
 2563        ˇ
 2564    "});
 2565}
 2566
 2567#[gpui::test]
 2568fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2569    init_test(cx, |_| {});
 2570
 2571    let editor = cx.add_window(|cx| {
 2572        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2573        let mut editor = build_editor(buffer.clone(), cx);
 2574        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2575        editor
 2576    });
 2577
 2578    _ = editor.update(cx, |editor, cx| {
 2579        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2580        editor.buffer.update(cx, |buffer, cx| {
 2581            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2582            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2583        });
 2584        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2585
 2586        editor.insert("Z", cx);
 2587        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2588
 2589        // The selections are moved after the inserted characters
 2590        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2591    });
 2592}
 2593
 2594#[gpui::test]
 2595async fn test_tab(cx: &mut gpui::TestAppContext) {
 2596    init_test(cx, |settings| {
 2597        settings.defaults.tab_size = NonZeroU32::new(3)
 2598    });
 2599
 2600    let mut cx = EditorTestContext::new(cx).await;
 2601    cx.set_state(indoc! {"
 2602        ˇabˇc
 2603        ˇ🏀ˇ🏀ˇefg
 2604 2605    "});
 2606    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2607    cx.assert_editor_state(indoc! {"
 2608           ˇab ˇc
 2609           ˇ🏀  ˇ🏀  ˇefg
 2610        d  ˇ
 2611    "});
 2612
 2613    cx.set_state(indoc! {"
 2614        a
 2615        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2616    "});
 2617    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2618    cx.assert_editor_state(indoc! {"
 2619        a
 2620           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2621    "});
 2622}
 2623
 2624#[gpui::test]
 2625async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2626    init_test(cx, |_| {});
 2627
 2628    let mut cx = EditorTestContext::new(cx).await;
 2629    let language = Arc::new(
 2630        Language::new(
 2631            LanguageConfig::default(),
 2632            Some(tree_sitter_rust::LANGUAGE.into()),
 2633        )
 2634        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2635        .unwrap(),
 2636    );
 2637    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2638
 2639    // cursors that are already at the suggested indent level insert
 2640    // a soft tab. cursors that are to the left of the suggested indent
 2641    // auto-indent their line.
 2642    cx.set_state(indoc! {"
 2643        ˇ
 2644        const a: B = (
 2645            c(
 2646                d(
 2647        ˇ
 2648                )
 2649        ˇ
 2650        ˇ    )
 2651        );
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655            ˇ
 2656        const a: B = (
 2657            c(
 2658                d(
 2659                    ˇ
 2660                )
 2661                ˇ
 2662            ˇ)
 2663        );
 2664    "});
 2665
 2666    // handle auto-indent when there are multiple cursors on the same line
 2667    cx.set_state(indoc! {"
 2668        const a: B = (
 2669            c(
 2670        ˇ    ˇ
 2671        ˇ    )
 2672        );
 2673    "});
 2674    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2675    cx.assert_editor_state(indoc! {"
 2676        const a: B = (
 2677            c(
 2678                ˇ
 2679            ˇ)
 2680        );
 2681    "});
 2682}
 2683
 2684#[gpui::test]
 2685async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2686    init_test(cx, |settings| {
 2687        settings.defaults.tab_size = NonZeroU32::new(4)
 2688    });
 2689
 2690    let language = Arc::new(
 2691        Language::new(
 2692            LanguageConfig::default(),
 2693            Some(tree_sitter_rust::LANGUAGE.into()),
 2694        )
 2695        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2696        .unwrap(),
 2697    );
 2698
 2699    let mut cx = EditorTestContext::new(cx).await;
 2700    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2701    cx.set_state(indoc! {"
 2702        fn a() {
 2703            if b {
 2704        \t ˇc
 2705            }
 2706        }
 2707    "});
 2708
 2709    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2710    cx.assert_editor_state(indoc! {"
 2711        fn a() {
 2712            if b {
 2713                ˇc
 2714            }
 2715        }
 2716    "});
 2717}
 2718
 2719#[gpui::test]
 2720async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2721    init_test(cx, |settings| {
 2722        settings.defaults.tab_size = NonZeroU32::new(4);
 2723    });
 2724
 2725    let mut cx = EditorTestContext::new(cx).await;
 2726
 2727    cx.set_state(indoc! {"
 2728          «oneˇ» «twoˇ»
 2729        three
 2730         four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734            «oneˇ» «twoˇ»
 2735        three
 2736         four
 2737    "});
 2738
 2739    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2740    cx.assert_editor_state(indoc! {"
 2741        «oneˇ» «twoˇ»
 2742        three
 2743         four
 2744    "});
 2745
 2746    // select across line ending
 2747    cx.set_state(indoc! {"
 2748        one two
 2749        t«hree
 2750        ˇ» four
 2751    "});
 2752    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2753    cx.assert_editor_state(indoc! {"
 2754        one two
 2755            t«hree
 2756        ˇ» four
 2757    "});
 2758
 2759    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2760    cx.assert_editor_state(indoc! {"
 2761        one two
 2762        t«hree
 2763        ˇ» four
 2764    "});
 2765
 2766    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2767    cx.set_state(indoc! {"
 2768        one two
 2769        ˇthree
 2770            four
 2771    "});
 2772    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2773    cx.assert_editor_state(indoc! {"
 2774        one two
 2775            ˇthree
 2776            four
 2777    "});
 2778
 2779    cx.set_state(indoc! {"
 2780        one two
 2781        ˇ    three
 2782            four
 2783    "});
 2784    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2785    cx.assert_editor_state(indoc! {"
 2786        one two
 2787        ˇthree
 2788            four
 2789    "});
 2790}
 2791
 2792#[gpui::test]
 2793async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2794    init_test(cx, |settings| {
 2795        settings.defaults.hard_tabs = Some(true);
 2796    });
 2797
 2798    let mut cx = EditorTestContext::new(cx).await;
 2799
 2800    // select two ranges on one line
 2801    cx.set_state(indoc! {"
 2802        «oneˇ» «twoˇ»
 2803        three
 2804        four
 2805    "});
 2806    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2807    cx.assert_editor_state(indoc! {"
 2808        \t«oneˇ» «twoˇ»
 2809        three
 2810        four
 2811    "});
 2812    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2813    cx.assert_editor_state(indoc! {"
 2814        \t\t«oneˇ» «twoˇ»
 2815        three
 2816        four
 2817    "});
 2818    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2819    cx.assert_editor_state(indoc! {"
 2820        \t«oneˇ» «twoˇ»
 2821        three
 2822        four
 2823    "});
 2824    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2825    cx.assert_editor_state(indoc! {"
 2826        «oneˇ» «twoˇ»
 2827        three
 2828        four
 2829    "});
 2830
 2831    // select across a line ending
 2832    cx.set_state(indoc! {"
 2833        one two
 2834        t«hree
 2835        ˇ»four
 2836    "});
 2837    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2838    cx.assert_editor_state(indoc! {"
 2839        one two
 2840        \tt«hree
 2841        ˇ»four
 2842    "});
 2843    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2844    cx.assert_editor_state(indoc! {"
 2845        one two
 2846        \t\tt«hree
 2847        ˇ»four
 2848    "});
 2849    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2850    cx.assert_editor_state(indoc! {"
 2851        one two
 2852        \tt«hree
 2853        ˇ»four
 2854    "});
 2855    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2856    cx.assert_editor_state(indoc! {"
 2857        one two
 2858        t«hree
 2859        ˇ»four
 2860    "});
 2861
 2862    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2863    cx.set_state(indoc! {"
 2864        one two
 2865        ˇthree
 2866        four
 2867    "});
 2868    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        one two
 2871        ˇthree
 2872        four
 2873    "});
 2874    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2875    cx.assert_editor_state(indoc! {"
 2876        one two
 2877        \tˇthree
 2878        four
 2879    "});
 2880    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2881    cx.assert_editor_state(indoc! {"
 2882        one two
 2883        ˇthree
 2884        four
 2885    "});
 2886}
 2887
 2888#[gpui::test]
 2889fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2890    init_test(cx, |settings| {
 2891        settings.languages.extend([
 2892            (
 2893                "TOML".into(),
 2894                LanguageSettingsContent {
 2895                    tab_size: NonZeroU32::new(2),
 2896                    ..Default::default()
 2897                },
 2898            ),
 2899            (
 2900                "Rust".into(),
 2901                LanguageSettingsContent {
 2902                    tab_size: NonZeroU32::new(4),
 2903                    ..Default::default()
 2904                },
 2905            ),
 2906        ]);
 2907    });
 2908
 2909    let toml_language = Arc::new(Language::new(
 2910        LanguageConfig {
 2911            name: "TOML".into(),
 2912            ..Default::default()
 2913        },
 2914        None,
 2915    ));
 2916    let rust_language = Arc::new(Language::new(
 2917        LanguageConfig {
 2918            name: "Rust".into(),
 2919            ..Default::default()
 2920        },
 2921        None,
 2922    ));
 2923
 2924    let toml_buffer =
 2925        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2926    let rust_buffer = cx.new_model(|cx| {
 2927        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2928    });
 2929    let multibuffer = cx.new_model(|cx| {
 2930        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2931        multibuffer.push_excerpts(
 2932            toml_buffer.clone(),
 2933            [ExcerptRange {
 2934                context: Point::new(0, 0)..Point::new(2, 0),
 2935                primary: None,
 2936            }],
 2937            cx,
 2938        );
 2939        multibuffer.push_excerpts(
 2940            rust_buffer.clone(),
 2941            [ExcerptRange {
 2942                context: Point::new(0, 0)..Point::new(1, 0),
 2943                primary: None,
 2944            }],
 2945            cx,
 2946        );
 2947        multibuffer
 2948    });
 2949
 2950    cx.add_window(|cx| {
 2951        let mut editor = build_editor(multibuffer, cx);
 2952
 2953        assert_eq!(
 2954            editor.text(cx),
 2955            indoc! {"
 2956                a = 1
 2957                b = 2
 2958
 2959                const c: usize = 3;
 2960            "}
 2961        );
 2962
 2963        select_ranges(
 2964            &mut editor,
 2965            indoc! {"
 2966                «aˇ» = 1
 2967                b = 2
 2968
 2969                «const c:ˇ» usize = 3;
 2970            "},
 2971            cx,
 2972        );
 2973
 2974        editor.tab(&Tab, cx);
 2975        assert_text_with_selections(
 2976            &mut editor,
 2977            indoc! {"
 2978                  «aˇ» = 1
 2979                b = 2
 2980
 2981                    «const c:ˇ» usize = 3;
 2982            "},
 2983            cx,
 2984        );
 2985        editor.tab_prev(&TabPrev, cx);
 2986        assert_text_with_selections(
 2987            &mut editor,
 2988            indoc! {"
 2989                «aˇ» = 1
 2990                b = 2
 2991
 2992                «const c:ˇ» usize = 3;
 2993            "},
 2994            cx,
 2995        );
 2996
 2997        editor
 2998    });
 2999}
 3000
 3001#[gpui::test]
 3002async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3003    init_test(cx, |_| {});
 3004
 3005    let mut cx = EditorTestContext::new(cx).await;
 3006
 3007    // Basic backspace
 3008    cx.set_state(indoc! {"
 3009        onˇe two three
 3010        fou«rˇ» five six
 3011        seven «ˇeight nine
 3012        »ten
 3013    "});
 3014    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3015    cx.assert_editor_state(indoc! {"
 3016        oˇe two three
 3017        fouˇ five six
 3018        seven ˇten
 3019    "});
 3020
 3021    // Test backspace inside and around indents
 3022    cx.set_state(indoc! {"
 3023        zero
 3024            ˇone
 3025                ˇtwo
 3026            ˇ ˇ ˇ  three
 3027        ˇ  ˇ  four
 3028    "});
 3029    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3030    cx.assert_editor_state(indoc! {"
 3031        zero
 3032        ˇone
 3033            ˇtwo
 3034        ˇ  threeˇ  four
 3035    "});
 3036
 3037    // Test backspace with line_mode set to true
 3038    cx.update_editor(|e, _| e.selections.line_mode = true);
 3039    cx.set_state(indoc! {"
 3040        The ˇquick ˇbrown
 3041        fox jumps over
 3042        the lazy dog
 3043        ˇThe qu«ick bˇ»rown"});
 3044    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3045    cx.assert_editor_state(indoc! {"
 3046        ˇfox jumps over
 3047        the lazy dogˇ"});
 3048}
 3049
 3050#[gpui::test]
 3051async fn test_delete(cx: &mut gpui::TestAppContext) {
 3052    init_test(cx, |_| {});
 3053
 3054    let mut cx = EditorTestContext::new(cx).await;
 3055    cx.set_state(indoc! {"
 3056        onˇe two three
 3057        fou«rˇ» five six
 3058        seven «ˇeight nine
 3059        »ten
 3060    "});
 3061    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3062    cx.assert_editor_state(indoc! {"
 3063        onˇ two three
 3064        fouˇ five six
 3065        seven ˇten
 3066    "});
 3067
 3068    // Test backspace with line_mode set to true
 3069    cx.update_editor(|e, _| e.selections.line_mode = true);
 3070    cx.set_state(indoc! {"
 3071        The ˇquick ˇbrown
 3072        fox «ˇjum»ps over
 3073        the lazy dog
 3074        ˇThe qu«ick bˇ»rown"});
 3075    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3076    cx.assert_editor_state("ˇthe lazy dogˇ");
 3077}
 3078
 3079#[gpui::test]
 3080fn test_delete_line(cx: &mut TestAppContext) {
 3081    init_test(cx, |_| {});
 3082
 3083    let view = cx.add_window(|cx| {
 3084        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3085        build_editor(buffer, cx)
 3086    });
 3087    _ = view.update(cx, |view, cx| {
 3088        view.change_selections(None, cx, |s| {
 3089            s.select_display_ranges([
 3090                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3091                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3092                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3093            ])
 3094        });
 3095        view.delete_line(&DeleteLine, cx);
 3096        assert_eq!(view.display_text(cx), "ghi");
 3097        assert_eq!(
 3098            view.selections.display_ranges(cx),
 3099            vec![
 3100                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3101                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3102            ]
 3103        );
 3104    });
 3105
 3106    let view = cx.add_window(|cx| {
 3107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3108        build_editor(buffer, cx)
 3109    });
 3110    _ = view.update(cx, |view, cx| {
 3111        view.change_selections(None, cx, |s| {
 3112            s.select_display_ranges([
 3113                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3114            ])
 3115        });
 3116        view.delete_line(&DeleteLine, cx);
 3117        assert_eq!(view.display_text(cx), "ghi\n");
 3118        assert_eq!(
 3119            view.selections.display_ranges(cx),
 3120            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3121        );
 3122    });
 3123}
 3124
 3125#[gpui::test]
 3126fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3127    init_test(cx, |_| {});
 3128
 3129    cx.add_window(|cx| {
 3130        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3131        let mut editor = build_editor(buffer.clone(), cx);
 3132        let buffer = buffer.read(cx).as_singleton().unwrap();
 3133
 3134        assert_eq!(
 3135            editor.selections.ranges::<Point>(cx),
 3136            &[Point::new(0, 0)..Point::new(0, 0)]
 3137        );
 3138
 3139        // When on single line, replace newline at end by space
 3140        editor.join_lines(&JoinLines, cx);
 3141        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3142        assert_eq!(
 3143            editor.selections.ranges::<Point>(cx),
 3144            &[Point::new(0, 3)..Point::new(0, 3)]
 3145        );
 3146
 3147        // When multiple lines are selected, remove newlines that are spanned by the selection
 3148        editor.change_selections(None, cx, |s| {
 3149            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3150        });
 3151        editor.join_lines(&JoinLines, cx);
 3152        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3153        assert_eq!(
 3154            editor.selections.ranges::<Point>(cx),
 3155            &[Point::new(0, 11)..Point::new(0, 11)]
 3156        );
 3157
 3158        // Undo should be transactional
 3159        editor.undo(&Undo, cx);
 3160        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3161        assert_eq!(
 3162            editor.selections.ranges::<Point>(cx),
 3163            &[Point::new(0, 5)..Point::new(2, 2)]
 3164        );
 3165
 3166        // When joining an empty line don't insert a space
 3167        editor.change_selections(None, cx, |s| {
 3168            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3169        });
 3170        editor.join_lines(&JoinLines, cx);
 3171        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3172        assert_eq!(
 3173            editor.selections.ranges::<Point>(cx),
 3174            [Point::new(2, 3)..Point::new(2, 3)]
 3175        );
 3176
 3177        // We can remove trailing newlines
 3178        editor.join_lines(&JoinLines, cx);
 3179        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3180        assert_eq!(
 3181            editor.selections.ranges::<Point>(cx),
 3182            [Point::new(2, 3)..Point::new(2, 3)]
 3183        );
 3184
 3185        // We don't blow up on the last line
 3186        editor.join_lines(&JoinLines, cx);
 3187        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3188        assert_eq!(
 3189            editor.selections.ranges::<Point>(cx),
 3190            [Point::new(2, 3)..Point::new(2, 3)]
 3191        );
 3192
 3193        // reset to test indentation
 3194        editor.buffer.update(cx, |buffer, cx| {
 3195            buffer.edit(
 3196                [
 3197                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3198                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3199                ],
 3200                None,
 3201                cx,
 3202            )
 3203        });
 3204
 3205        // We remove any leading spaces
 3206        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3207        editor.change_selections(None, cx, |s| {
 3208            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3209        });
 3210        editor.join_lines(&JoinLines, cx);
 3211        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3212
 3213        // We don't insert a space for a line containing only spaces
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3216
 3217        // We ignore any leading tabs
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3220
 3221        editor
 3222    });
 3223}
 3224
 3225#[gpui::test]
 3226fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3227    init_test(cx, |_| {});
 3228
 3229    cx.add_window(|cx| {
 3230        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3231        let mut editor = build_editor(buffer.clone(), cx);
 3232        let buffer = buffer.read(cx).as_singleton().unwrap();
 3233
 3234        editor.change_selections(None, cx, |s| {
 3235            s.select_ranges([
 3236                Point::new(0, 2)..Point::new(1, 1),
 3237                Point::new(1, 2)..Point::new(1, 2),
 3238                Point::new(3, 1)..Point::new(3, 2),
 3239            ])
 3240        });
 3241
 3242        editor.join_lines(&JoinLines, cx);
 3243        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3244
 3245        assert_eq!(
 3246            editor.selections.ranges::<Point>(cx),
 3247            [
 3248                Point::new(0, 7)..Point::new(0, 7),
 3249                Point::new(1, 3)..Point::new(1, 3)
 3250            ]
 3251        );
 3252        editor
 3253    });
 3254}
 3255
 3256#[gpui::test]
 3257async fn test_join_lines_with_git_diff_base(
 3258    executor: BackgroundExecutor,
 3259    cx: &mut gpui::TestAppContext,
 3260) {
 3261    init_test(cx, |_| {});
 3262
 3263    let mut cx = EditorTestContext::new(cx).await;
 3264
 3265    let diff_base = r#"
 3266        Line 0
 3267        Line 1
 3268        Line 2
 3269        Line 3
 3270        "#
 3271    .unindent();
 3272
 3273    cx.set_state(
 3274        &r#"
 3275        ˇLine 0
 3276        Line 1
 3277        Line 2
 3278        Line 3
 3279        "#
 3280        .unindent(),
 3281    );
 3282
 3283    cx.set_diff_base(Some(&diff_base));
 3284    executor.run_until_parked();
 3285
 3286    // Join lines
 3287    cx.update_editor(|editor, cx| {
 3288        editor.join_lines(&JoinLines, cx);
 3289    });
 3290    executor.run_until_parked();
 3291
 3292    cx.assert_editor_state(
 3293        &r#"
 3294        Line 0ˇ Line 1
 3295        Line 2
 3296        Line 3
 3297        "#
 3298        .unindent(),
 3299    );
 3300    // Join again
 3301    cx.update_editor(|editor, cx| {
 3302        editor.join_lines(&JoinLines, cx);
 3303    });
 3304    executor.run_until_parked();
 3305
 3306    cx.assert_editor_state(
 3307        &r#"
 3308        Line 0 Line 1ˇ Line 2
 3309        Line 3
 3310        "#
 3311        .unindent(),
 3312    );
 3313}
 3314
 3315#[gpui::test]
 3316async fn test_custom_newlines_cause_no_false_positive_diffs(
 3317    executor: BackgroundExecutor,
 3318    cx: &mut gpui::TestAppContext,
 3319) {
 3320    init_test(cx, |_| {});
 3321    let mut cx = EditorTestContext::new(cx).await;
 3322    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3323    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3324    executor.run_until_parked();
 3325
 3326    cx.update_editor(|editor, cx| {
 3327        assert_eq!(
 3328            editor
 3329                .buffer()
 3330                .read(cx)
 3331                .snapshot(cx)
 3332                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3333                .collect::<Vec<_>>(),
 3334            Vec::new(),
 3335            "Should not have any diffs for files with custom newlines"
 3336        );
 3337    });
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    // Test sort_lines_case_insensitive()
 3347    cx.set_state(indoc! {"
 3348        «z
 3349        y
 3350        x
 3351        Z
 3352        Y
 3353        Xˇ»
 3354    "});
 3355    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3356    cx.assert_editor_state(indoc! {"
 3357        «x
 3358        X
 3359        y
 3360        Y
 3361        z
 3362        Zˇ»
 3363    "});
 3364
 3365    // Test reverse_lines()
 3366    cx.set_state(indoc! {"
 3367        «5
 3368        4
 3369        3
 3370        2
 3371        1ˇ»
 3372    "});
 3373    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3374    cx.assert_editor_state(indoc! {"
 3375        «1
 3376        2
 3377        3
 3378        4
 3379        5ˇ»
 3380    "});
 3381
 3382    // Skip testing shuffle_line()
 3383
 3384    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3385    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3386
 3387    // Don't manipulate when cursor is on single line, but expand the selection
 3388    cx.set_state(indoc! {"
 3389        ddˇdd
 3390        ccc
 3391        bb
 3392        a
 3393    "});
 3394    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3395    cx.assert_editor_state(indoc! {"
 3396        «ddddˇ»
 3397        ccc
 3398        bb
 3399        a
 3400    "});
 3401
 3402    // Basic manipulate case
 3403    // Start selection moves to column 0
 3404    // End of selection shrinks to fit shorter line
 3405    cx.set_state(indoc! {"
 3406        dd«d
 3407        ccc
 3408        bb
 3409        aaaaaˇ»
 3410    "});
 3411    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3412    cx.assert_editor_state(indoc! {"
 3413        «aaaaa
 3414        bb
 3415        ccc
 3416        dddˇ»
 3417    "});
 3418
 3419    // Manipulate case with newlines
 3420    cx.set_state(indoc! {"
 3421        dd«d
 3422        ccc
 3423
 3424        bb
 3425        aaaaa
 3426
 3427        ˇ»
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «
 3432
 3433        aaaaa
 3434        bb
 3435        ccc
 3436        dddˇ»
 3437
 3438    "});
 3439
 3440    // Adding new line
 3441    cx.set_state(indoc! {"
 3442        aa«a
 3443        bbˇ»b
 3444    "});
 3445    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3446    cx.assert_editor_state(indoc! {"
 3447        «aaa
 3448        bbb
 3449        added_lineˇ»
 3450    "});
 3451
 3452    // Removing line
 3453    cx.set_state(indoc! {"
 3454        aa«a
 3455        bbbˇ»
 3456    "});
 3457    cx.update_editor(|e, cx| {
 3458        e.manipulate_lines(cx, |lines| {
 3459            lines.pop();
 3460        })
 3461    });
 3462    cx.assert_editor_state(indoc! {"
 3463        «aaaˇ»
 3464    "});
 3465
 3466    // Removing all lines
 3467    cx.set_state(indoc! {"
 3468        aa«a
 3469        bbbˇ»
 3470    "});
 3471    cx.update_editor(|e, cx| {
 3472        e.manipulate_lines(cx, |lines| {
 3473            lines.drain(..);
 3474        })
 3475    });
 3476    cx.assert_editor_state(indoc! {"
 3477        ˇ
 3478    "});
 3479}
 3480
 3481#[gpui::test]
 3482async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3483    init_test(cx, |_| {});
 3484
 3485    let mut cx = EditorTestContext::new(cx).await;
 3486
 3487    // Consider continuous selection as single selection
 3488    cx.set_state(indoc! {"
 3489        Aaa«aa
 3490        cˇ»c«c
 3491        bb
 3492        aaaˇ»aa
 3493    "});
 3494    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3495    cx.assert_editor_state(indoc! {"
 3496        «Aaaaa
 3497        ccc
 3498        bb
 3499        aaaaaˇ»
 3500    "});
 3501
 3502    cx.set_state(indoc! {"
 3503        Aaa«aa
 3504        cˇ»c«c
 3505        bb
 3506        aaaˇ»aa
 3507    "});
 3508    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3509    cx.assert_editor_state(indoc! {"
 3510        «Aaaaa
 3511        ccc
 3512        bbˇ»
 3513    "});
 3514
 3515    // Consider non continuous selection as distinct dedup operations
 3516    cx.set_state(indoc! {"
 3517        «aaaaa
 3518        bb
 3519        aaaaa
 3520        aaaaaˇ»
 3521
 3522        aaa«aaˇ»
 3523    "});
 3524    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3525    cx.assert_editor_state(indoc! {"
 3526        «aaaaa
 3527        bbˇ»
 3528
 3529        «aaaaaˇ»
 3530    "});
 3531}
 3532
 3533#[gpui::test]
 3534async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3535    init_test(cx, |_| {});
 3536
 3537    let mut cx = EditorTestContext::new(cx).await;
 3538
 3539    cx.set_state(indoc! {"
 3540        «Aaa
 3541        aAa
 3542        Aaaˇ»
 3543    "});
 3544    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3545    cx.assert_editor_state(indoc! {"
 3546        «Aaa
 3547        aAaˇ»
 3548    "});
 3549
 3550    cx.set_state(indoc! {"
 3551        «Aaa
 3552        aAa
 3553        aaAˇ»
 3554    "});
 3555    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «Aaaˇ»
 3558    "});
 3559}
 3560
 3561#[gpui::test]
 3562async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3563    init_test(cx, |_| {});
 3564
 3565    let mut cx = EditorTestContext::new(cx).await;
 3566
 3567    // Manipulate with multiple selections on a single line
 3568    cx.set_state(indoc! {"
 3569        dd«dd
 3570        cˇ»c«c
 3571        bb
 3572        aaaˇ»aa
 3573    "});
 3574    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «aaaaa
 3577        bb
 3578        ccc
 3579        ddddˇ»
 3580    "});
 3581
 3582    // Manipulate with multiple disjoin selections
 3583    cx.set_state(indoc! {"
 3584 3585        4
 3586        3
 3587        2
 3588        1ˇ»
 3589
 3590        dd«dd
 3591        ccc
 3592        bb
 3593        aaaˇ»aa
 3594    "});
 3595    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3596    cx.assert_editor_state(indoc! {"
 3597        «1
 3598        2
 3599        3
 3600        4
 3601        5ˇ»
 3602
 3603        «aaaaa
 3604        bb
 3605        ccc
 3606        ddddˇ»
 3607    "});
 3608
 3609    // Adding lines on each selection
 3610    cx.set_state(indoc! {"
 3611 3612        1ˇ»
 3613
 3614        bb«bb
 3615        aaaˇ»aa
 3616    "});
 3617    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3618    cx.assert_editor_state(indoc! {"
 3619        «2
 3620        1
 3621        added lineˇ»
 3622
 3623        «bbbb
 3624        aaaaa
 3625        added lineˇ»
 3626    "});
 3627
 3628    // Removing lines on each selection
 3629    cx.set_state(indoc! {"
 3630 3631        1ˇ»
 3632
 3633        bb«bb
 3634        aaaˇ»aa
 3635    "});
 3636    cx.update_editor(|e, cx| {
 3637        e.manipulate_lines(cx, |lines| {
 3638            lines.pop();
 3639        })
 3640    });
 3641    cx.assert_editor_state(indoc! {"
 3642        «2ˇ»
 3643
 3644        «bbbbˇ»
 3645    "});
 3646}
 3647
 3648#[gpui::test]
 3649async fn test_manipulate_text(cx: &mut TestAppContext) {
 3650    init_test(cx, |_| {});
 3651
 3652    let mut cx = EditorTestContext::new(cx).await;
 3653
 3654    // Test convert_to_upper_case()
 3655    cx.set_state(indoc! {"
 3656        «hello worldˇ»
 3657    "});
 3658    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3659    cx.assert_editor_state(indoc! {"
 3660        «HELLO WORLDˇ»
 3661    "});
 3662
 3663    // Test convert_to_lower_case()
 3664    cx.set_state(indoc! {"
 3665        «HELLO WORLDˇ»
 3666    "});
 3667    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3668    cx.assert_editor_state(indoc! {"
 3669        «hello worldˇ»
 3670    "});
 3671
 3672    // Test multiple line, single selection case
 3673    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3674    cx.set_state(indoc! {"
 3675        «The quick brown
 3676        fox jumps over
 3677        the lazy dogˇ»
 3678    "});
 3679    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3680    cx.assert_editor_state(indoc! {"
 3681        «The Quick Brown
 3682        Fox Jumps Over
 3683        The Lazy Dogˇ»
 3684    "});
 3685
 3686    // Test multiple line, single selection case
 3687    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3688    cx.set_state(indoc! {"
 3689        «The quick brown
 3690        fox jumps over
 3691        the lazy dogˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «TheQuickBrown
 3696        FoxJumpsOver
 3697        TheLazyDogˇ»
 3698    "});
 3699
 3700    // From here on out, test more complex cases of manipulate_text()
 3701
 3702    // Test no selection case - should affect words cursors are in
 3703    // Cursor at beginning, middle, and end of word
 3704    cx.set_state(indoc! {"
 3705        ˇhello big beauˇtiful worldˇ
 3706    "});
 3707    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3708    cx.assert_editor_state(indoc! {"
 3709        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3710    "});
 3711
 3712    // Test multiple selections on a single line and across multiple lines
 3713    cx.set_state(indoc! {"
 3714        «Theˇ» quick «brown
 3715        foxˇ» jumps «overˇ»
 3716        the «lazyˇ» dog
 3717    "});
 3718    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3719    cx.assert_editor_state(indoc! {"
 3720        «THEˇ» quick «BROWN
 3721        FOXˇ» jumps «OVERˇ»
 3722        the «LAZYˇ» dog
 3723    "});
 3724
 3725    // Test case where text length grows
 3726    cx.set_state(indoc! {"
 3727        «tschüߡ»
 3728    "});
 3729    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3730    cx.assert_editor_state(indoc! {"
 3731        «TSCHÜSSˇ»
 3732    "});
 3733
 3734    // Test to make sure we don't crash when text shrinks
 3735    cx.set_state(indoc! {"
 3736        aaa_bbbˇ
 3737    "});
 3738    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3739    cx.assert_editor_state(indoc! {"
 3740        «aaaBbbˇ»
 3741    "});
 3742
 3743    // Test to make sure we all aware of the fact that each word can grow and shrink
 3744    // Final selections should be aware of this fact
 3745    cx.set_state(indoc! {"
 3746        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3747    "});
 3748    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3749    cx.assert_editor_state(indoc! {"
 3750        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3751    "});
 3752
 3753    cx.set_state(indoc! {"
 3754        «hElLo, WoRld!ˇ»
 3755    "});
 3756    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3757    cx.assert_editor_state(indoc! {"
 3758        «HeLlO, wOrLD!ˇ»
 3759    "});
 3760}
 3761
 3762#[gpui::test]
 3763fn test_duplicate_line(cx: &mut TestAppContext) {
 3764    init_test(cx, |_| {});
 3765
 3766    let view = cx.add_window(|cx| {
 3767        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3768        build_editor(buffer, cx)
 3769    });
 3770    _ = view.update(cx, |view, cx| {
 3771        view.change_selections(None, cx, |s| {
 3772            s.select_display_ranges([
 3773                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3774                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3775                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3776                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3777            ])
 3778        });
 3779        view.duplicate_line_down(&DuplicateLineDown, cx);
 3780        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3781        assert_eq!(
 3782            view.selections.display_ranges(cx),
 3783            vec![
 3784                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3785                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3786                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3787                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3788            ]
 3789        );
 3790    });
 3791
 3792    let view = cx.add_window(|cx| {
 3793        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3794        build_editor(buffer, cx)
 3795    });
 3796    _ = view.update(cx, |view, cx| {
 3797        view.change_selections(None, cx, |s| {
 3798            s.select_display_ranges([
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3800                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3801            ])
 3802        });
 3803        view.duplicate_line_down(&DuplicateLineDown, cx);
 3804        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3805        assert_eq!(
 3806            view.selections.display_ranges(cx),
 3807            vec![
 3808                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3809                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3810            ]
 3811        );
 3812    });
 3813
 3814    // With `move_upwards` the selections stay in place, except for
 3815    // the lines inserted above them
 3816    let view = cx.add_window(|cx| {
 3817        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3818        build_editor(buffer, cx)
 3819    });
 3820    _ = view.update(cx, |view, cx| {
 3821        view.change_selections(None, cx, |s| {
 3822            s.select_display_ranges([
 3823                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3824                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3825                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3826                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3827            ])
 3828        });
 3829        view.duplicate_line_up(&DuplicateLineUp, cx);
 3830        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3831        assert_eq!(
 3832            view.selections.display_ranges(cx),
 3833            vec![
 3834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3835                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3836                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3837                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3838            ]
 3839        );
 3840    });
 3841
 3842    let view = cx.add_window(|cx| {
 3843        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3844        build_editor(buffer, cx)
 3845    });
 3846    _ = view.update(cx, |view, cx| {
 3847        view.change_selections(None, cx, |s| {
 3848            s.select_display_ranges([
 3849                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3850                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3851            ])
 3852        });
 3853        view.duplicate_line_up(&DuplicateLineUp, cx);
 3854        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3855        assert_eq!(
 3856            view.selections.display_ranges(cx),
 3857            vec![
 3858                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3859                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3860            ]
 3861        );
 3862    });
 3863}
 3864
 3865#[gpui::test]
 3866fn test_move_line_up_down(cx: &mut TestAppContext) {
 3867    init_test(cx, |_| {});
 3868
 3869    let view = cx.add_window(|cx| {
 3870        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3871        build_editor(buffer, cx)
 3872    });
 3873    _ = view.update(cx, |view, cx| {
 3874        view.fold_ranges(
 3875            vec![
 3876                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3877                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3878                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3879            ],
 3880            true,
 3881            cx,
 3882        );
 3883        view.change_selections(None, cx, |s| {
 3884            s.select_display_ranges([
 3885                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3886                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3887                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3888                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3889            ])
 3890        });
 3891        assert_eq!(
 3892            view.display_text(cx),
 3893            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3894        );
 3895
 3896        view.move_line_up(&MoveLineUp, cx);
 3897        assert_eq!(
 3898            view.display_text(cx),
 3899            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3900        );
 3901        assert_eq!(
 3902            view.selections.display_ranges(cx),
 3903            vec![
 3904                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3905                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3906                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3907                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3908            ]
 3909        );
 3910    });
 3911
 3912    _ = view.update(cx, |view, cx| {
 3913        view.move_line_down(&MoveLineDown, cx);
 3914        assert_eq!(
 3915            view.display_text(cx),
 3916            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3917        );
 3918        assert_eq!(
 3919            view.selections.display_ranges(cx),
 3920            vec![
 3921                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3922                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3923                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3924                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3925            ]
 3926        );
 3927    });
 3928
 3929    _ = view.update(cx, |view, cx| {
 3930        view.move_line_down(&MoveLineDown, cx);
 3931        assert_eq!(
 3932            view.display_text(cx),
 3933            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3934        );
 3935        assert_eq!(
 3936            view.selections.display_ranges(cx),
 3937            vec![
 3938                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3939                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3940                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3941                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3942            ]
 3943        );
 3944    });
 3945
 3946    _ = view.update(cx, |view, cx| {
 3947        view.move_line_up(&MoveLineUp, cx);
 3948        assert_eq!(
 3949            view.display_text(cx),
 3950            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3951        );
 3952        assert_eq!(
 3953            view.selections.display_ranges(cx),
 3954            vec![
 3955                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3956                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3957                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3958                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3959            ]
 3960        );
 3961    });
 3962}
 3963
 3964#[gpui::test]
 3965fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3966    init_test(cx, |_| {});
 3967
 3968    let editor = cx.add_window(|cx| {
 3969        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3970        build_editor(buffer, cx)
 3971    });
 3972    _ = editor.update(cx, |editor, cx| {
 3973        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3974        editor.insert_blocks(
 3975            [BlockProperties {
 3976                style: BlockStyle::Fixed,
 3977                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 3978                height: 1,
 3979                render: Box::new(|_| div().into_any()),
 3980                priority: 0,
 3981            }],
 3982            Some(Autoscroll::fit()),
 3983            cx,
 3984        );
 3985        editor.change_selections(None, cx, |s| {
 3986            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3987        });
 3988        editor.move_line_down(&MoveLineDown, cx);
 3989    });
 3990}
 3991
 3992#[gpui::test]
 3993fn test_transpose(cx: &mut TestAppContext) {
 3994    init_test(cx, |_| {});
 3995
 3996    _ = cx.add_window(|cx| {
 3997        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3998        editor.set_style(EditorStyle::default(), cx);
 3999        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4000        editor.transpose(&Default::default(), cx);
 4001        assert_eq!(editor.text(cx), "bac");
 4002        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4003
 4004        editor.transpose(&Default::default(), cx);
 4005        assert_eq!(editor.text(cx), "bca");
 4006        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4007
 4008        editor.transpose(&Default::default(), cx);
 4009        assert_eq!(editor.text(cx), "bac");
 4010        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4011
 4012        editor
 4013    });
 4014
 4015    _ = cx.add_window(|cx| {
 4016        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4017        editor.set_style(EditorStyle::default(), cx);
 4018        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4019        editor.transpose(&Default::default(), cx);
 4020        assert_eq!(editor.text(cx), "acb\nde");
 4021        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4022
 4023        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4024        editor.transpose(&Default::default(), cx);
 4025        assert_eq!(editor.text(cx), "acbd\ne");
 4026        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4027
 4028        editor.transpose(&Default::default(), cx);
 4029        assert_eq!(editor.text(cx), "acbde\n");
 4030        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4031
 4032        editor.transpose(&Default::default(), cx);
 4033        assert_eq!(editor.text(cx), "acbd\ne");
 4034        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4035
 4036        editor
 4037    });
 4038
 4039    _ = cx.add_window(|cx| {
 4040        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4041        editor.set_style(EditorStyle::default(), cx);
 4042        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4043        editor.transpose(&Default::default(), cx);
 4044        assert_eq!(editor.text(cx), "bacd\ne");
 4045        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4046
 4047        editor.transpose(&Default::default(), cx);
 4048        assert_eq!(editor.text(cx), "bcade\n");
 4049        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4050
 4051        editor.transpose(&Default::default(), cx);
 4052        assert_eq!(editor.text(cx), "bcda\ne");
 4053        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4054
 4055        editor.transpose(&Default::default(), cx);
 4056        assert_eq!(editor.text(cx), "bcade\n");
 4057        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4058
 4059        editor.transpose(&Default::default(), cx);
 4060        assert_eq!(editor.text(cx), "bcaed\n");
 4061        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4062
 4063        editor
 4064    });
 4065
 4066    _ = cx.add_window(|cx| {
 4067        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4068        editor.set_style(EditorStyle::default(), cx);
 4069        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4070        editor.transpose(&Default::default(), cx);
 4071        assert_eq!(editor.text(cx), "🏀🍐✋");
 4072        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4073
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "🏀✋🍐");
 4076        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "🏀🍐✋");
 4080        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4081
 4082        editor
 4083    });
 4084}
 4085
 4086#[gpui::test]
 4087async fn test_rewrap(cx: &mut TestAppContext) {
 4088    init_test(cx, |_| {});
 4089
 4090    let mut cx = EditorTestContext::new(cx).await;
 4091
 4092    {
 4093        let language = Arc::new(Language::new(
 4094            LanguageConfig {
 4095                line_comments: vec!["// ".into()],
 4096                ..LanguageConfig::default()
 4097            },
 4098            None,
 4099        ));
 4100        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4101
 4102        let unwrapped_text = indoc! {"
 4103            // ˇ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.
 4104        "};
 4105
 4106        let wrapped_text = indoc! {"
 4107            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4108            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4109            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4110            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4111            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4112            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4113            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4114            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4115            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4116            // porttitor id. Aliquam id accumsan eros.ˇ
 4117        "};
 4118
 4119        cx.set_state(unwrapped_text);
 4120        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4121        cx.assert_editor_state(wrapped_text);
 4122    }
 4123
 4124    // Test that rewrapping works inside of a selection
 4125    {
 4126        let language = Arc::new(Language::new(
 4127            LanguageConfig {
 4128                line_comments: vec!["// ".into()],
 4129                ..LanguageConfig::default()
 4130            },
 4131            None,
 4132        ));
 4133        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4134
 4135        let unwrapped_text = indoc! {"
 4136            «// 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.ˇ»
 4137        "};
 4138
 4139        let wrapped_text = indoc! {"
 4140            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4141            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4142            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4143            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4144            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4145            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4146            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4147            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4148            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4149            // porttitor id. Aliquam id accumsan eros.ˇ
 4150        "};
 4151
 4152        cx.set_state(unwrapped_text);
 4153        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4154        cx.assert_editor_state(wrapped_text);
 4155    }
 4156
 4157    // Test that cursors that expand to the same region are collapsed.
 4158    {
 4159        let language = Arc::new(Language::new(
 4160            LanguageConfig {
 4161                line_comments: vec!["// ".into()],
 4162                ..LanguageConfig::default()
 4163            },
 4164            None,
 4165        ));
 4166        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4167
 4168        let unwrapped_text = indoc! {"
 4169            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4170            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4171            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4172            // ˇ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.
 4173        "};
 4174
 4175        let wrapped_text = indoc! {"
 4176            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4177            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4178            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4179            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4180            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4181            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4182            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4183            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4184            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4185            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4186        "};
 4187
 4188        cx.set_state(unwrapped_text);
 4189        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4190        cx.assert_editor_state(wrapped_text);
 4191    }
 4192
 4193    // Test that non-contiguous selections are treated separately.
 4194    {
 4195        let language = Arc::new(Language::new(
 4196            LanguageConfig {
 4197                line_comments: vec!["// ".into()],
 4198                ..LanguageConfig::default()
 4199            },
 4200            None,
 4201        ));
 4202        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4203
 4204        let unwrapped_text = indoc! {"
 4205            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4206            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4207            //
 4208            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4209            // ˇ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.
 4210        "};
 4211
 4212        let wrapped_text = indoc! {"
 4213            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4214            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4215            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4216            //
 4217            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4218            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4219            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4220            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4221            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4222            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4223            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4224        "};
 4225
 4226        cx.set_state(unwrapped_text);
 4227        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4228        cx.assert_editor_state(wrapped_text);
 4229    }
 4230
 4231    // Test that different comment prefixes are supported.
 4232    {
 4233        let language = Arc::new(Language::new(
 4234            LanguageConfig {
 4235                line_comments: vec!["# ".into()],
 4236                ..LanguageConfig::default()
 4237            },
 4238            None,
 4239        ));
 4240        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4241
 4242        let unwrapped_text = indoc! {"
 4243            # ˇ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.
 4244        "};
 4245
 4246        let wrapped_text = indoc! {"
 4247            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4248            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4249            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4250            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4251            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4252            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4253            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4254            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4255            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4256            # accumsan eros.ˇ
 4257        "};
 4258
 4259        cx.set_state(unwrapped_text);
 4260        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4261        cx.assert_editor_state(wrapped_text);
 4262    }
 4263
 4264    // Test that rewrapping is ignored outside of comments in most languages.
 4265    {
 4266        let language = Arc::new(Language::new(
 4267            LanguageConfig {
 4268                line_comments: vec!["// ".into(), "/// ".into()],
 4269                ..LanguageConfig::default()
 4270            },
 4271            Some(tree_sitter_rust::LANGUAGE.into()),
 4272        ));
 4273        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4274
 4275        let unwrapped_text = indoc! {"
 4276            /// Adds two numbers.
 4277            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4278            fn add(a: u32, b: u32) -> u32 {
 4279                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ˇ
 4280            }
 4281        "};
 4282
 4283        let wrapped_text = indoc! {"
 4284            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4285            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4286            fn add(a: u32, b: u32) -> u32 {
 4287                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ˇ
 4288            }
 4289        "};
 4290
 4291        cx.set_state(unwrapped_text);
 4292        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4293        cx.assert_editor_state(wrapped_text);
 4294    }
 4295
 4296    // Test that rewrapping works in Markdown and Plain Text languages.
 4297    {
 4298        let markdown_language = Arc::new(Language::new(
 4299            LanguageConfig {
 4300                name: "Markdown".into(),
 4301                ..LanguageConfig::default()
 4302            },
 4303            None,
 4304        ));
 4305        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4306
 4307        let unwrapped_text = indoc! {"
 4308            # Hello
 4309
 4310            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.
 4311        "};
 4312
 4313        let wrapped_text = indoc! {"
 4314            # Hello
 4315
 4316            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4317            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4318            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4319            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4320            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4321            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4322            Integer sit amet scelerisque nisi.ˇ
 4323        "};
 4324
 4325        cx.set_state(unwrapped_text);
 4326        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4327        cx.assert_editor_state(wrapped_text);
 4328
 4329        let plaintext_language = Arc::new(Language::new(
 4330            LanguageConfig {
 4331                name: "Plain Text".into(),
 4332                ..LanguageConfig::default()
 4333            },
 4334            None,
 4335        ));
 4336        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4337
 4338        let unwrapped_text = indoc! {"
 4339            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.
 4340        "};
 4341
 4342        let wrapped_text = indoc! {"
 4343            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4344            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4345            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4346            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4347            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4348            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4349            Integer sit amet scelerisque nisi.ˇ
 4350        "};
 4351
 4352        cx.set_state(unwrapped_text);
 4353        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4354        cx.assert_editor_state(wrapped_text);
 4355    }
 4356
 4357    // Test rewrapping unaligned comments in a selection.
 4358    {
 4359        let language = Arc::new(Language::new(
 4360            LanguageConfig {
 4361                line_comments: vec!["// ".into(), "/// ".into()],
 4362                ..LanguageConfig::default()
 4363            },
 4364            Some(tree_sitter_rust::LANGUAGE.into()),
 4365        ));
 4366        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4367
 4368        let unwrapped_text = indoc! {"
 4369            fn foo() {
 4370                if true {
 4371            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4372            // Praesent semper egestas tellus id dignissim.ˇ»
 4373                    do_something();
 4374                } else {
 4375                    //
 4376                }
 4377            }
 4378        "};
 4379
 4380        let wrapped_text = indoc! {"
 4381            fn foo() {
 4382                if true {
 4383                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4384                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4385                    // egestas tellus id dignissim.ˇ
 4386                    do_something();
 4387                } else {
 4388                    //
 4389                }
 4390            }
 4391        "};
 4392
 4393        cx.set_state(unwrapped_text);
 4394        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4395        cx.assert_editor_state(wrapped_text);
 4396
 4397        let unwrapped_text = indoc! {"
 4398            fn foo() {
 4399                if true {
 4400            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4401            // Praesent semper egestas tellus id dignissim.»
 4402                    do_something();
 4403                } else {
 4404                    //
 4405                }
 4406
 4407            }
 4408        "};
 4409
 4410        let wrapped_text = indoc! {"
 4411            fn foo() {
 4412                if true {
 4413                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4414                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4415                    // egestas tellus id dignissim.ˇ
 4416                    do_something();
 4417                } else {
 4418                    //
 4419                }
 4420
 4421            }
 4422        "};
 4423
 4424        cx.set_state(unwrapped_text);
 4425        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4426        cx.assert_editor_state(wrapped_text);
 4427    }
 4428}
 4429
 4430#[gpui::test]
 4431async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4432    init_test(cx, |_| {});
 4433
 4434    let mut cx = EditorTestContext::new(cx).await;
 4435
 4436    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4437    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4438    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4439
 4440    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4441    cx.set_state("two ˇfour ˇsix ˇ");
 4442    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4443    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4444
 4445    // Paste again but with only two cursors. Since the number of cursors doesn't
 4446    // match the number of slices in the clipboard, the entire clipboard text
 4447    // is pasted at each cursor.
 4448    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4449    cx.update_editor(|e, cx| {
 4450        e.handle_input("( ", cx);
 4451        e.paste(&Paste, cx);
 4452        e.handle_input(") ", cx);
 4453    });
 4454    cx.assert_editor_state(
 4455        &([
 4456            "( one✅ ",
 4457            "three ",
 4458            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4459            "three ",
 4460            "five ) ˇ",
 4461        ]
 4462        .join("\n")),
 4463    );
 4464
 4465    // Cut with three selections, one of which is full-line.
 4466    cx.set_state(indoc! {"
 4467        1«2ˇ»3
 4468        4ˇ567
 4469        «8ˇ»9"});
 4470    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4471    cx.assert_editor_state(indoc! {"
 4472        1ˇ3
 4473        ˇ9"});
 4474
 4475    // Paste with three selections, noticing how the copied selection that was full-line
 4476    // gets inserted before the second cursor.
 4477    cx.set_state(indoc! {"
 4478        1ˇ3
 4479 4480        «oˇ»ne"});
 4481    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4482    cx.assert_editor_state(indoc! {"
 4483        12ˇ3
 4484        4567
 4485 4486        8ˇne"});
 4487
 4488    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4489    cx.set_state(indoc! {"
 4490        The quick brown
 4491        fox juˇmps over
 4492        the lazy dog"});
 4493    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4494    assert_eq!(
 4495        cx.read_from_clipboard()
 4496            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4497        Some("fox jumps over\n".to_string())
 4498    );
 4499
 4500    // Paste with three selections, noticing how the copied full-line selection is inserted
 4501    // before the empty selections but replaces the selection that is non-empty.
 4502    cx.set_state(indoc! {"
 4503        Tˇhe quick brown
 4504        «foˇ»x jumps over
 4505        tˇhe lazy dog"});
 4506    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4507    cx.assert_editor_state(indoc! {"
 4508        fox jumps over
 4509        Tˇhe quick brown
 4510        fox jumps over
 4511        ˇx jumps over
 4512        fox jumps over
 4513        tˇhe lazy dog"});
 4514}
 4515
 4516#[gpui::test]
 4517async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4518    init_test(cx, |_| {});
 4519
 4520    let mut cx = EditorTestContext::new(cx).await;
 4521    let language = Arc::new(Language::new(
 4522        LanguageConfig::default(),
 4523        Some(tree_sitter_rust::LANGUAGE.into()),
 4524    ));
 4525    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4526
 4527    // Cut an indented block, without the leading whitespace.
 4528    cx.set_state(indoc! {"
 4529        const a: B = (
 4530            c(),
 4531            «d(
 4532                e,
 4533                f
 4534            )ˇ»
 4535        );
 4536    "});
 4537    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4538    cx.assert_editor_state(indoc! {"
 4539        const a: B = (
 4540            c(),
 4541            ˇ
 4542        );
 4543    "});
 4544
 4545    // Paste it at the same position.
 4546    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4547    cx.assert_editor_state(indoc! {"
 4548        const a: B = (
 4549            c(),
 4550            d(
 4551                e,
 4552                f
 4553 4554        );
 4555    "});
 4556
 4557    // Paste it at a line with a lower indent level.
 4558    cx.set_state(indoc! {"
 4559        ˇ
 4560        const a: B = (
 4561            c(),
 4562        );
 4563    "});
 4564    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4565    cx.assert_editor_state(indoc! {"
 4566        d(
 4567            e,
 4568            f
 4569 4570        const a: B = (
 4571            c(),
 4572        );
 4573    "});
 4574
 4575    // Cut an indented block, with the leading whitespace.
 4576    cx.set_state(indoc! {"
 4577        const a: B = (
 4578            c(),
 4579        «    d(
 4580                e,
 4581                f
 4582            )
 4583        ˇ»);
 4584    "});
 4585    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4586    cx.assert_editor_state(indoc! {"
 4587        const a: B = (
 4588            c(),
 4589        ˇ);
 4590    "});
 4591
 4592    // Paste it at the same position.
 4593    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4594    cx.assert_editor_state(indoc! {"
 4595        const a: B = (
 4596            c(),
 4597            d(
 4598                e,
 4599                f
 4600            )
 4601        ˇ);
 4602    "});
 4603
 4604    // Paste it at a line with a higher indent level.
 4605    cx.set_state(indoc! {"
 4606        const a: B = (
 4607            c(),
 4608            d(
 4609                e,
 4610 4611            )
 4612        );
 4613    "});
 4614    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4615    cx.assert_editor_state(indoc! {"
 4616        const a: B = (
 4617            c(),
 4618            d(
 4619                e,
 4620                f    d(
 4621                    e,
 4622                    f
 4623                )
 4624        ˇ
 4625            )
 4626        );
 4627    "});
 4628}
 4629
 4630#[gpui::test]
 4631fn test_select_all(cx: &mut TestAppContext) {
 4632    init_test(cx, |_| {});
 4633
 4634    let view = cx.add_window(|cx| {
 4635        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4636        build_editor(buffer, cx)
 4637    });
 4638    _ = view.update(cx, |view, cx| {
 4639        view.select_all(&SelectAll, cx);
 4640        assert_eq!(
 4641            view.selections.display_ranges(cx),
 4642            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4643        );
 4644    });
 4645}
 4646
 4647#[gpui::test]
 4648fn test_select_line(cx: &mut TestAppContext) {
 4649    init_test(cx, |_| {});
 4650
 4651    let view = cx.add_window(|cx| {
 4652        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4653        build_editor(buffer, cx)
 4654    });
 4655    _ = view.update(cx, |view, cx| {
 4656        view.change_selections(None, cx, |s| {
 4657            s.select_display_ranges([
 4658                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4659                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4660                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4661                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4662            ])
 4663        });
 4664        view.select_line(&SelectLine, cx);
 4665        assert_eq!(
 4666            view.selections.display_ranges(cx),
 4667            vec![
 4668                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4669                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4670            ]
 4671        );
 4672    });
 4673
 4674    _ = view.update(cx, |view, cx| {
 4675        view.select_line(&SelectLine, cx);
 4676        assert_eq!(
 4677            view.selections.display_ranges(cx),
 4678            vec![
 4679                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4680                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4681            ]
 4682        );
 4683    });
 4684
 4685    _ = view.update(cx, |view, cx| {
 4686        view.select_line(&SelectLine, cx);
 4687        assert_eq!(
 4688            view.selections.display_ranges(cx),
 4689            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4690        );
 4691    });
 4692}
 4693
 4694#[gpui::test]
 4695fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4696    init_test(cx, |_| {});
 4697
 4698    let view = cx.add_window(|cx| {
 4699        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4700        build_editor(buffer, cx)
 4701    });
 4702    _ = view.update(cx, |view, cx| {
 4703        view.fold_ranges(
 4704            vec![
 4705                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4706                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4707                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4708            ],
 4709            true,
 4710            cx,
 4711        );
 4712        view.change_selections(None, cx, |s| {
 4713            s.select_display_ranges([
 4714                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4715                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4716                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4717                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4718            ])
 4719        });
 4720        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4721    });
 4722
 4723    _ = view.update(cx, |view, cx| {
 4724        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4725        assert_eq!(
 4726            view.display_text(cx),
 4727            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4728        );
 4729        assert_eq!(
 4730            view.selections.display_ranges(cx),
 4731            [
 4732                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4733                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4734                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4735                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4736            ]
 4737        );
 4738    });
 4739
 4740    _ = view.update(cx, |view, cx| {
 4741        view.change_selections(None, cx, |s| {
 4742            s.select_display_ranges([
 4743                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4744            ])
 4745        });
 4746        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4747        assert_eq!(
 4748            view.display_text(cx),
 4749            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4750        );
 4751        assert_eq!(
 4752            view.selections.display_ranges(cx),
 4753            [
 4754                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4755                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4756                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4757                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4758                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4759                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4760                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4761                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4762            ]
 4763        );
 4764    });
 4765}
 4766
 4767#[gpui::test]
 4768async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4769    init_test(cx, |_| {});
 4770
 4771    let mut cx = EditorTestContext::new(cx).await;
 4772
 4773    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4774    cx.set_state(indoc!(
 4775        r#"abc
 4776           defˇghi
 4777
 4778           jk
 4779           nlmo
 4780           "#
 4781    ));
 4782
 4783    cx.update_editor(|editor, cx| {
 4784        editor.add_selection_above(&Default::default(), cx);
 4785    });
 4786
 4787    cx.assert_editor_state(indoc!(
 4788        r#"abcˇ
 4789           defˇghi
 4790
 4791           jk
 4792           nlmo
 4793           "#
 4794    ));
 4795
 4796    cx.update_editor(|editor, cx| {
 4797        editor.add_selection_above(&Default::default(), cx);
 4798    });
 4799
 4800    cx.assert_editor_state(indoc!(
 4801        r#"abcˇ
 4802            defˇghi
 4803
 4804            jk
 4805            nlmo
 4806            "#
 4807    ));
 4808
 4809    cx.update_editor(|view, cx| {
 4810        view.add_selection_below(&Default::default(), cx);
 4811    });
 4812
 4813    cx.assert_editor_state(indoc!(
 4814        r#"abc
 4815           defˇghi
 4816
 4817           jk
 4818           nlmo
 4819           "#
 4820    ));
 4821
 4822    cx.update_editor(|view, cx| {
 4823        view.undo_selection(&Default::default(), cx);
 4824    });
 4825
 4826    cx.assert_editor_state(indoc!(
 4827        r#"abcˇ
 4828           defˇghi
 4829
 4830           jk
 4831           nlmo
 4832           "#
 4833    ));
 4834
 4835    cx.update_editor(|view, cx| {
 4836        view.redo_selection(&Default::default(), cx);
 4837    });
 4838
 4839    cx.assert_editor_state(indoc!(
 4840        r#"abc
 4841           defˇghi
 4842
 4843           jk
 4844           nlmo
 4845           "#
 4846    ));
 4847
 4848    cx.update_editor(|view, cx| {
 4849        view.add_selection_below(&Default::default(), cx);
 4850    });
 4851
 4852    cx.assert_editor_state(indoc!(
 4853        r#"abc
 4854           defˇghi
 4855
 4856           jk
 4857           nlmˇo
 4858           "#
 4859    ));
 4860
 4861    cx.update_editor(|view, cx| {
 4862        view.add_selection_below(&Default::default(), cx);
 4863    });
 4864
 4865    cx.assert_editor_state(indoc!(
 4866        r#"abc
 4867           defˇghi
 4868
 4869           jk
 4870           nlmˇo
 4871           "#
 4872    ));
 4873
 4874    // change selections
 4875    cx.set_state(indoc!(
 4876        r#"abc
 4877           def«ˇg»hi
 4878
 4879           jk
 4880           nlmo
 4881           "#
 4882    ));
 4883
 4884    cx.update_editor(|view, cx| {
 4885        view.add_selection_below(&Default::default(), cx);
 4886    });
 4887
 4888    cx.assert_editor_state(indoc!(
 4889        r#"abc
 4890           def«ˇg»hi
 4891
 4892           jk
 4893           nlm«ˇo»
 4894           "#
 4895    ));
 4896
 4897    cx.update_editor(|view, cx| {
 4898        view.add_selection_below(&Default::default(), cx);
 4899    });
 4900
 4901    cx.assert_editor_state(indoc!(
 4902        r#"abc
 4903           def«ˇg»hi
 4904
 4905           jk
 4906           nlm«ˇo»
 4907           "#
 4908    ));
 4909
 4910    cx.update_editor(|view, cx| {
 4911        view.add_selection_above(&Default::default(), cx);
 4912    });
 4913
 4914    cx.assert_editor_state(indoc!(
 4915        r#"abc
 4916           def«ˇg»hi
 4917
 4918           jk
 4919           nlmo
 4920           "#
 4921    ));
 4922
 4923    cx.update_editor(|view, cx| {
 4924        view.add_selection_above(&Default::default(), cx);
 4925    });
 4926
 4927    cx.assert_editor_state(indoc!(
 4928        r#"abc
 4929           def«ˇg»hi
 4930
 4931           jk
 4932           nlmo
 4933           "#
 4934    ));
 4935
 4936    // Change selections again
 4937    cx.set_state(indoc!(
 4938        r#"a«bc
 4939           defgˇ»hi
 4940
 4941           jk
 4942           nlmo
 4943           "#
 4944    ));
 4945
 4946    cx.update_editor(|view, cx| {
 4947        view.add_selection_below(&Default::default(), cx);
 4948    });
 4949
 4950    cx.assert_editor_state(indoc!(
 4951        r#"a«bcˇ»
 4952           d«efgˇ»hi
 4953
 4954           j«kˇ»
 4955           nlmo
 4956           "#
 4957    ));
 4958
 4959    cx.update_editor(|view, cx| {
 4960        view.add_selection_below(&Default::default(), cx);
 4961    });
 4962    cx.assert_editor_state(indoc!(
 4963        r#"a«bcˇ»
 4964           d«efgˇ»hi
 4965
 4966           j«kˇ»
 4967           n«lmoˇ»
 4968           "#
 4969    ));
 4970    cx.update_editor(|view, cx| {
 4971        view.add_selection_above(&Default::default(), cx);
 4972    });
 4973
 4974    cx.assert_editor_state(indoc!(
 4975        r#"a«bcˇ»
 4976           d«efgˇ»hi
 4977
 4978           j«kˇ»
 4979           nlmo
 4980           "#
 4981    ));
 4982
 4983    // Change selections again
 4984    cx.set_state(indoc!(
 4985        r#"abc
 4986           d«ˇefghi
 4987
 4988           jk
 4989           nlm»o
 4990           "#
 4991    ));
 4992
 4993    cx.update_editor(|view, cx| {
 4994        view.add_selection_above(&Default::default(), cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"a«ˇbc»
 4999           d«ˇef»ghi
 5000
 5001           j«ˇk»
 5002           n«ˇlm»o
 5003           "#
 5004    ));
 5005
 5006    cx.update_editor(|view, cx| {
 5007        view.add_selection_below(&Default::default(), cx);
 5008    });
 5009
 5010    cx.assert_editor_state(indoc!(
 5011        r#"abc
 5012           d«ˇef»ghi
 5013
 5014           j«ˇk»
 5015           n«ˇlm»o
 5016           "#
 5017    ));
 5018}
 5019
 5020#[gpui::test]
 5021async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5022    init_test(cx, |_| {});
 5023
 5024    let mut cx = EditorTestContext::new(cx).await;
 5025    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5026
 5027    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5028        .unwrap();
 5029    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5030
 5031    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5032        .unwrap();
 5033    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5034
 5035    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5036    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5037
 5038    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5039    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5040
 5041    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5042        .unwrap();
 5043    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5044
 5045    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5046        .unwrap();
 5047    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5048}
 5049
 5050#[gpui::test]
 5051async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5052    init_test(cx, |_| {});
 5053
 5054    let mut cx = EditorTestContext::new(cx).await;
 5055    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5056
 5057    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5058        .unwrap();
 5059    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5060}
 5061
 5062#[gpui::test]
 5063async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5064    init_test(cx, |_| {});
 5065
 5066    let mut cx = EditorTestContext::new(cx).await;
 5067    cx.set_state(
 5068        r#"let foo = 2;
 5069lˇet foo = 2;
 5070let fooˇ = 2;
 5071let foo = 2;
 5072let foo = ˇ2;"#,
 5073    );
 5074
 5075    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5076        .unwrap();
 5077    cx.assert_editor_state(
 5078        r#"let foo = 2;
 5079«letˇ» foo = 2;
 5080let «fooˇ» = 2;
 5081let foo = 2;
 5082let foo = «2ˇ»;"#,
 5083    );
 5084
 5085    // noop for multiple selections with different contents
 5086    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5087        .unwrap();
 5088    cx.assert_editor_state(
 5089        r#"let foo = 2;
 5090«letˇ» foo = 2;
 5091let «fooˇ» = 2;
 5092let foo = 2;
 5093let foo = «2ˇ»;"#,
 5094    );
 5095}
 5096
 5097#[gpui::test]
 5098async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5099    init_test(cx, |_| {});
 5100
 5101    let mut cx =
 5102        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5103
 5104    cx.assert_editor_state(indoc! {"
 5105        ˇbbb
 5106        ccc
 5107
 5108        bbb
 5109        ccc
 5110        "});
 5111    cx.dispatch_action(SelectPrevious::default());
 5112    cx.assert_editor_state(indoc! {"
 5113                «bbbˇ»
 5114                ccc
 5115
 5116                bbb
 5117                ccc
 5118                "});
 5119    cx.dispatch_action(SelectPrevious::default());
 5120    cx.assert_editor_state(indoc! {"
 5121                «bbbˇ»
 5122                ccc
 5123
 5124                «bbbˇ»
 5125                ccc
 5126                "});
 5127}
 5128
 5129#[gpui::test]
 5130async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5131    init_test(cx, |_| {});
 5132
 5133    let mut cx = EditorTestContext::new(cx).await;
 5134    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5135
 5136    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5137        .unwrap();
 5138    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5139
 5140    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5141        .unwrap();
 5142    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5143
 5144    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5145    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5146
 5147    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5148    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5149
 5150    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5151        .unwrap();
 5152    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5153
 5154    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5155        .unwrap();
 5156    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5157
 5158    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5159        .unwrap();
 5160    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5161}
 5162
 5163#[gpui::test]
 5164async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5165    init_test(cx, |_| {});
 5166
 5167    let mut cx = EditorTestContext::new(cx).await;
 5168    cx.set_state(
 5169        r#"let foo = 2;
 5170lˇet foo = 2;
 5171let fooˇ = 2;
 5172let foo = 2;
 5173let foo = ˇ2;"#,
 5174    );
 5175
 5176    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5177        .unwrap();
 5178    cx.assert_editor_state(
 5179        r#"let foo = 2;
 5180«letˇ» foo = 2;
 5181let «fooˇ» = 2;
 5182let foo = 2;
 5183let foo = «2ˇ»;"#,
 5184    );
 5185
 5186    // noop for multiple selections with different contents
 5187    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5188        .unwrap();
 5189    cx.assert_editor_state(
 5190        r#"let foo = 2;
 5191«letˇ» foo = 2;
 5192let «fooˇ» = 2;
 5193let foo = 2;
 5194let foo = «2ˇ»;"#,
 5195    );
 5196}
 5197
 5198#[gpui::test]
 5199async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5200    init_test(cx, |_| {});
 5201
 5202    let mut cx = EditorTestContext::new(cx).await;
 5203    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5204
 5205    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5206        .unwrap();
 5207    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5208
 5209    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5210        .unwrap();
 5211    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5212
 5213    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5214    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5215
 5216    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5217    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5218
 5219    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5220        .unwrap();
 5221    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5222
 5223    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5224        .unwrap();
 5225    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5226}
 5227
 5228#[gpui::test]
 5229async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5230    init_test(cx, |_| {});
 5231
 5232    let language = Arc::new(Language::new(
 5233        LanguageConfig::default(),
 5234        Some(tree_sitter_rust::LANGUAGE.into()),
 5235    ));
 5236
 5237    let text = r#"
 5238        use mod1::mod2::{mod3, mod4};
 5239
 5240        fn fn_1(param1: bool, param2: &str) {
 5241            let var1 = "text";
 5242        }
 5243    "#
 5244    .unindent();
 5245
 5246    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5247    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5248    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5249
 5250    editor
 5251        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5252        .await;
 5253
 5254    editor.update(cx, |view, cx| {
 5255        view.change_selections(None, cx, |s| {
 5256            s.select_display_ranges([
 5257                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5258                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5259                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5260            ]);
 5261        });
 5262        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5263    });
 5264    editor.update(cx, |editor, cx| {
 5265        assert_text_with_selections(
 5266            editor,
 5267            indoc! {r#"
 5268                use mod1::mod2::{mod3, «mod4ˇ»};
 5269
 5270                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5271                    let var1 = "«textˇ»";
 5272                }
 5273            "#},
 5274            cx,
 5275        );
 5276    });
 5277
 5278    editor.update(cx, |view, cx| {
 5279        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5280    });
 5281    editor.update(cx, |editor, cx| {
 5282        assert_text_with_selections(
 5283            editor,
 5284            indoc! {r#"
 5285                use mod1::mod2::«{mod3, mod4}ˇ»;
 5286
 5287                «ˇfn fn_1(param1: bool, param2: &str) {
 5288                    let var1 = "text";
 5289 5290            "#},
 5291            cx,
 5292        );
 5293    });
 5294
 5295    editor.update(cx, |view, cx| {
 5296        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5297    });
 5298    assert_eq!(
 5299        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5300        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5301    );
 5302
 5303    // Trying to expand the selected syntax node one more time has no effect.
 5304    editor.update(cx, |view, cx| {
 5305        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5306    });
 5307    assert_eq!(
 5308        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5309        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5310    );
 5311
 5312    editor.update(cx, |view, cx| {
 5313        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5314    });
 5315    editor.update(cx, |editor, cx| {
 5316        assert_text_with_selections(
 5317            editor,
 5318            indoc! {r#"
 5319                use mod1::mod2::«{mod3, mod4}ˇ»;
 5320
 5321                «ˇfn fn_1(param1: bool, param2: &str) {
 5322                    let var1 = "text";
 5323 5324            "#},
 5325            cx,
 5326        );
 5327    });
 5328
 5329    editor.update(cx, |view, cx| {
 5330        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5331    });
 5332    editor.update(cx, |editor, cx| {
 5333        assert_text_with_selections(
 5334            editor,
 5335            indoc! {r#"
 5336                use mod1::mod2::{mod3, «mod4ˇ»};
 5337
 5338                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5339                    let var1 = "«textˇ»";
 5340                }
 5341            "#},
 5342            cx,
 5343        );
 5344    });
 5345
 5346    editor.update(cx, |view, cx| {
 5347        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5348    });
 5349    editor.update(cx, |editor, cx| {
 5350        assert_text_with_selections(
 5351            editor,
 5352            indoc! {r#"
 5353                use mod1::mod2::{mod3, mo«ˇ»d4};
 5354
 5355                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5356                    let var1 = "te«ˇ»xt";
 5357                }
 5358            "#},
 5359            cx,
 5360        );
 5361    });
 5362
 5363    // Trying to shrink the selected syntax node one more time has no effect.
 5364    editor.update(cx, |view, cx| {
 5365        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5366    });
 5367    editor.update(cx, |editor, cx| {
 5368        assert_text_with_selections(
 5369            editor,
 5370            indoc! {r#"
 5371                use mod1::mod2::{mod3, mo«ˇ»d4};
 5372
 5373                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5374                    let var1 = "te«ˇ»xt";
 5375                }
 5376            "#},
 5377            cx,
 5378        );
 5379    });
 5380
 5381    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5382    // a fold.
 5383    editor.update(cx, |view, cx| {
 5384        view.fold_ranges(
 5385            vec![
 5386                (
 5387                    Point::new(0, 21)..Point::new(0, 24),
 5388                    FoldPlaceholder::test(),
 5389                ),
 5390                (
 5391                    Point::new(3, 20)..Point::new(3, 22),
 5392                    FoldPlaceholder::test(),
 5393                ),
 5394            ],
 5395            true,
 5396            cx,
 5397        );
 5398        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5399    });
 5400    editor.update(cx, |editor, cx| {
 5401        assert_text_with_selections(
 5402            editor,
 5403            indoc! {r#"
 5404                use mod1::mod2::«{mod3, mod4}ˇ»;
 5405
 5406                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5407                    «let var1 = "text";ˇ»
 5408                }
 5409            "#},
 5410            cx,
 5411        );
 5412    });
 5413}
 5414
 5415#[gpui::test]
 5416async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5417    init_test(cx, |_| {});
 5418
 5419    let language = Arc::new(
 5420        Language::new(
 5421            LanguageConfig {
 5422                brackets: BracketPairConfig {
 5423                    pairs: vec![
 5424                        BracketPair {
 5425                            start: "{".to_string(),
 5426                            end: "}".to_string(),
 5427                            close: false,
 5428                            surround: false,
 5429                            newline: true,
 5430                        },
 5431                        BracketPair {
 5432                            start: "(".to_string(),
 5433                            end: ")".to_string(),
 5434                            close: false,
 5435                            surround: false,
 5436                            newline: true,
 5437                        },
 5438                    ],
 5439                    ..Default::default()
 5440                },
 5441                ..Default::default()
 5442            },
 5443            Some(tree_sitter_rust::LANGUAGE.into()),
 5444        )
 5445        .with_indents_query(
 5446            r#"
 5447                (_ "(" ")" @end) @indent
 5448                (_ "{" "}" @end) @indent
 5449            "#,
 5450        )
 5451        .unwrap(),
 5452    );
 5453
 5454    let text = "fn a() {}";
 5455
 5456    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5457    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5458    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5459    editor
 5460        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5461        .await;
 5462
 5463    editor.update(cx, |editor, cx| {
 5464        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5465        editor.newline(&Newline, cx);
 5466        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5467        assert_eq!(
 5468            editor.selections.ranges(cx),
 5469            &[
 5470                Point::new(1, 4)..Point::new(1, 4),
 5471                Point::new(3, 4)..Point::new(3, 4),
 5472                Point::new(5, 0)..Point::new(5, 0)
 5473            ]
 5474        );
 5475    });
 5476}
 5477
 5478#[gpui::test]
 5479async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5480    init_test(cx, |_| {});
 5481
 5482    let mut cx = EditorTestContext::new(cx).await;
 5483
 5484    let language = Arc::new(Language::new(
 5485        LanguageConfig {
 5486            brackets: BracketPairConfig {
 5487                pairs: vec![
 5488                    BracketPair {
 5489                        start: "{".to_string(),
 5490                        end: "}".to_string(),
 5491                        close: true,
 5492                        surround: true,
 5493                        newline: true,
 5494                    },
 5495                    BracketPair {
 5496                        start: "(".to_string(),
 5497                        end: ")".to_string(),
 5498                        close: true,
 5499                        surround: true,
 5500                        newline: true,
 5501                    },
 5502                    BracketPair {
 5503                        start: "/*".to_string(),
 5504                        end: " */".to_string(),
 5505                        close: true,
 5506                        surround: true,
 5507                        newline: true,
 5508                    },
 5509                    BracketPair {
 5510                        start: "[".to_string(),
 5511                        end: "]".to_string(),
 5512                        close: false,
 5513                        surround: false,
 5514                        newline: true,
 5515                    },
 5516                    BracketPair {
 5517                        start: "\"".to_string(),
 5518                        end: "\"".to_string(),
 5519                        close: true,
 5520                        surround: true,
 5521                        newline: false,
 5522                    },
 5523                    BracketPair {
 5524                        start: "<".to_string(),
 5525                        end: ">".to_string(),
 5526                        close: false,
 5527                        surround: true,
 5528                        newline: true,
 5529                    },
 5530                ],
 5531                ..Default::default()
 5532            },
 5533            autoclose_before: "})]".to_string(),
 5534            ..Default::default()
 5535        },
 5536        Some(tree_sitter_rust::LANGUAGE.into()),
 5537    ));
 5538
 5539    cx.language_registry().add(language.clone());
 5540    cx.update_buffer(|buffer, cx| {
 5541        buffer.set_language(Some(language), cx);
 5542    });
 5543
 5544    cx.set_state(
 5545        &r#"
 5546            🏀ˇ
 5547            εˇ
 5548            ❤️ˇ
 5549        "#
 5550        .unindent(),
 5551    );
 5552
 5553    // autoclose multiple nested brackets at multiple cursors
 5554    cx.update_editor(|view, cx| {
 5555        view.handle_input("{", cx);
 5556        view.handle_input("{", cx);
 5557        view.handle_input("{", cx);
 5558    });
 5559    cx.assert_editor_state(
 5560        &"
 5561            🏀{{{ˇ}}}
 5562            ε{{{ˇ}}}
 5563            ❤️{{{ˇ}}}
 5564        "
 5565        .unindent(),
 5566    );
 5567
 5568    // insert a different closing bracket
 5569    cx.update_editor(|view, cx| {
 5570        view.handle_input(")", cx);
 5571    });
 5572    cx.assert_editor_state(
 5573        &"
 5574            🏀{{{)ˇ}}}
 5575            ε{{{)ˇ}}}
 5576            ❤️{{{)ˇ}}}
 5577        "
 5578        .unindent(),
 5579    );
 5580
 5581    // skip over the auto-closed brackets when typing a closing bracket
 5582    cx.update_editor(|view, cx| {
 5583        view.move_right(&MoveRight, cx);
 5584        view.handle_input("}", cx);
 5585        view.handle_input("}", cx);
 5586        view.handle_input("}", cx);
 5587    });
 5588    cx.assert_editor_state(
 5589        &"
 5590            🏀{{{)}}}}ˇ
 5591            ε{{{)}}}}ˇ
 5592            ❤️{{{)}}}}ˇ
 5593        "
 5594        .unindent(),
 5595    );
 5596
 5597    // autoclose multi-character pairs
 5598    cx.set_state(
 5599        &"
 5600            ˇ
 5601            ˇ
 5602        "
 5603        .unindent(),
 5604    );
 5605    cx.update_editor(|view, cx| {
 5606        view.handle_input("/", cx);
 5607        view.handle_input("*", cx);
 5608    });
 5609    cx.assert_editor_state(
 5610        &"
 5611            /*ˇ */
 5612            /*ˇ */
 5613        "
 5614        .unindent(),
 5615    );
 5616
 5617    // one cursor autocloses a multi-character pair, one cursor
 5618    // does not autoclose.
 5619    cx.set_state(
 5620        &"
 5621 5622            ˇ
 5623        "
 5624        .unindent(),
 5625    );
 5626    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5627    cx.assert_editor_state(
 5628        &"
 5629            /*ˇ */
 5630 5631        "
 5632        .unindent(),
 5633    );
 5634
 5635    // Don't autoclose if the next character isn't whitespace and isn't
 5636    // listed in the language's "autoclose_before" section.
 5637    cx.set_state("ˇa b");
 5638    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5639    cx.assert_editor_state("{ˇa b");
 5640
 5641    // Don't autoclose if `close` is false for the bracket pair
 5642    cx.set_state("ˇ");
 5643    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5644    cx.assert_editor_state("");
 5645
 5646    // Surround with brackets if text is selected
 5647    cx.set_state("«aˇ» b");
 5648    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5649    cx.assert_editor_state("{«aˇ»} b");
 5650
 5651    // Autclose pair where the start and end characters are the same
 5652    cx.set_state("");
 5653    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5654    cx.assert_editor_state("a\"ˇ\"");
 5655    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5656    cx.assert_editor_state("a\"\"ˇ");
 5657
 5658    // Don't autoclose pair if autoclose is disabled
 5659    cx.set_state("ˇ");
 5660    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5661    cx.assert_editor_state("");
 5662
 5663    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5664    cx.set_state("«aˇ» b");
 5665    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5666    cx.assert_editor_state("<«aˇ»> b");
 5667}
 5668
 5669#[gpui::test]
 5670async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5671    init_test(cx, |settings| {
 5672        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5673    });
 5674
 5675    let mut cx = EditorTestContext::new(cx).await;
 5676
 5677    let language = Arc::new(Language::new(
 5678        LanguageConfig {
 5679            brackets: BracketPairConfig {
 5680                pairs: vec![
 5681                    BracketPair {
 5682                        start: "{".to_string(),
 5683                        end: "}".to_string(),
 5684                        close: true,
 5685                        surround: true,
 5686                        newline: true,
 5687                    },
 5688                    BracketPair {
 5689                        start: "(".to_string(),
 5690                        end: ")".to_string(),
 5691                        close: true,
 5692                        surround: true,
 5693                        newline: true,
 5694                    },
 5695                    BracketPair {
 5696                        start: "[".to_string(),
 5697                        end: "]".to_string(),
 5698                        close: false,
 5699                        surround: false,
 5700                        newline: true,
 5701                    },
 5702                ],
 5703                ..Default::default()
 5704            },
 5705            autoclose_before: "})]".to_string(),
 5706            ..Default::default()
 5707        },
 5708        Some(tree_sitter_rust::LANGUAGE.into()),
 5709    ));
 5710
 5711    cx.language_registry().add(language.clone());
 5712    cx.update_buffer(|buffer, cx| {
 5713        buffer.set_language(Some(language), cx);
 5714    });
 5715
 5716    cx.set_state(
 5717        &"
 5718            ˇ
 5719            ˇ
 5720            ˇ
 5721        "
 5722        .unindent(),
 5723    );
 5724
 5725    // ensure only matching closing brackets are skipped over
 5726    cx.update_editor(|view, cx| {
 5727        view.handle_input("}", cx);
 5728        view.move_left(&MoveLeft, cx);
 5729        view.handle_input(")", cx);
 5730        view.move_left(&MoveLeft, cx);
 5731    });
 5732    cx.assert_editor_state(
 5733        &"
 5734            ˇ)}
 5735            ˇ)}
 5736            ˇ)}
 5737        "
 5738        .unindent(),
 5739    );
 5740
 5741    // skip-over closing brackets at multiple cursors
 5742    cx.update_editor(|view, cx| {
 5743        view.handle_input(")", cx);
 5744        view.handle_input("}", cx);
 5745    });
 5746    cx.assert_editor_state(
 5747        &"
 5748            )}ˇ
 5749            )}ˇ
 5750            )}ˇ
 5751        "
 5752        .unindent(),
 5753    );
 5754
 5755    // ignore non-close brackets
 5756    cx.update_editor(|view, cx| {
 5757        view.handle_input("]", cx);
 5758        view.move_left(&MoveLeft, cx);
 5759        view.handle_input("]", cx);
 5760    });
 5761    cx.assert_editor_state(
 5762        &"
 5763            )}]ˇ]
 5764            )}]ˇ]
 5765            )}]ˇ]
 5766        "
 5767        .unindent(),
 5768    );
 5769}
 5770
 5771#[gpui::test]
 5772async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5773    init_test(cx, |_| {});
 5774
 5775    let mut cx = EditorTestContext::new(cx).await;
 5776
 5777    let html_language = Arc::new(
 5778        Language::new(
 5779            LanguageConfig {
 5780                name: "HTML".into(),
 5781                brackets: BracketPairConfig {
 5782                    pairs: vec![
 5783                        BracketPair {
 5784                            start: "<".into(),
 5785                            end: ">".into(),
 5786                            close: true,
 5787                            ..Default::default()
 5788                        },
 5789                        BracketPair {
 5790                            start: "{".into(),
 5791                            end: "}".into(),
 5792                            close: true,
 5793                            ..Default::default()
 5794                        },
 5795                        BracketPair {
 5796                            start: "(".into(),
 5797                            end: ")".into(),
 5798                            close: true,
 5799                            ..Default::default()
 5800                        },
 5801                    ],
 5802                    ..Default::default()
 5803                },
 5804                autoclose_before: "})]>".into(),
 5805                ..Default::default()
 5806            },
 5807            Some(tree_sitter_html::language()),
 5808        )
 5809        .with_injection_query(
 5810            r#"
 5811            (script_element
 5812                (raw_text) @content
 5813                (#set! "language" "javascript"))
 5814            "#,
 5815        )
 5816        .unwrap(),
 5817    );
 5818
 5819    let javascript_language = Arc::new(Language::new(
 5820        LanguageConfig {
 5821            name: "JavaScript".into(),
 5822            brackets: BracketPairConfig {
 5823                pairs: vec![
 5824                    BracketPair {
 5825                        start: "/*".into(),
 5826                        end: " */".into(),
 5827                        close: true,
 5828                        ..Default::default()
 5829                    },
 5830                    BracketPair {
 5831                        start: "{".into(),
 5832                        end: "}".into(),
 5833                        close: true,
 5834                        ..Default::default()
 5835                    },
 5836                    BracketPair {
 5837                        start: "(".into(),
 5838                        end: ")".into(),
 5839                        close: true,
 5840                        ..Default::default()
 5841                    },
 5842                ],
 5843                ..Default::default()
 5844            },
 5845            autoclose_before: "})]>".into(),
 5846            ..Default::default()
 5847        },
 5848        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5849    ));
 5850
 5851    cx.language_registry().add(html_language.clone());
 5852    cx.language_registry().add(javascript_language.clone());
 5853
 5854    cx.update_buffer(|buffer, cx| {
 5855        buffer.set_language(Some(html_language), cx);
 5856    });
 5857
 5858    cx.set_state(
 5859        &r#"
 5860            <body>ˇ
 5861                <script>
 5862                    var x = 1;ˇ
 5863                </script>
 5864            </body>ˇ
 5865        "#
 5866        .unindent(),
 5867    );
 5868
 5869    // Precondition: different languages are active at different locations.
 5870    cx.update_editor(|editor, cx| {
 5871        let snapshot = editor.snapshot(cx);
 5872        let cursors = editor.selections.ranges::<usize>(cx);
 5873        let languages = cursors
 5874            .iter()
 5875            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5876            .collect::<Vec<_>>();
 5877        assert_eq!(
 5878            languages,
 5879            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5880        );
 5881    });
 5882
 5883    // Angle brackets autoclose in HTML, but not JavaScript.
 5884    cx.update_editor(|editor, cx| {
 5885        editor.handle_input("<", cx);
 5886        editor.handle_input("a", cx);
 5887    });
 5888    cx.assert_editor_state(
 5889        &r#"
 5890            <body><aˇ>
 5891                <script>
 5892                    var x = 1;<aˇ
 5893                </script>
 5894            </body><aˇ>
 5895        "#
 5896        .unindent(),
 5897    );
 5898
 5899    // Curly braces and parens autoclose in both HTML and JavaScript.
 5900    cx.update_editor(|editor, cx| {
 5901        editor.handle_input(" b=", cx);
 5902        editor.handle_input("{", cx);
 5903        editor.handle_input("c", cx);
 5904        editor.handle_input("(", cx);
 5905    });
 5906    cx.assert_editor_state(
 5907        &r#"
 5908            <body><a b={c(ˇ)}>
 5909                <script>
 5910                    var x = 1;<a b={c(ˇ)}
 5911                </script>
 5912            </body><a b={c(ˇ)}>
 5913        "#
 5914        .unindent(),
 5915    );
 5916
 5917    // Brackets that were already autoclosed are skipped.
 5918    cx.update_editor(|editor, cx| {
 5919        editor.handle_input(")", cx);
 5920        editor.handle_input("d", cx);
 5921        editor.handle_input("}", cx);
 5922    });
 5923    cx.assert_editor_state(
 5924        &r#"
 5925            <body><a b={c()d}ˇ>
 5926                <script>
 5927                    var x = 1;<a b={c()d}ˇ
 5928                </script>
 5929            </body><a b={c()d}ˇ>
 5930        "#
 5931        .unindent(),
 5932    );
 5933    cx.update_editor(|editor, cx| {
 5934        editor.handle_input(">", cx);
 5935    });
 5936    cx.assert_editor_state(
 5937        &r#"
 5938            <body><a b={c()d}>ˇ
 5939                <script>
 5940                    var x = 1;<a b={c()d}>ˇ
 5941                </script>
 5942            </body><a b={c()d}>ˇ
 5943        "#
 5944        .unindent(),
 5945    );
 5946
 5947    // Reset
 5948    cx.set_state(
 5949        &r#"
 5950            <body>ˇ
 5951                <script>
 5952                    var x = 1;ˇ
 5953                </script>
 5954            </body>ˇ
 5955        "#
 5956        .unindent(),
 5957    );
 5958
 5959    cx.update_editor(|editor, cx| {
 5960        editor.handle_input("<", cx);
 5961    });
 5962    cx.assert_editor_state(
 5963        &r#"
 5964            <body><ˇ>
 5965                <script>
 5966                    var x = 1;<ˇ
 5967                </script>
 5968            </body><ˇ>
 5969        "#
 5970        .unindent(),
 5971    );
 5972
 5973    // When backspacing, the closing angle brackets are removed.
 5974    cx.update_editor(|editor, cx| {
 5975        editor.backspace(&Backspace, cx);
 5976    });
 5977    cx.assert_editor_state(
 5978        &r#"
 5979            <body>ˇ
 5980                <script>
 5981                    var x = 1;ˇ
 5982                </script>
 5983            </body>ˇ
 5984        "#
 5985        .unindent(),
 5986    );
 5987
 5988    // Block comments autoclose in JavaScript, but not HTML.
 5989    cx.update_editor(|editor, cx| {
 5990        editor.handle_input("/", cx);
 5991        editor.handle_input("*", cx);
 5992    });
 5993    cx.assert_editor_state(
 5994        &r#"
 5995            <body>/*ˇ
 5996                <script>
 5997                    var x = 1;/*ˇ */
 5998                </script>
 5999            </body>/*ˇ
 6000        "#
 6001        .unindent(),
 6002    );
 6003}
 6004
 6005#[gpui::test]
 6006async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6007    init_test(cx, |_| {});
 6008
 6009    let mut cx = EditorTestContext::new(cx).await;
 6010
 6011    let rust_language = Arc::new(
 6012        Language::new(
 6013            LanguageConfig {
 6014                name: "Rust".into(),
 6015                brackets: serde_json::from_value(json!([
 6016                    { "start": "{", "end": "}", "close": true, "newline": true },
 6017                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6018                ]))
 6019                .unwrap(),
 6020                autoclose_before: "})]>".into(),
 6021                ..Default::default()
 6022            },
 6023            Some(tree_sitter_rust::LANGUAGE.into()),
 6024        )
 6025        .with_override_query("(string_literal) @string")
 6026        .unwrap(),
 6027    );
 6028
 6029    cx.language_registry().add(rust_language.clone());
 6030    cx.update_buffer(|buffer, cx| {
 6031        buffer.set_language(Some(rust_language), cx);
 6032    });
 6033
 6034    cx.set_state(
 6035        &r#"
 6036            let x = ˇ
 6037        "#
 6038        .unindent(),
 6039    );
 6040
 6041    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6042    cx.update_editor(|editor, cx| {
 6043        editor.handle_input("\"", cx);
 6044    });
 6045    cx.assert_editor_state(
 6046        &r#"
 6047            let x = "ˇ"
 6048        "#
 6049        .unindent(),
 6050    );
 6051
 6052    // Inserting another quotation mark. The cursor moves across the existing
 6053    // automatically-inserted quotation mark.
 6054    cx.update_editor(|editor, cx| {
 6055        editor.handle_input("\"", cx);
 6056    });
 6057    cx.assert_editor_state(
 6058        &r#"
 6059            let x = ""ˇ
 6060        "#
 6061        .unindent(),
 6062    );
 6063
 6064    // Reset
 6065    cx.set_state(
 6066        &r#"
 6067            let x = ˇ
 6068        "#
 6069        .unindent(),
 6070    );
 6071
 6072    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6073    cx.update_editor(|editor, cx| {
 6074        editor.handle_input("\"", cx);
 6075        editor.handle_input(" ", cx);
 6076        editor.move_left(&Default::default(), cx);
 6077        editor.handle_input("\\", cx);
 6078        editor.handle_input("\"", cx);
 6079    });
 6080    cx.assert_editor_state(
 6081        &r#"
 6082            let x = "\"ˇ "
 6083        "#
 6084        .unindent(),
 6085    );
 6086
 6087    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6088    // mark. Nothing is inserted.
 6089    cx.update_editor(|editor, cx| {
 6090        editor.move_right(&Default::default(), cx);
 6091        editor.handle_input("\"", cx);
 6092    });
 6093    cx.assert_editor_state(
 6094        &r#"
 6095            let x = "\" "ˇ
 6096        "#
 6097        .unindent(),
 6098    );
 6099}
 6100
 6101#[gpui::test]
 6102async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6103    init_test(cx, |_| {});
 6104
 6105    let language = Arc::new(Language::new(
 6106        LanguageConfig {
 6107            brackets: BracketPairConfig {
 6108                pairs: vec![
 6109                    BracketPair {
 6110                        start: "{".to_string(),
 6111                        end: "}".to_string(),
 6112                        close: true,
 6113                        surround: true,
 6114                        newline: true,
 6115                    },
 6116                    BracketPair {
 6117                        start: "/* ".to_string(),
 6118                        end: "*/".to_string(),
 6119                        close: true,
 6120                        surround: true,
 6121                        ..Default::default()
 6122                    },
 6123                ],
 6124                ..Default::default()
 6125            },
 6126            ..Default::default()
 6127        },
 6128        Some(tree_sitter_rust::LANGUAGE.into()),
 6129    ));
 6130
 6131    let text = r#"
 6132        a
 6133        b
 6134        c
 6135    "#
 6136    .unindent();
 6137
 6138    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6139    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6140    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6141    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6142        .await;
 6143
 6144    view.update(cx, |view, cx| {
 6145        view.change_selections(None, cx, |s| {
 6146            s.select_display_ranges([
 6147                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6148                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6150            ])
 6151        });
 6152
 6153        view.handle_input("{", cx);
 6154        view.handle_input("{", cx);
 6155        view.handle_input("{", cx);
 6156        assert_eq!(
 6157            view.text(cx),
 6158            "
 6159                {{{a}}}
 6160                {{{b}}}
 6161                {{{c}}}
 6162            "
 6163            .unindent()
 6164        );
 6165        assert_eq!(
 6166            view.selections.display_ranges(cx),
 6167            [
 6168                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6169                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6170                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6171            ]
 6172        );
 6173
 6174        view.undo(&Undo, cx);
 6175        view.undo(&Undo, cx);
 6176        view.undo(&Undo, cx);
 6177        assert_eq!(
 6178            view.text(cx),
 6179            "
 6180                a
 6181                b
 6182                c
 6183            "
 6184            .unindent()
 6185        );
 6186        assert_eq!(
 6187            view.selections.display_ranges(cx),
 6188            [
 6189                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6190                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6191                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6192            ]
 6193        );
 6194
 6195        // Ensure inserting the first character of a multi-byte bracket pair
 6196        // doesn't surround the selections with the bracket.
 6197        view.handle_input("/", cx);
 6198        assert_eq!(
 6199            view.text(cx),
 6200            "
 6201                /
 6202                /
 6203                /
 6204            "
 6205            .unindent()
 6206        );
 6207        assert_eq!(
 6208            view.selections.display_ranges(cx),
 6209            [
 6210                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6211                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6212                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6213            ]
 6214        );
 6215
 6216        view.undo(&Undo, cx);
 6217        assert_eq!(
 6218            view.text(cx),
 6219            "
 6220                a
 6221                b
 6222                c
 6223            "
 6224            .unindent()
 6225        );
 6226        assert_eq!(
 6227            view.selections.display_ranges(cx),
 6228            [
 6229                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6230                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6231                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6232            ]
 6233        );
 6234
 6235        // Ensure inserting the last character of a multi-byte bracket pair
 6236        // doesn't surround the selections with the bracket.
 6237        view.handle_input("*", cx);
 6238        assert_eq!(
 6239            view.text(cx),
 6240            "
 6241                *
 6242                *
 6243                *
 6244            "
 6245            .unindent()
 6246        );
 6247        assert_eq!(
 6248            view.selections.display_ranges(cx),
 6249            [
 6250                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6251                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6252                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6253            ]
 6254        );
 6255    });
 6256}
 6257
 6258#[gpui::test]
 6259async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6260    init_test(cx, |_| {});
 6261
 6262    let language = Arc::new(Language::new(
 6263        LanguageConfig {
 6264            brackets: BracketPairConfig {
 6265                pairs: vec![BracketPair {
 6266                    start: "{".to_string(),
 6267                    end: "}".to_string(),
 6268                    close: true,
 6269                    surround: true,
 6270                    newline: true,
 6271                }],
 6272                ..Default::default()
 6273            },
 6274            autoclose_before: "}".to_string(),
 6275            ..Default::default()
 6276        },
 6277        Some(tree_sitter_rust::LANGUAGE.into()),
 6278    ));
 6279
 6280    let text = r#"
 6281        a
 6282        b
 6283        c
 6284    "#
 6285    .unindent();
 6286
 6287    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6288    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6289    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6290    editor
 6291        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6292        .await;
 6293
 6294    editor.update(cx, |editor, cx| {
 6295        editor.change_selections(None, cx, |s| {
 6296            s.select_ranges([
 6297                Point::new(0, 1)..Point::new(0, 1),
 6298                Point::new(1, 1)..Point::new(1, 1),
 6299                Point::new(2, 1)..Point::new(2, 1),
 6300            ])
 6301        });
 6302
 6303        editor.handle_input("{", cx);
 6304        editor.handle_input("{", cx);
 6305        editor.handle_input("_", cx);
 6306        assert_eq!(
 6307            editor.text(cx),
 6308            "
 6309                a{{_}}
 6310                b{{_}}
 6311                c{{_}}
 6312            "
 6313            .unindent()
 6314        );
 6315        assert_eq!(
 6316            editor.selections.ranges::<Point>(cx),
 6317            [
 6318                Point::new(0, 4)..Point::new(0, 4),
 6319                Point::new(1, 4)..Point::new(1, 4),
 6320                Point::new(2, 4)..Point::new(2, 4)
 6321            ]
 6322        );
 6323
 6324        editor.backspace(&Default::default(), cx);
 6325        editor.backspace(&Default::default(), cx);
 6326        assert_eq!(
 6327            editor.text(cx),
 6328            "
 6329                a{}
 6330                b{}
 6331                c{}
 6332            "
 6333            .unindent()
 6334        );
 6335        assert_eq!(
 6336            editor.selections.ranges::<Point>(cx),
 6337            [
 6338                Point::new(0, 2)..Point::new(0, 2),
 6339                Point::new(1, 2)..Point::new(1, 2),
 6340                Point::new(2, 2)..Point::new(2, 2)
 6341            ]
 6342        );
 6343
 6344        editor.delete_to_previous_word_start(&Default::default(), cx);
 6345        assert_eq!(
 6346            editor.text(cx),
 6347            "
 6348                a
 6349                b
 6350                c
 6351            "
 6352            .unindent()
 6353        );
 6354        assert_eq!(
 6355            editor.selections.ranges::<Point>(cx),
 6356            [
 6357                Point::new(0, 1)..Point::new(0, 1),
 6358                Point::new(1, 1)..Point::new(1, 1),
 6359                Point::new(2, 1)..Point::new(2, 1)
 6360            ]
 6361        );
 6362    });
 6363}
 6364
 6365#[gpui::test]
 6366async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6367    init_test(cx, |settings| {
 6368        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6369    });
 6370
 6371    let mut cx = EditorTestContext::new(cx).await;
 6372
 6373    let language = Arc::new(Language::new(
 6374        LanguageConfig {
 6375            brackets: BracketPairConfig {
 6376                pairs: vec![
 6377                    BracketPair {
 6378                        start: "{".to_string(),
 6379                        end: "}".to_string(),
 6380                        close: true,
 6381                        surround: true,
 6382                        newline: true,
 6383                    },
 6384                    BracketPair {
 6385                        start: "(".to_string(),
 6386                        end: ")".to_string(),
 6387                        close: true,
 6388                        surround: true,
 6389                        newline: true,
 6390                    },
 6391                    BracketPair {
 6392                        start: "[".to_string(),
 6393                        end: "]".to_string(),
 6394                        close: false,
 6395                        surround: true,
 6396                        newline: true,
 6397                    },
 6398                ],
 6399                ..Default::default()
 6400            },
 6401            autoclose_before: "})]".to_string(),
 6402            ..Default::default()
 6403        },
 6404        Some(tree_sitter_rust::LANGUAGE.into()),
 6405    ));
 6406
 6407    cx.language_registry().add(language.clone());
 6408    cx.update_buffer(|buffer, cx| {
 6409        buffer.set_language(Some(language), cx);
 6410    });
 6411
 6412    cx.set_state(
 6413        &"
 6414            {(ˇ)}
 6415            [[ˇ]]
 6416            {(ˇ)}
 6417        "
 6418        .unindent(),
 6419    );
 6420
 6421    cx.update_editor(|view, cx| {
 6422        view.backspace(&Default::default(), cx);
 6423        view.backspace(&Default::default(), cx);
 6424    });
 6425
 6426    cx.assert_editor_state(
 6427        &"
 6428            ˇ
 6429            ˇ]]
 6430            ˇ
 6431        "
 6432        .unindent(),
 6433    );
 6434
 6435    cx.update_editor(|view, cx| {
 6436        view.handle_input("{", cx);
 6437        view.handle_input("{", cx);
 6438        view.move_right(&MoveRight, cx);
 6439        view.move_right(&MoveRight, cx);
 6440        view.move_left(&MoveLeft, cx);
 6441        view.move_left(&MoveLeft, cx);
 6442        view.backspace(&Default::default(), cx);
 6443    });
 6444
 6445    cx.assert_editor_state(
 6446        &"
 6447            {ˇ}
 6448            {ˇ}]]
 6449            {ˇ}
 6450        "
 6451        .unindent(),
 6452    );
 6453
 6454    cx.update_editor(|view, cx| {
 6455        view.backspace(&Default::default(), cx);
 6456    });
 6457
 6458    cx.assert_editor_state(
 6459        &"
 6460            ˇ
 6461            ˇ]]
 6462            ˇ
 6463        "
 6464        .unindent(),
 6465    );
 6466}
 6467
 6468#[gpui::test]
 6469async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6470    init_test(cx, |_| {});
 6471
 6472    let language = Arc::new(Language::new(
 6473        LanguageConfig::default(),
 6474        Some(tree_sitter_rust::LANGUAGE.into()),
 6475    ));
 6476
 6477    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6478    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6479    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6480    editor
 6481        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6482        .await;
 6483
 6484    editor.update(cx, |editor, cx| {
 6485        editor.set_auto_replace_emoji_shortcode(true);
 6486
 6487        editor.handle_input("Hello ", cx);
 6488        editor.handle_input(":wave", cx);
 6489        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6490
 6491        editor.handle_input(":", cx);
 6492        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6493
 6494        editor.handle_input(" :smile", cx);
 6495        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6496
 6497        editor.handle_input(":", cx);
 6498        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6499
 6500        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6501        editor.handle_input(":wave", cx);
 6502        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6503
 6504        editor.handle_input(":", cx);
 6505        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6506
 6507        editor.handle_input(":1", cx);
 6508        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6509
 6510        editor.handle_input(":", cx);
 6511        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6512
 6513        // Ensure shortcode does not get replaced when it is part of a word
 6514        editor.handle_input(" Test:wave", cx);
 6515        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6516
 6517        editor.handle_input(":", cx);
 6518        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6519
 6520        editor.set_auto_replace_emoji_shortcode(false);
 6521
 6522        // Ensure shortcode does not get replaced when auto replace is off
 6523        editor.handle_input(" :wave", cx);
 6524        assert_eq!(
 6525            editor.text(cx),
 6526            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6527        );
 6528
 6529        editor.handle_input(":", cx);
 6530        assert_eq!(
 6531            editor.text(cx),
 6532            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6533        );
 6534    });
 6535}
 6536
 6537#[gpui::test]
 6538async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6539    init_test(cx, |_| {});
 6540
 6541    let (text, insertion_ranges) = marked_text_ranges(
 6542        indoc! {"
 6543            a.ˇ b
 6544            a.ˇ b
 6545            a.ˇ b
 6546        "},
 6547        false,
 6548    );
 6549
 6550    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6551    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6552
 6553    editor.update(cx, |editor, cx| {
 6554        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6555
 6556        editor
 6557            .insert_snippet(&insertion_ranges, snippet, cx)
 6558            .unwrap();
 6559
 6560        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6561            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6562            assert_eq!(editor.text(cx), expected_text);
 6563            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6564        }
 6565
 6566        assert(
 6567            editor,
 6568            cx,
 6569            indoc! {"
 6570                a.f(«one», two, «three») b
 6571                a.f(«one», two, «three») b
 6572                a.f(«one», two, «three») b
 6573            "},
 6574        );
 6575
 6576        // Can't move earlier than the first tab stop
 6577        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6578        assert(
 6579            editor,
 6580            cx,
 6581            indoc! {"
 6582                a.f(«one», two, «three») b
 6583                a.f(«one», two, «three») b
 6584                a.f(«one», two, «three») b
 6585            "},
 6586        );
 6587
 6588        assert!(editor.move_to_next_snippet_tabstop(cx));
 6589        assert(
 6590            editor,
 6591            cx,
 6592            indoc! {"
 6593                a.f(one, «two», three) b
 6594                a.f(one, «two», three) b
 6595                a.f(one, «two», three) b
 6596            "},
 6597        );
 6598
 6599        editor.move_to_prev_snippet_tabstop(cx);
 6600        assert(
 6601            editor,
 6602            cx,
 6603            indoc! {"
 6604                a.f(«one», two, «three») b
 6605                a.f(«one», two, «three») b
 6606                a.f(«one», two, «three») b
 6607            "},
 6608        );
 6609
 6610        assert!(editor.move_to_next_snippet_tabstop(cx));
 6611        assert(
 6612            editor,
 6613            cx,
 6614            indoc! {"
 6615                a.f(one, «two», three) b
 6616                a.f(one, «two», three) b
 6617                a.f(one, «two», three) b
 6618            "},
 6619        );
 6620        assert!(editor.move_to_next_snippet_tabstop(cx));
 6621        assert(
 6622            editor,
 6623            cx,
 6624            indoc! {"
 6625                a.f(one, two, three)ˇ b
 6626                a.f(one, two, three)ˇ b
 6627                a.f(one, two, three)ˇ b
 6628            "},
 6629        );
 6630
 6631        // As soon as the last tab stop is reached, snippet state is gone
 6632        editor.move_to_prev_snippet_tabstop(cx);
 6633        assert(
 6634            editor,
 6635            cx,
 6636            indoc! {"
 6637                a.f(one, two, three)ˇ b
 6638                a.f(one, two, three)ˇ b
 6639                a.f(one, two, three)ˇ b
 6640            "},
 6641        );
 6642    });
 6643}
 6644
 6645#[gpui::test]
 6646async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6647    init_test(cx, |_| {});
 6648
 6649    let fs = FakeFs::new(cx.executor());
 6650    fs.insert_file("/file.rs", Default::default()).await;
 6651
 6652    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6653
 6654    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6655    language_registry.add(rust_lang());
 6656    let mut fake_servers = language_registry.register_fake_lsp(
 6657        "Rust",
 6658        FakeLspAdapter {
 6659            capabilities: lsp::ServerCapabilities {
 6660                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6661                ..Default::default()
 6662            },
 6663            ..Default::default()
 6664        },
 6665    );
 6666
 6667    let buffer = project
 6668        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6669        .await
 6670        .unwrap();
 6671
 6672    cx.executor().start_waiting();
 6673    let fake_server = fake_servers.next().await.unwrap();
 6674
 6675    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6676    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6677    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6678    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6679
 6680    let save = editor
 6681        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6682        .unwrap();
 6683    fake_server
 6684        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6685            assert_eq!(
 6686                params.text_document.uri,
 6687                lsp::Url::from_file_path("/file.rs").unwrap()
 6688            );
 6689            assert_eq!(params.options.tab_size, 4);
 6690            Ok(Some(vec![lsp::TextEdit::new(
 6691                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6692                ", ".to_string(),
 6693            )]))
 6694        })
 6695        .next()
 6696        .await;
 6697    cx.executor().start_waiting();
 6698    save.await;
 6699
 6700    assert_eq!(
 6701        editor.update(cx, |editor, cx| editor.text(cx)),
 6702        "one, two\nthree\n"
 6703    );
 6704    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6705
 6706    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6707    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6708
 6709    // Ensure we can still save even if formatting hangs.
 6710    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6711        assert_eq!(
 6712            params.text_document.uri,
 6713            lsp::Url::from_file_path("/file.rs").unwrap()
 6714        );
 6715        futures::future::pending::<()>().await;
 6716        unreachable!()
 6717    });
 6718    let save = editor
 6719        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6720        .unwrap();
 6721    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6722    cx.executor().start_waiting();
 6723    save.await;
 6724    assert_eq!(
 6725        editor.update(cx, |editor, cx| editor.text(cx)),
 6726        "one\ntwo\nthree\n"
 6727    );
 6728    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6729
 6730    // For non-dirty buffer, no formatting request should be sent
 6731    let save = editor
 6732        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6733        .unwrap();
 6734    let _pending_format_request = fake_server
 6735        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6736            panic!("Should not be invoked on non-dirty buffer");
 6737        })
 6738        .next();
 6739    cx.executor().start_waiting();
 6740    save.await;
 6741
 6742    // Set rust language override and assert overridden tabsize is sent to language server
 6743    update_test_language_settings(cx, |settings| {
 6744        settings.languages.insert(
 6745            "Rust".into(),
 6746            LanguageSettingsContent {
 6747                tab_size: NonZeroU32::new(8),
 6748                ..Default::default()
 6749            },
 6750        );
 6751    });
 6752
 6753    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6754    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6755    let save = editor
 6756        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6757        .unwrap();
 6758    fake_server
 6759        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6760            assert_eq!(
 6761                params.text_document.uri,
 6762                lsp::Url::from_file_path("/file.rs").unwrap()
 6763            );
 6764            assert_eq!(params.options.tab_size, 8);
 6765            Ok(Some(vec![]))
 6766        })
 6767        .next()
 6768        .await;
 6769    cx.executor().start_waiting();
 6770    save.await;
 6771}
 6772
 6773#[gpui::test]
 6774async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6775    init_test(cx, |_| {});
 6776
 6777    let cols = 4;
 6778    let rows = 10;
 6779    let sample_text_1 = sample_text(rows, cols, 'a');
 6780    assert_eq!(
 6781        sample_text_1,
 6782        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6783    );
 6784    let sample_text_2 = sample_text(rows, cols, 'l');
 6785    assert_eq!(
 6786        sample_text_2,
 6787        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6788    );
 6789    let sample_text_3 = sample_text(rows, cols, 'v');
 6790    assert_eq!(
 6791        sample_text_3,
 6792        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6793    );
 6794
 6795    let fs = FakeFs::new(cx.executor());
 6796    fs.insert_tree(
 6797        "/a",
 6798        json!({
 6799            "main.rs": sample_text_1,
 6800            "other.rs": sample_text_2,
 6801            "lib.rs": sample_text_3,
 6802        }),
 6803    )
 6804    .await;
 6805
 6806    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6807    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6808    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6809
 6810    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6811    language_registry.add(rust_lang());
 6812    let mut fake_servers = language_registry.register_fake_lsp(
 6813        "Rust",
 6814        FakeLspAdapter {
 6815            capabilities: lsp::ServerCapabilities {
 6816                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6817                ..Default::default()
 6818            },
 6819            ..Default::default()
 6820        },
 6821    );
 6822
 6823    let worktree = project.update(cx, |project, cx| {
 6824        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6825        assert_eq!(worktrees.len(), 1);
 6826        worktrees.pop().unwrap()
 6827    });
 6828    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6829
 6830    let buffer_1 = project
 6831        .update(cx, |project, cx| {
 6832            project.open_buffer((worktree_id, "main.rs"), cx)
 6833        })
 6834        .await
 6835        .unwrap();
 6836    let buffer_2 = project
 6837        .update(cx, |project, cx| {
 6838            project.open_buffer((worktree_id, "other.rs"), cx)
 6839        })
 6840        .await
 6841        .unwrap();
 6842    let buffer_3 = project
 6843        .update(cx, |project, cx| {
 6844            project.open_buffer((worktree_id, "lib.rs"), cx)
 6845        })
 6846        .await
 6847        .unwrap();
 6848
 6849    let multi_buffer = cx.new_model(|cx| {
 6850        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6851        multi_buffer.push_excerpts(
 6852            buffer_1.clone(),
 6853            [
 6854                ExcerptRange {
 6855                    context: Point::new(0, 0)..Point::new(3, 0),
 6856                    primary: None,
 6857                },
 6858                ExcerptRange {
 6859                    context: Point::new(5, 0)..Point::new(7, 0),
 6860                    primary: None,
 6861                },
 6862                ExcerptRange {
 6863                    context: Point::new(9, 0)..Point::new(10, 4),
 6864                    primary: None,
 6865                },
 6866            ],
 6867            cx,
 6868        );
 6869        multi_buffer.push_excerpts(
 6870            buffer_2.clone(),
 6871            [
 6872                ExcerptRange {
 6873                    context: Point::new(0, 0)..Point::new(3, 0),
 6874                    primary: None,
 6875                },
 6876                ExcerptRange {
 6877                    context: Point::new(5, 0)..Point::new(7, 0),
 6878                    primary: None,
 6879                },
 6880                ExcerptRange {
 6881                    context: Point::new(9, 0)..Point::new(10, 4),
 6882                    primary: None,
 6883                },
 6884            ],
 6885            cx,
 6886        );
 6887        multi_buffer.push_excerpts(
 6888            buffer_3.clone(),
 6889            [
 6890                ExcerptRange {
 6891                    context: Point::new(0, 0)..Point::new(3, 0),
 6892                    primary: None,
 6893                },
 6894                ExcerptRange {
 6895                    context: Point::new(5, 0)..Point::new(7, 0),
 6896                    primary: None,
 6897                },
 6898                ExcerptRange {
 6899                    context: Point::new(9, 0)..Point::new(10, 4),
 6900                    primary: None,
 6901                },
 6902            ],
 6903            cx,
 6904        );
 6905        multi_buffer
 6906    });
 6907    let multi_buffer_editor = cx.new_view(|cx| {
 6908        Editor::new(
 6909            EditorMode::Full,
 6910            multi_buffer,
 6911            Some(project.clone()),
 6912            true,
 6913            cx,
 6914        )
 6915    });
 6916
 6917    multi_buffer_editor.update(cx, |editor, cx| {
 6918        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6919        editor.insert("|one|two|three|", cx);
 6920    });
 6921    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6922    multi_buffer_editor.update(cx, |editor, cx| {
 6923        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6924            s.select_ranges(Some(60..70))
 6925        });
 6926        editor.insert("|four|five|six|", cx);
 6927    });
 6928    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6929
 6930    // First two buffers should be edited, but not the third one.
 6931    assert_eq!(
 6932        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6933        "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}",
 6934    );
 6935    buffer_1.update(cx, |buffer, _| {
 6936        assert!(buffer.is_dirty());
 6937        assert_eq!(
 6938            buffer.text(),
 6939            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6940        )
 6941    });
 6942    buffer_2.update(cx, |buffer, _| {
 6943        assert!(buffer.is_dirty());
 6944        assert_eq!(
 6945            buffer.text(),
 6946            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6947        )
 6948    });
 6949    buffer_3.update(cx, |buffer, _| {
 6950        assert!(!buffer.is_dirty());
 6951        assert_eq!(buffer.text(), sample_text_3,)
 6952    });
 6953
 6954    cx.executor().start_waiting();
 6955    let save = multi_buffer_editor
 6956        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6957        .unwrap();
 6958
 6959    let fake_server = fake_servers.next().await.unwrap();
 6960    fake_server
 6961        .server
 6962        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6963            Ok(Some(vec![lsp::TextEdit::new(
 6964                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6965                format!("[{} formatted]", params.text_document.uri),
 6966            )]))
 6967        })
 6968        .detach();
 6969    save.await;
 6970
 6971    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6972    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6973    assert_eq!(
 6974        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6975        "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}",
 6976    );
 6977    buffer_1.update(cx, |buffer, _| {
 6978        assert!(!buffer.is_dirty());
 6979        assert_eq!(
 6980            buffer.text(),
 6981            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6982        )
 6983    });
 6984    buffer_2.update(cx, |buffer, _| {
 6985        assert!(!buffer.is_dirty());
 6986        assert_eq!(
 6987            buffer.text(),
 6988            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6989        )
 6990    });
 6991    buffer_3.update(cx, |buffer, _| {
 6992        assert!(!buffer.is_dirty());
 6993        assert_eq!(buffer.text(), sample_text_3,)
 6994    });
 6995}
 6996
 6997#[gpui::test]
 6998async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6999    init_test(cx, |_| {});
 7000
 7001    let fs = FakeFs::new(cx.executor());
 7002    fs.insert_file("/file.rs", Default::default()).await;
 7003
 7004    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7005
 7006    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7007    language_registry.add(rust_lang());
 7008    let mut fake_servers = language_registry.register_fake_lsp(
 7009        "Rust",
 7010        FakeLspAdapter {
 7011            capabilities: lsp::ServerCapabilities {
 7012                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7013                ..Default::default()
 7014            },
 7015            ..Default::default()
 7016        },
 7017    );
 7018
 7019    let buffer = project
 7020        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7021        .await
 7022        .unwrap();
 7023
 7024    cx.executor().start_waiting();
 7025    let fake_server = fake_servers.next().await.unwrap();
 7026
 7027    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7028    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7029    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7030    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7031
 7032    let save = editor
 7033        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7034        .unwrap();
 7035    fake_server
 7036        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7037            assert_eq!(
 7038                params.text_document.uri,
 7039                lsp::Url::from_file_path("/file.rs").unwrap()
 7040            );
 7041            assert_eq!(params.options.tab_size, 4);
 7042            Ok(Some(vec![lsp::TextEdit::new(
 7043                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7044                ", ".to_string(),
 7045            )]))
 7046        })
 7047        .next()
 7048        .await;
 7049    cx.executor().start_waiting();
 7050    save.await;
 7051    assert_eq!(
 7052        editor.update(cx, |editor, cx| editor.text(cx)),
 7053        "one, two\nthree\n"
 7054    );
 7055    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7056
 7057    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7058    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7059
 7060    // Ensure we can still save even if formatting hangs.
 7061    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7062        move |params, _| async move {
 7063            assert_eq!(
 7064                params.text_document.uri,
 7065                lsp::Url::from_file_path("/file.rs").unwrap()
 7066            );
 7067            futures::future::pending::<()>().await;
 7068            unreachable!()
 7069        },
 7070    );
 7071    let save = editor
 7072        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7073        .unwrap();
 7074    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7075    cx.executor().start_waiting();
 7076    save.await;
 7077    assert_eq!(
 7078        editor.update(cx, |editor, cx| editor.text(cx)),
 7079        "one\ntwo\nthree\n"
 7080    );
 7081    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7082
 7083    // For non-dirty buffer, no formatting request should be sent
 7084    let save = editor
 7085        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7086        .unwrap();
 7087    let _pending_format_request = fake_server
 7088        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7089            panic!("Should not be invoked on non-dirty buffer");
 7090        })
 7091        .next();
 7092    cx.executor().start_waiting();
 7093    save.await;
 7094
 7095    // Set Rust language override and assert overridden tabsize is sent to language server
 7096    update_test_language_settings(cx, |settings| {
 7097        settings.languages.insert(
 7098            "Rust".into(),
 7099            LanguageSettingsContent {
 7100                tab_size: NonZeroU32::new(8),
 7101                ..Default::default()
 7102            },
 7103        );
 7104    });
 7105
 7106    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7107    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7108    let save = editor
 7109        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7110        .unwrap();
 7111    fake_server
 7112        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7113            assert_eq!(
 7114                params.text_document.uri,
 7115                lsp::Url::from_file_path("/file.rs").unwrap()
 7116            );
 7117            assert_eq!(params.options.tab_size, 8);
 7118            Ok(Some(vec![]))
 7119        })
 7120        .next()
 7121        .await;
 7122    cx.executor().start_waiting();
 7123    save.await;
 7124}
 7125
 7126#[gpui::test]
 7127async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7128    init_test(cx, |settings| {
 7129        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7130            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7131        ))
 7132    });
 7133
 7134    let fs = FakeFs::new(cx.executor());
 7135    fs.insert_file("/file.rs", Default::default()).await;
 7136
 7137    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7138
 7139    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7140    language_registry.add(Arc::new(Language::new(
 7141        LanguageConfig {
 7142            name: "Rust".into(),
 7143            matcher: LanguageMatcher {
 7144                path_suffixes: vec!["rs".to_string()],
 7145                ..Default::default()
 7146            },
 7147            ..LanguageConfig::default()
 7148        },
 7149        Some(tree_sitter_rust::LANGUAGE.into()),
 7150    )));
 7151    update_test_language_settings(cx, |settings| {
 7152        // Enable Prettier formatting for the same buffer, and ensure
 7153        // LSP is called instead of Prettier.
 7154        settings.defaults.prettier = Some(PrettierSettings {
 7155            allowed: true,
 7156            ..PrettierSettings::default()
 7157        });
 7158    });
 7159    let mut fake_servers = language_registry.register_fake_lsp(
 7160        "Rust",
 7161        FakeLspAdapter {
 7162            capabilities: lsp::ServerCapabilities {
 7163                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7164                ..Default::default()
 7165            },
 7166            ..Default::default()
 7167        },
 7168    );
 7169
 7170    let buffer = project
 7171        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7172        .await
 7173        .unwrap();
 7174
 7175    cx.executor().start_waiting();
 7176    let fake_server = fake_servers.next().await.unwrap();
 7177
 7178    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7179    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7180    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7181
 7182    let format = editor
 7183        .update(cx, |editor, cx| {
 7184            editor.perform_format(
 7185                project.clone(),
 7186                FormatTrigger::Manual,
 7187                FormatTarget::Buffer,
 7188                cx,
 7189            )
 7190        })
 7191        .unwrap();
 7192    fake_server
 7193        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7194            assert_eq!(
 7195                params.text_document.uri,
 7196                lsp::Url::from_file_path("/file.rs").unwrap()
 7197            );
 7198            assert_eq!(params.options.tab_size, 4);
 7199            Ok(Some(vec![lsp::TextEdit::new(
 7200                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7201                ", ".to_string(),
 7202            )]))
 7203        })
 7204        .next()
 7205        .await;
 7206    cx.executor().start_waiting();
 7207    format.await;
 7208    assert_eq!(
 7209        editor.update(cx, |editor, cx| editor.text(cx)),
 7210        "one, two\nthree\n"
 7211    );
 7212
 7213    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7214    // Ensure we don't lock if formatting hangs.
 7215    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7216        assert_eq!(
 7217            params.text_document.uri,
 7218            lsp::Url::from_file_path("/file.rs").unwrap()
 7219        );
 7220        futures::future::pending::<()>().await;
 7221        unreachable!()
 7222    });
 7223    let format = editor
 7224        .update(cx, |editor, cx| {
 7225            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7226        })
 7227        .unwrap();
 7228    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7229    cx.executor().start_waiting();
 7230    format.await;
 7231    assert_eq!(
 7232        editor.update(cx, |editor, cx| editor.text(cx)),
 7233        "one\ntwo\nthree\n"
 7234    );
 7235}
 7236
 7237#[gpui::test]
 7238async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7239    init_test(cx, |_| {});
 7240
 7241    let mut cx = EditorLspTestContext::new_rust(
 7242        lsp::ServerCapabilities {
 7243            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7244            ..Default::default()
 7245        },
 7246        cx,
 7247    )
 7248    .await;
 7249
 7250    cx.set_state(indoc! {"
 7251        one.twoˇ
 7252    "});
 7253
 7254    // The format request takes a long time. When it completes, it inserts
 7255    // a newline and an indent before the `.`
 7256    cx.lsp
 7257        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7258            let executor = cx.background_executor().clone();
 7259            async move {
 7260                executor.timer(Duration::from_millis(100)).await;
 7261                Ok(Some(vec![lsp::TextEdit {
 7262                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7263                    new_text: "\n    ".into(),
 7264                }]))
 7265            }
 7266        });
 7267
 7268    // Submit a format request.
 7269    let format_1 = cx
 7270        .update_editor(|editor, cx| editor.format(&Format, cx))
 7271        .unwrap();
 7272    cx.executor().run_until_parked();
 7273
 7274    // Submit a second format request.
 7275    let format_2 = cx
 7276        .update_editor(|editor, cx| editor.format(&Format, cx))
 7277        .unwrap();
 7278    cx.executor().run_until_parked();
 7279
 7280    // Wait for both format requests to complete
 7281    cx.executor().advance_clock(Duration::from_millis(200));
 7282    cx.executor().start_waiting();
 7283    format_1.await.unwrap();
 7284    cx.executor().start_waiting();
 7285    format_2.await.unwrap();
 7286
 7287    // The formatting edits only happens once.
 7288    cx.assert_editor_state(indoc! {"
 7289        one
 7290            .twoˇ
 7291    "});
 7292}
 7293
 7294#[gpui::test]
 7295async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7296    init_test(cx, |settings| {
 7297        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7298    });
 7299
 7300    let mut cx = EditorLspTestContext::new_rust(
 7301        lsp::ServerCapabilities {
 7302            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7303            ..Default::default()
 7304        },
 7305        cx,
 7306    )
 7307    .await;
 7308
 7309    // Set up a buffer white some trailing whitespace and no trailing newline.
 7310    cx.set_state(
 7311        &[
 7312            "one ",   //
 7313            "twoˇ",   //
 7314            "three ", //
 7315            "four",   //
 7316        ]
 7317        .join("\n"),
 7318    );
 7319
 7320    // Submit a format request.
 7321    let format = cx
 7322        .update_editor(|editor, cx| editor.format(&Format, cx))
 7323        .unwrap();
 7324
 7325    // Record which buffer changes have been sent to the language server
 7326    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7327    cx.lsp
 7328        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7329            let buffer_changes = buffer_changes.clone();
 7330            move |params, _| {
 7331                buffer_changes.lock().extend(
 7332                    params
 7333                        .content_changes
 7334                        .into_iter()
 7335                        .map(|e| (e.range.unwrap(), e.text)),
 7336                );
 7337            }
 7338        });
 7339
 7340    // Handle formatting requests to the language server.
 7341    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7342        let buffer_changes = buffer_changes.clone();
 7343        move |_, _| {
 7344            // When formatting is requested, trailing whitespace has already been stripped,
 7345            // and the trailing newline has already been added.
 7346            assert_eq!(
 7347                &buffer_changes.lock()[1..],
 7348                &[
 7349                    (
 7350                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7351                        "".into()
 7352                    ),
 7353                    (
 7354                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7355                        "".into()
 7356                    ),
 7357                    (
 7358                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7359                        "\n".into()
 7360                    ),
 7361                ]
 7362            );
 7363
 7364            // Insert blank lines between each line of the buffer.
 7365            async move {
 7366                Ok(Some(vec![
 7367                    lsp::TextEdit {
 7368                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7369                        new_text: "\n".into(),
 7370                    },
 7371                    lsp::TextEdit {
 7372                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7373                        new_text: "\n".into(),
 7374                    },
 7375                ]))
 7376            }
 7377        }
 7378    });
 7379
 7380    // After formatting the buffer, the trailing whitespace is stripped,
 7381    // a newline is appended, and the edits provided by the language server
 7382    // have been applied.
 7383    format.await.unwrap();
 7384    cx.assert_editor_state(
 7385        &[
 7386            "one",   //
 7387            "",      //
 7388            "twoˇ",  //
 7389            "",      //
 7390            "three", //
 7391            "four",  //
 7392            "",      //
 7393        ]
 7394        .join("\n"),
 7395    );
 7396
 7397    // Undoing the formatting undoes the trailing whitespace removal, the
 7398    // trailing newline, and the LSP edits.
 7399    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7400    cx.assert_editor_state(
 7401        &[
 7402            "one ",   //
 7403            "twoˇ",   //
 7404            "three ", //
 7405            "four",   //
 7406        ]
 7407        .join("\n"),
 7408    );
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7413    cx: &mut gpui::TestAppContext,
 7414) {
 7415    init_test(cx, |_| {});
 7416
 7417    cx.update(|cx| {
 7418        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7419            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7420                settings.auto_signature_help = Some(true);
 7421            });
 7422        });
 7423    });
 7424
 7425    let mut cx = EditorLspTestContext::new_rust(
 7426        lsp::ServerCapabilities {
 7427            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7428                ..Default::default()
 7429            }),
 7430            ..Default::default()
 7431        },
 7432        cx,
 7433    )
 7434    .await;
 7435
 7436    let language = Language::new(
 7437        LanguageConfig {
 7438            name: "Rust".into(),
 7439            brackets: BracketPairConfig {
 7440                pairs: vec![
 7441                    BracketPair {
 7442                        start: "{".to_string(),
 7443                        end: "}".to_string(),
 7444                        close: true,
 7445                        surround: true,
 7446                        newline: true,
 7447                    },
 7448                    BracketPair {
 7449                        start: "(".to_string(),
 7450                        end: ")".to_string(),
 7451                        close: true,
 7452                        surround: true,
 7453                        newline: true,
 7454                    },
 7455                    BracketPair {
 7456                        start: "/*".to_string(),
 7457                        end: " */".to_string(),
 7458                        close: true,
 7459                        surround: true,
 7460                        newline: true,
 7461                    },
 7462                    BracketPair {
 7463                        start: "[".to_string(),
 7464                        end: "]".to_string(),
 7465                        close: false,
 7466                        surround: false,
 7467                        newline: true,
 7468                    },
 7469                    BracketPair {
 7470                        start: "\"".to_string(),
 7471                        end: "\"".to_string(),
 7472                        close: true,
 7473                        surround: true,
 7474                        newline: false,
 7475                    },
 7476                    BracketPair {
 7477                        start: "<".to_string(),
 7478                        end: ">".to_string(),
 7479                        close: false,
 7480                        surround: true,
 7481                        newline: true,
 7482                    },
 7483                ],
 7484                ..Default::default()
 7485            },
 7486            autoclose_before: "})]".to_string(),
 7487            ..Default::default()
 7488        },
 7489        Some(tree_sitter_rust::LANGUAGE.into()),
 7490    );
 7491    let language = Arc::new(language);
 7492
 7493    cx.language_registry().add(language.clone());
 7494    cx.update_buffer(|buffer, cx| {
 7495        buffer.set_language(Some(language), cx);
 7496    });
 7497
 7498    cx.set_state(
 7499        &r#"
 7500            fn main() {
 7501                sampleˇ
 7502            }
 7503        "#
 7504        .unindent(),
 7505    );
 7506
 7507    cx.update_editor(|view, cx| {
 7508        view.handle_input("(", cx);
 7509    });
 7510    cx.assert_editor_state(
 7511        &"
 7512            fn main() {
 7513                sample(ˇ)
 7514            }
 7515        "
 7516        .unindent(),
 7517    );
 7518
 7519    let mocked_response = lsp::SignatureHelp {
 7520        signatures: vec![lsp::SignatureInformation {
 7521            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7522            documentation: None,
 7523            parameters: Some(vec![
 7524                lsp::ParameterInformation {
 7525                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7526                    documentation: None,
 7527                },
 7528                lsp::ParameterInformation {
 7529                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7530                    documentation: None,
 7531                },
 7532            ]),
 7533            active_parameter: None,
 7534        }],
 7535        active_signature: Some(0),
 7536        active_parameter: Some(0),
 7537    };
 7538    handle_signature_help_request(&mut cx, mocked_response).await;
 7539
 7540    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7541        .await;
 7542
 7543    cx.editor(|editor, _| {
 7544        let signature_help_state = editor.signature_help_state.popover().cloned();
 7545        assert!(signature_help_state.is_some());
 7546        let ParsedMarkdown {
 7547            text, highlights, ..
 7548        } = signature_help_state.unwrap().parsed_content;
 7549        assert_eq!(text, "param1: u8, param2: u8");
 7550        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7551    });
 7552}
 7553
 7554#[gpui::test]
 7555async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7556    init_test(cx, |_| {});
 7557
 7558    cx.update(|cx| {
 7559        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7560            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7561                settings.auto_signature_help = Some(false);
 7562                settings.show_signature_help_after_edits = Some(false);
 7563            });
 7564        });
 7565    });
 7566
 7567    let mut cx = EditorLspTestContext::new_rust(
 7568        lsp::ServerCapabilities {
 7569            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7570                ..Default::default()
 7571            }),
 7572            ..Default::default()
 7573        },
 7574        cx,
 7575    )
 7576    .await;
 7577
 7578    let language = Language::new(
 7579        LanguageConfig {
 7580            name: "Rust".into(),
 7581            brackets: BracketPairConfig {
 7582                pairs: vec![
 7583                    BracketPair {
 7584                        start: "{".to_string(),
 7585                        end: "}".to_string(),
 7586                        close: true,
 7587                        surround: true,
 7588                        newline: true,
 7589                    },
 7590                    BracketPair {
 7591                        start: "(".to_string(),
 7592                        end: ")".to_string(),
 7593                        close: true,
 7594                        surround: true,
 7595                        newline: true,
 7596                    },
 7597                    BracketPair {
 7598                        start: "/*".to_string(),
 7599                        end: " */".to_string(),
 7600                        close: true,
 7601                        surround: true,
 7602                        newline: true,
 7603                    },
 7604                    BracketPair {
 7605                        start: "[".to_string(),
 7606                        end: "]".to_string(),
 7607                        close: false,
 7608                        surround: false,
 7609                        newline: true,
 7610                    },
 7611                    BracketPair {
 7612                        start: "\"".to_string(),
 7613                        end: "\"".to_string(),
 7614                        close: true,
 7615                        surround: true,
 7616                        newline: false,
 7617                    },
 7618                    BracketPair {
 7619                        start: "<".to_string(),
 7620                        end: ">".to_string(),
 7621                        close: false,
 7622                        surround: true,
 7623                        newline: true,
 7624                    },
 7625                ],
 7626                ..Default::default()
 7627            },
 7628            autoclose_before: "})]".to_string(),
 7629            ..Default::default()
 7630        },
 7631        Some(tree_sitter_rust::LANGUAGE.into()),
 7632    );
 7633    let language = Arc::new(language);
 7634
 7635    cx.language_registry().add(language.clone());
 7636    cx.update_buffer(|buffer, cx| {
 7637        buffer.set_language(Some(language), cx);
 7638    });
 7639
 7640    // Ensure that signature_help is not called when no signature help is enabled.
 7641    cx.set_state(
 7642        &r#"
 7643            fn main() {
 7644                sampleˇ
 7645            }
 7646        "#
 7647        .unindent(),
 7648    );
 7649    cx.update_editor(|view, cx| {
 7650        view.handle_input("(", cx);
 7651    });
 7652    cx.assert_editor_state(
 7653        &"
 7654            fn main() {
 7655                sample(ˇ)
 7656            }
 7657        "
 7658        .unindent(),
 7659    );
 7660    cx.editor(|editor, _| {
 7661        assert!(editor.signature_help_state.task().is_none());
 7662    });
 7663
 7664    let mocked_response = lsp::SignatureHelp {
 7665        signatures: vec![lsp::SignatureInformation {
 7666            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7667            documentation: None,
 7668            parameters: Some(vec![
 7669                lsp::ParameterInformation {
 7670                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7671                    documentation: None,
 7672                },
 7673                lsp::ParameterInformation {
 7674                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7675                    documentation: None,
 7676                },
 7677            ]),
 7678            active_parameter: None,
 7679        }],
 7680        active_signature: Some(0),
 7681        active_parameter: Some(0),
 7682    };
 7683
 7684    // Ensure that signature_help is called when enabled afte edits
 7685    cx.update(|cx| {
 7686        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7687            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7688                settings.auto_signature_help = Some(false);
 7689                settings.show_signature_help_after_edits = Some(true);
 7690            });
 7691        });
 7692    });
 7693    cx.set_state(
 7694        &r#"
 7695            fn main() {
 7696                sampleˇ
 7697            }
 7698        "#
 7699        .unindent(),
 7700    );
 7701    cx.update_editor(|view, cx| {
 7702        view.handle_input("(", cx);
 7703    });
 7704    cx.assert_editor_state(
 7705        &"
 7706            fn main() {
 7707                sample(ˇ)
 7708            }
 7709        "
 7710        .unindent(),
 7711    );
 7712    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7713    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7714        .await;
 7715    cx.update_editor(|editor, _| {
 7716        let signature_help_state = editor.signature_help_state.popover().cloned();
 7717        assert!(signature_help_state.is_some());
 7718        let ParsedMarkdown {
 7719            text, highlights, ..
 7720        } = signature_help_state.unwrap().parsed_content;
 7721        assert_eq!(text, "param1: u8, param2: u8");
 7722        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7723        editor.signature_help_state = SignatureHelpState::default();
 7724    });
 7725
 7726    // Ensure that signature_help is called when auto signature help override is enabled
 7727    cx.update(|cx| {
 7728        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7729            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7730                settings.auto_signature_help = Some(true);
 7731                settings.show_signature_help_after_edits = Some(false);
 7732            });
 7733        });
 7734    });
 7735    cx.set_state(
 7736        &r#"
 7737            fn main() {
 7738                sampleˇ
 7739            }
 7740        "#
 7741        .unindent(),
 7742    );
 7743    cx.update_editor(|view, cx| {
 7744        view.handle_input("(", cx);
 7745    });
 7746    cx.assert_editor_state(
 7747        &"
 7748            fn main() {
 7749                sample(ˇ)
 7750            }
 7751        "
 7752        .unindent(),
 7753    );
 7754    handle_signature_help_request(&mut cx, mocked_response).await;
 7755    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7756        .await;
 7757    cx.editor(|editor, _| {
 7758        let signature_help_state = editor.signature_help_state.popover().cloned();
 7759        assert!(signature_help_state.is_some());
 7760        let ParsedMarkdown {
 7761            text, highlights, ..
 7762        } = signature_help_state.unwrap().parsed_content;
 7763        assert_eq!(text, "param1: u8, param2: u8");
 7764        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7765    });
 7766}
 7767
 7768#[gpui::test]
 7769async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7770    init_test(cx, |_| {});
 7771    cx.update(|cx| {
 7772        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7773            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7774                settings.auto_signature_help = Some(true);
 7775            });
 7776        });
 7777    });
 7778
 7779    let mut cx = EditorLspTestContext::new_rust(
 7780        lsp::ServerCapabilities {
 7781            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7782                ..Default::default()
 7783            }),
 7784            ..Default::default()
 7785        },
 7786        cx,
 7787    )
 7788    .await;
 7789
 7790    // A test that directly calls `show_signature_help`
 7791    cx.update_editor(|editor, cx| {
 7792        editor.show_signature_help(&ShowSignatureHelp, cx);
 7793    });
 7794
 7795    let mocked_response = lsp::SignatureHelp {
 7796        signatures: vec![lsp::SignatureInformation {
 7797            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7798            documentation: None,
 7799            parameters: Some(vec![
 7800                lsp::ParameterInformation {
 7801                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7802                    documentation: None,
 7803                },
 7804                lsp::ParameterInformation {
 7805                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7806                    documentation: None,
 7807                },
 7808            ]),
 7809            active_parameter: None,
 7810        }],
 7811        active_signature: Some(0),
 7812        active_parameter: Some(0),
 7813    };
 7814    handle_signature_help_request(&mut cx, mocked_response).await;
 7815
 7816    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7817        .await;
 7818
 7819    cx.editor(|editor, _| {
 7820        let signature_help_state = editor.signature_help_state.popover().cloned();
 7821        assert!(signature_help_state.is_some());
 7822        let ParsedMarkdown {
 7823            text, highlights, ..
 7824        } = signature_help_state.unwrap().parsed_content;
 7825        assert_eq!(text, "param1: u8, param2: u8");
 7826        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7827    });
 7828
 7829    // When exiting outside from inside the brackets, `signature_help` is closed.
 7830    cx.set_state(indoc! {"
 7831        fn main() {
 7832            sample(ˇ);
 7833        }
 7834
 7835        fn sample(param1: u8, param2: u8) {}
 7836    "});
 7837
 7838    cx.update_editor(|editor, cx| {
 7839        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7840    });
 7841
 7842    let mocked_response = lsp::SignatureHelp {
 7843        signatures: Vec::new(),
 7844        active_signature: None,
 7845        active_parameter: None,
 7846    };
 7847    handle_signature_help_request(&mut cx, mocked_response).await;
 7848
 7849    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7850        .await;
 7851
 7852    cx.editor(|editor, _| {
 7853        assert!(!editor.signature_help_state.is_shown());
 7854    });
 7855
 7856    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7857    cx.set_state(indoc! {"
 7858        fn main() {
 7859            sample(ˇ);
 7860        }
 7861
 7862        fn sample(param1: u8, param2: u8) {}
 7863    "});
 7864
 7865    let mocked_response = lsp::SignatureHelp {
 7866        signatures: vec![lsp::SignatureInformation {
 7867            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7868            documentation: None,
 7869            parameters: Some(vec![
 7870                lsp::ParameterInformation {
 7871                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7872                    documentation: None,
 7873                },
 7874                lsp::ParameterInformation {
 7875                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7876                    documentation: None,
 7877                },
 7878            ]),
 7879            active_parameter: None,
 7880        }],
 7881        active_signature: Some(0),
 7882        active_parameter: Some(0),
 7883    };
 7884    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7885    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7886        .await;
 7887    cx.editor(|editor, _| {
 7888        assert!(editor.signature_help_state.is_shown());
 7889    });
 7890
 7891    // Restore the popover with more parameter input
 7892    cx.set_state(indoc! {"
 7893        fn main() {
 7894            sample(param1, param2ˇ);
 7895        }
 7896
 7897        fn sample(param1: u8, param2: u8) {}
 7898    "});
 7899
 7900    let mocked_response = lsp::SignatureHelp {
 7901        signatures: vec![lsp::SignatureInformation {
 7902            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7903            documentation: None,
 7904            parameters: Some(vec![
 7905                lsp::ParameterInformation {
 7906                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7907                    documentation: None,
 7908                },
 7909                lsp::ParameterInformation {
 7910                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7911                    documentation: None,
 7912                },
 7913            ]),
 7914            active_parameter: None,
 7915        }],
 7916        active_signature: Some(0),
 7917        active_parameter: Some(1),
 7918    };
 7919    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7920    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7921        .await;
 7922
 7923    // When selecting a range, the popover is gone.
 7924    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7925    cx.update_editor(|editor, cx| {
 7926        editor.change_selections(None, cx, |s| {
 7927            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7928        })
 7929    });
 7930    cx.assert_editor_state(indoc! {"
 7931        fn main() {
 7932            sample(param1, «ˇparam2»);
 7933        }
 7934
 7935        fn sample(param1: u8, param2: u8) {}
 7936    "});
 7937    cx.editor(|editor, _| {
 7938        assert!(!editor.signature_help_state.is_shown());
 7939    });
 7940
 7941    // When unselecting again, the popover is back if within the brackets.
 7942    cx.update_editor(|editor, cx| {
 7943        editor.change_selections(None, cx, |s| {
 7944            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7945        })
 7946    });
 7947    cx.assert_editor_state(indoc! {"
 7948        fn main() {
 7949            sample(param1, ˇparam2);
 7950        }
 7951
 7952        fn sample(param1: u8, param2: u8) {}
 7953    "});
 7954    handle_signature_help_request(&mut cx, mocked_response).await;
 7955    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7956        .await;
 7957    cx.editor(|editor, _| {
 7958        assert!(editor.signature_help_state.is_shown());
 7959    });
 7960
 7961    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7962    cx.update_editor(|editor, cx| {
 7963        editor.change_selections(None, cx, |s| {
 7964            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7965            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7966        })
 7967    });
 7968    cx.assert_editor_state(indoc! {"
 7969        fn main() {
 7970            sample(param1, ˇparam2);
 7971        }
 7972
 7973        fn sample(param1: u8, param2: u8) {}
 7974    "});
 7975
 7976    let mocked_response = lsp::SignatureHelp {
 7977        signatures: vec![lsp::SignatureInformation {
 7978            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7979            documentation: None,
 7980            parameters: Some(vec![
 7981                lsp::ParameterInformation {
 7982                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7983                    documentation: None,
 7984                },
 7985                lsp::ParameterInformation {
 7986                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7987                    documentation: None,
 7988                },
 7989            ]),
 7990            active_parameter: None,
 7991        }],
 7992        active_signature: Some(0),
 7993        active_parameter: Some(1),
 7994    };
 7995    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7996    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7997        .await;
 7998    cx.update_editor(|editor, cx| {
 7999        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8000    });
 8001    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8002        .await;
 8003    cx.update_editor(|editor, cx| {
 8004        editor.change_selections(None, cx, |s| {
 8005            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8006        })
 8007    });
 8008    cx.assert_editor_state(indoc! {"
 8009        fn main() {
 8010            sample(param1, «ˇparam2»);
 8011        }
 8012
 8013        fn sample(param1: u8, param2: u8) {}
 8014    "});
 8015    cx.update_editor(|editor, cx| {
 8016        editor.change_selections(None, cx, |s| {
 8017            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8018        })
 8019    });
 8020    cx.assert_editor_state(indoc! {"
 8021        fn main() {
 8022            sample(param1, ˇparam2);
 8023        }
 8024
 8025        fn sample(param1: u8, param2: u8) {}
 8026    "});
 8027    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8028        .await;
 8029}
 8030
 8031#[gpui::test]
 8032async fn test_completion(cx: &mut gpui::TestAppContext) {
 8033    init_test(cx, |_| {});
 8034
 8035    let mut cx = EditorLspTestContext::new_rust(
 8036        lsp::ServerCapabilities {
 8037            completion_provider: Some(lsp::CompletionOptions {
 8038                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8039                resolve_provider: Some(true),
 8040                ..Default::default()
 8041            }),
 8042            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8043            ..Default::default()
 8044        },
 8045        cx,
 8046    )
 8047    .await;
 8048    let counter = Arc::new(AtomicUsize::new(0));
 8049
 8050    cx.set_state(indoc! {"
 8051        oneˇ
 8052        two
 8053        three
 8054    "});
 8055    cx.simulate_keystroke(".");
 8056    handle_completion_request(
 8057        &mut cx,
 8058        indoc! {"
 8059            one.|<>
 8060            two
 8061            three
 8062        "},
 8063        vec!["first_completion", "second_completion"],
 8064        counter.clone(),
 8065    )
 8066    .await;
 8067    cx.condition(|editor, _| editor.context_menu_visible())
 8068        .await;
 8069    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8070
 8071    let _handler = handle_signature_help_request(
 8072        &mut cx,
 8073        lsp::SignatureHelp {
 8074            signatures: vec![lsp::SignatureInformation {
 8075                label: "test signature".to_string(),
 8076                documentation: None,
 8077                parameters: Some(vec![lsp::ParameterInformation {
 8078                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8079                    documentation: None,
 8080                }]),
 8081                active_parameter: None,
 8082            }],
 8083            active_signature: None,
 8084            active_parameter: None,
 8085        },
 8086    );
 8087    cx.update_editor(|editor, cx| {
 8088        assert!(
 8089            !editor.signature_help_state.is_shown(),
 8090            "No signature help was called for"
 8091        );
 8092        editor.show_signature_help(&ShowSignatureHelp, cx);
 8093    });
 8094    cx.run_until_parked();
 8095    cx.update_editor(|editor, _| {
 8096        assert!(
 8097            !editor.signature_help_state.is_shown(),
 8098            "No signature help should be shown when completions menu is open"
 8099        );
 8100    });
 8101
 8102    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8103        editor.context_menu_next(&Default::default(), cx);
 8104        editor
 8105            .confirm_completion(&ConfirmCompletion::default(), cx)
 8106            .unwrap()
 8107    });
 8108    cx.assert_editor_state(indoc! {"
 8109        one.second_completionˇ
 8110        two
 8111        three
 8112    "});
 8113
 8114    handle_resolve_completion_request(
 8115        &mut cx,
 8116        Some(vec![
 8117            (
 8118                //This overlaps with the primary completion edit which is
 8119                //misbehavior from the LSP spec, test that we filter it out
 8120                indoc! {"
 8121                    one.second_ˇcompletion
 8122                    two
 8123                    threeˇ
 8124                "},
 8125                "overlapping additional edit",
 8126            ),
 8127            (
 8128                indoc! {"
 8129                    one.second_completion
 8130                    two
 8131                    threeˇ
 8132                "},
 8133                "\nadditional edit",
 8134            ),
 8135        ]),
 8136    )
 8137    .await;
 8138    apply_additional_edits.await.unwrap();
 8139    cx.assert_editor_state(indoc! {"
 8140        one.second_completionˇ
 8141        two
 8142        three
 8143        additional edit
 8144    "});
 8145
 8146    cx.set_state(indoc! {"
 8147        one.second_completion
 8148        twoˇ
 8149        threeˇ
 8150        additional edit
 8151    "});
 8152    cx.simulate_keystroke(" ");
 8153    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8154    cx.simulate_keystroke("s");
 8155    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8156
 8157    cx.assert_editor_state(indoc! {"
 8158        one.second_completion
 8159        two sˇ
 8160        three sˇ
 8161        additional edit
 8162    "});
 8163    handle_completion_request(
 8164        &mut cx,
 8165        indoc! {"
 8166            one.second_completion
 8167            two s
 8168            three <s|>
 8169            additional edit
 8170        "},
 8171        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8172        counter.clone(),
 8173    )
 8174    .await;
 8175    cx.condition(|editor, _| editor.context_menu_visible())
 8176        .await;
 8177    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8178
 8179    cx.simulate_keystroke("i");
 8180
 8181    handle_completion_request(
 8182        &mut cx,
 8183        indoc! {"
 8184            one.second_completion
 8185            two si
 8186            three <si|>
 8187            additional edit
 8188        "},
 8189        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8190        counter.clone(),
 8191    )
 8192    .await;
 8193    cx.condition(|editor, _| editor.context_menu_visible())
 8194        .await;
 8195    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8196
 8197    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8198        editor
 8199            .confirm_completion(&ConfirmCompletion::default(), cx)
 8200            .unwrap()
 8201    });
 8202    cx.assert_editor_state(indoc! {"
 8203        one.second_completion
 8204        two sixth_completionˇ
 8205        three sixth_completionˇ
 8206        additional edit
 8207    "});
 8208
 8209    handle_resolve_completion_request(&mut cx, None).await;
 8210    apply_additional_edits.await.unwrap();
 8211
 8212    cx.update(|cx| {
 8213        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8214            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8215                settings.show_completions_on_input = Some(false);
 8216            });
 8217        })
 8218    });
 8219    cx.set_state("editorˇ");
 8220    cx.simulate_keystroke(".");
 8221    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8222    cx.simulate_keystroke("c");
 8223    cx.simulate_keystroke("l");
 8224    cx.simulate_keystroke("o");
 8225    cx.assert_editor_state("editor.cloˇ");
 8226    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8227    cx.update_editor(|editor, cx| {
 8228        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8229    });
 8230    handle_completion_request(
 8231        &mut cx,
 8232        "editor.<clo|>",
 8233        vec!["close", "clobber"],
 8234        counter.clone(),
 8235    )
 8236    .await;
 8237    cx.condition(|editor, _| editor.context_menu_visible())
 8238        .await;
 8239    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8240
 8241    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8242        editor
 8243            .confirm_completion(&ConfirmCompletion::default(), cx)
 8244            .unwrap()
 8245    });
 8246    cx.assert_editor_state("editor.closeˇ");
 8247    handle_resolve_completion_request(&mut cx, None).await;
 8248    apply_additional_edits.await.unwrap();
 8249}
 8250
 8251#[gpui::test]
 8252async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8253    init_test(cx, |_| {});
 8254    let mut cx = EditorLspTestContext::new_rust(
 8255        lsp::ServerCapabilities {
 8256            completion_provider: Some(lsp::CompletionOptions {
 8257                trigger_characters: Some(vec![".".to_string()]),
 8258                ..Default::default()
 8259            }),
 8260            ..Default::default()
 8261        },
 8262        cx,
 8263    )
 8264    .await;
 8265    cx.lsp
 8266        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8267            Ok(Some(lsp::CompletionResponse::Array(vec![
 8268                lsp::CompletionItem {
 8269                    label: "first".into(),
 8270                    ..Default::default()
 8271                },
 8272                lsp::CompletionItem {
 8273                    label: "last".into(),
 8274                    ..Default::default()
 8275                },
 8276            ])))
 8277        });
 8278    cx.set_state("variableˇ");
 8279    cx.simulate_keystroke(".");
 8280    cx.executor().run_until_parked();
 8281
 8282    cx.update_editor(|editor, _| {
 8283        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8284            assert_eq!(
 8285                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8286                &["first", "last"]
 8287            );
 8288        } else {
 8289            panic!("expected completion menu to be open");
 8290        }
 8291    });
 8292
 8293    cx.update_editor(|editor, cx| {
 8294        editor.move_page_down(&MovePageDown::default(), cx);
 8295        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8296            assert!(
 8297                menu.selected_item == 1,
 8298                "expected PageDown to select the last item from the context menu"
 8299            );
 8300        } else {
 8301            panic!("expected completion menu to stay open after PageDown");
 8302        }
 8303    });
 8304
 8305    cx.update_editor(|editor, cx| {
 8306        editor.move_page_up(&MovePageUp::default(), cx);
 8307        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8308            assert!(
 8309                menu.selected_item == 0,
 8310                "expected PageUp to select the first item from the context menu"
 8311            );
 8312        } else {
 8313            panic!("expected completion menu to stay open after PageUp");
 8314        }
 8315    });
 8316}
 8317
 8318#[gpui::test]
 8319async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8320    init_test(cx, |_| {});
 8321
 8322    let mut cx = EditorLspTestContext::new_rust(
 8323        lsp::ServerCapabilities {
 8324            completion_provider: Some(lsp::CompletionOptions {
 8325                trigger_characters: Some(vec![".".to_string()]),
 8326                resolve_provider: Some(true),
 8327                ..Default::default()
 8328            }),
 8329            ..Default::default()
 8330        },
 8331        cx,
 8332    )
 8333    .await;
 8334
 8335    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8336    cx.simulate_keystroke(".");
 8337    let completion_item = lsp::CompletionItem {
 8338        label: "Some".into(),
 8339        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8340        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8341        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8342            kind: lsp::MarkupKind::Markdown,
 8343            value: "```rust\nSome(2)\n```".to_string(),
 8344        })),
 8345        deprecated: Some(false),
 8346        sort_text: Some("Some".to_string()),
 8347        filter_text: Some("Some".to_string()),
 8348        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8349        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8350            range: lsp::Range {
 8351                start: lsp::Position {
 8352                    line: 0,
 8353                    character: 22,
 8354                },
 8355                end: lsp::Position {
 8356                    line: 0,
 8357                    character: 22,
 8358                },
 8359            },
 8360            new_text: "Some(2)".to_string(),
 8361        })),
 8362        additional_text_edits: Some(vec![lsp::TextEdit {
 8363            range: lsp::Range {
 8364                start: lsp::Position {
 8365                    line: 0,
 8366                    character: 20,
 8367                },
 8368                end: lsp::Position {
 8369                    line: 0,
 8370                    character: 22,
 8371                },
 8372            },
 8373            new_text: "".to_string(),
 8374        }]),
 8375        ..Default::default()
 8376    };
 8377
 8378    let closure_completion_item = completion_item.clone();
 8379    let counter = Arc::new(AtomicUsize::new(0));
 8380    let counter_clone = counter.clone();
 8381    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8382        let task_completion_item = closure_completion_item.clone();
 8383        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8384        async move {
 8385            Ok(Some(lsp::CompletionResponse::Array(vec![
 8386                task_completion_item,
 8387            ])))
 8388        }
 8389    });
 8390
 8391    cx.condition(|editor, _| editor.context_menu_visible())
 8392        .await;
 8393    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8394    assert!(request.next().await.is_some());
 8395    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8396
 8397    cx.simulate_keystroke("S");
 8398    cx.simulate_keystroke("o");
 8399    cx.simulate_keystroke("m");
 8400    cx.condition(|editor, _| editor.context_menu_visible())
 8401        .await;
 8402    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8403    assert!(request.next().await.is_some());
 8404    assert!(request.next().await.is_some());
 8405    assert!(request.next().await.is_some());
 8406    request.close();
 8407    assert!(request.next().await.is_none());
 8408    assert_eq!(
 8409        counter.load(atomic::Ordering::Acquire),
 8410        4,
 8411        "With the completions menu open, only one LSP request should happen per input"
 8412    );
 8413}
 8414
 8415#[gpui::test]
 8416async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8417    init_test(cx, |_| {});
 8418    let mut cx = EditorTestContext::new(cx).await;
 8419    let language = Arc::new(Language::new(
 8420        LanguageConfig {
 8421            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8422            ..Default::default()
 8423        },
 8424        Some(tree_sitter_rust::LANGUAGE.into()),
 8425    ));
 8426    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8427
 8428    // If multiple selections intersect a line, the line is only toggled once.
 8429    cx.set_state(indoc! {"
 8430        fn a() {
 8431            «//b();
 8432            ˇ»// «c();
 8433            //ˇ»  d();
 8434        }
 8435    "});
 8436
 8437    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8438
 8439    cx.assert_editor_state(indoc! {"
 8440        fn a() {
 8441            «b();
 8442            c();
 8443            ˇ» d();
 8444        }
 8445    "});
 8446
 8447    // The comment prefix is inserted at the same column for every line in a
 8448    // selection.
 8449    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8450
 8451    cx.assert_editor_state(indoc! {"
 8452        fn a() {
 8453            // «b();
 8454            // c();
 8455            ˇ»//  d();
 8456        }
 8457    "});
 8458
 8459    // If a selection ends at the beginning of a line, that line is not toggled.
 8460    cx.set_selections_state(indoc! {"
 8461        fn a() {
 8462            // b();
 8463            «// c();
 8464        ˇ»    //  d();
 8465        }
 8466    "});
 8467
 8468    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8469
 8470    cx.assert_editor_state(indoc! {"
 8471        fn a() {
 8472            // b();
 8473            «c();
 8474        ˇ»    //  d();
 8475        }
 8476    "});
 8477
 8478    // If a selection span a single line and is empty, the line is toggled.
 8479    cx.set_state(indoc! {"
 8480        fn a() {
 8481            a();
 8482            b();
 8483        ˇ
 8484        }
 8485    "});
 8486
 8487    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8488
 8489    cx.assert_editor_state(indoc! {"
 8490        fn a() {
 8491            a();
 8492            b();
 8493        //•ˇ
 8494        }
 8495    "});
 8496
 8497    // If a selection span multiple lines, empty lines are not toggled.
 8498    cx.set_state(indoc! {"
 8499        fn a() {
 8500            «a();
 8501
 8502            c();ˇ»
 8503        }
 8504    "});
 8505
 8506    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8507
 8508    cx.assert_editor_state(indoc! {"
 8509        fn a() {
 8510            // «a();
 8511
 8512            // c();ˇ»
 8513        }
 8514    "});
 8515
 8516    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8517    cx.set_state(indoc! {"
 8518        fn a() {
 8519            «// a();
 8520            /// b();
 8521            //! c();ˇ»
 8522        }
 8523    "});
 8524
 8525    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8526
 8527    cx.assert_editor_state(indoc! {"
 8528        fn a() {
 8529            «a();
 8530            b();
 8531            c();ˇ»
 8532        }
 8533    "});
 8534}
 8535
 8536#[gpui::test]
 8537async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8538    init_test(cx, |_| {});
 8539
 8540    let language = Arc::new(Language::new(
 8541        LanguageConfig {
 8542            line_comments: vec!["// ".into()],
 8543            ..Default::default()
 8544        },
 8545        Some(tree_sitter_rust::LANGUAGE.into()),
 8546    ));
 8547
 8548    let mut cx = EditorTestContext::new(cx).await;
 8549
 8550    cx.language_registry().add(language.clone());
 8551    cx.update_buffer(|buffer, cx| {
 8552        buffer.set_language(Some(language), cx);
 8553    });
 8554
 8555    let toggle_comments = &ToggleComments {
 8556        advance_downwards: true,
 8557    };
 8558
 8559    // Single cursor on one line -> advance
 8560    // Cursor moves horizontally 3 characters as well on non-blank line
 8561    cx.set_state(indoc!(
 8562        "fn a() {
 8563             ˇdog();
 8564             cat();
 8565        }"
 8566    ));
 8567    cx.update_editor(|editor, cx| {
 8568        editor.toggle_comments(toggle_comments, cx);
 8569    });
 8570    cx.assert_editor_state(indoc!(
 8571        "fn a() {
 8572             // dog();
 8573             catˇ();
 8574        }"
 8575    ));
 8576
 8577    // Single selection on one line -> don't advance
 8578    cx.set_state(indoc!(
 8579        "fn a() {
 8580             «dog()ˇ»;
 8581             cat();
 8582        }"
 8583    ));
 8584    cx.update_editor(|editor, cx| {
 8585        editor.toggle_comments(toggle_comments, cx);
 8586    });
 8587    cx.assert_editor_state(indoc!(
 8588        "fn a() {
 8589             // «dog()ˇ»;
 8590             cat();
 8591        }"
 8592    ));
 8593
 8594    // Multiple cursors on one line -> advance
 8595    cx.set_state(indoc!(
 8596        "fn a() {
 8597             ˇdˇog();
 8598             cat();
 8599        }"
 8600    ));
 8601    cx.update_editor(|editor, cx| {
 8602        editor.toggle_comments(toggle_comments, cx);
 8603    });
 8604    cx.assert_editor_state(indoc!(
 8605        "fn a() {
 8606             // dog();
 8607             catˇ(ˇ);
 8608        }"
 8609    ));
 8610
 8611    // Multiple cursors on one line, with selection -> don't advance
 8612    cx.set_state(indoc!(
 8613        "fn a() {
 8614             ˇdˇog«()ˇ»;
 8615             cat();
 8616        }"
 8617    ));
 8618    cx.update_editor(|editor, cx| {
 8619        editor.toggle_comments(toggle_comments, cx);
 8620    });
 8621    cx.assert_editor_state(indoc!(
 8622        "fn a() {
 8623             // ˇdˇog«()ˇ»;
 8624             cat();
 8625        }"
 8626    ));
 8627
 8628    // Single cursor on one line -> advance
 8629    // Cursor moves to column 0 on blank line
 8630    cx.set_state(indoc!(
 8631        "fn a() {
 8632             ˇdog();
 8633
 8634             cat();
 8635        }"
 8636    ));
 8637    cx.update_editor(|editor, cx| {
 8638        editor.toggle_comments(toggle_comments, cx);
 8639    });
 8640    cx.assert_editor_state(indoc!(
 8641        "fn a() {
 8642             // dog();
 8643        ˇ
 8644             cat();
 8645        }"
 8646    ));
 8647
 8648    // Single cursor on one line -> advance
 8649    // Cursor starts and ends at column 0
 8650    cx.set_state(indoc!(
 8651        "fn a() {
 8652         ˇ    dog();
 8653             cat();
 8654        }"
 8655    ));
 8656    cx.update_editor(|editor, cx| {
 8657        editor.toggle_comments(toggle_comments, cx);
 8658    });
 8659    cx.assert_editor_state(indoc!(
 8660        "fn a() {
 8661             // dog();
 8662         ˇ    cat();
 8663        }"
 8664    ));
 8665}
 8666
 8667#[gpui::test]
 8668async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8669    init_test(cx, |_| {});
 8670
 8671    let mut cx = EditorTestContext::new(cx).await;
 8672
 8673    let html_language = Arc::new(
 8674        Language::new(
 8675            LanguageConfig {
 8676                name: "HTML".into(),
 8677                block_comment: Some(("<!-- ".into(), " -->".into())),
 8678                ..Default::default()
 8679            },
 8680            Some(tree_sitter_html::language()),
 8681        )
 8682        .with_injection_query(
 8683            r#"
 8684            (script_element
 8685                (raw_text) @content
 8686                (#set! "language" "javascript"))
 8687            "#,
 8688        )
 8689        .unwrap(),
 8690    );
 8691
 8692    let javascript_language = Arc::new(Language::new(
 8693        LanguageConfig {
 8694            name: "JavaScript".into(),
 8695            line_comments: vec!["// ".into()],
 8696            ..Default::default()
 8697        },
 8698        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8699    ));
 8700
 8701    cx.language_registry().add(html_language.clone());
 8702    cx.language_registry().add(javascript_language.clone());
 8703    cx.update_buffer(|buffer, cx| {
 8704        buffer.set_language(Some(html_language), cx);
 8705    });
 8706
 8707    // Toggle comments for empty selections
 8708    cx.set_state(
 8709        &r#"
 8710            <p>A</p>ˇ
 8711            <p>B</p>ˇ
 8712            <p>C</p>ˇ
 8713        "#
 8714        .unindent(),
 8715    );
 8716    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8717    cx.assert_editor_state(
 8718        &r#"
 8719            <!-- <p>A</p>ˇ -->
 8720            <!-- <p>B</p>ˇ -->
 8721            <!-- <p>C</p>ˇ -->
 8722        "#
 8723        .unindent(),
 8724    );
 8725    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8726    cx.assert_editor_state(
 8727        &r#"
 8728            <p>A</p>ˇ
 8729            <p>B</p>ˇ
 8730            <p>C</p>ˇ
 8731        "#
 8732        .unindent(),
 8733    );
 8734
 8735    // Toggle comments for mixture of empty and non-empty selections, where
 8736    // multiple selections occupy a given line.
 8737    cx.set_state(
 8738        &r#"
 8739            <p>A«</p>
 8740            <p>ˇ»B</p>ˇ
 8741            <p>C«</p>
 8742            <p>ˇ»D</p>ˇ
 8743        "#
 8744        .unindent(),
 8745    );
 8746
 8747    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8748    cx.assert_editor_state(
 8749        &r#"
 8750            <!-- <p>A«</p>
 8751            <p>ˇ»B</p>ˇ -->
 8752            <!-- <p>C«</p>
 8753            <p>ˇ»D</p>ˇ -->
 8754        "#
 8755        .unindent(),
 8756    );
 8757    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8758    cx.assert_editor_state(
 8759        &r#"
 8760            <p>A«</p>
 8761            <p>ˇ»B</p>ˇ
 8762            <p>C«</p>
 8763            <p>ˇ»D</p>ˇ
 8764        "#
 8765        .unindent(),
 8766    );
 8767
 8768    // Toggle comments when different languages are active for different
 8769    // selections.
 8770    cx.set_state(
 8771        &r#"
 8772            ˇ<script>
 8773                ˇvar x = new Y();
 8774            ˇ</script>
 8775        "#
 8776        .unindent(),
 8777    );
 8778    cx.executor().run_until_parked();
 8779    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8780    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8781    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8782    cx.assert_editor_state(
 8783        &r#"
 8784            <!-- ˇ<script> -->
 8785                // ˇvar x = new Y();
 8786            // ˇ</script>
 8787        "#
 8788        .unindent(),
 8789    );
 8790}
 8791
 8792#[gpui::test]
 8793fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8794    init_test(cx, |_| {});
 8795
 8796    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8797    let multibuffer = cx.new_model(|cx| {
 8798        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8799        multibuffer.push_excerpts(
 8800            buffer.clone(),
 8801            [
 8802                ExcerptRange {
 8803                    context: Point::new(0, 0)..Point::new(0, 4),
 8804                    primary: None,
 8805                },
 8806                ExcerptRange {
 8807                    context: Point::new(1, 0)..Point::new(1, 4),
 8808                    primary: None,
 8809                },
 8810            ],
 8811            cx,
 8812        );
 8813        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8814        multibuffer
 8815    });
 8816
 8817    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8818    view.update(cx, |view, cx| {
 8819        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8820        view.change_selections(None, cx, |s| {
 8821            s.select_ranges([
 8822                Point::new(0, 0)..Point::new(0, 0),
 8823                Point::new(1, 0)..Point::new(1, 0),
 8824            ])
 8825        });
 8826
 8827        view.handle_input("X", cx);
 8828        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8829        assert_eq!(
 8830            view.selections.ranges(cx),
 8831            [
 8832                Point::new(0, 1)..Point::new(0, 1),
 8833                Point::new(1, 1)..Point::new(1, 1),
 8834            ]
 8835        );
 8836
 8837        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8838        view.change_selections(None, cx, |s| {
 8839            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8840        });
 8841        view.backspace(&Default::default(), cx);
 8842        assert_eq!(view.text(cx), "Xa\nbbb");
 8843        assert_eq!(
 8844            view.selections.ranges(cx),
 8845            [Point::new(1, 0)..Point::new(1, 0)]
 8846        );
 8847
 8848        view.change_selections(None, cx, |s| {
 8849            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8850        });
 8851        view.backspace(&Default::default(), cx);
 8852        assert_eq!(view.text(cx), "X\nbb");
 8853        assert_eq!(
 8854            view.selections.ranges(cx),
 8855            [Point::new(0, 1)..Point::new(0, 1)]
 8856        );
 8857    });
 8858}
 8859
 8860#[gpui::test]
 8861fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8862    init_test(cx, |_| {});
 8863
 8864    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8865    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8866        indoc! {"
 8867            [aaaa
 8868            (bbbb]
 8869            cccc)",
 8870        },
 8871        markers.clone(),
 8872    );
 8873    let excerpt_ranges = markers.into_iter().map(|marker| {
 8874        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8875        ExcerptRange {
 8876            context,
 8877            primary: None,
 8878        }
 8879    });
 8880    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8881    let multibuffer = cx.new_model(|cx| {
 8882        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8883        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8884        multibuffer
 8885    });
 8886
 8887    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8888    view.update(cx, |view, cx| {
 8889        let (expected_text, selection_ranges) = marked_text_ranges(
 8890            indoc! {"
 8891                aaaa
 8892                bˇbbb
 8893                bˇbbˇb
 8894                cccc"
 8895            },
 8896            true,
 8897        );
 8898        assert_eq!(view.text(cx), expected_text);
 8899        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8900
 8901        view.handle_input("X", cx);
 8902
 8903        let (expected_text, expected_selections) = marked_text_ranges(
 8904            indoc! {"
 8905                aaaa
 8906                bXˇbbXb
 8907                bXˇbbXˇb
 8908                cccc"
 8909            },
 8910            false,
 8911        );
 8912        assert_eq!(view.text(cx), expected_text);
 8913        assert_eq!(view.selections.ranges(cx), expected_selections);
 8914
 8915        view.newline(&Newline, cx);
 8916        let (expected_text, expected_selections) = marked_text_ranges(
 8917            indoc! {"
 8918                aaaa
 8919                bX
 8920                ˇbbX
 8921                b
 8922                bX
 8923                ˇbbX
 8924                ˇb
 8925                cccc"
 8926            },
 8927            false,
 8928        );
 8929        assert_eq!(view.text(cx), expected_text);
 8930        assert_eq!(view.selections.ranges(cx), expected_selections);
 8931    });
 8932}
 8933
 8934#[gpui::test]
 8935fn test_refresh_selections(cx: &mut TestAppContext) {
 8936    init_test(cx, |_| {});
 8937
 8938    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8939    let mut excerpt1_id = None;
 8940    let multibuffer = cx.new_model(|cx| {
 8941        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8942        excerpt1_id = multibuffer
 8943            .push_excerpts(
 8944                buffer.clone(),
 8945                [
 8946                    ExcerptRange {
 8947                        context: Point::new(0, 0)..Point::new(1, 4),
 8948                        primary: None,
 8949                    },
 8950                    ExcerptRange {
 8951                        context: Point::new(1, 0)..Point::new(2, 4),
 8952                        primary: None,
 8953                    },
 8954                ],
 8955                cx,
 8956            )
 8957            .into_iter()
 8958            .next();
 8959        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8960        multibuffer
 8961    });
 8962
 8963    let editor = cx.add_window(|cx| {
 8964        let mut editor = build_editor(multibuffer.clone(), cx);
 8965        let snapshot = editor.snapshot(cx);
 8966        editor.change_selections(None, cx, |s| {
 8967            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8968        });
 8969        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8970        assert_eq!(
 8971            editor.selections.ranges(cx),
 8972            [
 8973                Point::new(1, 3)..Point::new(1, 3),
 8974                Point::new(2, 1)..Point::new(2, 1),
 8975            ]
 8976        );
 8977        editor
 8978    });
 8979
 8980    // Refreshing selections is a no-op when excerpts haven't changed.
 8981    _ = editor.update(cx, |editor, cx| {
 8982        editor.change_selections(None, cx, |s| s.refresh());
 8983        assert_eq!(
 8984            editor.selections.ranges(cx),
 8985            [
 8986                Point::new(1, 3)..Point::new(1, 3),
 8987                Point::new(2, 1)..Point::new(2, 1),
 8988            ]
 8989        );
 8990    });
 8991
 8992    multibuffer.update(cx, |multibuffer, cx| {
 8993        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8994    });
 8995    _ = editor.update(cx, |editor, cx| {
 8996        // Removing an excerpt causes the first selection to become degenerate.
 8997        assert_eq!(
 8998            editor.selections.ranges(cx),
 8999            [
 9000                Point::new(0, 0)..Point::new(0, 0),
 9001                Point::new(0, 1)..Point::new(0, 1)
 9002            ]
 9003        );
 9004
 9005        // Refreshing selections will relocate the first selection to the original buffer
 9006        // location.
 9007        editor.change_selections(None, cx, |s| s.refresh());
 9008        assert_eq!(
 9009            editor.selections.ranges(cx),
 9010            [
 9011                Point::new(0, 1)..Point::new(0, 1),
 9012                Point::new(0, 3)..Point::new(0, 3)
 9013            ]
 9014        );
 9015        assert!(editor.selections.pending_anchor().is_some());
 9016    });
 9017}
 9018
 9019#[gpui::test]
 9020fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9021    init_test(cx, |_| {});
 9022
 9023    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9024    let mut excerpt1_id = None;
 9025    let multibuffer = cx.new_model(|cx| {
 9026        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9027        excerpt1_id = multibuffer
 9028            .push_excerpts(
 9029                buffer.clone(),
 9030                [
 9031                    ExcerptRange {
 9032                        context: Point::new(0, 0)..Point::new(1, 4),
 9033                        primary: None,
 9034                    },
 9035                    ExcerptRange {
 9036                        context: Point::new(1, 0)..Point::new(2, 4),
 9037                        primary: None,
 9038                    },
 9039                ],
 9040                cx,
 9041            )
 9042            .into_iter()
 9043            .next();
 9044        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9045        multibuffer
 9046    });
 9047
 9048    let editor = cx.add_window(|cx| {
 9049        let mut editor = build_editor(multibuffer.clone(), cx);
 9050        let snapshot = editor.snapshot(cx);
 9051        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9052        assert_eq!(
 9053            editor.selections.ranges(cx),
 9054            [Point::new(1, 3)..Point::new(1, 3)]
 9055        );
 9056        editor
 9057    });
 9058
 9059    multibuffer.update(cx, |multibuffer, cx| {
 9060        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9061    });
 9062    _ = editor.update(cx, |editor, cx| {
 9063        assert_eq!(
 9064            editor.selections.ranges(cx),
 9065            [Point::new(0, 0)..Point::new(0, 0)]
 9066        );
 9067
 9068        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9069        editor.change_selections(None, cx, |s| s.refresh());
 9070        assert_eq!(
 9071            editor.selections.ranges(cx),
 9072            [Point::new(0, 3)..Point::new(0, 3)]
 9073        );
 9074        assert!(editor.selections.pending_anchor().is_some());
 9075    });
 9076}
 9077
 9078#[gpui::test]
 9079async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9080    init_test(cx, |_| {});
 9081
 9082    let language = Arc::new(
 9083        Language::new(
 9084            LanguageConfig {
 9085                brackets: BracketPairConfig {
 9086                    pairs: vec![
 9087                        BracketPair {
 9088                            start: "{".to_string(),
 9089                            end: "}".to_string(),
 9090                            close: true,
 9091                            surround: true,
 9092                            newline: true,
 9093                        },
 9094                        BracketPair {
 9095                            start: "/* ".to_string(),
 9096                            end: " */".to_string(),
 9097                            close: true,
 9098                            surround: true,
 9099                            newline: true,
 9100                        },
 9101                    ],
 9102                    ..Default::default()
 9103                },
 9104                ..Default::default()
 9105            },
 9106            Some(tree_sitter_rust::LANGUAGE.into()),
 9107        )
 9108        .with_indents_query("")
 9109        .unwrap(),
 9110    );
 9111
 9112    let text = concat!(
 9113        "{   }\n",     //
 9114        "  x\n",       //
 9115        "  /*   */\n", //
 9116        "x\n",         //
 9117        "{{} }\n",     //
 9118    );
 9119
 9120    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9121    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9122    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9123    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9124        .await;
 9125
 9126    view.update(cx, |view, cx| {
 9127        view.change_selections(None, cx, |s| {
 9128            s.select_display_ranges([
 9129                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9130                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9131                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9132            ])
 9133        });
 9134        view.newline(&Newline, cx);
 9135
 9136        assert_eq!(
 9137            view.buffer().read(cx).read(cx).text(),
 9138            concat!(
 9139                "{ \n",    // Suppress rustfmt
 9140                "\n",      //
 9141                "}\n",     //
 9142                "  x\n",   //
 9143                "  /* \n", //
 9144                "  \n",    //
 9145                "  */\n",  //
 9146                "x\n",     //
 9147                "{{} \n",  //
 9148                "}\n",     //
 9149            )
 9150        );
 9151    });
 9152}
 9153
 9154#[gpui::test]
 9155fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9156    init_test(cx, |_| {});
 9157
 9158    let editor = cx.add_window(|cx| {
 9159        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9160        build_editor(buffer.clone(), cx)
 9161    });
 9162
 9163    _ = editor.update(cx, |editor, cx| {
 9164        struct Type1;
 9165        struct Type2;
 9166
 9167        let buffer = editor.buffer.read(cx).snapshot(cx);
 9168
 9169        let anchor_range =
 9170            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9171
 9172        editor.highlight_background::<Type1>(
 9173            &[
 9174                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9175                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9176                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9177                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9178            ],
 9179            |_| Hsla::red(),
 9180            cx,
 9181        );
 9182        editor.highlight_background::<Type2>(
 9183            &[
 9184                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9185                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9186                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9187                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9188            ],
 9189            |_| Hsla::green(),
 9190            cx,
 9191        );
 9192
 9193        let snapshot = editor.snapshot(cx);
 9194        let mut highlighted_ranges = editor.background_highlights_in_range(
 9195            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9196            &snapshot,
 9197            cx.theme().colors(),
 9198        );
 9199        // Enforce a consistent ordering based on color without relying on the ordering of the
 9200        // highlight's `TypeId` which is non-executor.
 9201        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9202        assert_eq!(
 9203            highlighted_ranges,
 9204            &[
 9205                (
 9206                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9207                    Hsla::red(),
 9208                ),
 9209                (
 9210                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9211                    Hsla::red(),
 9212                ),
 9213                (
 9214                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9215                    Hsla::green(),
 9216                ),
 9217                (
 9218                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9219                    Hsla::green(),
 9220                ),
 9221            ]
 9222        );
 9223        assert_eq!(
 9224            editor.background_highlights_in_range(
 9225                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9226                &snapshot,
 9227                cx.theme().colors(),
 9228            ),
 9229            &[(
 9230                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9231                Hsla::red(),
 9232            )]
 9233        );
 9234    });
 9235}
 9236
 9237#[gpui::test]
 9238async fn test_following(cx: &mut gpui::TestAppContext) {
 9239    init_test(cx, |_| {});
 9240
 9241    let fs = FakeFs::new(cx.executor());
 9242    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9243
 9244    let buffer = project.update(cx, |project, cx| {
 9245        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9246        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9247    });
 9248    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9249    let follower = cx.update(|cx| {
 9250        cx.open_window(
 9251            WindowOptions {
 9252                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9253                    gpui::Point::new(px(0.), px(0.)),
 9254                    gpui::Point::new(px(10.), px(80.)),
 9255                ))),
 9256                ..Default::default()
 9257            },
 9258            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9259        )
 9260        .unwrap()
 9261    });
 9262
 9263    let is_still_following = Rc::new(RefCell::new(true));
 9264    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9265    let pending_update = Rc::new(RefCell::new(None));
 9266    _ = follower.update(cx, {
 9267        let update = pending_update.clone();
 9268        let is_still_following = is_still_following.clone();
 9269        let follower_edit_event_count = follower_edit_event_count.clone();
 9270        |_, cx| {
 9271            cx.subscribe(
 9272                &leader.root_view(cx).unwrap(),
 9273                move |_, leader, event, cx| {
 9274                    leader
 9275                        .read(cx)
 9276                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9277                },
 9278            )
 9279            .detach();
 9280
 9281            cx.subscribe(
 9282                &follower.root_view(cx).unwrap(),
 9283                move |_, _, event: &EditorEvent, _cx| {
 9284                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9285                        *is_still_following.borrow_mut() = false;
 9286                    }
 9287
 9288                    if let EditorEvent::BufferEdited = event {
 9289                        *follower_edit_event_count.borrow_mut() += 1;
 9290                    }
 9291                },
 9292            )
 9293            .detach();
 9294        }
 9295    });
 9296
 9297    // Update the selections only
 9298    _ = leader.update(cx, |leader, cx| {
 9299        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9300    });
 9301    follower
 9302        .update(cx, |follower, cx| {
 9303            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9304        })
 9305        .unwrap()
 9306        .await
 9307        .unwrap();
 9308    _ = follower.update(cx, |follower, cx| {
 9309        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9310    });
 9311    assert!(*is_still_following.borrow());
 9312    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9313
 9314    // Update the scroll position only
 9315    _ = leader.update(cx, |leader, cx| {
 9316        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9317    });
 9318    follower
 9319        .update(cx, |follower, cx| {
 9320            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9321        })
 9322        .unwrap()
 9323        .await
 9324        .unwrap();
 9325    assert_eq!(
 9326        follower
 9327            .update(cx, |follower, cx| follower.scroll_position(cx))
 9328            .unwrap(),
 9329        gpui::Point::new(1.5, 3.5)
 9330    );
 9331    assert!(*is_still_following.borrow());
 9332    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9333
 9334    // Update the selections and scroll position. The follower's scroll position is updated
 9335    // via autoscroll, not via the leader's exact scroll position.
 9336    _ = leader.update(cx, |leader, cx| {
 9337        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9338        leader.request_autoscroll(Autoscroll::newest(), cx);
 9339        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9340    });
 9341    follower
 9342        .update(cx, |follower, cx| {
 9343            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9344        })
 9345        .unwrap()
 9346        .await
 9347        .unwrap();
 9348    _ = follower.update(cx, |follower, cx| {
 9349        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9350        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9351    });
 9352    assert!(*is_still_following.borrow());
 9353
 9354    // Creating a pending selection that precedes another selection
 9355    _ = leader.update(cx, |leader, cx| {
 9356        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9357        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9358    });
 9359    follower
 9360        .update(cx, |follower, cx| {
 9361            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9362        })
 9363        .unwrap()
 9364        .await
 9365        .unwrap();
 9366    _ = follower.update(cx, |follower, cx| {
 9367        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9368    });
 9369    assert!(*is_still_following.borrow());
 9370
 9371    // Extend the pending selection so that it surrounds another selection
 9372    _ = leader.update(cx, |leader, cx| {
 9373        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9374    });
 9375    follower
 9376        .update(cx, |follower, cx| {
 9377            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9378        })
 9379        .unwrap()
 9380        .await
 9381        .unwrap();
 9382    _ = follower.update(cx, |follower, cx| {
 9383        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9384    });
 9385
 9386    // Scrolling locally breaks the follow
 9387    _ = follower.update(cx, |follower, cx| {
 9388        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9389        follower.set_scroll_anchor(
 9390            ScrollAnchor {
 9391                anchor: top_anchor,
 9392                offset: gpui::Point::new(0.0, 0.5),
 9393            },
 9394            cx,
 9395        );
 9396    });
 9397    assert!(!(*is_still_following.borrow()));
 9398}
 9399
 9400#[gpui::test]
 9401async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9402    init_test(cx, |_| {});
 9403
 9404    let fs = FakeFs::new(cx.executor());
 9405    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9406    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9407    let pane = workspace
 9408        .update(cx, |workspace, _| workspace.active_pane().clone())
 9409        .unwrap();
 9410
 9411    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9412
 9413    let leader = pane.update(cx, |_, cx| {
 9414        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9415        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9416    });
 9417
 9418    // Start following the editor when it has no excerpts.
 9419    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9420    let follower_1 = cx
 9421        .update_window(*workspace.deref(), |_, cx| {
 9422            Editor::from_state_proto(
 9423                workspace.root_view(cx).unwrap(),
 9424                ViewId {
 9425                    creator: Default::default(),
 9426                    id: 0,
 9427                },
 9428                &mut state_message,
 9429                cx,
 9430            )
 9431        })
 9432        .unwrap()
 9433        .unwrap()
 9434        .await
 9435        .unwrap();
 9436
 9437    let update_message = Rc::new(RefCell::new(None));
 9438    follower_1.update(cx, {
 9439        let update = update_message.clone();
 9440        |_, cx| {
 9441            cx.subscribe(&leader, move |_, leader, event, cx| {
 9442                leader
 9443                    .read(cx)
 9444                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9445            })
 9446            .detach();
 9447        }
 9448    });
 9449
 9450    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9451        (
 9452            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9453            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9454        )
 9455    });
 9456
 9457    // Insert some excerpts.
 9458    leader.update(cx, |leader, cx| {
 9459        leader.buffer.update(cx, |multibuffer, cx| {
 9460            let excerpt_ids = multibuffer.push_excerpts(
 9461                buffer_1.clone(),
 9462                [
 9463                    ExcerptRange {
 9464                        context: 1..6,
 9465                        primary: None,
 9466                    },
 9467                    ExcerptRange {
 9468                        context: 12..15,
 9469                        primary: None,
 9470                    },
 9471                    ExcerptRange {
 9472                        context: 0..3,
 9473                        primary: None,
 9474                    },
 9475                ],
 9476                cx,
 9477            );
 9478            multibuffer.insert_excerpts_after(
 9479                excerpt_ids[0],
 9480                buffer_2.clone(),
 9481                [
 9482                    ExcerptRange {
 9483                        context: 8..12,
 9484                        primary: None,
 9485                    },
 9486                    ExcerptRange {
 9487                        context: 0..6,
 9488                        primary: None,
 9489                    },
 9490                ],
 9491                cx,
 9492            );
 9493        });
 9494    });
 9495
 9496    // Apply the update of adding the excerpts.
 9497    follower_1
 9498        .update(cx, |follower, cx| {
 9499            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9500        })
 9501        .await
 9502        .unwrap();
 9503    assert_eq!(
 9504        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9505        leader.update(cx, |editor, cx| editor.text(cx))
 9506    );
 9507    update_message.borrow_mut().take();
 9508
 9509    // Start following separately after it already has excerpts.
 9510    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9511    let follower_2 = cx
 9512        .update_window(*workspace.deref(), |_, cx| {
 9513            Editor::from_state_proto(
 9514                workspace.root_view(cx).unwrap().clone(),
 9515                ViewId {
 9516                    creator: Default::default(),
 9517                    id: 0,
 9518                },
 9519                &mut state_message,
 9520                cx,
 9521            )
 9522        })
 9523        .unwrap()
 9524        .unwrap()
 9525        .await
 9526        .unwrap();
 9527    assert_eq!(
 9528        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9529        leader.update(cx, |editor, cx| editor.text(cx))
 9530    );
 9531
 9532    // Remove some excerpts.
 9533    leader.update(cx, |leader, cx| {
 9534        leader.buffer.update(cx, |multibuffer, cx| {
 9535            let excerpt_ids = multibuffer.excerpt_ids();
 9536            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9537            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9538        });
 9539    });
 9540
 9541    // Apply the update of removing the excerpts.
 9542    follower_1
 9543        .update(cx, |follower, cx| {
 9544            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9545        })
 9546        .await
 9547        .unwrap();
 9548    follower_2
 9549        .update(cx, |follower, cx| {
 9550            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9551        })
 9552        .await
 9553        .unwrap();
 9554    update_message.borrow_mut().take();
 9555    assert_eq!(
 9556        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9557        leader.update(cx, |editor, cx| editor.text(cx))
 9558    );
 9559}
 9560
 9561#[gpui::test]
 9562async fn go_to_prev_overlapping_diagnostic(
 9563    executor: BackgroundExecutor,
 9564    cx: &mut gpui::TestAppContext,
 9565) {
 9566    init_test(cx, |_| {});
 9567
 9568    let mut cx = EditorTestContext::new(cx).await;
 9569    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9570
 9571    cx.set_state(indoc! {"
 9572        ˇfn func(abc def: i32) -> u32 {
 9573        }
 9574    "});
 9575
 9576    cx.update(|cx| {
 9577        project.update(cx, |project, cx| {
 9578            project
 9579                .update_diagnostics(
 9580                    LanguageServerId(0),
 9581                    lsp::PublishDiagnosticsParams {
 9582                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9583                        version: None,
 9584                        diagnostics: vec![
 9585                            lsp::Diagnostic {
 9586                                range: lsp::Range::new(
 9587                                    lsp::Position::new(0, 11),
 9588                                    lsp::Position::new(0, 12),
 9589                                ),
 9590                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9591                                ..Default::default()
 9592                            },
 9593                            lsp::Diagnostic {
 9594                                range: lsp::Range::new(
 9595                                    lsp::Position::new(0, 12),
 9596                                    lsp::Position::new(0, 15),
 9597                                ),
 9598                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9599                                ..Default::default()
 9600                            },
 9601                            lsp::Diagnostic {
 9602                                range: lsp::Range::new(
 9603                                    lsp::Position::new(0, 25),
 9604                                    lsp::Position::new(0, 28),
 9605                                ),
 9606                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9607                                ..Default::default()
 9608                            },
 9609                        ],
 9610                    },
 9611                    &[],
 9612                    cx,
 9613                )
 9614                .unwrap()
 9615        });
 9616    });
 9617
 9618    executor.run_until_parked();
 9619
 9620    cx.update_editor(|editor, cx| {
 9621        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9622    });
 9623
 9624    cx.assert_editor_state(indoc! {"
 9625        fn func(abc def: i32) -> ˇu32 {
 9626        }
 9627    "});
 9628
 9629    cx.update_editor(|editor, cx| {
 9630        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9631    });
 9632
 9633    cx.assert_editor_state(indoc! {"
 9634        fn func(abc ˇdef: i32) -> u32 {
 9635        }
 9636    "});
 9637
 9638    cx.update_editor(|editor, cx| {
 9639        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9640    });
 9641
 9642    cx.assert_editor_state(indoc! {"
 9643        fn func(abcˇ def: i32) -> u32 {
 9644        }
 9645    "});
 9646
 9647    cx.update_editor(|editor, cx| {
 9648        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9649    });
 9650
 9651    cx.assert_editor_state(indoc! {"
 9652        fn func(abc def: i32) -> ˇu32 {
 9653        }
 9654    "});
 9655}
 9656
 9657#[gpui::test]
 9658async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9659    init_test(cx, |_| {});
 9660
 9661    let mut cx = EditorTestContext::new(cx).await;
 9662
 9663    cx.set_state(indoc! {"
 9664        fn func(abˇc def: i32) -> u32 {
 9665        }
 9666    "});
 9667    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9668
 9669    cx.update(|cx| {
 9670        project.update(cx, |project, cx| {
 9671            project.update_diagnostics(
 9672                LanguageServerId(0),
 9673                lsp::PublishDiagnosticsParams {
 9674                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9675                    version: None,
 9676                    diagnostics: vec![lsp::Diagnostic {
 9677                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9678                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9679                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9680                        ..Default::default()
 9681                    }],
 9682                },
 9683                &[],
 9684                cx,
 9685            )
 9686        })
 9687    }).unwrap();
 9688    cx.run_until_parked();
 9689    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9690    cx.run_until_parked();
 9691    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9692}
 9693
 9694#[gpui::test]
 9695async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9696    init_test(cx, |_| {});
 9697
 9698    let mut cx = EditorTestContext::new(cx).await;
 9699
 9700    let diff_base = r#"
 9701        use some::mod;
 9702
 9703        const A: u32 = 42;
 9704
 9705        fn main() {
 9706            println!("hello");
 9707
 9708            println!("world");
 9709        }
 9710        "#
 9711    .unindent();
 9712
 9713    // Edits are modified, removed, modified, added
 9714    cx.set_state(
 9715        &r#"
 9716        use some::modified;
 9717
 9718        ˇ
 9719        fn main() {
 9720            println!("hello there");
 9721
 9722            println!("around the");
 9723            println!("world");
 9724        }
 9725        "#
 9726        .unindent(),
 9727    );
 9728
 9729    cx.set_diff_base(Some(&diff_base));
 9730    executor.run_until_parked();
 9731
 9732    cx.update_editor(|editor, cx| {
 9733        //Wrap around the bottom of the buffer
 9734        for _ in 0..3 {
 9735            editor.go_to_next_hunk(&GoToHunk, cx);
 9736        }
 9737    });
 9738
 9739    cx.assert_editor_state(
 9740        &r#"
 9741        ˇuse some::modified;
 9742
 9743
 9744        fn main() {
 9745            println!("hello there");
 9746
 9747            println!("around the");
 9748            println!("world");
 9749        }
 9750        "#
 9751        .unindent(),
 9752    );
 9753
 9754    cx.update_editor(|editor, cx| {
 9755        //Wrap around the top of the buffer
 9756        for _ in 0..2 {
 9757            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9758        }
 9759    });
 9760
 9761    cx.assert_editor_state(
 9762        &r#"
 9763        use some::modified;
 9764
 9765
 9766        fn main() {
 9767        ˇ    println!("hello there");
 9768
 9769            println!("around the");
 9770            println!("world");
 9771        }
 9772        "#
 9773        .unindent(),
 9774    );
 9775
 9776    cx.update_editor(|editor, cx| {
 9777        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9778    });
 9779
 9780    cx.assert_editor_state(
 9781        &r#"
 9782        use some::modified;
 9783
 9784        ˇ
 9785        fn main() {
 9786            println!("hello there");
 9787
 9788            println!("around the");
 9789            println!("world");
 9790        }
 9791        "#
 9792        .unindent(),
 9793    );
 9794
 9795    cx.update_editor(|editor, cx| {
 9796        for _ in 0..3 {
 9797            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9798        }
 9799    });
 9800
 9801    cx.assert_editor_state(
 9802        &r#"
 9803        use some::modified;
 9804
 9805
 9806        fn main() {
 9807        ˇ    println!("hello there");
 9808
 9809            println!("around the");
 9810            println!("world");
 9811        }
 9812        "#
 9813        .unindent(),
 9814    );
 9815
 9816    cx.update_editor(|editor, cx| {
 9817        editor.fold(&Fold, cx);
 9818
 9819        //Make sure that the fold only gets one hunk
 9820        for _ in 0..4 {
 9821            editor.go_to_next_hunk(&GoToHunk, cx);
 9822        }
 9823    });
 9824
 9825    cx.assert_editor_state(
 9826        &r#"
 9827        ˇuse some::modified;
 9828
 9829
 9830        fn main() {
 9831            println!("hello there");
 9832
 9833            println!("around the");
 9834            println!("world");
 9835        }
 9836        "#
 9837        .unindent(),
 9838    );
 9839}
 9840
 9841#[test]
 9842fn test_split_words() {
 9843    fn split(text: &str) -> Vec<&str> {
 9844        split_words(text).collect()
 9845    }
 9846
 9847    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9848    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9849    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9850    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9851    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9852    assert_eq!(split("helloworld"), &["helloworld"]);
 9853
 9854    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9855}
 9856
 9857#[gpui::test]
 9858async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9859    init_test(cx, |_| {});
 9860
 9861    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9862    let mut assert = |before, after| {
 9863        let _state_context = cx.set_state(before);
 9864        cx.update_editor(|editor, cx| {
 9865            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9866        });
 9867        cx.assert_editor_state(after);
 9868    };
 9869
 9870    // Outside bracket jumps to outside of matching bracket
 9871    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9872    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9873
 9874    // Inside bracket jumps to inside of matching bracket
 9875    assert("console.log(ˇvar);", "console.log(varˇ);");
 9876    assert("console.log(varˇ);", "console.log(ˇvar);");
 9877
 9878    // When outside a bracket and inside, favor jumping to the inside bracket
 9879    assert(
 9880        "console.log('foo', [1, 2, 3]ˇ);",
 9881        "console.log(ˇ'foo', [1, 2, 3]);",
 9882    );
 9883    assert(
 9884        "console.log(ˇ'foo', [1, 2, 3]);",
 9885        "console.log('foo', [1, 2, 3]ˇ);",
 9886    );
 9887
 9888    // Bias forward if two options are equally likely
 9889    assert(
 9890        "let result = curried_fun()ˇ();",
 9891        "let result = curried_fun()()ˇ;",
 9892    );
 9893
 9894    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9895    assert(
 9896        indoc! {"
 9897            function test() {
 9898                console.log('test')ˇ
 9899            }"},
 9900        indoc! {"
 9901            function test() {
 9902                console.logˇ('test')
 9903            }"},
 9904    );
 9905}
 9906
 9907#[gpui::test]
 9908async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9909    init_test(cx, |_| {});
 9910
 9911    let fs = FakeFs::new(cx.executor());
 9912    fs.insert_tree(
 9913        "/a",
 9914        json!({
 9915            "main.rs": "fn main() { let a = 5; }",
 9916            "other.rs": "// Test file",
 9917        }),
 9918    )
 9919    .await;
 9920    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9921
 9922    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9923    language_registry.add(Arc::new(Language::new(
 9924        LanguageConfig {
 9925            name: "Rust".into(),
 9926            matcher: LanguageMatcher {
 9927                path_suffixes: vec!["rs".to_string()],
 9928                ..Default::default()
 9929            },
 9930            brackets: BracketPairConfig {
 9931                pairs: vec![BracketPair {
 9932                    start: "{".to_string(),
 9933                    end: "}".to_string(),
 9934                    close: true,
 9935                    surround: true,
 9936                    newline: true,
 9937                }],
 9938                disabled_scopes_by_bracket_ix: Vec::new(),
 9939            },
 9940            ..Default::default()
 9941        },
 9942        Some(tree_sitter_rust::LANGUAGE.into()),
 9943    )));
 9944    let mut fake_servers = language_registry.register_fake_lsp(
 9945        "Rust",
 9946        FakeLspAdapter {
 9947            capabilities: lsp::ServerCapabilities {
 9948                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9949                    first_trigger_character: "{".to_string(),
 9950                    more_trigger_character: None,
 9951                }),
 9952                ..Default::default()
 9953            },
 9954            ..Default::default()
 9955        },
 9956    );
 9957
 9958    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9959
 9960    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9961
 9962    let worktree_id = workspace
 9963        .update(cx, |workspace, cx| {
 9964            workspace.project().update(cx, |project, cx| {
 9965                project.worktrees(cx).next().unwrap().read(cx).id()
 9966            })
 9967        })
 9968        .unwrap();
 9969
 9970    let buffer = project
 9971        .update(cx, |project, cx| {
 9972            project.open_local_buffer("/a/main.rs", cx)
 9973        })
 9974        .await
 9975        .unwrap();
 9976    cx.executor().run_until_parked();
 9977    cx.executor().start_waiting();
 9978    let fake_server = fake_servers.next().await.unwrap();
 9979    let editor_handle = workspace
 9980        .update(cx, |workspace, cx| {
 9981            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9982        })
 9983        .unwrap()
 9984        .await
 9985        .unwrap()
 9986        .downcast::<Editor>()
 9987        .unwrap();
 9988
 9989    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9990        assert_eq!(
 9991            params.text_document_position.text_document.uri,
 9992            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9993        );
 9994        assert_eq!(
 9995            params.text_document_position.position,
 9996            lsp::Position::new(0, 21),
 9997        );
 9998
 9999        Ok(Some(vec![lsp::TextEdit {
10000            new_text: "]".to_string(),
10001            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10002        }]))
10003    });
10004
10005    editor_handle.update(cx, |editor, cx| {
10006        editor.focus(cx);
10007        editor.change_selections(None, cx, |s| {
10008            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10009        });
10010        editor.handle_input("{", cx);
10011    });
10012
10013    cx.executor().run_until_parked();
10014
10015    buffer.update(cx, |buffer, _| {
10016        assert_eq!(
10017            buffer.text(),
10018            "fn main() { let a = {5}; }",
10019            "No extra braces from on type formatting should appear in the buffer"
10020        )
10021    });
10022}
10023
10024#[gpui::test]
10025async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10026    init_test(cx, |_| {});
10027
10028    let fs = FakeFs::new(cx.executor());
10029    fs.insert_tree(
10030        "/a",
10031        json!({
10032            "main.rs": "fn main() { let a = 5; }",
10033            "other.rs": "// Test file",
10034        }),
10035    )
10036    .await;
10037
10038    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10039
10040    let server_restarts = Arc::new(AtomicUsize::new(0));
10041    let closure_restarts = Arc::clone(&server_restarts);
10042    let language_server_name = "test language server";
10043    let language_name: LanguageName = "Rust".into();
10044
10045    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10046    language_registry.add(Arc::new(Language::new(
10047        LanguageConfig {
10048            name: language_name.clone(),
10049            matcher: LanguageMatcher {
10050                path_suffixes: vec!["rs".to_string()],
10051                ..Default::default()
10052            },
10053            ..Default::default()
10054        },
10055        Some(tree_sitter_rust::LANGUAGE.into()),
10056    )));
10057    let mut fake_servers = language_registry.register_fake_lsp(
10058        "Rust",
10059        FakeLspAdapter {
10060            name: language_server_name,
10061            initialization_options: Some(json!({
10062                "testOptionValue": true
10063            })),
10064            initializer: Some(Box::new(move |fake_server| {
10065                let task_restarts = Arc::clone(&closure_restarts);
10066                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10067                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10068                    futures::future::ready(Ok(()))
10069                });
10070            })),
10071            ..Default::default()
10072        },
10073    );
10074
10075    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10076    let _buffer = project
10077        .update(cx, |project, cx| {
10078            project.open_local_buffer("/a/main.rs", cx)
10079        })
10080        .await
10081        .unwrap();
10082    let _fake_server = fake_servers.next().await.unwrap();
10083    update_test_language_settings(cx, |language_settings| {
10084        language_settings.languages.insert(
10085            language_name.clone(),
10086            LanguageSettingsContent {
10087                tab_size: NonZeroU32::new(8),
10088                ..Default::default()
10089            },
10090        );
10091    });
10092    cx.executor().run_until_parked();
10093    assert_eq!(
10094        server_restarts.load(atomic::Ordering::Acquire),
10095        0,
10096        "Should not restart LSP server on an unrelated change"
10097    );
10098
10099    update_test_project_settings(cx, |project_settings| {
10100        project_settings.lsp.insert(
10101            "Some other server name".into(),
10102            LspSettings {
10103                binary: None,
10104                settings: None,
10105                initialization_options: Some(json!({
10106                    "some other init value": false
10107                })),
10108            },
10109        );
10110    });
10111    cx.executor().run_until_parked();
10112    assert_eq!(
10113        server_restarts.load(atomic::Ordering::Acquire),
10114        0,
10115        "Should not restart LSP server on an unrelated LSP settings change"
10116    );
10117
10118    update_test_project_settings(cx, |project_settings| {
10119        project_settings.lsp.insert(
10120            language_server_name.into(),
10121            LspSettings {
10122                binary: None,
10123                settings: None,
10124                initialization_options: Some(json!({
10125                    "anotherInitValue": false
10126                })),
10127            },
10128        );
10129    });
10130    cx.executor().run_until_parked();
10131    assert_eq!(
10132        server_restarts.load(atomic::Ordering::Acquire),
10133        1,
10134        "Should restart LSP server on a related LSP settings change"
10135    );
10136
10137    update_test_project_settings(cx, |project_settings| {
10138        project_settings.lsp.insert(
10139            language_server_name.into(),
10140            LspSettings {
10141                binary: None,
10142                settings: None,
10143                initialization_options: Some(json!({
10144                    "anotherInitValue": false
10145                })),
10146            },
10147        );
10148    });
10149    cx.executor().run_until_parked();
10150    assert_eq!(
10151        server_restarts.load(atomic::Ordering::Acquire),
10152        1,
10153        "Should not restart LSP server on a related LSP settings change that is the same"
10154    );
10155
10156    update_test_project_settings(cx, |project_settings| {
10157        project_settings.lsp.insert(
10158            language_server_name.into(),
10159            LspSettings {
10160                binary: None,
10161                settings: None,
10162                initialization_options: None,
10163            },
10164        );
10165    });
10166    cx.executor().run_until_parked();
10167    assert_eq!(
10168        server_restarts.load(atomic::Ordering::Acquire),
10169        2,
10170        "Should restart LSP server on another related LSP settings change"
10171    );
10172}
10173
10174#[gpui::test]
10175async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10176    init_test(cx, |_| {});
10177
10178    let mut cx = EditorLspTestContext::new_rust(
10179        lsp::ServerCapabilities {
10180            completion_provider: Some(lsp::CompletionOptions {
10181                trigger_characters: Some(vec![".".to_string()]),
10182                resolve_provider: Some(true),
10183                ..Default::default()
10184            }),
10185            ..Default::default()
10186        },
10187        cx,
10188    )
10189    .await;
10190
10191    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10192    cx.simulate_keystroke(".");
10193    let completion_item = lsp::CompletionItem {
10194        label: "some".into(),
10195        kind: Some(lsp::CompletionItemKind::SNIPPET),
10196        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10197        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10198            kind: lsp::MarkupKind::Markdown,
10199            value: "```rust\nSome(2)\n```".to_string(),
10200        })),
10201        deprecated: Some(false),
10202        sort_text: Some("fffffff2".to_string()),
10203        filter_text: Some("some".to_string()),
10204        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10205        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10206            range: lsp::Range {
10207                start: lsp::Position {
10208                    line: 0,
10209                    character: 22,
10210                },
10211                end: lsp::Position {
10212                    line: 0,
10213                    character: 22,
10214                },
10215            },
10216            new_text: "Some(2)".to_string(),
10217        })),
10218        additional_text_edits: Some(vec![lsp::TextEdit {
10219            range: lsp::Range {
10220                start: lsp::Position {
10221                    line: 0,
10222                    character: 20,
10223                },
10224                end: lsp::Position {
10225                    line: 0,
10226                    character: 22,
10227                },
10228            },
10229            new_text: "".to_string(),
10230        }]),
10231        ..Default::default()
10232    };
10233
10234    let closure_completion_item = completion_item.clone();
10235    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10236        let task_completion_item = closure_completion_item.clone();
10237        async move {
10238            Ok(Some(lsp::CompletionResponse::Array(vec![
10239                task_completion_item,
10240            ])))
10241        }
10242    });
10243
10244    request.next().await;
10245
10246    cx.condition(|editor, _| editor.context_menu_visible())
10247        .await;
10248    let apply_additional_edits = cx.update_editor(|editor, cx| {
10249        editor
10250            .confirm_completion(&ConfirmCompletion::default(), cx)
10251            .unwrap()
10252    });
10253    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10254
10255    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10256        let task_completion_item = completion_item.clone();
10257        async move { Ok(task_completion_item) }
10258    })
10259    .next()
10260    .await
10261    .unwrap();
10262    apply_additional_edits.await.unwrap();
10263    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10264}
10265
10266#[gpui::test]
10267async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10268    init_test(cx, |_| {});
10269
10270    let mut cx = EditorLspTestContext::new(
10271        Language::new(
10272            LanguageConfig {
10273                matcher: LanguageMatcher {
10274                    path_suffixes: vec!["jsx".into()],
10275                    ..Default::default()
10276                },
10277                overrides: [(
10278                    "element".into(),
10279                    LanguageConfigOverride {
10280                        word_characters: Override::Set(['-'].into_iter().collect()),
10281                        ..Default::default()
10282                    },
10283                )]
10284                .into_iter()
10285                .collect(),
10286                ..Default::default()
10287            },
10288            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10289        )
10290        .with_override_query("(jsx_self_closing_element) @element")
10291        .unwrap(),
10292        lsp::ServerCapabilities {
10293            completion_provider: Some(lsp::CompletionOptions {
10294                trigger_characters: Some(vec![":".to_string()]),
10295                ..Default::default()
10296            }),
10297            ..Default::default()
10298        },
10299        cx,
10300    )
10301    .await;
10302
10303    cx.lsp
10304        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10305            Ok(Some(lsp::CompletionResponse::Array(vec![
10306                lsp::CompletionItem {
10307                    label: "bg-blue".into(),
10308                    ..Default::default()
10309                },
10310                lsp::CompletionItem {
10311                    label: "bg-red".into(),
10312                    ..Default::default()
10313                },
10314                lsp::CompletionItem {
10315                    label: "bg-yellow".into(),
10316                    ..Default::default()
10317                },
10318            ])))
10319        });
10320
10321    cx.set_state(r#"<p class="bgˇ" />"#);
10322
10323    // Trigger completion when typing a dash, because the dash is an extra
10324    // word character in the 'element' scope, which contains the cursor.
10325    cx.simulate_keystroke("-");
10326    cx.executor().run_until_parked();
10327    cx.update_editor(|editor, _| {
10328        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10329            assert_eq!(
10330                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10331                &["bg-red", "bg-blue", "bg-yellow"]
10332            );
10333        } else {
10334            panic!("expected completion menu to be open");
10335        }
10336    });
10337
10338    cx.simulate_keystroke("l");
10339    cx.executor().run_until_parked();
10340    cx.update_editor(|editor, _| {
10341        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10342            assert_eq!(
10343                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10344                &["bg-blue", "bg-yellow"]
10345            );
10346        } else {
10347            panic!("expected completion menu to be open");
10348        }
10349    });
10350
10351    // When filtering completions, consider the character after the '-' to
10352    // be the start of a subword.
10353    cx.set_state(r#"<p class="yelˇ" />"#);
10354    cx.simulate_keystroke("l");
10355    cx.executor().run_until_parked();
10356    cx.update_editor(|editor, _| {
10357        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10358            assert_eq!(
10359                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10360                &["bg-yellow"]
10361            );
10362        } else {
10363            panic!("expected completion menu to be open");
10364        }
10365    });
10366}
10367
10368#[gpui::test]
10369async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10370    init_test(cx, |settings| {
10371        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10372            FormatterList(vec![Formatter::Prettier].into()),
10373        ))
10374    });
10375
10376    let fs = FakeFs::new(cx.executor());
10377    fs.insert_file("/file.ts", Default::default()).await;
10378
10379    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10380    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10381
10382    language_registry.add(Arc::new(Language::new(
10383        LanguageConfig {
10384            name: "TypeScript".into(),
10385            matcher: LanguageMatcher {
10386                path_suffixes: vec!["ts".to_string()],
10387                ..Default::default()
10388            },
10389            ..Default::default()
10390        },
10391        Some(tree_sitter_rust::LANGUAGE.into()),
10392    )));
10393    update_test_language_settings(cx, |settings| {
10394        settings.defaults.prettier = Some(PrettierSettings {
10395            allowed: true,
10396            ..PrettierSettings::default()
10397        });
10398    });
10399
10400    let test_plugin = "test_plugin";
10401    let _ = language_registry.register_fake_lsp(
10402        "TypeScript",
10403        FakeLspAdapter {
10404            prettier_plugins: vec![test_plugin],
10405            ..Default::default()
10406        },
10407    );
10408
10409    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10410    let buffer = project
10411        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10412        .await
10413        .unwrap();
10414
10415    let buffer_text = "one\ntwo\nthree\n";
10416    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10417    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10418    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10419
10420    editor
10421        .update(cx, |editor, cx| {
10422            editor.perform_format(
10423                project.clone(),
10424                FormatTrigger::Manual,
10425                FormatTarget::Buffer,
10426                cx,
10427            )
10428        })
10429        .unwrap()
10430        .await;
10431    assert_eq!(
10432        editor.update(cx, |editor, cx| editor.text(cx)),
10433        buffer_text.to_string() + prettier_format_suffix,
10434        "Test prettier formatting was not applied to the original buffer text",
10435    );
10436
10437    update_test_language_settings(cx, |settings| {
10438        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10439    });
10440    let format = editor.update(cx, |editor, cx| {
10441        editor.perform_format(
10442            project.clone(),
10443            FormatTrigger::Manual,
10444            FormatTarget::Buffer,
10445            cx,
10446        )
10447    });
10448    format.await.unwrap();
10449    assert_eq!(
10450        editor.update(cx, |editor, cx| editor.text(cx)),
10451        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10452        "Autoformatting (via test prettier) was not applied to the original buffer text",
10453    );
10454}
10455
10456#[gpui::test]
10457async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10458    init_test(cx, |_| {});
10459    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10460    let base_text = indoc! {r#"struct Row;
10461struct Row1;
10462struct Row2;
10463
10464struct Row4;
10465struct Row5;
10466struct Row6;
10467
10468struct Row8;
10469struct Row9;
10470struct Row10;"#};
10471
10472    // When addition hunks are not adjacent to carets, no hunk revert is performed
10473    assert_hunk_revert(
10474        indoc! {r#"struct Row;
10475                   struct Row1;
10476                   struct Row1.1;
10477                   struct Row1.2;
10478                   struct Row2;ˇ
10479
10480                   struct Row4;
10481                   struct Row5;
10482                   struct Row6;
10483
10484                   struct Row8;
10485                   ˇstruct Row9;
10486                   struct Row9.1;
10487                   struct Row9.2;
10488                   struct Row9.3;
10489                   struct Row10;"#},
10490        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10491        indoc! {r#"struct Row;
10492                   struct Row1;
10493                   struct Row1.1;
10494                   struct Row1.2;
10495                   struct Row2;ˇ
10496
10497                   struct Row4;
10498                   struct Row5;
10499                   struct Row6;
10500
10501                   struct Row8;
10502                   ˇstruct Row9;
10503                   struct Row9.1;
10504                   struct Row9.2;
10505                   struct Row9.3;
10506                   struct Row10;"#},
10507        base_text,
10508        &mut cx,
10509    );
10510    // Same for selections
10511    assert_hunk_revert(
10512        indoc! {r#"struct Row;
10513                   struct Row1;
10514                   struct Row2;
10515                   struct Row2.1;
10516                   struct Row2.2;
10517                   «ˇ
10518                   struct Row4;
10519                   struct» Row5;
10520                   «struct Row6;
10521                   ˇ»
10522                   struct Row9.1;
10523                   struct Row9.2;
10524                   struct Row9.3;
10525                   struct Row8;
10526                   struct Row9;
10527                   struct Row10;"#},
10528        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10529        indoc! {r#"struct Row;
10530                   struct Row1;
10531                   struct Row2;
10532                   struct Row2.1;
10533                   struct Row2.2;
10534                   «ˇ
10535                   struct Row4;
10536                   struct» Row5;
10537                   «struct Row6;
10538                   ˇ»
10539                   struct Row9.1;
10540                   struct Row9.2;
10541                   struct Row9.3;
10542                   struct Row8;
10543                   struct Row9;
10544                   struct Row10;"#},
10545        base_text,
10546        &mut cx,
10547    );
10548
10549    // When carets and selections intersect the addition hunks, those are reverted.
10550    // Adjacent carets got merged.
10551    assert_hunk_revert(
10552        indoc! {r#"struct Row;
10553                   ˇ// something on the top
10554                   struct Row1;
10555                   struct Row2;
10556                   struct Roˇw3.1;
10557                   struct Row2.2;
10558                   struct Row2.3;ˇ
10559
10560                   struct Row4;
10561                   struct ˇRow5.1;
10562                   struct Row5.2;
10563                   struct «Rowˇ»5.3;
10564                   struct Row5;
10565                   struct Row6;
10566                   ˇ
10567                   struct Row9.1;
10568                   struct «Rowˇ»9.2;
10569                   struct «ˇRow»9.3;
10570                   struct Row8;
10571                   struct Row9;
10572                   «ˇ// something on bottom»
10573                   struct Row10;"#},
10574        vec![
10575            DiffHunkStatus::Added,
10576            DiffHunkStatus::Added,
10577            DiffHunkStatus::Added,
10578            DiffHunkStatus::Added,
10579            DiffHunkStatus::Added,
10580        ],
10581        indoc! {r#"struct Row;
10582                   ˇstruct Row1;
10583                   struct Row2;
10584                   ˇ
10585                   struct Row4;
10586                   ˇstruct Row5;
10587                   struct Row6;
10588                   ˇ
10589                   ˇstruct Row8;
10590                   struct Row9;
10591                   ˇstruct Row10;"#},
10592        base_text,
10593        &mut cx,
10594    );
10595}
10596
10597#[gpui::test]
10598async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10599    init_test(cx, |_| {});
10600    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10601    let base_text = indoc! {r#"struct Row;
10602struct Row1;
10603struct Row2;
10604
10605struct Row4;
10606struct Row5;
10607struct Row6;
10608
10609struct Row8;
10610struct Row9;
10611struct Row10;"#};
10612
10613    // Modification hunks behave the same as the addition ones.
10614    assert_hunk_revert(
10615        indoc! {r#"struct Row;
10616                   struct Row1;
10617                   struct Row33;
10618                   ˇ
10619                   struct Row4;
10620                   struct Row5;
10621                   struct Row6;
10622                   ˇ
10623                   struct Row99;
10624                   struct Row9;
10625                   struct Row10;"#},
10626        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10627        indoc! {r#"struct Row;
10628                   struct Row1;
10629                   struct Row33;
10630                   ˇ
10631                   struct Row4;
10632                   struct Row5;
10633                   struct Row6;
10634                   ˇ
10635                   struct Row99;
10636                   struct Row9;
10637                   struct Row10;"#},
10638        base_text,
10639        &mut cx,
10640    );
10641    assert_hunk_revert(
10642        indoc! {r#"struct Row;
10643                   struct Row1;
10644                   struct Row33;
10645                   «ˇ
10646                   struct Row4;
10647                   struct» Row5;
10648                   «struct Row6;
10649                   ˇ»
10650                   struct Row99;
10651                   struct Row9;
10652                   struct Row10;"#},
10653        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10654        indoc! {r#"struct Row;
10655                   struct Row1;
10656                   struct Row33;
10657                   «ˇ
10658                   struct Row4;
10659                   struct» Row5;
10660                   «struct Row6;
10661                   ˇ»
10662                   struct Row99;
10663                   struct Row9;
10664                   struct Row10;"#},
10665        base_text,
10666        &mut cx,
10667    );
10668
10669    assert_hunk_revert(
10670        indoc! {r#"ˇstruct Row1.1;
10671                   struct Row1;
10672                   «ˇstr»uct Row22;
10673
10674                   struct ˇRow44;
10675                   struct Row5;
10676                   struct «Rˇ»ow66;ˇ
10677
10678                   «struˇ»ct Row88;
10679                   struct Row9;
10680                   struct Row1011;ˇ"#},
10681        vec![
10682            DiffHunkStatus::Modified,
10683            DiffHunkStatus::Modified,
10684            DiffHunkStatus::Modified,
10685            DiffHunkStatus::Modified,
10686            DiffHunkStatus::Modified,
10687            DiffHunkStatus::Modified,
10688        ],
10689        indoc! {r#"struct Row;
10690                   ˇstruct Row1;
10691                   struct Row2;
10692                   ˇ
10693                   struct Row4;
10694                   ˇstruct Row5;
10695                   struct Row6;
10696                   ˇ
10697                   struct Row8;
10698                   ˇstruct Row9;
10699                   struct Row10;ˇ"#},
10700        base_text,
10701        &mut cx,
10702    );
10703}
10704
10705#[gpui::test]
10706async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10707    init_test(cx, |_| {});
10708    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10709    let base_text = indoc! {r#"struct Row;
10710struct Row1;
10711struct Row2;
10712
10713struct Row4;
10714struct Row5;
10715struct Row6;
10716
10717struct Row8;
10718struct Row9;
10719struct Row10;"#};
10720
10721    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10722    assert_hunk_revert(
10723        indoc! {r#"struct Row;
10724                   struct Row2;
10725
10726                   ˇstruct Row4;
10727                   struct Row5;
10728                   struct Row6;
10729                   ˇ
10730                   struct Row8;
10731                   struct Row10;"#},
10732        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10733        indoc! {r#"struct Row;
10734                   struct Row2;
10735
10736                   ˇstruct Row4;
10737                   struct Row5;
10738                   struct Row6;
10739                   ˇ
10740                   struct Row8;
10741                   struct Row10;"#},
10742        base_text,
10743        &mut cx,
10744    );
10745    assert_hunk_revert(
10746        indoc! {r#"struct Row;
10747                   struct Row2;
10748
10749                   «ˇstruct Row4;
10750                   struct» Row5;
10751                   «struct Row6;
10752                   ˇ»
10753                   struct Row8;
10754                   struct Row10;"#},
10755        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10756        indoc! {r#"struct Row;
10757                   struct Row2;
10758
10759                   «ˇstruct Row4;
10760                   struct» Row5;
10761                   «struct Row6;
10762                   ˇ»
10763                   struct Row8;
10764                   struct Row10;"#},
10765        base_text,
10766        &mut cx,
10767    );
10768
10769    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10770    assert_hunk_revert(
10771        indoc! {r#"struct Row;
10772                   ˇstruct Row2;
10773
10774                   struct Row4;
10775                   struct Row5;
10776                   struct Row6;
10777
10778                   struct Row8;ˇ
10779                   struct Row10;"#},
10780        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10781        indoc! {r#"struct Row;
10782                   struct Row1;
10783                   ˇstruct Row2;
10784
10785                   struct Row4;
10786                   struct Row5;
10787                   struct Row6;
10788
10789                   struct Row8;ˇ
10790                   struct Row9;
10791                   struct Row10;"#},
10792        base_text,
10793        &mut cx,
10794    );
10795    assert_hunk_revert(
10796        indoc! {r#"struct Row;
10797                   struct Row2«ˇ;
10798                   struct Row4;
10799                   struct» Row5;
10800                   «struct Row6;
10801
10802                   struct Row8;ˇ»
10803                   struct Row10;"#},
10804        vec![
10805            DiffHunkStatus::Removed,
10806            DiffHunkStatus::Removed,
10807            DiffHunkStatus::Removed,
10808        ],
10809        indoc! {r#"struct Row;
10810                   struct Row1;
10811                   struct Row2«ˇ;
10812
10813                   struct Row4;
10814                   struct» Row5;
10815                   «struct Row6;
10816
10817                   struct Row8;ˇ»
10818                   struct Row9;
10819                   struct Row10;"#},
10820        base_text,
10821        &mut cx,
10822    );
10823}
10824
10825#[gpui::test]
10826async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10827    init_test(cx, |_| {});
10828
10829    let cols = 4;
10830    let rows = 10;
10831    let sample_text_1 = sample_text(rows, cols, 'a');
10832    assert_eq!(
10833        sample_text_1,
10834        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10835    );
10836    let sample_text_2 = sample_text(rows, cols, 'l');
10837    assert_eq!(
10838        sample_text_2,
10839        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10840    );
10841    let sample_text_3 = sample_text(rows, cols, 'v');
10842    assert_eq!(
10843        sample_text_3,
10844        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10845    );
10846
10847    fn diff_every_buffer_row(
10848        buffer: &Model<Buffer>,
10849        sample_text: String,
10850        cols: usize,
10851        cx: &mut gpui::TestAppContext,
10852    ) {
10853        // revert first character in each row, creating one large diff hunk per buffer
10854        let is_first_char = |offset: usize| offset % cols == 0;
10855        buffer.update(cx, |buffer, cx| {
10856            buffer.set_text(
10857                sample_text
10858                    .chars()
10859                    .enumerate()
10860                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10861                    .collect::<String>(),
10862                cx,
10863            );
10864            buffer.set_diff_base(Some(sample_text), cx);
10865        });
10866        cx.executor().run_until_parked();
10867    }
10868
10869    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10870    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10871
10872    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10873    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10874
10875    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10876    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10877
10878    let multibuffer = cx.new_model(|cx| {
10879        let mut multibuffer = MultiBuffer::new(ReadWrite);
10880        multibuffer.push_excerpts(
10881            buffer_1.clone(),
10882            [
10883                ExcerptRange {
10884                    context: Point::new(0, 0)..Point::new(3, 0),
10885                    primary: None,
10886                },
10887                ExcerptRange {
10888                    context: Point::new(5, 0)..Point::new(7, 0),
10889                    primary: None,
10890                },
10891                ExcerptRange {
10892                    context: Point::new(9, 0)..Point::new(10, 4),
10893                    primary: None,
10894                },
10895            ],
10896            cx,
10897        );
10898        multibuffer.push_excerpts(
10899            buffer_2.clone(),
10900            [
10901                ExcerptRange {
10902                    context: Point::new(0, 0)..Point::new(3, 0),
10903                    primary: None,
10904                },
10905                ExcerptRange {
10906                    context: Point::new(5, 0)..Point::new(7, 0),
10907                    primary: None,
10908                },
10909                ExcerptRange {
10910                    context: Point::new(9, 0)..Point::new(10, 4),
10911                    primary: None,
10912                },
10913            ],
10914            cx,
10915        );
10916        multibuffer.push_excerpts(
10917            buffer_3.clone(),
10918            [
10919                ExcerptRange {
10920                    context: Point::new(0, 0)..Point::new(3, 0),
10921                    primary: None,
10922                },
10923                ExcerptRange {
10924                    context: Point::new(5, 0)..Point::new(7, 0),
10925                    primary: None,
10926                },
10927                ExcerptRange {
10928                    context: Point::new(9, 0)..Point::new(10, 4),
10929                    primary: None,
10930                },
10931            ],
10932            cx,
10933        );
10934        multibuffer
10935    });
10936
10937    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10938    editor.update(cx, |editor, cx| {
10939        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
10940        editor.select_all(&SelectAll, cx);
10941        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10942    });
10943    cx.executor().run_until_parked();
10944    // When all ranges are selected, all buffer hunks are reverted.
10945    editor.update(cx, |editor, cx| {
10946        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");
10947    });
10948    buffer_1.update(cx, |buffer, _| {
10949        assert_eq!(buffer.text(), sample_text_1);
10950    });
10951    buffer_2.update(cx, |buffer, _| {
10952        assert_eq!(buffer.text(), sample_text_2);
10953    });
10954    buffer_3.update(cx, |buffer, _| {
10955        assert_eq!(buffer.text(), sample_text_3);
10956    });
10957
10958    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10959    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10960    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10961    editor.update(cx, |editor, cx| {
10962        editor.change_selections(None, cx, |s| {
10963            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10964        });
10965        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10966    });
10967    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10968    // but not affect buffer_2 and its related excerpts.
10969    editor.update(cx, |editor, cx| {
10970        assert_eq!(
10971            editor.text(cx),
10972            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
10973        );
10974    });
10975    buffer_1.update(cx, |buffer, _| {
10976        assert_eq!(buffer.text(), sample_text_1);
10977    });
10978    buffer_2.update(cx, |buffer, _| {
10979        assert_eq!(
10980            buffer.text(),
10981            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10982        );
10983    });
10984    buffer_3.update(cx, |buffer, _| {
10985        assert_eq!(
10986            buffer.text(),
10987            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10988        );
10989    });
10990}
10991
10992#[gpui::test]
10993async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10994    init_test(cx, |_| {});
10995
10996    let cols = 4;
10997    let rows = 10;
10998    let sample_text_1 = sample_text(rows, cols, 'a');
10999    assert_eq!(
11000        sample_text_1,
11001        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11002    );
11003    let sample_text_2 = sample_text(rows, cols, 'l');
11004    assert_eq!(
11005        sample_text_2,
11006        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11007    );
11008    let sample_text_3 = sample_text(rows, cols, 'v');
11009    assert_eq!(
11010        sample_text_3,
11011        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11012    );
11013
11014    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11015    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11016    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11017
11018    let multi_buffer = cx.new_model(|cx| {
11019        let mut multibuffer = MultiBuffer::new(ReadWrite);
11020        multibuffer.push_excerpts(
11021            buffer_1.clone(),
11022            [
11023                ExcerptRange {
11024                    context: Point::new(0, 0)..Point::new(3, 0),
11025                    primary: None,
11026                },
11027                ExcerptRange {
11028                    context: Point::new(5, 0)..Point::new(7, 0),
11029                    primary: None,
11030                },
11031                ExcerptRange {
11032                    context: Point::new(9, 0)..Point::new(10, 4),
11033                    primary: None,
11034                },
11035            ],
11036            cx,
11037        );
11038        multibuffer.push_excerpts(
11039            buffer_2.clone(),
11040            [
11041                ExcerptRange {
11042                    context: Point::new(0, 0)..Point::new(3, 0),
11043                    primary: None,
11044                },
11045                ExcerptRange {
11046                    context: Point::new(5, 0)..Point::new(7, 0),
11047                    primary: None,
11048                },
11049                ExcerptRange {
11050                    context: Point::new(9, 0)..Point::new(10, 4),
11051                    primary: None,
11052                },
11053            ],
11054            cx,
11055        );
11056        multibuffer.push_excerpts(
11057            buffer_3.clone(),
11058            [
11059                ExcerptRange {
11060                    context: Point::new(0, 0)..Point::new(3, 0),
11061                    primary: None,
11062                },
11063                ExcerptRange {
11064                    context: Point::new(5, 0)..Point::new(7, 0),
11065                    primary: None,
11066                },
11067                ExcerptRange {
11068                    context: Point::new(9, 0)..Point::new(10, 4),
11069                    primary: None,
11070                },
11071            ],
11072            cx,
11073        );
11074        multibuffer
11075    });
11076
11077    let fs = FakeFs::new(cx.executor());
11078    fs.insert_tree(
11079        "/a",
11080        json!({
11081            "main.rs": sample_text_1,
11082            "other.rs": sample_text_2,
11083            "lib.rs": sample_text_3,
11084        }),
11085    )
11086    .await;
11087    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11088    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11089    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11090    let multi_buffer_editor = cx.new_view(|cx| {
11091        Editor::new(
11092            EditorMode::Full,
11093            multi_buffer,
11094            Some(project.clone()),
11095            true,
11096            cx,
11097        )
11098    });
11099    let multibuffer_item_id = workspace
11100        .update(cx, |workspace, cx| {
11101            assert!(
11102                workspace.active_item(cx).is_none(),
11103                "active item should be None before the first item is added"
11104            );
11105            workspace.add_item_to_active_pane(
11106                Box::new(multi_buffer_editor.clone()),
11107                None,
11108                true,
11109                cx,
11110            );
11111            let active_item = workspace
11112                .active_item(cx)
11113                .expect("should have an active item after adding the multi buffer");
11114            assert!(
11115                !active_item.is_singleton(cx),
11116                "A multi buffer was expected to active after adding"
11117            );
11118            active_item.item_id()
11119        })
11120        .unwrap();
11121    cx.executor().run_until_parked();
11122
11123    multi_buffer_editor.update(cx, |editor, cx| {
11124        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11125        editor.open_excerpts(&OpenExcerpts, cx);
11126    });
11127    cx.executor().run_until_parked();
11128    let first_item_id = workspace
11129        .update(cx, |workspace, cx| {
11130            let active_item = workspace
11131                .active_item(cx)
11132                .expect("should have an active item after navigating into the 1st buffer");
11133            let first_item_id = active_item.item_id();
11134            assert_ne!(
11135                first_item_id, multibuffer_item_id,
11136                "Should navigate into the 1st buffer and activate it"
11137            );
11138            assert!(
11139                active_item.is_singleton(cx),
11140                "New active item should be a singleton buffer"
11141            );
11142            assert_eq!(
11143                active_item
11144                    .act_as::<Editor>(cx)
11145                    .expect("should have navigated into an editor for the 1st buffer")
11146                    .read(cx)
11147                    .text(cx),
11148                sample_text_1
11149            );
11150
11151            workspace
11152                .go_back(workspace.active_pane().downgrade(), cx)
11153                .detach_and_log_err(cx);
11154
11155            first_item_id
11156        })
11157        .unwrap();
11158    cx.executor().run_until_parked();
11159    workspace
11160        .update(cx, |workspace, cx| {
11161            let active_item = workspace
11162                .active_item(cx)
11163                .expect("should have an active item after navigating back");
11164            assert_eq!(
11165                active_item.item_id(),
11166                multibuffer_item_id,
11167                "Should navigate back to the multi buffer"
11168            );
11169            assert!(!active_item.is_singleton(cx));
11170        })
11171        .unwrap();
11172
11173    multi_buffer_editor.update(cx, |editor, cx| {
11174        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11175            s.select_ranges(Some(39..40))
11176        });
11177        editor.open_excerpts(&OpenExcerpts, cx);
11178    });
11179    cx.executor().run_until_parked();
11180    let second_item_id = workspace
11181        .update(cx, |workspace, cx| {
11182            let active_item = workspace
11183                .active_item(cx)
11184                .expect("should have an active item after navigating into the 2nd buffer");
11185            let second_item_id = active_item.item_id();
11186            assert_ne!(
11187                second_item_id, multibuffer_item_id,
11188                "Should navigate away from the multibuffer"
11189            );
11190            assert_ne!(
11191                second_item_id, first_item_id,
11192                "Should navigate into the 2nd buffer and activate it"
11193            );
11194            assert!(
11195                active_item.is_singleton(cx),
11196                "New active item should be a singleton buffer"
11197            );
11198            assert_eq!(
11199                active_item
11200                    .act_as::<Editor>(cx)
11201                    .expect("should have navigated into an editor")
11202                    .read(cx)
11203                    .text(cx),
11204                sample_text_2
11205            );
11206
11207            workspace
11208                .go_back(workspace.active_pane().downgrade(), cx)
11209                .detach_and_log_err(cx);
11210
11211            second_item_id
11212        })
11213        .unwrap();
11214    cx.executor().run_until_parked();
11215    workspace
11216        .update(cx, |workspace, cx| {
11217            let active_item = workspace
11218                .active_item(cx)
11219                .expect("should have an active item after navigating back from the 2nd buffer");
11220            assert_eq!(
11221                active_item.item_id(),
11222                multibuffer_item_id,
11223                "Should navigate back from the 2nd buffer to the multi buffer"
11224            );
11225            assert!(!active_item.is_singleton(cx));
11226        })
11227        .unwrap();
11228
11229    multi_buffer_editor.update(cx, |editor, cx| {
11230        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11231            s.select_ranges(Some(60..70))
11232        });
11233        editor.open_excerpts(&OpenExcerpts, cx);
11234    });
11235    cx.executor().run_until_parked();
11236    workspace
11237        .update(cx, |workspace, cx| {
11238            let active_item = workspace
11239                .active_item(cx)
11240                .expect("should have an active item after navigating into the 3rd buffer");
11241            let third_item_id = active_item.item_id();
11242            assert_ne!(
11243                third_item_id, multibuffer_item_id,
11244                "Should navigate into the 3rd buffer and activate it"
11245            );
11246            assert_ne!(third_item_id, first_item_id);
11247            assert_ne!(third_item_id, second_item_id);
11248            assert!(
11249                active_item.is_singleton(cx),
11250                "New active item should be a singleton buffer"
11251            );
11252            assert_eq!(
11253                active_item
11254                    .act_as::<Editor>(cx)
11255                    .expect("should have navigated into an editor")
11256                    .read(cx)
11257                    .text(cx),
11258                sample_text_3
11259            );
11260
11261            workspace
11262                .go_back(workspace.active_pane().downgrade(), cx)
11263                .detach_and_log_err(cx);
11264        })
11265        .unwrap();
11266    cx.executor().run_until_parked();
11267    workspace
11268        .update(cx, |workspace, cx| {
11269            let active_item = workspace
11270                .active_item(cx)
11271                .expect("should have an active item after navigating back from the 3rd buffer");
11272            assert_eq!(
11273                active_item.item_id(),
11274                multibuffer_item_id,
11275                "Should navigate back from the 3rd buffer to the multi buffer"
11276            );
11277            assert!(!active_item.is_singleton(cx));
11278        })
11279        .unwrap();
11280}
11281
11282#[gpui::test]
11283async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11284    init_test(cx, |_| {});
11285
11286    let mut cx = EditorTestContext::new(cx).await;
11287
11288    let diff_base = r#"
11289        use some::mod;
11290
11291        const A: u32 = 42;
11292
11293        fn main() {
11294            println!("hello");
11295
11296            println!("world");
11297        }
11298        "#
11299    .unindent();
11300
11301    cx.set_state(
11302        &r#"
11303        use some::modified;
11304
11305        ˇ
11306        fn main() {
11307            println!("hello there");
11308
11309            println!("around the");
11310            println!("world");
11311        }
11312        "#
11313        .unindent(),
11314    );
11315
11316    cx.set_diff_base(Some(&diff_base));
11317    executor.run_until_parked();
11318
11319    cx.update_editor(|editor, cx| {
11320        editor.go_to_next_hunk(&GoToHunk, cx);
11321        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11322    });
11323    executor.run_until_parked();
11324    cx.assert_diff_hunks(
11325        r#"
11326          use some::modified;
11327
11328
11329          fn main() {
11330        -     println!("hello");
11331        +     println!("hello there");
11332
11333              println!("around the");
11334              println!("world");
11335          }
11336        "#
11337        .unindent(),
11338    );
11339
11340    cx.update_editor(|editor, cx| {
11341        for _ in 0..3 {
11342            editor.go_to_next_hunk(&GoToHunk, cx);
11343            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11344        }
11345    });
11346    executor.run_until_parked();
11347    cx.assert_editor_state(
11348        &r#"
11349        use some::modified;
11350
11351        ˇ
11352        fn main() {
11353            println!("hello there");
11354
11355            println!("around the");
11356            println!("world");
11357        }
11358        "#
11359        .unindent(),
11360    );
11361
11362    cx.assert_diff_hunks(
11363        r#"
11364        - use some::mod;
11365        + use some::modified;
11366
11367        - const A: u32 = 42;
11368
11369          fn main() {
11370        -     println!("hello");
11371        +     println!("hello there");
11372
11373        +     println!("around the");
11374              println!("world");
11375          }
11376        "#
11377        .unindent(),
11378    );
11379
11380    cx.update_editor(|editor, cx| {
11381        editor.cancel(&Cancel, cx);
11382    });
11383
11384    cx.assert_diff_hunks(
11385        r#"
11386          use some::modified;
11387
11388
11389          fn main() {
11390              println!("hello there");
11391
11392              println!("around the");
11393              println!("world");
11394          }
11395        "#
11396        .unindent(),
11397    );
11398}
11399
11400#[gpui::test]
11401async fn test_diff_base_change_with_expanded_diff_hunks(
11402    executor: BackgroundExecutor,
11403    cx: &mut gpui::TestAppContext,
11404) {
11405    init_test(cx, |_| {});
11406
11407    let mut cx = EditorTestContext::new(cx).await;
11408
11409    let diff_base = r#"
11410        use some::mod1;
11411        use some::mod2;
11412
11413        const A: u32 = 42;
11414        const B: u32 = 42;
11415        const C: u32 = 42;
11416
11417        fn main() {
11418            println!("hello");
11419
11420            println!("world");
11421        }
11422        "#
11423    .unindent();
11424
11425    cx.set_state(
11426        &r#"
11427        use some::mod2;
11428
11429        const A: u32 = 42;
11430        const C: u32 = 42;
11431
11432        fn main(ˇ) {
11433            //println!("hello");
11434
11435            println!("world");
11436            //
11437            //
11438        }
11439        "#
11440        .unindent(),
11441    );
11442
11443    cx.set_diff_base(Some(&diff_base));
11444    executor.run_until_parked();
11445
11446    cx.update_editor(|editor, cx| {
11447        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11448    });
11449    executor.run_until_parked();
11450    cx.assert_diff_hunks(
11451        r#"
11452        - use some::mod1;
11453          use some::mod2;
11454
11455          const A: u32 = 42;
11456        - const B: u32 = 42;
11457          const C: u32 = 42;
11458
11459          fn main() {
11460        -     println!("hello");
11461        +     //println!("hello");
11462
11463              println!("world");
11464        +     //
11465        +     //
11466          }
11467        "#
11468        .unindent(),
11469    );
11470
11471    cx.set_diff_base(Some("new diff base!"));
11472    executor.run_until_parked();
11473    cx.assert_diff_hunks(
11474        r#"
11475          use some::mod2;
11476
11477          const A: u32 = 42;
11478          const C: u32 = 42;
11479
11480          fn main() {
11481              //println!("hello");
11482
11483              println!("world");
11484              //
11485              //
11486          }
11487        "#
11488        .unindent(),
11489    );
11490
11491    cx.update_editor(|editor, cx| {
11492        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11493    });
11494    executor.run_until_parked();
11495    cx.assert_diff_hunks(
11496        r#"
11497        - new diff base!
11498        + use some::mod2;
11499        +
11500        + const A: u32 = 42;
11501        + const C: u32 = 42;
11502        +
11503        + fn main() {
11504        +     //println!("hello");
11505        +
11506        +     println!("world");
11507        +     //
11508        +     //
11509        + }
11510        "#
11511        .unindent(),
11512    );
11513}
11514
11515#[gpui::test]
11516async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11517    init_test(cx, |_| {});
11518
11519    let mut cx = EditorTestContext::new(cx).await;
11520
11521    let diff_base = r#"
11522        use some::mod1;
11523        use some::mod2;
11524
11525        const A: u32 = 42;
11526        const B: u32 = 42;
11527        const C: u32 = 42;
11528
11529        fn main() {
11530            println!("hello");
11531
11532            println!("world");
11533        }
11534
11535        fn another() {
11536            println!("another");
11537        }
11538
11539        fn another2() {
11540            println!("another2");
11541        }
11542        "#
11543    .unindent();
11544
11545    cx.set_state(
11546        &r#"
11547        «use some::mod2;
11548
11549        const A: u32 = 42;
11550        const C: u32 = 42;
11551
11552        fn main() {
11553            //println!("hello");
11554
11555            println!("world");
11556            //
11557            //ˇ»
11558        }
11559
11560        fn another() {
11561            println!("another");
11562            println!("another");
11563        }
11564
11565            println!("another2");
11566        }
11567        "#
11568        .unindent(),
11569    );
11570
11571    cx.set_diff_base(Some(&diff_base));
11572    executor.run_until_parked();
11573
11574    cx.update_editor(|editor, cx| {
11575        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11576    });
11577    executor.run_until_parked();
11578
11579    cx.assert_diff_hunks(
11580        r#"
11581        - use some::mod1;
11582          use some::mod2;
11583
11584          const A: u32 = 42;
11585        - const B: u32 = 42;
11586          const C: u32 = 42;
11587
11588          fn main() {
11589        -     println!("hello");
11590        +     //println!("hello");
11591
11592              println!("world");
11593        +     //
11594        +     //
11595          }
11596
11597          fn another() {
11598              println!("another");
11599        +     println!("another");
11600          }
11601
11602        - fn another2() {
11603              println!("another2");
11604          }
11605        "#
11606        .unindent(),
11607    );
11608
11609    // Fold across some of the diff hunks. They should no longer appear expanded.
11610    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11611    cx.executor().run_until_parked();
11612
11613    // Hunks are not shown if their position is within a fold
11614    cx.assert_diff_hunks(
11615        r#"
11616          use some::mod2;
11617
11618          const A: u32 = 42;
11619          const C: u32 = 42;
11620
11621          fn main() {
11622              //println!("hello");
11623
11624              println!("world");
11625              //
11626              //
11627          }
11628
11629          fn another() {
11630              println!("another");
11631        +     println!("another");
11632          }
11633
11634        - fn another2() {
11635              println!("another2");
11636          }
11637        "#
11638        .unindent(),
11639    );
11640
11641    cx.update_editor(|editor, cx| {
11642        editor.select_all(&SelectAll, cx);
11643        editor.unfold_lines(&UnfoldLines, cx);
11644    });
11645    cx.executor().run_until_parked();
11646
11647    // The deletions reappear when unfolding.
11648    cx.assert_diff_hunks(
11649        r#"
11650        - use some::mod1;
11651          use some::mod2;
11652
11653          const A: u32 = 42;
11654        - const B: u32 = 42;
11655          const C: u32 = 42;
11656
11657          fn main() {
11658        -     println!("hello");
11659        +     //println!("hello");
11660
11661              println!("world");
11662        +     //
11663        +     //
11664          }
11665
11666          fn another() {
11667              println!("another");
11668        +     println!("another");
11669          }
11670
11671        - fn another2() {
11672              println!("another2");
11673          }
11674        "#
11675        .unindent(),
11676    );
11677}
11678
11679#[gpui::test]
11680async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11681    init_test(cx, |_| {});
11682
11683    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11684    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11685    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11686    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11687    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11688    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11689
11690    let buffer_1 = cx.new_model(|cx| {
11691        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11692        buffer.set_diff_base(Some(file_1_old.into()), cx);
11693        buffer
11694    });
11695    let buffer_2 = cx.new_model(|cx| {
11696        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11697        buffer.set_diff_base(Some(file_2_old.into()), cx);
11698        buffer
11699    });
11700    let buffer_3 = cx.new_model(|cx| {
11701        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11702        buffer.set_diff_base(Some(file_3_old.into()), cx);
11703        buffer
11704    });
11705
11706    let multi_buffer = cx.new_model(|cx| {
11707        let mut multibuffer = MultiBuffer::new(ReadWrite);
11708        multibuffer.push_excerpts(
11709            buffer_1.clone(),
11710            [
11711                ExcerptRange {
11712                    context: Point::new(0, 0)..Point::new(3, 0),
11713                    primary: None,
11714                },
11715                ExcerptRange {
11716                    context: Point::new(5, 0)..Point::new(7, 0),
11717                    primary: None,
11718                },
11719                ExcerptRange {
11720                    context: Point::new(9, 0)..Point::new(10, 3),
11721                    primary: None,
11722                },
11723            ],
11724            cx,
11725        );
11726        multibuffer.push_excerpts(
11727            buffer_2.clone(),
11728            [
11729                ExcerptRange {
11730                    context: Point::new(0, 0)..Point::new(3, 0),
11731                    primary: None,
11732                },
11733                ExcerptRange {
11734                    context: Point::new(5, 0)..Point::new(7, 0),
11735                    primary: None,
11736                },
11737                ExcerptRange {
11738                    context: Point::new(9, 0)..Point::new(10, 3),
11739                    primary: None,
11740                },
11741            ],
11742            cx,
11743        );
11744        multibuffer.push_excerpts(
11745            buffer_3.clone(),
11746            [
11747                ExcerptRange {
11748                    context: Point::new(0, 0)..Point::new(3, 0),
11749                    primary: None,
11750                },
11751                ExcerptRange {
11752                    context: Point::new(5, 0)..Point::new(7, 0),
11753                    primary: None,
11754                },
11755                ExcerptRange {
11756                    context: Point::new(9, 0)..Point::new(10, 3),
11757                    primary: None,
11758                },
11759            ],
11760            cx,
11761        );
11762        multibuffer
11763    });
11764
11765    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11766    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11767    cx.run_until_parked();
11768
11769    cx.assert_editor_state(
11770        &"
11771            ˇaaa
11772            ccc
11773            ddd
11774
11775            ggg
11776            hhh
11777
11778
11779            lll
11780            mmm
11781            NNN
11782
11783            qqq
11784            rrr
11785
11786            uuu
11787            111
11788            222
11789            333
11790
11791            666
11792            777
11793
11794            000
11795            !!!"
11796        .unindent(),
11797    );
11798
11799    cx.update_editor(|editor, cx| {
11800        editor.select_all(&SelectAll, cx);
11801        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11802    });
11803    cx.executor().run_until_parked();
11804
11805    cx.assert_diff_hunks(
11806        "
11807            aaa
11808          - bbb
11809            ccc
11810            ddd
11811
11812            ggg
11813            hhh
11814
11815
11816            lll
11817            mmm
11818          - nnn
11819          + NNN
11820
11821            qqq
11822            rrr
11823
11824            uuu
11825            111
11826            222
11827            333
11828
11829          + 666
11830            777
11831
11832            000
11833            !!!"
11834        .unindent(),
11835    );
11836}
11837
11838#[gpui::test]
11839async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
11840    init_test(cx, |_| {});
11841
11842    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
11843    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
11844
11845    let buffer = cx.new_model(|cx| {
11846        let mut buffer = Buffer::local(text.to_string(), cx);
11847        buffer.set_diff_base(Some(base.into()), cx);
11848        buffer
11849    });
11850
11851    let multi_buffer = cx.new_model(|cx| {
11852        let mut multibuffer = MultiBuffer::new(ReadWrite);
11853        multibuffer.push_excerpts(
11854            buffer.clone(),
11855            [
11856                ExcerptRange {
11857                    context: Point::new(0, 0)..Point::new(2, 0),
11858                    primary: None,
11859                },
11860                ExcerptRange {
11861                    context: Point::new(5, 0)..Point::new(7, 0),
11862                    primary: None,
11863                },
11864            ],
11865            cx,
11866        );
11867        multibuffer
11868    });
11869
11870    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11871    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11872    cx.run_until_parked();
11873
11874    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
11875    cx.executor().run_until_parked();
11876
11877    cx.assert_diff_hunks(
11878        "
11879            aaa
11880          - bbb
11881          + BBB
11882
11883          - ddd
11884          - eee
11885          + EEE
11886            fff
11887        "
11888        .unindent(),
11889    );
11890}
11891
11892#[gpui::test]
11893async fn test_edits_around_expanded_insertion_hunks(
11894    executor: BackgroundExecutor,
11895    cx: &mut gpui::TestAppContext,
11896) {
11897    init_test(cx, |_| {});
11898
11899    let mut cx = EditorTestContext::new(cx).await;
11900
11901    let diff_base = r#"
11902        use some::mod1;
11903        use some::mod2;
11904
11905        const A: u32 = 42;
11906
11907        fn main() {
11908            println!("hello");
11909
11910            println!("world");
11911        }
11912        "#
11913    .unindent();
11914    executor.run_until_parked();
11915    cx.set_state(
11916        &r#"
11917        use some::mod1;
11918        use some::mod2;
11919
11920        const A: u32 = 42;
11921        const B: u32 = 42;
11922        const C: u32 = 42;
11923        ˇ
11924
11925        fn main() {
11926            println!("hello");
11927
11928            println!("world");
11929        }
11930        "#
11931        .unindent(),
11932    );
11933
11934    cx.set_diff_base(Some(&diff_base));
11935    executor.run_until_parked();
11936
11937    cx.update_editor(|editor, cx| {
11938        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11939    });
11940    executor.run_until_parked();
11941
11942    cx.assert_diff_hunks(
11943        r#"
11944        use some::mod1;
11945        use some::mod2;
11946
11947        const A: u32 = 42;
11948      + const B: u32 = 42;
11949      + const C: u32 = 42;
11950      +
11951
11952        fn main() {
11953            println!("hello");
11954
11955            println!("world");
11956        }
11957        "#
11958        .unindent(),
11959    );
11960
11961    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11962    executor.run_until_parked();
11963
11964    cx.assert_diff_hunks(
11965        r#"
11966        use some::mod1;
11967        use some::mod2;
11968
11969        const A: u32 = 42;
11970      + const B: u32 = 42;
11971      + const C: u32 = 42;
11972      + const D: u32 = 42;
11973      +
11974
11975        fn main() {
11976            println!("hello");
11977
11978            println!("world");
11979        }
11980        "#
11981        .unindent(),
11982    );
11983
11984    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11985    executor.run_until_parked();
11986
11987    cx.assert_diff_hunks(
11988        r#"
11989        use some::mod1;
11990        use some::mod2;
11991
11992        const A: u32 = 42;
11993      + const B: u32 = 42;
11994      + const C: u32 = 42;
11995      + const D: u32 = 42;
11996      + const E: u32 = 42;
11997      +
11998
11999        fn main() {
12000            println!("hello");
12001
12002            println!("world");
12003        }
12004        "#
12005        .unindent(),
12006    );
12007
12008    cx.update_editor(|editor, cx| {
12009        editor.delete_line(&DeleteLine, cx);
12010    });
12011    executor.run_until_parked();
12012
12013    cx.assert_diff_hunks(
12014        r#"
12015        use some::mod1;
12016        use some::mod2;
12017
12018        const A: u32 = 42;
12019      + const B: u32 = 42;
12020      + const C: u32 = 42;
12021      + const D: u32 = 42;
12022      + const E: u32 = 42;
12023
12024        fn main() {
12025            println!("hello");
12026
12027            println!("world");
12028        }
12029        "#
12030        .unindent(),
12031    );
12032
12033    cx.update_editor(|editor, cx| {
12034        editor.move_up(&MoveUp, cx);
12035        editor.delete_line(&DeleteLine, cx);
12036        editor.move_up(&MoveUp, cx);
12037        editor.delete_line(&DeleteLine, cx);
12038        editor.move_up(&MoveUp, cx);
12039        editor.delete_line(&DeleteLine, cx);
12040    });
12041    executor.run_until_parked();
12042    cx.assert_editor_state(
12043        &r#"
12044        use some::mod1;
12045        use some::mod2;
12046
12047        const A: u32 = 42;
12048        const B: u32 = 42;
12049        ˇ
12050        fn main() {
12051            println!("hello");
12052
12053            println!("world");
12054        }
12055        "#
12056        .unindent(),
12057    );
12058
12059    cx.assert_diff_hunks(
12060        r#"
12061        use some::mod1;
12062        use some::mod2;
12063
12064        const A: u32 = 42;
12065      + const B: u32 = 42;
12066
12067        fn main() {
12068            println!("hello");
12069
12070            println!("world");
12071        }
12072        "#
12073        .unindent(),
12074    );
12075
12076    cx.update_editor(|editor, cx| {
12077        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12078        editor.delete_line(&DeleteLine, cx);
12079    });
12080    executor.run_until_parked();
12081    cx.assert_diff_hunks(
12082        r#"
12083        use some::mod1;
12084      - use some::mod2;
12085      -
12086      - const A: u32 = 42;
12087
12088        fn main() {
12089            println!("hello");
12090
12091            println!("world");
12092        }
12093        "#
12094        .unindent(),
12095    );
12096}
12097
12098#[gpui::test]
12099async fn test_edits_around_expanded_deletion_hunks(
12100    executor: BackgroundExecutor,
12101    cx: &mut gpui::TestAppContext,
12102) {
12103    init_test(cx, |_| {});
12104
12105    let mut cx = EditorTestContext::new(cx).await;
12106
12107    let diff_base = r#"
12108        use some::mod1;
12109        use some::mod2;
12110
12111        const A: u32 = 42;
12112        const B: u32 = 42;
12113        const C: u32 = 42;
12114
12115
12116        fn main() {
12117            println!("hello");
12118
12119            println!("world");
12120        }
12121    "#
12122    .unindent();
12123    executor.run_until_parked();
12124    cx.set_state(
12125        &r#"
12126        use some::mod1;
12127        use some::mod2;
12128
12129        ˇconst B: u32 = 42;
12130        const C: u32 = 42;
12131
12132
12133        fn main() {
12134            println!("hello");
12135
12136            println!("world");
12137        }
12138        "#
12139        .unindent(),
12140    );
12141
12142    cx.set_diff_base(Some(&diff_base));
12143    executor.run_until_parked();
12144
12145    cx.update_editor(|editor, cx| {
12146        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12147    });
12148    executor.run_until_parked();
12149
12150    cx.assert_diff_hunks(
12151        r#"
12152        use some::mod1;
12153        use some::mod2;
12154
12155      - const A: u32 = 42;
12156        const B: u32 = 42;
12157        const C: u32 = 42;
12158
12159
12160        fn main() {
12161            println!("hello");
12162
12163            println!("world");
12164        }
12165        "#
12166        .unindent(),
12167    );
12168
12169    cx.update_editor(|editor, cx| {
12170        editor.delete_line(&DeleteLine, cx);
12171    });
12172    executor.run_until_parked();
12173    cx.assert_editor_state(
12174        &r#"
12175        use some::mod1;
12176        use some::mod2;
12177
12178        ˇconst C: u32 = 42;
12179
12180
12181        fn main() {
12182            println!("hello");
12183
12184            println!("world");
12185        }
12186        "#
12187        .unindent(),
12188    );
12189    cx.assert_diff_hunks(
12190        r#"
12191        use some::mod1;
12192        use some::mod2;
12193
12194      - const A: u32 = 42;
12195      - const B: u32 = 42;
12196        const C: u32 = 42;
12197
12198
12199        fn main() {
12200            println!("hello");
12201
12202            println!("world");
12203        }
12204        "#
12205        .unindent(),
12206    );
12207
12208    cx.update_editor(|editor, cx| {
12209        editor.delete_line(&DeleteLine, cx);
12210    });
12211    executor.run_until_parked();
12212    cx.assert_editor_state(
12213        &r#"
12214        use some::mod1;
12215        use some::mod2;
12216
12217        ˇ
12218
12219        fn main() {
12220            println!("hello");
12221
12222            println!("world");
12223        }
12224        "#
12225        .unindent(),
12226    );
12227    cx.assert_diff_hunks(
12228        r#"
12229        use some::mod1;
12230        use some::mod2;
12231
12232      - const A: u32 = 42;
12233      - const B: u32 = 42;
12234      - const C: u32 = 42;
12235
12236
12237        fn main() {
12238            println!("hello");
12239
12240            println!("world");
12241        }
12242        "#
12243        .unindent(),
12244    );
12245
12246    cx.update_editor(|editor, cx| {
12247        editor.handle_input("replacement", cx);
12248    });
12249    executor.run_until_parked();
12250    cx.assert_editor_state(
12251        &r#"
12252        use some::mod1;
12253        use some::mod2;
12254
12255        replacementˇ
12256
12257        fn main() {
12258            println!("hello");
12259
12260            println!("world");
12261        }
12262        "#
12263        .unindent(),
12264    );
12265    cx.assert_diff_hunks(
12266        r#"
12267        use some::mod1;
12268        use some::mod2;
12269
12270      - const A: u32 = 42;
12271      - const B: u32 = 42;
12272      - const C: u32 = 42;
12273      -
12274      + replacement
12275
12276        fn main() {
12277            println!("hello");
12278
12279            println!("world");
12280        }
12281        "#
12282        .unindent(),
12283    );
12284}
12285
12286#[gpui::test]
12287async fn test_edit_after_expanded_modification_hunk(
12288    executor: BackgroundExecutor,
12289    cx: &mut gpui::TestAppContext,
12290) {
12291    init_test(cx, |_| {});
12292
12293    let mut cx = EditorTestContext::new(cx).await;
12294
12295    let diff_base = r#"
12296        use some::mod1;
12297        use some::mod2;
12298
12299        const A: u32 = 42;
12300        const B: u32 = 42;
12301        const C: u32 = 42;
12302        const D: u32 = 42;
12303
12304
12305        fn main() {
12306            println!("hello");
12307
12308            println!("world");
12309        }"#
12310    .unindent();
12311
12312    cx.set_state(
12313        &r#"
12314        use some::mod1;
12315        use some::mod2;
12316
12317        const A: u32 = 42;
12318        const B: u32 = 42;
12319        const C: u32 = 43ˇ
12320        const D: u32 = 42;
12321
12322
12323        fn main() {
12324            println!("hello");
12325
12326            println!("world");
12327        }"#
12328        .unindent(),
12329    );
12330
12331    cx.set_diff_base(Some(&diff_base));
12332    executor.run_until_parked();
12333    cx.update_editor(|editor, cx| {
12334        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12335    });
12336    executor.run_until_parked();
12337
12338    cx.assert_diff_hunks(
12339        r#"
12340        use some::mod1;
12341        use some::mod2;
12342
12343        const A: u32 = 42;
12344        const B: u32 = 42;
12345      - const C: u32 = 42;
12346      + const C: u32 = 43
12347        const D: u32 = 42;
12348
12349
12350        fn main() {
12351            println!("hello");
12352
12353            println!("world");
12354        }"#
12355        .unindent(),
12356    );
12357
12358    cx.update_editor(|editor, cx| {
12359        editor.handle_input("\nnew_line\n", cx);
12360    });
12361    executor.run_until_parked();
12362
12363    cx.assert_diff_hunks(
12364        r#"
12365        use some::mod1;
12366        use some::mod2;
12367
12368        const A: u32 = 42;
12369        const B: u32 = 42;
12370      - const C: u32 = 42;
12371      + const C: u32 = 43
12372      + new_line
12373      +
12374        const D: u32 = 42;
12375
12376
12377        fn main() {
12378            println!("hello");
12379
12380            println!("world");
12381        }"#
12382        .unindent(),
12383    );
12384}
12385
12386async fn setup_indent_guides_editor(
12387    text: &str,
12388    cx: &mut gpui::TestAppContext,
12389) -> (BufferId, EditorTestContext) {
12390    init_test(cx, |_| {});
12391
12392    let mut cx = EditorTestContext::new(cx).await;
12393
12394    let buffer_id = cx.update_editor(|editor, cx| {
12395        editor.set_text(text, cx);
12396        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12397
12398        buffer_ids[0]
12399    });
12400
12401    (buffer_id, cx)
12402}
12403
12404fn assert_indent_guides(
12405    range: Range<u32>,
12406    expected: Vec<IndentGuide>,
12407    active_indices: Option<Vec<usize>>,
12408    cx: &mut EditorTestContext,
12409) {
12410    let indent_guides = cx.update_editor(|editor, cx| {
12411        let snapshot = editor.snapshot(cx).display_snapshot;
12412        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12413            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12414            true,
12415            &snapshot,
12416            cx,
12417        );
12418
12419        indent_guides.sort_by(|a, b| {
12420            a.depth.cmp(&b.depth).then(
12421                a.start_row
12422                    .cmp(&b.start_row)
12423                    .then(a.end_row.cmp(&b.end_row)),
12424            )
12425        });
12426        indent_guides
12427    });
12428
12429    if let Some(expected) = active_indices {
12430        let active_indices = cx.update_editor(|editor, cx| {
12431            let snapshot = editor.snapshot(cx).display_snapshot;
12432            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12433        });
12434
12435        assert_eq!(
12436            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12437            expected,
12438            "Active indent guide indices do not match"
12439        );
12440    }
12441
12442    let expected: Vec<_> = expected
12443        .into_iter()
12444        .map(|guide| MultiBufferIndentGuide {
12445            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12446            buffer: guide,
12447        })
12448        .collect();
12449
12450    assert_eq!(indent_guides, expected, "Indent guides do not match");
12451}
12452
12453fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12454    IndentGuide {
12455        buffer_id,
12456        start_row,
12457        end_row,
12458        depth,
12459        tab_size: 4,
12460        settings: IndentGuideSettings {
12461            enabled: true,
12462            line_width: 1,
12463            active_line_width: 1,
12464            ..Default::default()
12465        },
12466    }
12467}
12468
12469#[gpui::test]
12470async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12471    let (buffer_id, mut cx) = setup_indent_guides_editor(
12472        &"
12473    fn main() {
12474        let a = 1;
12475    }"
12476        .unindent(),
12477        cx,
12478    )
12479    .await;
12480
12481    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12482}
12483
12484#[gpui::test]
12485async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12486    let (buffer_id, mut cx) = setup_indent_guides_editor(
12487        &"
12488    fn main() {
12489        let a = 1;
12490        let b = 2;
12491    }"
12492        .unindent(),
12493        cx,
12494    )
12495    .await;
12496
12497    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12498}
12499
12500#[gpui::test]
12501async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12502    let (buffer_id, mut cx) = setup_indent_guides_editor(
12503        &"
12504    fn main() {
12505        let a = 1;
12506        if a == 3 {
12507            let b = 2;
12508        } else {
12509            let c = 3;
12510        }
12511    }"
12512        .unindent(),
12513        cx,
12514    )
12515    .await;
12516
12517    assert_indent_guides(
12518        0..8,
12519        vec![
12520            indent_guide(buffer_id, 1, 6, 0),
12521            indent_guide(buffer_id, 3, 3, 1),
12522            indent_guide(buffer_id, 5, 5, 1),
12523        ],
12524        None,
12525        &mut cx,
12526    );
12527}
12528
12529#[gpui::test]
12530async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12531    let (buffer_id, mut cx) = setup_indent_guides_editor(
12532        &"
12533    fn main() {
12534        let a = 1;
12535            let b = 2;
12536        let c = 3;
12537    }"
12538        .unindent(),
12539        cx,
12540    )
12541    .await;
12542
12543    assert_indent_guides(
12544        0..5,
12545        vec![
12546            indent_guide(buffer_id, 1, 3, 0),
12547            indent_guide(buffer_id, 2, 2, 1),
12548        ],
12549        None,
12550        &mut cx,
12551    );
12552}
12553
12554#[gpui::test]
12555async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12556    let (buffer_id, mut cx) = setup_indent_guides_editor(
12557        &"
12558        fn main() {
12559            let a = 1;
12560
12561            let c = 3;
12562        }"
12563        .unindent(),
12564        cx,
12565    )
12566    .await;
12567
12568    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12569}
12570
12571#[gpui::test]
12572async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12573    let (buffer_id, mut cx) = setup_indent_guides_editor(
12574        &"
12575        fn main() {
12576            let a = 1;
12577
12578            let c = 3;
12579
12580            if a == 3 {
12581                let b = 2;
12582            } else {
12583                let c = 3;
12584            }
12585        }"
12586        .unindent(),
12587        cx,
12588    )
12589    .await;
12590
12591    assert_indent_guides(
12592        0..11,
12593        vec![
12594            indent_guide(buffer_id, 1, 9, 0),
12595            indent_guide(buffer_id, 6, 6, 1),
12596            indent_guide(buffer_id, 8, 8, 1),
12597        ],
12598        None,
12599        &mut cx,
12600    );
12601}
12602
12603#[gpui::test]
12604async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12605    let (buffer_id, mut cx) = setup_indent_guides_editor(
12606        &"
12607        fn main() {
12608            let a = 1;
12609
12610            let c = 3;
12611
12612            if a == 3 {
12613                let b = 2;
12614            } else {
12615                let c = 3;
12616            }
12617        }"
12618        .unindent(),
12619        cx,
12620    )
12621    .await;
12622
12623    assert_indent_guides(
12624        1..11,
12625        vec![
12626            indent_guide(buffer_id, 1, 9, 0),
12627            indent_guide(buffer_id, 6, 6, 1),
12628            indent_guide(buffer_id, 8, 8, 1),
12629        ],
12630        None,
12631        &mut cx,
12632    );
12633}
12634
12635#[gpui::test]
12636async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12637    let (buffer_id, mut cx) = setup_indent_guides_editor(
12638        &"
12639        fn main() {
12640            let a = 1;
12641
12642            let c = 3;
12643
12644            if a == 3 {
12645                let b = 2;
12646            } else {
12647                let c = 3;
12648            }
12649        }"
12650        .unindent(),
12651        cx,
12652    )
12653    .await;
12654
12655    assert_indent_guides(
12656        1..10,
12657        vec![
12658            indent_guide(buffer_id, 1, 9, 0),
12659            indent_guide(buffer_id, 6, 6, 1),
12660            indent_guide(buffer_id, 8, 8, 1),
12661        ],
12662        None,
12663        &mut cx,
12664    );
12665}
12666
12667#[gpui::test]
12668async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12669    let (buffer_id, mut cx) = setup_indent_guides_editor(
12670        &"
12671        block1
12672            block2
12673                block3
12674                    block4
12675            block2
12676        block1
12677        block1"
12678            .unindent(),
12679        cx,
12680    )
12681    .await;
12682
12683    assert_indent_guides(
12684        1..10,
12685        vec![
12686            indent_guide(buffer_id, 1, 4, 0),
12687            indent_guide(buffer_id, 2, 3, 1),
12688            indent_guide(buffer_id, 3, 3, 2),
12689        ],
12690        None,
12691        &mut cx,
12692    );
12693}
12694
12695#[gpui::test]
12696async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12697    let (buffer_id, mut cx) = setup_indent_guides_editor(
12698        &"
12699        block1
12700            block2
12701                block3
12702
12703        block1
12704        block1"
12705            .unindent(),
12706        cx,
12707    )
12708    .await;
12709
12710    assert_indent_guides(
12711        0..6,
12712        vec![
12713            indent_guide(buffer_id, 1, 2, 0),
12714            indent_guide(buffer_id, 2, 2, 1),
12715        ],
12716        None,
12717        &mut cx,
12718    );
12719}
12720
12721#[gpui::test]
12722async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12723    let (buffer_id, mut cx) = setup_indent_guides_editor(
12724        &"
12725        block1
12726
12727
12728
12729            block2
12730        "
12731        .unindent(),
12732        cx,
12733    )
12734    .await;
12735
12736    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12737}
12738
12739#[gpui::test]
12740async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12741    let (buffer_id, mut cx) = setup_indent_guides_editor(
12742        &"
12743        def a:
12744        \tb = 3
12745        \tif True:
12746        \t\tc = 4
12747        \t\td = 5
12748        \tprint(b)
12749        "
12750        .unindent(),
12751        cx,
12752    )
12753    .await;
12754
12755    assert_indent_guides(
12756        0..6,
12757        vec![
12758            indent_guide(buffer_id, 1, 6, 0),
12759            indent_guide(buffer_id, 3, 4, 1),
12760        ],
12761        None,
12762        &mut cx,
12763    );
12764}
12765
12766#[gpui::test]
12767async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12768    let (buffer_id, mut cx) = setup_indent_guides_editor(
12769        &"
12770    fn main() {
12771        let a = 1;
12772    }"
12773        .unindent(),
12774        cx,
12775    )
12776    .await;
12777
12778    cx.update_editor(|editor, cx| {
12779        editor.change_selections(None, cx, |s| {
12780            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12781        });
12782    });
12783
12784    assert_indent_guides(
12785        0..3,
12786        vec![indent_guide(buffer_id, 1, 1, 0)],
12787        Some(vec![0]),
12788        &mut cx,
12789    );
12790}
12791
12792#[gpui::test]
12793async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12794    let (buffer_id, mut cx) = setup_indent_guides_editor(
12795        &"
12796    fn main() {
12797        if 1 == 2 {
12798            let a = 1;
12799        }
12800    }"
12801        .unindent(),
12802        cx,
12803    )
12804    .await;
12805
12806    cx.update_editor(|editor, cx| {
12807        editor.change_selections(None, cx, |s| {
12808            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12809        });
12810    });
12811
12812    assert_indent_guides(
12813        0..4,
12814        vec![
12815            indent_guide(buffer_id, 1, 3, 0),
12816            indent_guide(buffer_id, 2, 2, 1),
12817        ],
12818        Some(vec![1]),
12819        &mut cx,
12820    );
12821
12822    cx.update_editor(|editor, cx| {
12823        editor.change_selections(None, cx, |s| {
12824            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12825        });
12826    });
12827
12828    assert_indent_guides(
12829        0..4,
12830        vec![
12831            indent_guide(buffer_id, 1, 3, 0),
12832            indent_guide(buffer_id, 2, 2, 1),
12833        ],
12834        Some(vec![1]),
12835        &mut cx,
12836    );
12837
12838    cx.update_editor(|editor, cx| {
12839        editor.change_selections(None, cx, |s| {
12840            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12841        });
12842    });
12843
12844    assert_indent_guides(
12845        0..4,
12846        vec![
12847            indent_guide(buffer_id, 1, 3, 0),
12848            indent_guide(buffer_id, 2, 2, 1),
12849        ],
12850        Some(vec![0]),
12851        &mut cx,
12852    );
12853}
12854
12855#[gpui::test]
12856async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12857    let (buffer_id, mut cx) = setup_indent_guides_editor(
12858        &"
12859    fn main() {
12860        let a = 1;
12861
12862        let b = 2;
12863    }"
12864        .unindent(),
12865        cx,
12866    )
12867    .await;
12868
12869    cx.update_editor(|editor, cx| {
12870        editor.change_selections(None, cx, |s| {
12871            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12872        });
12873    });
12874
12875    assert_indent_guides(
12876        0..5,
12877        vec![indent_guide(buffer_id, 1, 3, 0)],
12878        Some(vec![0]),
12879        &mut cx,
12880    );
12881}
12882
12883#[gpui::test]
12884async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12885    let (buffer_id, mut cx) = setup_indent_guides_editor(
12886        &"
12887    def m:
12888        a = 1
12889        pass"
12890            .unindent(),
12891        cx,
12892    )
12893    .await;
12894
12895    cx.update_editor(|editor, cx| {
12896        editor.change_selections(None, cx, |s| {
12897            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12898        });
12899    });
12900
12901    assert_indent_guides(
12902        0..3,
12903        vec![indent_guide(buffer_id, 1, 2, 0)],
12904        Some(vec![0]),
12905        &mut cx,
12906    );
12907}
12908
12909#[gpui::test]
12910fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12911    init_test(cx, |_| {});
12912
12913    let editor = cx.add_window(|cx| {
12914        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12915        build_editor(buffer, cx)
12916    });
12917
12918    let render_args = Arc::new(Mutex::new(None));
12919    let snapshot = editor
12920        .update(cx, |editor, cx| {
12921            let snapshot = editor.buffer().read(cx).snapshot(cx);
12922            let range =
12923                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12924
12925            struct RenderArgs {
12926                row: MultiBufferRow,
12927                folded: bool,
12928                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12929            }
12930
12931            let crease = Crease::new(
12932                range,
12933                FoldPlaceholder::test(),
12934                {
12935                    let toggle_callback = render_args.clone();
12936                    move |row, folded, callback, _cx| {
12937                        *toggle_callback.lock() = Some(RenderArgs {
12938                            row,
12939                            folded,
12940                            callback,
12941                        });
12942                        div()
12943                    }
12944                },
12945                |_row, _folded, _cx| div(),
12946            );
12947
12948            editor.insert_creases(Some(crease), cx);
12949            let snapshot = editor.snapshot(cx);
12950            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12951            snapshot
12952        })
12953        .unwrap();
12954
12955    let render_args = render_args.lock().take().unwrap();
12956    assert_eq!(render_args.row, MultiBufferRow(1));
12957    assert!(!render_args.folded);
12958    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12959
12960    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12961        .unwrap();
12962    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12963    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12964
12965    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12966        .unwrap();
12967    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12968    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12969}
12970
12971#[gpui::test]
12972async fn test_input_text(cx: &mut gpui::TestAppContext) {
12973    init_test(cx, |_| {});
12974    let mut cx = EditorTestContext::new(cx).await;
12975
12976    cx.set_state(
12977        &r#"ˇone
12978        two
12979
12980        three
12981        fourˇ
12982        five
12983
12984        siˇx"#
12985            .unindent(),
12986    );
12987
12988    cx.dispatch_action(HandleInput(String::new()));
12989    cx.assert_editor_state(
12990        &r#"ˇone
12991        two
12992
12993        three
12994        fourˇ
12995        five
12996
12997        siˇx"#
12998            .unindent(),
12999    );
13000
13001    cx.dispatch_action(HandleInput("AAAA".to_string()));
13002    cx.assert_editor_state(
13003        &r#"AAAAˇone
13004        two
13005
13006        three
13007        fourAAAAˇ
13008        five
13009
13010        siAAAAˇx"#
13011            .unindent(),
13012    );
13013}
13014
13015#[gpui::test]
13016async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13017    init_test(cx, |_| {});
13018
13019    let mut cx = EditorTestContext::new(cx).await;
13020    cx.set_state(
13021        r#"let foo = 1;
13022let foo = 2;
13023let foo = 3;
13024let fooˇ = 4;
13025let foo = 5;
13026let foo = 6;
13027let foo = 7;
13028let foo = 8;
13029let foo = 9;
13030let foo = 10;
13031let foo = 11;
13032let foo = 12;
13033let foo = 13;
13034let foo = 14;
13035let foo = 15;"#,
13036    );
13037
13038    cx.update_editor(|e, cx| {
13039        assert_eq!(
13040            e.next_scroll_position,
13041            NextScrollCursorCenterTopBottom::Center,
13042            "Default next scroll direction is center",
13043        );
13044
13045        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13046        assert_eq!(
13047            e.next_scroll_position,
13048            NextScrollCursorCenterTopBottom::Top,
13049            "After center, next scroll direction should be top",
13050        );
13051
13052        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13053        assert_eq!(
13054            e.next_scroll_position,
13055            NextScrollCursorCenterTopBottom::Bottom,
13056            "After top, next scroll direction should be bottom",
13057        );
13058
13059        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13060        assert_eq!(
13061            e.next_scroll_position,
13062            NextScrollCursorCenterTopBottom::Center,
13063            "After bottom, scrolling should start over",
13064        );
13065
13066        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13067        assert_eq!(
13068            e.next_scroll_position,
13069            NextScrollCursorCenterTopBottom::Top,
13070            "Scrolling continues if retriggered fast enough"
13071        );
13072    });
13073
13074    cx.executor()
13075        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13076    cx.executor().run_until_parked();
13077    cx.update_editor(|e, _| {
13078        assert_eq!(
13079            e.next_scroll_position,
13080            NextScrollCursorCenterTopBottom::Center,
13081            "If scrolling is not triggered fast enough, it should reset"
13082        );
13083    });
13084}
13085
13086#[gpui::test]
13087async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13088    init_test(cx, |_| {});
13089    let mut cx = EditorLspTestContext::new_rust(
13090        lsp::ServerCapabilities {
13091            definition_provider: Some(lsp::OneOf::Left(true)),
13092            references_provider: Some(lsp::OneOf::Left(true)),
13093            ..lsp::ServerCapabilities::default()
13094        },
13095        cx,
13096    )
13097    .await;
13098
13099    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13100        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13101            move |params, _| async move {
13102                if empty_go_to_definition {
13103                    Ok(None)
13104                } else {
13105                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13106                        uri: params.text_document_position_params.text_document.uri,
13107                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13108                    })))
13109                }
13110            },
13111        );
13112        let references =
13113            cx.lsp
13114                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13115                    Ok(Some(vec![lsp::Location {
13116                        uri: params.text_document_position.text_document.uri,
13117                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13118                    }]))
13119                });
13120        (go_to_definition, references)
13121    };
13122
13123    cx.set_state(
13124        &r#"fn one() {
13125            let mut a = ˇtwo();
13126        }
13127
13128        fn two() {}"#
13129            .unindent(),
13130    );
13131    set_up_lsp_handlers(false, &mut cx);
13132    let navigated = cx
13133        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13134        .await
13135        .expect("Failed to navigate to definition");
13136    assert_eq!(
13137        navigated,
13138        Navigated::Yes,
13139        "Should have navigated to definition from the GetDefinition response"
13140    );
13141    cx.assert_editor_state(
13142        &r#"fn one() {
13143            let mut a = two();
13144        }
13145
13146        fn «twoˇ»() {}"#
13147            .unindent(),
13148    );
13149
13150    let editors = cx.update_workspace(|workspace, cx| {
13151        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13152    });
13153    cx.update_editor(|_, test_editor_cx| {
13154        assert_eq!(
13155            editors.len(),
13156            1,
13157            "Initially, only one, test, editor should be open in the workspace"
13158        );
13159        assert_eq!(
13160            test_editor_cx.view(),
13161            editors.last().expect("Asserted len is 1")
13162        );
13163    });
13164
13165    set_up_lsp_handlers(true, &mut cx);
13166    let navigated = cx
13167        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13168        .await
13169        .expect("Failed to navigate to lookup references");
13170    assert_eq!(
13171        navigated,
13172        Navigated::Yes,
13173        "Should have navigated to references as a fallback after empty GoToDefinition response"
13174    );
13175    // We should not change the selections in the existing file,
13176    // if opening another milti buffer with the references
13177    cx.assert_editor_state(
13178        &r#"fn one() {
13179            let mut a = two();
13180        }
13181
13182        fn «twoˇ»() {}"#
13183            .unindent(),
13184    );
13185    let editors = cx.update_workspace(|workspace, cx| {
13186        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13187    });
13188    cx.update_editor(|_, test_editor_cx| {
13189        assert_eq!(
13190            editors.len(),
13191            2,
13192            "After falling back to references search, we open a new editor with the results"
13193        );
13194        let references_fallback_text = editors
13195            .into_iter()
13196            .find(|new_editor| new_editor != test_editor_cx.view())
13197            .expect("Should have one non-test editor now")
13198            .read(test_editor_cx)
13199            .text(test_editor_cx);
13200        assert_eq!(
13201            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13202            "Should use the range from the references response and not the GoToDefinition one"
13203        );
13204    });
13205}
13206
13207fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13208    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13209    point..point
13210}
13211
13212fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13213    let (text, ranges) = marked_text_ranges(marked_text, true);
13214    assert_eq!(view.text(cx), text);
13215    assert_eq!(
13216        view.selections.ranges(cx),
13217        ranges,
13218        "Assert selections are {}",
13219        marked_text
13220    );
13221}
13222
13223pub fn handle_signature_help_request(
13224    cx: &mut EditorLspTestContext,
13225    mocked_response: lsp::SignatureHelp,
13226) -> impl Future<Output = ()> {
13227    let mut request =
13228        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13229            let mocked_response = mocked_response.clone();
13230            async move { Ok(Some(mocked_response)) }
13231        });
13232
13233    async move {
13234        request.next().await;
13235    }
13236}
13237
13238/// Handle completion request passing a marked string specifying where the completion
13239/// should be triggered from using '|' character, what range should be replaced, and what completions
13240/// should be returned using '<' and '>' to delimit the range
13241pub fn handle_completion_request(
13242    cx: &mut EditorLspTestContext,
13243    marked_string: &str,
13244    completions: Vec<&'static str>,
13245    counter: Arc<AtomicUsize>,
13246) -> impl Future<Output = ()> {
13247    let complete_from_marker: TextRangeMarker = '|'.into();
13248    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13249    let (_, mut marked_ranges) = marked_text_ranges_by(
13250        marked_string,
13251        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13252    );
13253
13254    let complete_from_position =
13255        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13256    let replace_range =
13257        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13258
13259    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13260        let completions = completions.clone();
13261        counter.fetch_add(1, atomic::Ordering::Release);
13262        async move {
13263            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13264            assert_eq!(
13265                params.text_document_position.position,
13266                complete_from_position
13267            );
13268            Ok(Some(lsp::CompletionResponse::Array(
13269                completions
13270                    .iter()
13271                    .map(|completion_text| lsp::CompletionItem {
13272                        label: completion_text.to_string(),
13273                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13274                            range: replace_range,
13275                            new_text: completion_text.to_string(),
13276                        })),
13277                        ..Default::default()
13278                    })
13279                    .collect(),
13280            )))
13281        }
13282    });
13283
13284    async move {
13285        request.next().await;
13286    }
13287}
13288
13289fn handle_resolve_completion_request(
13290    cx: &mut EditorLspTestContext,
13291    edits: Option<Vec<(&'static str, &'static str)>>,
13292) -> impl Future<Output = ()> {
13293    let edits = edits.map(|edits| {
13294        edits
13295            .iter()
13296            .map(|(marked_string, new_text)| {
13297                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13298                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13299                lsp::TextEdit::new(replace_range, new_text.to_string())
13300            })
13301            .collect::<Vec<_>>()
13302    });
13303
13304    let mut request =
13305        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13306            let edits = edits.clone();
13307            async move {
13308                Ok(lsp::CompletionItem {
13309                    additional_text_edits: edits,
13310                    ..Default::default()
13311                })
13312            }
13313        });
13314
13315    async move {
13316        request.next().await;
13317    }
13318}
13319
13320pub(crate) fn update_test_language_settings(
13321    cx: &mut TestAppContext,
13322    f: impl Fn(&mut AllLanguageSettingsContent),
13323) {
13324    cx.update(|cx| {
13325        SettingsStore::update_global(cx, |store, cx| {
13326            store.update_user_settings::<AllLanguageSettings>(cx, f);
13327        });
13328    });
13329}
13330
13331pub(crate) fn update_test_project_settings(
13332    cx: &mut TestAppContext,
13333    f: impl Fn(&mut ProjectSettings),
13334) {
13335    cx.update(|cx| {
13336        SettingsStore::update_global(cx, |store, cx| {
13337            store.update_user_settings::<ProjectSettings>(cx, f);
13338        });
13339    });
13340}
13341
13342pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13343    cx.update(|cx| {
13344        assets::Assets.load_test_fonts(cx);
13345        let store = SettingsStore::test(cx);
13346        cx.set_global(store);
13347        theme::init(theme::LoadThemes::JustBase, cx);
13348        release_channel::init(SemanticVersion::default(), cx);
13349        client::init_settings(cx);
13350        language::init(cx);
13351        Project::init_settings(cx);
13352        workspace::init_settings(cx);
13353        crate::init(cx);
13354    });
13355
13356    update_test_language_settings(cx, f);
13357}
13358
13359pub(crate) fn rust_lang() -> Arc<Language> {
13360    Arc::new(Language::new(
13361        LanguageConfig {
13362            name: "Rust".into(),
13363            matcher: LanguageMatcher {
13364                path_suffixes: vec!["rs".to_string()],
13365                ..Default::default()
13366            },
13367            ..Default::default()
13368        },
13369        Some(tree_sitter_rust::LANGUAGE.into()),
13370    ))
13371}
13372
13373#[track_caller]
13374fn assert_hunk_revert(
13375    not_reverted_text_with_selections: &str,
13376    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13377    expected_reverted_text_with_selections: &str,
13378    base_text: &str,
13379    cx: &mut EditorLspTestContext,
13380) {
13381    cx.set_state(not_reverted_text_with_selections);
13382    cx.update_editor(|editor, cx| {
13383        editor
13384            .buffer()
13385            .read(cx)
13386            .as_singleton()
13387            .unwrap()
13388            .update(cx, |buffer, cx| {
13389                buffer.set_diff_base(Some(base_text.into()), cx);
13390            });
13391    });
13392    cx.executor().run_until_parked();
13393
13394    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13395        let snapshot = editor.buffer().read(cx).snapshot(cx);
13396        let reverted_hunk_statuses = snapshot
13397            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13398            .map(|hunk| hunk_status(&hunk))
13399            .collect::<Vec<_>>();
13400
13401        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13402        reverted_hunk_statuses
13403    });
13404    cx.executor().run_until_parked();
13405    cx.assert_editor_state(expected_reverted_text_with_selections);
13406    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13407}