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]
 3993async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 3994    init_test(cx, |_| {});
 3995
 3996    let mut cx = EditorTestContext::new(cx).await;
 3997    cx.set_state(
 3998        &"
 3999            ˇzero
 4000            one
 4001            two
 4002            three
 4003            four
 4004            five
 4005        "
 4006        .unindent(),
 4007    );
 4008
 4009    // Create a four-line block that replaces three lines of text.
 4010    cx.update_editor(|editor, cx| {
 4011        let snapshot = editor.snapshot(cx);
 4012        let snapshot = &snapshot.buffer_snapshot;
 4013        let placement = BlockPlacement::Replace(
 4014            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4015        );
 4016        editor.insert_blocks(
 4017            [BlockProperties {
 4018                placement,
 4019                height: 4,
 4020                style: BlockStyle::Sticky,
 4021                render: Box::new(|_| gpui::div().into_any_element()),
 4022                priority: 0,
 4023            }],
 4024            None,
 4025            cx,
 4026        );
 4027    });
 4028
 4029    // Move down so that the cursor touches the block.
 4030    cx.update_editor(|editor, cx| {
 4031        editor.move_down(&Default::default(), cx);
 4032    });
 4033    cx.assert_editor_state(
 4034        &"
 4035            zero
 4036            «one
 4037            two
 4038            threeˇ»
 4039            four
 4040            five
 4041        "
 4042        .unindent(),
 4043    );
 4044
 4045    // Move down past the block.
 4046    cx.update_editor(|editor, cx| {
 4047        editor.move_down(&Default::default(), cx);
 4048    });
 4049    cx.assert_editor_state(
 4050        &"
 4051            zero
 4052            one
 4053            two
 4054            three
 4055            ˇfour
 4056            five
 4057        "
 4058        .unindent(),
 4059    );
 4060}
 4061
 4062#[gpui::test]
 4063fn test_transpose(cx: &mut TestAppContext) {
 4064    init_test(cx, |_| {});
 4065
 4066    _ = cx.add_window(|cx| {
 4067        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4068        editor.set_style(EditorStyle::default(), cx);
 4069        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4070        editor.transpose(&Default::default(), cx);
 4071        assert_eq!(editor.text(cx), "bac");
 4072        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4073
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "bca");
 4076        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "bac");
 4080        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4081
 4082        editor
 4083    });
 4084
 4085    _ = cx.add_window(|cx| {
 4086        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4087        editor.set_style(EditorStyle::default(), cx);
 4088        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4089        editor.transpose(&Default::default(), cx);
 4090        assert_eq!(editor.text(cx), "acb\nde");
 4091        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4092
 4093        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4094        editor.transpose(&Default::default(), cx);
 4095        assert_eq!(editor.text(cx), "acbd\ne");
 4096        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4097
 4098        editor.transpose(&Default::default(), cx);
 4099        assert_eq!(editor.text(cx), "acbde\n");
 4100        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4101
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "acbd\ne");
 4104        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4105
 4106        editor
 4107    });
 4108
 4109    _ = cx.add_window(|cx| {
 4110        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4111        editor.set_style(EditorStyle::default(), cx);
 4112        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4113        editor.transpose(&Default::default(), cx);
 4114        assert_eq!(editor.text(cx), "bacd\ne");
 4115        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4116
 4117        editor.transpose(&Default::default(), cx);
 4118        assert_eq!(editor.text(cx), "bcade\n");
 4119        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4120
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "bcda\ne");
 4123        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4124
 4125        editor.transpose(&Default::default(), cx);
 4126        assert_eq!(editor.text(cx), "bcade\n");
 4127        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4128
 4129        editor.transpose(&Default::default(), cx);
 4130        assert_eq!(editor.text(cx), "bcaed\n");
 4131        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4132
 4133        editor
 4134    });
 4135
 4136    _ = cx.add_window(|cx| {
 4137        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4138        editor.set_style(EditorStyle::default(), cx);
 4139        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4140        editor.transpose(&Default::default(), cx);
 4141        assert_eq!(editor.text(cx), "🏀🍐✋");
 4142        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4143
 4144        editor.transpose(&Default::default(), cx);
 4145        assert_eq!(editor.text(cx), "🏀✋🍐");
 4146        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4147
 4148        editor.transpose(&Default::default(), cx);
 4149        assert_eq!(editor.text(cx), "🏀🍐✋");
 4150        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4151
 4152        editor
 4153    });
 4154}
 4155
 4156#[gpui::test]
 4157async fn test_rewrap(cx: &mut TestAppContext) {
 4158    init_test(cx, |_| {});
 4159
 4160    let mut cx = EditorTestContext::new(cx).await;
 4161
 4162    {
 4163        let language = Arc::new(Language::new(
 4164            LanguageConfig {
 4165                line_comments: vec!["// ".into()],
 4166                ..LanguageConfig::default()
 4167            },
 4168            None,
 4169        ));
 4170        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4171
 4172        let unwrapped_text = indoc! {"
 4173            // ˇ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.
 4174        "};
 4175
 4176        let wrapped_text = indoc! {"
 4177            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4178            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4179            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4180            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4181            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4182            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4183            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4184            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4185            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4186            // porttitor id. Aliquam id accumsan eros.ˇ
 4187        "};
 4188
 4189        cx.set_state(unwrapped_text);
 4190        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4191        cx.assert_editor_state(wrapped_text);
 4192    }
 4193
 4194    // Test that rewrapping works inside of a selection
 4195    {
 4196        let language = Arc::new(Language::new(
 4197            LanguageConfig {
 4198                line_comments: vec!["// ".into()],
 4199                ..LanguageConfig::default()
 4200            },
 4201            None,
 4202        ));
 4203        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4204
 4205        let unwrapped_text = indoc! {"
 4206            «// 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.ˇ»
 4207        "};
 4208
 4209        let wrapped_text = indoc! {"
 4210            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4211            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4212            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4213            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4214            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4215            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4216            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4217            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4218            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4219            // porttitor id. Aliquam id accumsan eros.ˇ
 4220        "};
 4221
 4222        cx.set_state(unwrapped_text);
 4223        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4224        cx.assert_editor_state(wrapped_text);
 4225    }
 4226
 4227    // Test that cursors that expand to the same region are collapsed.
 4228    {
 4229        let language = Arc::new(Language::new(
 4230            LanguageConfig {
 4231                line_comments: vec!["// ".into()],
 4232                ..LanguageConfig::default()
 4233            },
 4234            None,
 4235        ));
 4236        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4237
 4238        let unwrapped_text = indoc! {"
 4239            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4240            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4241            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4242            // ˇ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.
 4243        "};
 4244
 4245        let wrapped_text = indoc! {"
 4246            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4247            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4248            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4249            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4250            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4251            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4252            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4253            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4254            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4255            // porttitor id. Aliquam id accumsan eros.ˇ
 4256        "};
 4257
 4258        cx.set_state(unwrapped_text);
 4259        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4260        cx.assert_editor_state(wrapped_text);
 4261    }
 4262
 4263    // Test that non-contiguous selections are treated separately.
 4264    {
 4265        let language = Arc::new(Language::new(
 4266            LanguageConfig {
 4267                line_comments: vec!["// ".into()],
 4268                ..LanguageConfig::default()
 4269            },
 4270            None,
 4271        ));
 4272        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4273
 4274        let unwrapped_text = indoc! {"
 4275            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4276            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4277            //
 4278            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4279            // ˇ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.
 4280        "};
 4281
 4282        let wrapped_text = indoc! {"
 4283            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4284            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4285            // auctor, eu lacinia sapien scelerisque.ˇ
 4286            //
 4287            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4288            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4289            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4290            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4291            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4292            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4293            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ
 4294        "};
 4295
 4296        cx.set_state(unwrapped_text);
 4297        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4298        cx.assert_editor_state(wrapped_text);
 4299    }
 4300
 4301    // Test that different comment prefixes are supported.
 4302    {
 4303        let language = Arc::new(Language::new(
 4304            LanguageConfig {
 4305                line_comments: vec!["# ".into()],
 4306                ..LanguageConfig::default()
 4307            },
 4308            None,
 4309        ));
 4310        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4311
 4312        let unwrapped_text = indoc! {"
 4313            # ˇ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.
 4314        "};
 4315
 4316        let wrapped_text = indoc! {"
 4317            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4318            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4319            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4320            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4321            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4322            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4323            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4324            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4325            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4326            # accumsan eros.ˇ
 4327        "};
 4328
 4329        cx.set_state(unwrapped_text);
 4330        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4331        cx.assert_editor_state(wrapped_text);
 4332    }
 4333
 4334    // Test that rewrapping is ignored outside of comments in most languages.
 4335    {
 4336        let language = Arc::new(Language::new(
 4337            LanguageConfig {
 4338                line_comments: vec!["// ".into(), "/// ".into()],
 4339                ..LanguageConfig::default()
 4340            },
 4341            Some(tree_sitter_rust::LANGUAGE.into()),
 4342        ));
 4343        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4344
 4345        let unwrapped_text = indoc! {"
 4346            /// Adds two numbers.
 4347            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4348            fn add(a: u32, b: u32) -> u32 {
 4349                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ˇ
 4350            }
 4351        "};
 4352
 4353        let wrapped_text = indoc! {"
 4354            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4355            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4356            fn add(a: u32, b: u32) -> u32 {
 4357                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ˇ
 4358            }
 4359        "};
 4360
 4361        cx.set_state(unwrapped_text);
 4362        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4363        cx.assert_editor_state(wrapped_text);
 4364    }
 4365
 4366    // Test that rewrapping works in Markdown and Plain Text languages.
 4367    {
 4368        let markdown_language = Arc::new(Language::new(
 4369            LanguageConfig {
 4370                name: "Markdown".into(),
 4371                ..LanguageConfig::default()
 4372            },
 4373            None,
 4374        ));
 4375        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4376
 4377        let unwrapped_text = indoc! {"
 4378            # Hello
 4379
 4380            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.
 4381        "};
 4382
 4383        let wrapped_text = indoc! {"
 4384            # Hello
 4385
 4386            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4387            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4388            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4389            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4390            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4391            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4392            Integer sit amet scelerisque nisi.ˇ
 4393        "};
 4394
 4395        cx.set_state(unwrapped_text);
 4396        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4397        cx.assert_editor_state(wrapped_text);
 4398
 4399        let plaintext_language = Arc::new(Language::new(
 4400            LanguageConfig {
 4401                name: "Plain Text".into(),
 4402                ..LanguageConfig::default()
 4403            },
 4404            None,
 4405        ));
 4406        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4407
 4408        let unwrapped_text = indoc! {"
 4409            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.
 4410        "};
 4411
 4412        let wrapped_text = indoc! {"
 4413            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4414            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4415            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4416            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4417            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4418            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4419            Integer sit amet scelerisque nisi.ˇ
 4420        "};
 4421
 4422        cx.set_state(unwrapped_text);
 4423        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4424        cx.assert_editor_state(wrapped_text);
 4425    }
 4426
 4427    // Test rewrapping unaligned comments in a selection.
 4428    {
 4429        let language = Arc::new(Language::new(
 4430            LanguageConfig {
 4431                line_comments: vec!["// ".into(), "/// ".into()],
 4432                ..LanguageConfig::default()
 4433            },
 4434            Some(tree_sitter_rust::LANGUAGE.into()),
 4435        ));
 4436        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4437
 4438        let unwrapped_text = indoc! {"
 4439            fn foo() {
 4440                if true {
 4441            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4442            // Praesent semper egestas tellus id dignissim.ˇ»
 4443                    do_something();
 4444                } else {
 4445                    //
 4446                }
 4447            }
 4448        "};
 4449
 4450        let wrapped_text = indoc! {"
 4451            fn foo() {
 4452                if true {
 4453                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4454                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4455                    // egestas tellus id dignissim.ˇ
 4456                    do_something();
 4457                } else {
 4458                    //
 4459                }
 4460            }
 4461        "};
 4462
 4463        cx.set_state(unwrapped_text);
 4464        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4465        cx.assert_editor_state(wrapped_text);
 4466
 4467        let unwrapped_text = indoc! {"
 4468            fn foo() {
 4469                if true {
 4470            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4471            // Praesent semper egestas tellus id dignissim.»
 4472                    do_something();
 4473                } else {
 4474                    //
 4475                }
 4476
 4477            }
 4478        "};
 4479
 4480        let wrapped_text = indoc! {"
 4481            fn foo() {
 4482                if true {
 4483                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4484                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4485                    // egestas tellus id dignissim.ˇ
 4486                    do_something();
 4487                } else {
 4488                    //
 4489                }
 4490
 4491            }
 4492        "};
 4493
 4494        cx.set_state(unwrapped_text);
 4495        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4496        cx.assert_editor_state(wrapped_text);
 4497    }
 4498}
 4499
 4500#[gpui::test]
 4501async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4502    init_test(cx, |_| {});
 4503
 4504    let mut cx = EditorTestContext::new(cx).await;
 4505
 4506    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4507    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4508    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4509
 4510    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4511    cx.set_state("two ˇfour ˇsix ˇ");
 4512    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4513    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4514
 4515    // Paste again but with only two cursors. Since the number of cursors doesn't
 4516    // match the number of slices in the clipboard, the entire clipboard text
 4517    // is pasted at each cursor.
 4518    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4519    cx.update_editor(|e, cx| {
 4520        e.handle_input("( ", cx);
 4521        e.paste(&Paste, cx);
 4522        e.handle_input(") ", cx);
 4523    });
 4524    cx.assert_editor_state(
 4525        &([
 4526            "( one✅ ",
 4527            "three ",
 4528            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4529            "three ",
 4530            "five ) ˇ",
 4531        ]
 4532        .join("\n")),
 4533    );
 4534
 4535    // Cut with three selections, one of which is full-line.
 4536    cx.set_state(indoc! {"
 4537        1«2ˇ»3
 4538        4ˇ567
 4539        «8ˇ»9"});
 4540    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4541    cx.assert_editor_state(indoc! {"
 4542        1ˇ3
 4543        ˇ9"});
 4544
 4545    // Paste with three selections, noticing how the copied selection that was full-line
 4546    // gets inserted before the second cursor.
 4547    cx.set_state(indoc! {"
 4548        1ˇ3
 4549 4550        «oˇ»ne"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        12ˇ3
 4554        4567
 4555 4556        8ˇne"});
 4557
 4558    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4559    cx.set_state(indoc! {"
 4560        The quick brown
 4561        fox juˇmps over
 4562        the lazy dog"});
 4563    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4564    assert_eq!(
 4565        cx.read_from_clipboard()
 4566            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4567        Some("fox jumps over\n".to_string())
 4568    );
 4569
 4570    // Paste with three selections, noticing how the copied full-line selection is inserted
 4571    // before the empty selections but replaces the selection that is non-empty.
 4572    cx.set_state(indoc! {"
 4573        Tˇhe quick brown
 4574        «foˇ»x jumps over
 4575        tˇhe lazy dog"});
 4576    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4577    cx.assert_editor_state(indoc! {"
 4578        fox jumps over
 4579        Tˇhe quick brown
 4580        fox jumps over
 4581        ˇx jumps over
 4582        fox jumps over
 4583        tˇhe lazy dog"});
 4584}
 4585
 4586#[gpui::test]
 4587async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4588    init_test(cx, |_| {});
 4589
 4590    let mut cx = EditorTestContext::new(cx).await;
 4591    let language = Arc::new(Language::new(
 4592        LanguageConfig::default(),
 4593        Some(tree_sitter_rust::LANGUAGE.into()),
 4594    ));
 4595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4596
 4597    // Cut an indented block, without the leading whitespace.
 4598    cx.set_state(indoc! {"
 4599        const a: B = (
 4600            c(),
 4601            «d(
 4602                e,
 4603                f
 4604            )ˇ»
 4605        );
 4606    "});
 4607    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4608    cx.assert_editor_state(indoc! {"
 4609        const a: B = (
 4610            c(),
 4611            ˇ
 4612        );
 4613    "});
 4614
 4615    // Paste it at the same position.
 4616    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4617    cx.assert_editor_state(indoc! {"
 4618        const a: B = (
 4619            c(),
 4620            d(
 4621                e,
 4622                f
 4623 4624        );
 4625    "});
 4626
 4627    // Paste it at a line with a lower indent level.
 4628    cx.set_state(indoc! {"
 4629        ˇ
 4630        const a: B = (
 4631            c(),
 4632        );
 4633    "});
 4634    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4635    cx.assert_editor_state(indoc! {"
 4636        d(
 4637            e,
 4638            f
 4639 4640        const a: B = (
 4641            c(),
 4642        );
 4643    "});
 4644
 4645    // Cut an indented block, with the leading whitespace.
 4646    cx.set_state(indoc! {"
 4647        const a: B = (
 4648            c(),
 4649        «    d(
 4650                e,
 4651                f
 4652            )
 4653        ˇ»);
 4654    "});
 4655    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4656    cx.assert_editor_state(indoc! {"
 4657        const a: B = (
 4658            c(),
 4659        ˇ);
 4660    "});
 4661
 4662    // Paste it at the same position.
 4663    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        const a: B = (
 4666            c(),
 4667            d(
 4668                e,
 4669                f
 4670            )
 4671        ˇ);
 4672    "});
 4673
 4674    // Paste it at a line with a higher indent level.
 4675    cx.set_state(indoc! {"
 4676        const a: B = (
 4677            c(),
 4678            d(
 4679                e,
 4680 4681            )
 4682        );
 4683    "});
 4684    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4685    cx.assert_editor_state(indoc! {"
 4686        const a: B = (
 4687            c(),
 4688            d(
 4689                e,
 4690                f    d(
 4691                    e,
 4692                    f
 4693                )
 4694        ˇ
 4695            )
 4696        );
 4697    "});
 4698}
 4699
 4700#[gpui::test]
 4701fn test_select_all(cx: &mut TestAppContext) {
 4702    init_test(cx, |_| {});
 4703
 4704    let view = cx.add_window(|cx| {
 4705        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4706        build_editor(buffer, cx)
 4707    });
 4708    _ = view.update(cx, |view, cx| {
 4709        view.select_all(&SelectAll, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4713        );
 4714    });
 4715}
 4716
 4717#[gpui::test]
 4718fn test_select_line(cx: &mut TestAppContext) {
 4719    init_test(cx, |_| {});
 4720
 4721    let view = cx.add_window(|cx| {
 4722        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4723        build_editor(buffer, cx)
 4724    });
 4725    _ = view.update(cx, |view, cx| {
 4726        view.change_selections(None, cx, |s| {
 4727            s.select_display_ranges([
 4728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4730                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4731                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4732            ])
 4733        });
 4734        view.select_line(&SelectLine, cx);
 4735        assert_eq!(
 4736            view.selections.display_ranges(cx),
 4737            vec![
 4738                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4739                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4740            ]
 4741        );
 4742    });
 4743
 4744    _ = view.update(cx, |view, cx| {
 4745        view.select_line(&SelectLine, cx);
 4746        assert_eq!(
 4747            view.selections.display_ranges(cx),
 4748            vec![
 4749                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4750                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4751            ]
 4752        );
 4753    });
 4754
 4755    _ = view.update(cx, |view, cx| {
 4756        view.select_line(&SelectLine, cx);
 4757        assert_eq!(
 4758            view.selections.display_ranges(cx),
 4759            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4760        );
 4761    });
 4762}
 4763
 4764#[gpui::test]
 4765fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4766    init_test(cx, |_| {});
 4767
 4768    let view = cx.add_window(|cx| {
 4769        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4770        build_editor(buffer, cx)
 4771    });
 4772    _ = view.update(cx, |view, cx| {
 4773        view.fold_ranges(
 4774            vec![
 4775                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4776                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4777                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4778            ],
 4779            true,
 4780            cx,
 4781        );
 4782        view.change_selections(None, cx, |s| {
 4783            s.select_display_ranges([
 4784                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4785                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4786                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4787                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4788            ])
 4789        });
 4790        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4791    });
 4792
 4793    _ = view.update(cx, |view, cx| {
 4794        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4795        assert_eq!(
 4796            view.display_text(cx),
 4797            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4798        );
 4799        assert_eq!(
 4800            view.selections.display_ranges(cx),
 4801            [
 4802                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4803                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4804                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4805                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4806            ]
 4807        );
 4808    });
 4809
 4810    _ = view.update(cx, |view, cx| {
 4811        view.change_selections(None, cx, |s| {
 4812            s.select_display_ranges([
 4813                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4814            ])
 4815        });
 4816        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4817        assert_eq!(
 4818            view.display_text(cx),
 4819            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4820        );
 4821        assert_eq!(
 4822            view.selections.display_ranges(cx),
 4823            [
 4824                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4825                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4826                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4827                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4828                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4829                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4830                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4831                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4832            ]
 4833        );
 4834    });
 4835}
 4836
 4837#[gpui::test]
 4838async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4839    init_test(cx, |_| {});
 4840
 4841    let mut cx = EditorTestContext::new(cx).await;
 4842
 4843    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4844    cx.set_state(indoc!(
 4845        r#"abc
 4846           defˇghi
 4847
 4848           jk
 4849           nlmo
 4850           "#
 4851    ));
 4852
 4853    cx.update_editor(|editor, cx| {
 4854        editor.add_selection_above(&Default::default(), cx);
 4855    });
 4856
 4857    cx.assert_editor_state(indoc!(
 4858        r#"abcˇ
 4859           defˇghi
 4860
 4861           jk
 4862           nlmo
 4863           "#
 4864    ));
 4865
 4866    cx.update_editor(|editor, cx| {
 4867        editor.add_selection_above(&Default::default(), cx);
 4868    });
 4869
 4870    cx.assert_editor_state(indoc!(
 4871        r#"abcˇ
 4872            defˇghi
 4873
 4874            jk
 4875            nlmo
 4876            "#
 4877    ));
 4878
 4879    cx.update_editor(|view, cx| {
 4880        view.add_selection_below(&Default::default(), cx);
 4881    });
 4882
 4883    cx.assert_editor_state(indoc!(
 4884        r#"abc
 4885           defˇghi
 4886
 4887           jk
 4888           nlmo
 4889           "#
 4890    ));
 4891
 4892    cx.update_editor(|view, cx| {
 4893        view.undo_selection(&Default::default(), cx);
 4894    });
 4895
 4896    cx.assert_editor_state(indoc!(
 4897        r#"abcˇ
 4898           defˇghi
 4899
 4900           jk
 4901           nlmo
 4902           "#
 4903    ));
 4904
 4905    cx.update_editor(|view, cx| {
 4906        view.redo_selection(&Default::default(), cx);
 4907    });
 4908
 4909    cx.assert_editor_state(indoc!(
 4910        r#"abc
 4911           defˇghi
 4912
 4913           jk
 4914           nlmo
 4915           "#
 4916    ));
 4917
 4918    cx.update_editor(|view, cx| {
 4919        view.add_selection_below(&Default::default(), cx);
 4920    });
 4921
 4922    cx.assert_editor_state(indoc!(
 4923        r#"abc
 4924           defˇghi
 4925
 4926           jk
 4927           nlmˇo
 4928           "#
 4929    ));
 4930
 4931    cx.update_editor(|view, cx| {
 4932        view.add_selection_below(&Default::default(), cx);
 4933    });
 4934
 4935    cx.assert_editor_state(indoc!(
 4936        r#"abc
 4937           defˇghi
 4938
 4939           jk
 4940           nlmˇo
 4941           "#
 4942    ));
 4943
 4944    // change selections
 4945    cx.set_state(indoc!(
 4946        r#"abc
 4947           def«ˇg»hi
 4948
 4949           jk
 4950           nlmo
 4951           "#
 4952    ));
 4953
 4954    cx.update_editor(|view, cx| {
 4955        view.add_selection_below(&Default::default(), cx);
 4956    });
 4957
 4958    cx.assert_editor_state(indoc!(
 4959        r#"abc
 4960           def«ˇg»hi
 4961
 4962           jk
 4963           nlm«ˇo»
 4964           "#
 4965    ));
 4966
 4967    cx.update_editor(|view, cx| {
 4968        view.add_selection_below(&Default::default(), cx);
 4969    });
 4970
 4971    cx.assert_editor_state(indoc!(
 4972        r#"abc
 4973           def«ˇg»hi
 4974
 4975           jk
 4976           nlm«ˇo»
 4977           "#
 4978    ));
 4979
 4980    cx.update_editor(|view, cx| {
 4981        view.add_selection_above(&Default::default(), cx);
 4982    });
 4983
 4984    cx.assert_editor_state(indoc!(
 4985        r#"abc
 4986           def«ˇg»hi
 4987
 4988           jk
 4989           nlmo
 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#"abc
 4999           def«ˇg»hi
 5000
 5001           jk
 5002           nlmo
 5003           "#
 5004    ));
 5005
 5006    // Change selections again
 5007    cx.set_state(indoc!(
 5008        r#"a«bc
 5009           defgˇ»hi
 5010
 5011           jk
 5012           nlmo
 5013           "#
 5014    ));
 5015
 5016    cx.update_editor(|view, cx| {
 5017        view.add_selection_below(&Default::default(), cx);
 5018    });
 5019
 5020    cx.assert_editor_state(indoc!(
 5021        r#"a«bcˇ»
 5022           d«efgˇ»hi
 5023
 5024           j«kˇ»
 5025           nlmo
 5026           "#
 5027    ));
 5028
 5029    cx.update_editor(|view, cx| {
 5030        view.add_selection_below(&Default::default(), cx);
 5031    });
 5032    cx.assert_editor_state(indoc!(
 5033        r#"a«bcˇ»
 5034           d«efgˇ»hi
 5035
 5036           j«kˇ»
 5037           n«lmoˇ»
 5038           "#
 5039    ));
 5040    cx.update_editor(|view, cx| {
 5041        view.add_selection_above(&Default::default(), cx);
 5042    });
 5043
 5044    cx.assert_editor_state(indoc!(
 5045        r#"a«bcˇ»
 5046           d«efgˇ»hi
 5047
 5048           j«kˇ»
 5049           nlmo
 5050           "#
 5051    ));
 5052
 5053    // Change selections again
 5054    cx.set_state(indoc!(
 5055        r#"abc
 5056           d«ˇefghi
 5057
 5058           jk
 5059           nlm»o
 5060           "#
 5061    ));
 5062
 5063    cx.update_editor(|view, cx| {
 5064        view.add_selection_above(&Default::default(), cx);
 5065    });
 5066
 5067    cx.assert_editor_state(indoc!(
 5068        r#"a«ˇbc»
 5069           d«ˇef»ghi
 5070
 5071           j«ˇk»
 5072           n«ˇlm»o
 5073           "#
 5074    ));
 5075
 5076    cx.update_editor(|view, cx| {
 5077        view.add_selection_below(&Default::default(), cx);
 5078    });
 5079
 5080    cx.assert_editor_state(indoc!(
 5081        r#"abc
 5082           d«ˇef»ghi
 5083
 5084           j«ˇk»
 5085           n«ˇlm»o
 5086           "#
 5087    ));
 5088}
 5089
 5090#[gpui::test]
 5091async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5092    init_test(cx, |_| {});
 5093
 5094    let mut cx = EditorTestContext::new(cx).await;
 5095    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5096
 5097    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5098        .unwrap();
 5099    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5104
 5105    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5106    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5107
 5108    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5109    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5110
 5111    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5112        .unwrap();
 5113    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5114
 5115    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5118}
 5119
 5120#[gpui::test]
 5121async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5122    init_test(cx, |_| {});
 5123
 5124    let mut cx = EditorTestContext::new(cx).await;
 5125    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5126
 5127    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5128        .unwrap();
 5129    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5130}
 5131
 5132#[gpui::test]
 5133async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5134    init_test(cx, |_| {});
 5135
 5136    let mut cx = EditorTestContext::new(cx).await;
 5137    cx.set_state(
 5138        r#"let foo = 2;
 5139lˇet foo = 2;
 5140let fooˇ = 2;
 5141let foo = 2;
 5142let foo = ˇ2;"#,
 5143    );
 5144
 5145    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5146        .unwrap();
 5147    cx.assert_editor_state(
 5148        r#"let foo = 2;
 5149«letˇ» foo = 2;
 5150let «fooˇ» = 2;
 5151let foo = 2;
 5152let foo = «2ˇ»;"#,
 5153    );
 5154
 5155    // noop for multiple selections with different contents
 5156    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5157        .unwrap();
 5158    cx.assert_editor_state(
 5159        r#"let foo = 2;
 5160«letˇ» foo = 2;
 5161let «fooˇ» = 2;
 5162let foo = 2;
 5163let foo = «2ˇ»;"#,
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let mut cx =
 5172        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5173
 5174    cx.assert_editor_state(indoc! {"
 5175        ˇbbb
 5176        ccc
 5177
 5178        bbb
 5179        ccc
 5180        "});
 5181    cx.dispatch_action(SelectPrevious::default());
 5182    cx.assert_editor_state(indoc! {"
 5183                «bbbˇ»
 5184                ccc
 5185
 5186                bbb
 5187                ccc
 5188                "});
 5189    cx.dispatch_action(SelectPrevious::default());
 5190    cx.assert_editor_state(indoc! {"
 5191                «bbbˇ»
 5192                ccc
 5193
 5194                «bbbˇ»
 5195                ccc
 5196                "});
 5197}
 5198
 5199#[gpui::test]
 5200async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5201    init_test(cx, |_| {});
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5205
 5206    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5207        .unwrap();
 5208    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5209
 5210    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5211        .unwrap();
 5212    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5213
 5214    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5215    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5216
 5217    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5218    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5219
 5220    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5221        .unwrap();
 5222    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5223
 5224    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5225        .unwrap();
 5226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5227
 5228    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5229        .unwrap();
 5230    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5231}
 5232
 5233#[gpui::test]
 5234async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5235    init_test(cx, |_| {});
 5236
 5237    let mut cx = EditorTestContext::new(cx).await;
 5238    cx.set_state(
 5239        r#"let foo = 2;
 5240lˇet foo = 2;
 5241let fooˇ = 2;
 5242let foo = 2;
 5243let foo = ˇ2;"#,
 5244    );
 5245
 5246    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5247        .unwrap();
 5248    cx.assert_editor_state(
 5249        r#"let foo = 2;
 5250«letˇ» foo = 2;
 5251let «fooˇ» = 2;
 5252let foo = 2;
 5253let foo = «2ˇ»;"#,
 5254    );
 5255
 5256    // noop for multiple selections with different contents
 5257    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5258        .unwrap();
 5259    cx.assert_editor_state(
 5260        r#"let foo = 2;
 5261«letˇ» foo = 2;
 5262let «fooˇ» = 2;
 5263let foo = 2;
 5264let foo = «2ˇ»;"#,
 5265    );
 5266}
 5267
 5268#[gpui::test]
 5269async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5270    init_test(cx, |_| {});
 5271
 5272    let mut cx = EditorTestContext::new(cx).await;
 5273    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5274
 5275    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5276        .unwrap();
 5277    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5278
 5279    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5280        .unwrap();
 5281    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5282
 5283    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5284    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5285
 5286    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5287    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5288
 5289    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5290        .unwrap();
 5291    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5292
 5293    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5294        .unwrap();
 5295    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5296}
 5297
 5298#[gpui::test]
 5299async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5300    init_test(cx, |_| {});
 5301
 5302    let language = Arc::new(Language::new(
 5303        LanguageConfig::default(),
 5304        Some(tree_sitter_rust::LANGUAGE.into()),
 5305    ));
 5306
 5307    let text = r#"
 5308        use mod1::mod2::{mod3, mod4};
 5309
 5310        fn fn_1(param1: bool, param2: &str) {
 5311            let var1 = "text";
 5312        }
 5313    "#
 5314    .unindent();
 5315
 5316    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5317    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5318    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5319
 5320    editor
 5321        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5322        .await;
 5323
 5324    editor.update(cx, |view, cx| {
 5325        view.change_selections(None, cx, |s| {
 5326            s.select_display_ranges([
 5327                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5328                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5329                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5330            ]);
 5331        });
 5332        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5333    });
 5334    editor.update(cx, |editor, cx| {
 5335        assert_text_with_selections(
 5336            editor,
 5337            indoc! {r#"
 5338                use mod1::mod2::{mod3, «mod4ˇ»};
 5339
 5340                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5341                    let var1 = "«textˇ»";
 5342                }
 5343            "#},
 5344            cx,
 5345        );
 5346    });
 5347
 5348    editor.update(cx, |view, cx| {
 5349        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5350    });
 5351    editor.update(cx, |editor, cx| {
 5352        assert_text_with_selections(
 5353            editor,
 5354            indoc! {r#"
 5355                use mod1::mod2::«{mod3, mod4}ˇ»;
 5356
 5357                «ˇfn fn_1(param1: bool, param2: &str) {
 5358                    let var1 = "text";
 5359 5360            "#},
 5361            cx,
 5362        );
 5363    });
 5364
 5365    editor.update(cx, |view, cx| {
 5366        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5367    });
 5368    assert_eq!(
 5369        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5370        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5371    );
 5372
 5373    // Trying to expand the selected syntax node one more time has no effect.
 5374    editor.update(cx, |view, cx| {
 5375        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5376    });
 5377    assert_eq!(
 5378        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5379        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5380    );
 5381
 5382    editor.update(cx, |view, cx| {
 5383        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5384    });
 5385    editor.update(cx, |editor, cx| {
 5386        assert_text_with_selections(
 5387            editor,
 5388            indoc! {r#"
 5389                use mod1::mod2::«{mod3, mod4}ˇ»;
 5390
 5391                «ˇfn fn_1(param1: bool, param2: &str) {
 5392                    let var1 = "text";
 5393 5394            "#},
 5395            cx,
 5396        );
 5397    });
 5398
 5399    editor.update(cx, |view, cx| {
 5400        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5401    });
 5402    editor.update(cx, |editor, cx| {
 5403        assert_text_with_selections(
 5404            editor,
 5405            indoc! {r#"
 5406                use mod1::mod2::{mod3, «mod4ˇ»};
 5407
 5408                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5409                    let var1 = "«textˇ»";
 5410                }
 5411            "#},
 5412            cx,
 5413        );
 5414    });
 5415
 5416    editor.update(cx, |view, cx| {
 5417        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5418    });
 5419    editor.update(cx, |editor, cx| {
 5420        assert_text_with_selections(
 5421            editor,
 5422            indoc! {r#"
 5423                use mod1::mod2::{mod3, mo«ˇ»d4};
 5424
 5425                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5426                    let var1 = "te«ˇ»xt";
 5427                }
 5428            "#},
 5429            cx,
 5430        );
 5431    });
 5432
 5433    // Trying to shrink the selected syntax node one more time has no effect.
 5434    editor.update(cx, |view, cx| {
 5435        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5436    });
 5437    editor.update(cx, |editor, cx| {
 5438        assert_text_with_selections(
 5439            editor,
 5440            indoc! {r#"
 5441                use mod1::mod2::{mod3, mo«ˇ»d4};
 5442
 5443                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5444                    let var1 = "te«ˇ»xt";
 5445                }
 5446            "#},
 5447            cx,
 5448        );
 5449    });
 5450
 5451    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5452    // a fold.
 5453    editor.update(cx, |view, cx| {
 5454        view.fold_ranges(
 5455            vec![
 5456                (
 5457                    Point::new(0, 21)..Point::new(0, 24),
 5458                    FoldPlaceholder::test(),
 5459                ),
 5460                (
 5461                    Point::new(3, 20)..Point::new(3, 22),
 5462                    FoldPlaceholder::test(),
 5463                ),
 5464            ],
 5465            true,
 5466            cx,
 5467        );
 5468        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5469    });
 5470    editor.update(cx, |editor, cx| {
 5471        assert_text_with_selections(
 5472            editor,
 5473            indoc! {r#"
 5474                use mod1::mod2::«{mod3, mod4}ˇ»;
 5475
 5476                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5477                    «let var1 = "text";ˇ»
 5478                }
 5479            "#},
 5480            cx,
 5481        );
 5482    });
 5483}
 5484
 5485#[gpui::test]
 5486async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5487    init_test(cx, |_| {});
 5488
 5489    let language = Arc::new(
 5490        Language::new(
 5491            LanguageConfig {
 5492                brackets: BracketPairConfig {
 5493                    pairs: vec![
 5494                        BracketPair {
 5495                            start: "{".to_string(),
 5496                            end: "}".to_string(),
 5497                            close: false,
 5498                            surround: false,
 5499                            newline: true,
 5500                        },
 5501                        BracketPair {
 5502                            start: "(".to_string(),
 5503                            end: ")".to_string(),
 5504                            close: false,
 5505                            surround: false,
 5506                            newline: true,
 5507                        },
 5508                    ],
 5509                    ..Default::default()
 5510                },
 5511                ..Default::default()
 5512            },
 5513            Some(tree_sitter_rust::LANGUAGE.into()),
 5514        )
 5515        .with_indents_query(
 5516            r#"
 5517                (_ "(" ")" @end) @indent
 5518                (_ "{" "}" @end) @indent
 5519            "#,
 5520        )
 5521        .unwrap(),
 5522    );
 5523
 5524    let text = "fn a() {}";
 5525
 5526    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5527    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5528    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5529    editor
 5530        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5531        .await;
 5532
 5533    editor.update(cx, |editor, cx| {
 5534        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5535        editor.newline(&Newline, cx);
 5536        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5537        assert_eq!(
 5538            editor.selections.ranges(cx),
 5539            &[
 5540                Point::new(1, 4)..Point::new(1, 4),
 5541                Point::new(3, 4)..Point::new(3, 4),
 5542                Point::new(5, 0)..Point::new(5, 0)
 5543            ]
 5544        );
 5545    });
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    let language = Arc::new(Language::new(
 5555        LanguageConfig {
 5556            brackets: BracketPairConfig {
 5557                pairs: vec![
 5558                    BracketPair {
 5559                        start: "{".to_string(),
 5560                        end: "}".to_string(),
 5561                        close: true,
 5562                        surround: true,
 5563                        newline: true,
 5564                    },
 5565                    BracketPair {
 5566                        start: "(".to_string(),
 5567                        end: ")".to_string(),
 5568                        close: true,
 5569                        surround: true,
 5570                        newline: true,
 5571                    },
 5572                    BracketPair {
 5573                        start: "/*".to_string(),
 5574                        end: " */".to_string(),
 5575                        close: true,
 5576                        surround: true,
 5577                        newline: true,
 5578                    },
 5579                    BracketPair {
 5580                        start: "[".to_string(),
 5581                        end: "]".to_string(),
 5582                        close: false,
 5583                        surround: false,
 5584                        newline: true,
 5585                    },
 5586                    BracketPair {
 5587                        start: "\"".to_string(),
 5588                        end: "\"".to_string(),
 5589                        close: true,
 5590                        surround: true,
 5591                        newline: false,
 5592                    },
 5593                    BracketPair {
 5594                        start: "<".to_string(),
 5595                        end: ">".to_string(),
 5596                        close: false,
 5597                        surround: true,
 5598                        newline: true,
 5599                    },
 5600                ],
 5601                ..Default::default()
 5602            },
 5603            autoclose_before: "})]".to_string(),
 5604            ..Default::default()
 5605        },
 5606        Some(tree_sitter_rust::LANGUAGE.into()),
 5607    ));
 5608
 5609    cx.language_registry().add(language.clone());
 5610    cx.update_buffer(|buffer, cx| {
 5611        buffer.set_language(Some(language), cx);
 5612    });
 5613
 5614    cx.set_state(
 5615        &r#"
 5616            🏀ˇ
 5617            εˇ
 5618            ❤️ˇ
 5619        "#
 5620        .unindent(),
 5621    );
 5622
 5623    // autoclose multiple nested brackets at multiple cursors
 5624    cx.update_editor(|view, cx| {
 5625        view.handle_input("{", cx);
 5626        view.handle_input("{", cx);
 5627        view.handle_input("{", cx);
 5628    });
 5629    cx.assert_editor_state(
 5630        &"
 5631            🏀{{{ˇ}}}
 5632            ε{{{ˇ}}}
 5633            ❤️{{{ˇ}}}
 5634        "
 5635        .unindent(),
 5636    );
 5637
 5638    // insert a different closing bracket
 5639    cx.update_editor(|view, cx| {
 5640        view.handle_input(")", cx);
 5641    });
 5642    cx.assert_editor_state(
 5643        &"
 5644            🏀{{{)ˇ}}}
 5645            ε{{{)ˇ}}}
 5646            ❤️{{{)ˇ}}}
 5647        "
 5648        .unindent(),
 5649    );
 5650
 5651    // skip over the auto-closed brackets when typing a closing bracket
 5652    cx.update_editor(|view, cx| {
 5653        view.move_right(&MoveRight, cx);
 5654        view.handle_input("}", cx);
 5655        view.handle_input("}", cx);
 5656        view.handle_input("}", cx);
 5657    });
 5658    cx.assert_editor_state(
 5659        &"
 5660            🏀{{{)}}}}ˇ
 5661            ε{{{)}}}}ˇ
 5662            ❤️{{{)}}}}ˇ
 5663        "
 5664        .unindent(),
 5665    );
 5666
 5667    // autoclose multi-character pairs
 5668    cx.set_state(
 5669        &"
 5670            ˇ
 5671            ˇ
 5672        "
 5673        .unindent(),
 5674    );
 5675    cx.update_editor(|view, cx| {
 5676        view.handle_input("/", cx);
 5677        view.handle_input("*", cx);
 5678    });
 5679    cx.assert_editor_state(
 5680        &"
 5681            /*ˇ */
 5682            /*ˇ */
 5683        "
 5684        .unindent(),
 5685    );
 5686
 5687    // one cursor autocloses a multi-character pair, one cursor
 5688    // does not autoclose.
 5689    cx.set_state(
 5690        &"
 5691 5692            ˇ
 5693        "
 5694        .unindent(),
 5695    );
 5696    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5697    cx.assert_editor_state(
 5698        &"
 5699            /*ˇ */
 5700 5701        "
 5702        .unindent(),
 5703    );
 5704
 5705    // Don't autoclose if the next character isn't whitespace and isn't
 5706    // listed in the language's "autoclose_before" section.
 5707    cx.set_state("ˇa b");
 5708    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5709    cx.assert_editor_state("{ˇa b");
 5710
 5711    // Don't autoclose if `close` is false for the bracket pair
 5712    cx.set_state("ˇ");
 5713    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5714    cx.assert_editor_state("");
 5715
 5716    // Surround with brackets if text is selected
 5717    cx.set_state("«aˇ» b");
 5718    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5719    cx.assert_editor_state("{«aˇ»} b");
 5720
 5721    // Autclose pair where the start and end characters are the same
 5722    cx.set_state("");
 5723    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5724    cx.assert_editor_state("a\"ˇ\"");
 5725    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5726    cx.assert_editor_state("a\"\"ˇ");
 5727
 5728    // Don't autoclose pair if autoclose is disabled
 5729    cx.set_state("ˇ");
 5730    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5731    cx.assert_editor_state("");
 5732
 5733    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5734    cx.set_state("«aˇ» b");
 5735    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5736    cx.assert_editor_state("<«aˇ»> b");
 5737}
 5738
 5739#[gpui::test]
 5740async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5741    init_test(cx, |settings| {
 5742        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5743    });
 5744
 5745    let mut cx = EditorTestContext::new(cx).await;
 5746
 5747    let language = Arc::new(Language::new(
 5748        LanguageConfig {
 5749            brackets: BracketPairConfig {
 5750                pairs: vec![
 5751                    BracketPair {
 5752                        start: "{".to_string(),
 5753                        end: "}".to_string(),
 5754                        close: true,
 5755                        surround: true,
 5756                        newline: true,
 5757                    },
 5758                    BracketPair {
 5759                        start: "(".to_string(),
 5760                        end: ")".to_string(),
 5761                        close: true,
 5762                        surround: true,
 5763                        newline: true,
 5764                    },
 5765                    BracketPair {
 5766                        start: "[".to_string(),
 5767                        end: "]".to_string(),
 5768                        close: false,
 5769                        surround: false,
 5770                        newline: true,
 5771                    },
 5772                ],
 5773                ..Default::default()
 5774            },
 5775            autoclose_before: "})]".to_string(),
 5776            ..Default::default()
 5777        },
 5778        Some(tree_sitter_rust::LANGUAGE.into()),
 5779    ));
 5780
 5781    cx.language_registry().add(language.clone());
 5782    cx.update_buffer(|buffer, cx| {
 5783        buffer.set_language(Some(language), cx);
 5784    });
 5785
 5786    cx.set_state(
 5787        &"
 5788            ˇ
 5789            ˇ
 5790            ˇ
 5791        "
 5792        .unindent(),
 5793    );
 5794
 5795    // ensure only matching closing brackets are skipped over
 5796    cx.update_editor(|view, cx| {
 5797        view.handle_input("}", cx);
 5798        view.move_left(&MoveLeft, cx);
 5799        view.handle_input(")", cx);
 5800        view.move_left(&MoveLeft, cx);
 5801    });
 5802    cx.assert_editor_state(
 5803        &"
 5804            ˇ)}
 5805            ˇ)}
 5806            ˇ)}
 5807        "
 5808        .unindent(),
 5809    );
 5810
 5811    // skip-over closing brackets at multiple cursors
 5812    cx.update_editor(|view, cx| {
 5813        view.handle_input(")", cx);
 5814        view.handle_input("}", cx);
 5815    });
 5816    cx.assert_editor_state(
 5817        &"
 5818            )}ˇ
 5819            )}ˇ
 5820            )}ˇ
 5821        "
 5822        .unindent(),
 5823    );
 5824
 5825    // ignore non-close brackets
 5826    cx.update_editor(|view, cx| {
 5827        view.handle_input("]", cx);
 5828        view.move_left(&MoveLeft, cx);
 5829        view.handle_input("]", cx);
 5830    });
 5831    cx.assert_editor_state(
 5832        &"
 5833            )}]ˇ]
 5834            )}]ˇ]
 5835            )}]ˇ]
 5836        "
 5837        .unindent(),
 5838    );
 5839}
 5840
 5841#[gpui::test]
 5842async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5843    init_test(cx, |_| {});
 5844
 5845    let mut cx = EditorTestContext::new(cx).await;
 5846
 5847    let html_language = Arc::new(
 5848        Language::new(
 5849            LanguageConfig {
 5850                name: "HTML".into(),
 5851                brackets: BracketPairConfig {
 5852                    pairs: vec![
 5853                        BracketPair {
 5854                            start: "<".into(),
 5855                            end: ">".into(),
 5856                            close: true,
 5857                            ..Default::default()
 5858                        },
 5859                        BracketPair {
 5860                            start: "{".into(),
 5861                            end: "}".into(),
 5862                            close: true,
 5863                            ..Default::default()
 5864                        },
 5865                        BracketPair {
 5866                            start: "(".into(),
 5867                            end: ")".into(),
 5868                            close: true,
 5869                            ..Default::default()
 5870                        },
 5871                    ],
 5872                    ..Default::default()
 5873                },
 5874                autoclose_before: "})]>".into(),
 5875                ..Default::default()
 5876            },
 5877            Some(tree_sitter_html::language()),
 5878        )
 5879        .with_injection_query(
 5880            r#"
 5881            (script_element
 5882                (raw_text) @content
 5883                (#set! "language" "javascript"))
 5884            "#,
 5885        )
 5886        .unwrap(),
 5887    );
 5888
 5889    let javascript_language = Arc::new(Language::new(
 5890        LanguageConfig {
 5891            name: "JavaScript".into(),
 5892            brackets: BracketPairConfig {
 5893                pairs: vec![
 5894                    BracketPair {
 5895                        start: "/*".into(),
 5896                        end: " */".into(),
 5897                        close: true,
 5898                        ..Default::default()
 5899                    },
 5900                    BracketPair {
 5901                        start: "{".into(),
 5902                        end: "}".into(),
 5903                        close: true,
 5904                        ..Default::default()
 5905                    },
 5906                    BracketPair {
 5907                        start: "(".into(),
 5908                        end: ")".into(),
 5909                        close: true,
 5910                        ..Default::default()
 5911                    },
 5912                ],
 5913                ..Default::default()
 5914            },
 5915            autoclose_before: "})]>".into(),
 5916            ..Default::default()
 5917        },
 5918        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5919    ));
 5920
 5921    cx.language_registry().add(html_language.clone());
 5922    cx.language_registry().add(javascript_language.clone());
 5923
 5924    cx.update_buffer(|buffer, cx| {
 5925        buffer.set_language(Some(html_language), cx);
 5926    });
 5927
 5928    cx.set_state(
 5929        &r#"
 5930            <body>ˇ
 5931                <script>
 5932                    var x = 1;ˇ
 5933                </script>
 5934            </body>ˇ
 5935        "#
 5936        .unindent(),
 5937    );
 5938
 5939    // Precondition: different languages are active at different locations.
 5940    cx.update_editor(|editor, cx| {
 5941        let snapshot = editor.snapshot(cx);
 5942        let cursors = editor.selections.ranges::<usize>(cx);
 5943        let languages = cursors
 5944            .iter()
 5945            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5946            .collect::<Vec<_>>();
 5947        assert_eq!(
 5948            languages,
 5949            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5950        );
 5951    });
 5952
 5953    // Angle brackets autoclose in HTML, but not JavaScript.
 5954    cx.update_editor(|editor, cx| {
 5955        editor.handle_input("<", cx);
 5956        editor.handle_input("a", cx);
 5957    });
 5958    cx.assert_editor_state(
 5959        &r#"
 5960            <body><aˇ>
 5961                <script>
 5962                    var x = 1;<aˇ
 5963                </script>
 5964            </body><aˇ>
 5965        "#
 5966        .unindent(),
 5967    );
 5968
 5969    // Curly braces and parens autoclose in both HTML and JavaScript.
 5970    cx.update_editor(|editor, cx| {
 5971        editor.handle_input(" b=", cx);
 5972        editor.handle_input("{", cx);
 5973        editor.handle_input("c", cx);
 5974        editor.handle_input("(", cx);
 5975    });
 5976    cx.assert_editor_state(
 5977        &r#"
 5978            <body><a b={c(ˇ)}>
 5979                <script>
 5980                    var x = 1;<a b={c(ˇ)}
 5981                </script>
 5982            </body><a b={c(ˇ)}>
 5983        "#
 5984        .unindent(),
 5985    );
 5986
 5987    // Brackets that were already autoclosed are skipped.
 5988    cx.update_editor(|editor, cx| {
 5989        editor.handle_input(")", cx);
 5990        editor.handle_input("d", cx);
 5991        editor.handle_input("}", cx);
 5992    });
 5993    cx.assert_editor_state(
 5994        &r#"
 5995            <body><a b={c()d}ˇ>
 5996                <script>
 5997                    var x = 1;<a b={c()d}ˇ
 5998                </script>
 5999            </body><a b={c()d}ˇ>
 6000        "#
 6001        .unindent(),
 6002    );
 6003    cx.update_editor(|editor, cx| {
 6004        editor.handle_input(">", cx);
 6005    });
 6006    cx.assert_editor_state(
 6007        &r#"
 6008            <body><a b={c()d}>ˇ
 6009                <script>
 6010                    var x = 1;<a b={c()d}>ˇ
 6011                </script>
 6012            </body><a b={c()d}>ˇ
 6013        "#
 6014        .unindent(),
 6015    );
 6016
 6017    // Reset
 6018    cx.set_state(
 6019        &r#"
 6020            <body>ˇ
 6021                <script>
 6022                    var x = 1;ˇ
 6023                </script>
 6024            </body>ˇ
 6025        "#
 6026        .unindent(),
 6027    );
 6028
 6029    cx.update_editor(|editor, cx| {
 6030        editor.handle_input("<", cx);
 6031    });
 6032    cx.assert_editor_state(
 6033        &r#"
 6034            <body><ˇ>
 6035                <script>
 6036                    var x = 1;<ˇ
 6037                </script>
 6038            </body><ˇ>
 6039        "#
 6040        .unindent(),
 6041    );
 6042
 6043    // When backspacing, the closing angle brackets are removed.
 6044    cx.update_editor(|editor, cx| {
 6045        editor.backspace(&Backspace, cx);
 6046    });
 6047    cx.assert_editor_state(
 6048        &r#"
 6049            <body>ˇ
 6050                <script>
 6051                    var x = 1;ˇ
 6052                </script>
 6053            </body>ˇ
 6054        "#
 6055        .unindent(),
 6056    );
 6057
 6058    // Block comments autoclose in JavaScript, but not HTML.
 6059    cx.update_editor(|editor, cx| {
 6060        editor.handle_input("/", cx);
 6061        editor.handle_input("*", cx);
 6062    });
 6063    cx.assert_editor_state(
 6064        &r#"
 6065            <body>/*ˇ
 6066                <script>
 6067                    var x = 1;/*ˇ */
 6068                </script>
 6069            </body>/*ˇ
 6070        "#
 6071        .unindent(),
 6072    );
 6073}
 6074
 6075#[gpui::test]
 6076async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6077    init_test(cx, |_| {});
 6078
 6079    let mut cx = EditorTestContext::new(cx).await;
 6080
 6081    let rust_language = Arc::new(
 6082        Language::new(
 6083            LanguageConfig {
 6084                name: "Rust".into(),
 6085                brackets: serde_json::from_value(json!([
 6086                    { "start": "{", "end": "}", "close": true, "newline": true },
 6087                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6088                ]))
 6089                .unwrap(),
 6090                autoclose_before: "})]>".into(),
 6091                ..Default::default()
 6092            },
 6093            Some(tree_sitter_rust::LANGUAGE.into()),
 6094        )
 6095        .with_override_query("(string_literal) @string")
 6096        .unwrap(),
 6097    );
 6098
 6099    cx.language_registry().add(rust_language.clone());
 6100    cx.update_buffer(|buffer, cx| {
 6101        buffer.set_language(Some(rust_language), cx);
 6102    });
 6103
 6104    cx.set_state(
 6105        &r#"
 6106            let x = ˇ
 6107        "#
 6108        .unindent(),
 6109    );
 6110
 6111    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6112    cx.update_editor(|editor, cx| {
 6113        editor.handle_input("\"", cx);
 6114    });
 6115    cx.assert_editor_state(
 6116        &r#"
 6117            let x = "ˇ"
 6118        "#
 6119        .unindent(),
 6120    );
 6121
 6122    // Inserting another quotation mark. The cursor moves across the existing
 6123    // automatically-inserted quotation mark.
 6124    cx.update_editor(|editor, cx| {
 6125        editor.handle_input("\"", cx);
 6126    });
 6127    cx.assert_editor_state(
 6128        &r#"
 6129            let x = ""ˇ
 6130        "#
 6131        .unindent(),
 6132    );
 6133
 6134    // Reset
 6135    cx.set_state(
 6136        &r#"
 6137            let x = ˇ
 6138        "#
 6139        .unindent(),
 6140    );
 6141
 6142    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6143    cx.update_editor(|editor, cx| {
 6144        editor.handle_input("\"", cx);
 6145        editor.handle_input(" ", cx);
 6146        editor.move_left(&Default::default(), cx);
 6147        editor.handle_input("\\", cx);
 6148        editor.handle_input("\"", cx);
 6149    });
 6150    cx.assert_editor_state(
 6151        &r#"
 6152            let x = "\"ˇ "
 6153        "#
 6154        .unindent(),
 6155    );
 6156
 6157    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6158    // mark. Nothing is inserted.
 6159    cx.update_editor(|editor, cx| {
 6160        editor.move_right(&Default::default(), cx);
 6161        editor.handle_input("\"", cx);
 6162    });
 6163    cx.assert_editor_state(
 6164        &r#"
 6165            let x = "\" "ˇ
 6166        "#
 6167        .unindent(),
 6168    );
 6169}
 6170
 6171#[gpui::test]
 6172async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6173    init_test(cx, |_| {});
 6174
 6175    let language = Arc::new(Language::new(
 6176        LanguageConfig {
 6177            brackets: BracketPairConfig {
 6178                pairs: vec![
 6179                    BracketPair {
 6180                        start: "{".to_string(),
 6181                        end: "}".to_string(),
 6182                        close: true,
 6183                        surround: true,
 6184                        newline: true,
 6185                    },
 6186                    BracketPair {
 6187                        start: "/* ".to_string(),
 6188                        end: "*/".to_string(),
 6189                        close: true,
 6190                        surround: true,
 6191                        ..Default::default()
 6192                    },
 6193                ],
 6194                ..Default::default()
 6195            },
 6196            ..Default::default()
 6197        },
 6198        Some(tree_sitter_rust::LANGUAGE.into()),
 6199    ));
 6200
 6201    let text = r#"
 6202        a
 6203        b
 6204        c
 6205    "#
 6206    .unindent();
 6207
 6208    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6209    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6210    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6211    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6212        .await;
 6213
 6214    view.update(cx, |view, cx| {
 6215        view.change_selections(None, cx, |s| {
 6216            s.select_display_ranges([
 6217                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6218                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6219                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6220            ])
 6221        });
 6222
 6223        view.handle_input("{", cx);
 6224        view.handle_input("{", cx);
 6225        view.handle_input("{", cx);
 6226        assert_eq!(
 6227            view.text(cx),
 6228            "
 6229                {{{a}}}
 6230                {{{b}}}
 6231                {{{c}}}
 6232            "
 6233            .unindent()
 6234        );
 6235        assert_eq!(
 6236            view.selections.display_ranges(cx),
 6237            [
 6238                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6239                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6240                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6241            ]
 6242        );
 6243
 6244        view.undo(&Undo, cx);
 6245        view.undo(&Undo, cx);
 6246        view.undo(&Undo, cx);
 6247        assert_eq!(
 6248            view.text(cx),
 6249            "
 6250                a
 6251                b
 6252                c
 6253            "
 6254            .unindent()
 6255        );
 6256        assert_eq!(
 6257            view.selections.display_ranges(cx),
 6258            [
 6259                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6260                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6261                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6262            ]
 6263        );
 6264
 6265        // Ensure inserting the first character of a multi-byte bracket pair
 6266        // doesn't surround the selections with the bracket.
 6267        view.handle_input("/", cx);
 6268        assert_eq!(
 6269            view.text(cx),
 6270            "
 6271                /
 6272                /
 6273                /
 6274            "
 6275            .unindent()
 6276        );
 6277        assert_eq!(
 6278            view.selections.display_ranges(cx),
 6279            [
 6280                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6281                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6282                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6283            ]
 6284        );
 6285
 6286        view.undo(&Undo, cx);
 6287        assert_eq!(
 6288            view.text(cx),
 6289            "
 6290                a
 6291                b
 6292                c
 6293            "
 6294            .unindent()
 6295        );
 6296        assert_eq!(
 6297            view.selections.display_ranges(cx),
 6298            [
 6299                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6300                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6301                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6302            ]
 6303        );
 6304
 6305        // Ensure inserting the last character of a multi-byte bracket pair
 6306        // doesn't surround the selections with the bracket.
 6307        view.handle_input("*", cx);
 6308        assert_eq!(
 6309            view.text(cx),
 6310            "
 6311                *
 6312                *
 6313                *
 6314            "
 6315            .unindent()
 6316        );
 6317        assert_eq!(
 6318            view.selections.display_ranges(cx),
 6319            [
 6320                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6321                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6322                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6323            ]
 6324        );
 6325    });
 6326}
 6327
 6328#[gpui::test]
 6329async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6330    init_test(cx, |_| {});
 6331
 6332    let language = Arc::new(Language::new(
 6333        LanguageConfig {
 6334            brackets: BracketPairConfig {
 6335                pairs: vec![BracketPair {
 6336                    start: "{".to_string(),
 6337                    end: "}".to_string(),
 6338                    close: true,
 6339                    surround: true,
 6340                    newline: true,
 6341                }],
 6342                ..Default::default()
 6343            },
 6344            autoclose_before: "}".to_string(),
 6345            ..Default::default()
 6346        },
 6347        Some(tree_sitter_rust::LANGUAGE.into()),
 6348    ));
 6349
 6350    let text = r#"
 6351        a
 6352        b
 6353        c
 6354    "#
 6355    .unindent();
 6356
 6357    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6358    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6359    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6360    editor
 6361        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6362        .await;
 6363
 6364    editor.update(cx, |editor, cx| {
 6365        editor.change_selections(None, cx, |s| {
 6366            s.select_ranges([
 6367                Point::new(0, 1)..Point::new(0, 1),
 6368                Point::new(1, 1)..Point::new(1, 1),
 6369                Point::new(2, 1)..Point::new(2, 1),
 6370            ])
 6371        });
 6372
 6373        editor.handle_input("{", cx);
 6374        editor.handle_input("{", cx);
 6375        editor.handle_input("_", cx);
 6376        assert_eq!(
 6377            editor.text(cx),
 6378            "
 6379                a{{_}}
 6380                b{{_}}
 6381                c{{_}}
 6382            "
 6383            .unindent()
 6384        );
 6385        assert_eq!(
 6386            editor.selections.ranges::<Point>(cx),
 6387            [
 6388                Point::new(0, 4)..Point::new(0, 4),
 6389                Point::new(1, 4)..Point::new(1, 4),
 6390                Point::new(2, 4)..Point::new(2, 4)
 6391            ]
 6392        );
 6393
 6394        editor.backspace(&Default::default(), cx);
 6395        editor.backspace(&Default::default(), cx);
 6396        assert_eq!(
 6397            editor.text(cx),
 6398            "
 6399                a{}
 6400                b{}
 6401                c{}
 6402            "
 6403            .unindent()
 6404        );
 6405        assert_eq!(
 6406            editor.selections.ranges::<Point>(cx),
 6407            [
 6408                Point::new(0, 2)..Point::new(0, 2),
 6409                Point::new(1, 2)..Point::new(1, 2),
 6410                Point::new(2, 2)..Point::new(2, 2)
 6411            ]
 6412        );
 6413
 6414        editor.delete_to_previous_word_start(&Default::default(), cx);
 6415        assert_eq!(
 6416            editor.text(cx),
 6417            "
 6418                a
 6419                b
 6420                c
 6421            "
 6422            .unindent()
 6423        );
 6424        assert_eq!(
 6425            editor.selections.ranges::<Point>(cx),
 6426            [
 6427                Point::new(0, 1)..Point::new(0, 1),
 6428                Point::new(1, 1)..Point::new(1, 1),
 6429                Point::new(2, 1)..Point::new(2, 1)
 6430            ]
 6431        );
 6432    });
 6433}
 6434
 6435#[gpui::test]
 6436async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6437    init_test(cx, |settings| {
 6438        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6439    });
 6440
 6441    let mut cx = EditorTestContext::new(cx).await;
 6442
 6443    let language = Arc::new(Language::new(
 6444        LanguageConfig {
 6445            brackets: BracketPairConfig {
 6446                pairs: vec![
 6447                    BracketPair {
 6448                        start: "{".to_string(),
 6449                        end: "}".to_string(),
 6450                        close: true,
 6451                        surround: true,
 6452                        newline: true,
 6453                    },
 6454                    BracketPair {
 6455                        start: "(".to_string(),
 6456                        end: ")".to_string(),
 6457                        close: true,
 6458                        surround: true,
 6459                        newline: true,
 6460                    },
 6461                    BracketPair {
 6462                        start: "[".to_string(),
 6463                        end: "]".to_string(),
 6464                        close: false,
 6465                        surround: true,
 6466                        newline: true,
 6467                    },
 6468                ],
 6469                ..Default::default()
 6470            },
 6471            autoclose_before: "})]".to_string(),
 6472            ..Default::default()
 6473        },
 6474        Some(tree_sitter_rust::LANGUAGE.into()),
 6475    ));
 6476
 6477    cx.language_registry().add(language.clone());
 6478    cx.update_buffer(|buffer, cx| {
 6479        buffer.set_language(Some(language), cx);
 6480    });
 6481
 6482    cx.set_state(
 6483        &"
 6484            {(ˇ)}
 6485            [[ˇ]]
 6486            {(ˇ)}
 6487        "
 6488        .unindent(),
 6489    );
 6490
 6491    cx.update_editor(|view, cx| {
 6492        view.backspace(&Default::default(), cx);
 6493        view.backspace(&Default::default(), cx);
 6494    });
 6495
 6496    cx.assert_editor_state(
 6497        &"
 6498            ˇ
 6499            ˇ]]
 6500            ˇ
 6501        "
 6502        .unindent(),
 6503    );
 6504
 6505    cx.update_editor(|view, cx| {
 6506        view.handle_input("{", cx);
 6507        view.handle_input("{", cx);
 6508        view.move_right(&MoveRight, cx);
 6509        view.move_right(&MoveRight, cx);
 6510        view.move_left(&MoveLeft, cx);
 6511        view.move_left(&MoveLeft, cx);
 6512        view.backspace(&Default::default(), cx);
 6513    });
 6514
 6515    cx.assert_editor_state(
 6516        &"
 6517            {ˇ}
 6518            {ˇ}]]
 6519            {ˇ}
 6520        "
 6521        .unindent(),
 6522    );
 6523
 6524    cx.update_editor(|view, cx| {
 6525        view.backspace(&Default::default(), cx);
 6526    });
 6527
 6528    cx.assert_editor_state(
 6529        &"
 6530            ˇ
 6531            ˇ]]
 6532            ˇ
 6533        "
 6534        .unindent(),
 6535    );
 6536}
 6537
 6538#[gpui::test]
 6539async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6540    init_test(cx, |_| {});
 6541
 6542    let language = Arc::new(Language::new(
 6543        LanguageConfig::default(),
 6544        Some(tree_sitter_rust::LANGUAGE.into()),
 6545    ));
 6546
 6547    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6548    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6549    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6550    editor
 6551        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6552        .await;
 6553
 6554    editor.update(cx, |editor, cx| {
 6555        editor.set_auto_replace_emoji_shortcode(true);
 6556
 6557        editor.handle_input("Hello ", cx);
 6558        editor.handle_input(":wave", cx);
 6559        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6560
 6561        editor.handle_input(":", cx);
 6562        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6563
 6564        editor.handle_input(" :smile", cx);
 6565        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6566
 6567        editor.handle_input(":", cx);
 6568        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6569
 6570        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6571        editor.handle_input(":wave", cx);
 6572        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6573
 6574        editor.handle_input(":", cx);
 6575        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6576
 6577        editor.handle_input(":1", cx);
 6578        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6579
 6580        editor.handle_input(":", cx);
 6581        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6582
 6583        // Ensure shortcode does not get replaced when it is part of a word
 6584        editor.handle_input(" Test:wave", cx);
 6585        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6586
 6587        editor.handle_input(":", cx);
 6588        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6589
 6590        editor.set_auto_replace_emoji_shortcode(false);
 6591
 6592        // Ensure shortcode does not get replaced when auto replace is off
 6593        editor.handle_input(" :wave", cx);
 6594        assert_eq!(
 6595            editor.text(cx),
 6596            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6597        );
 6598
 6599        editor.handle_input(":", cx);
 6600        assert_eq!(
 6601            editor.text(cx),
 6602            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6603        );
 6604    });
 6605}
 6606
 6607#[gpui::test]
 6608async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6609    init_test(cx, |_| {});
 6610
 6611    let (text, insertion_ranges) = marked_text_ranges(
 6612        indoc! {"
 6613            a.ˇ b
 6614            a.ˇ b
 6615            a.ˇ b
 6616        "},
 6617        false,
 6618    );
 6619
 6620    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6621    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6622
 6623    editor.update(cx, |editor, cx| {
 6624        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6625
 6626        editor
 6627            .insert_snippet(&insertion_ranges, snippet, cx)
 6628            .unwrap();
 6629
 6630        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6631            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6632            assert_eq!(editor.text(cx), expected_text);
 6633            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6634        }
 6635
 6636        assert(
 6637            editor,
 6638            cx,
 6639            indoc! {"
 6640                a.f(«one», two, «three») b
 6641                a.f(«one», two, «three») b
 6642                a.f(«one», two, «three») b
 6643            "},
 6644        );
 6645
 6646        // Can't move earlier than the first tab stop
 6647        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6648        assert(
 6649            editor,
 6650            cx,
 6651            indoc! {"
 6652                a.f(«one», two, «three») b
 6653                a.f(«one», two, «three») b
 6654                a.f(«one», two, «three») b
 6655            "},
 6656        );
 6657
 6658        assert!(editor.move_to_next_snippet_tabstop(cx));
 6659        assert(
 6660            editor,
 6661            cx,
 6662            indoc! {"
 6663                a.f(one, «two», three) b
 6664                a.f(one, «two», three) b
 6665                a.f(one, «two», three) b
 6666            "},
 6667        );
 6668
 6669        editor.move_to_prev_snippet_tabstop(cx);
 6670        assert(
 6671            editor,
 6672            cx,
 6673            indoc! {"
 6674                a.f(«one», two, «three») b
 6675                a.f(«one», two, «three») b
 6676                a.f(«one», two, «three») b
 6677            "},
 6678        );
 6679
 6680        assert!(editor.move_to_next_snippet_tabstop(cx));
 6681        assert(
 6682            editor,
 6683            cx,
 6684            indoc! {"
 6685                a.f(one, «two», three) b
 6686                a.f(one, «two», three) b
 6687                a.f(one, «two», three) b
 6688            "},
 6689        );
 6690        assert!(editor.move_to_next_snippet_tabstop(cx));
 6691        assert(
 6692            editor,
 6693            cx,
 6694            indoc! {"
 6695                a.f(one, two, three)ˇ b
 6696                a.f(one, two, three)ˇ b
 6697                a.f(one, two, three)ˇ b
 6698            "},
 6699        );
 6700
 6701        // As soon as the last tab stop is reached, snippet state is gone
 6702        editor.move_to_prev_snippet_tabstop(cx);
 6703        assert(
 6704            editor,
 6705            cx,
 6706            indoc! {"
 6707                a.f(one, two, three)ˇ b
 6708                a.f(one, two, three)ˇ b
 6709                a.f(one, two, three)ˇ b
 6710            "},
 6711        );
 6712    });
 6713}
 6714
 6715#[gpui::test]
 6716async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6717    init_test(cx, |_| {});
 6718
 6719    let fs = FakeFs::new(cx.executor());
 6720    fs.insert_file("/file.rs", Default::default()).await;
 6721
 6722    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6723
 6724    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6725    language_registry.add(rust_lang());
 6726    let mut fake_servers = language_registry.register_fake_lsp(
 6727        "Rust",
 6728        FakeLspAdapter {
 6729            capabilities: lsp::ServerCapabilities {
 6730                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6731                ..Default::default()
 6732            },
 6733            ..Default::default()
 6734        },
 6735    );
 6736
 6737    let buffer = project
 6738        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6739        .await
 6740        .unwrap();
 6741
 6742    cx.executor().start_waiting();
 6743    let fake_server = fake_servers.next().await.unwrap();
 6744
 6745    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6746    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6747    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6748    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6749
 6750    let save = editor
 6751        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6752        .unwrap();
 6753    fake_server
 6754        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6755            assert_eq!(
 6756                params.text_document.uri,
 6757                lsp::Url::from_file_path("/file.rs").unwrap()
 6758            );
 6759            assert_eq!(params.options.tab_size, 4);
 6760            Ok(Some(vec![lsp::TextEdit::new(
 6761                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6762                ", ".to_string(),
 6763            )]))
 6764        })
 6765        .next()
 6766        .await;
 6767    cx.executor().start_waiting();
 6768    save.await;
 6769
 6770    assert_eq!(
 6771        editor.update(cx, |editor, cx| editor.text(cx)),
 6772        "one, two\nthree\n"
 6773    );
 6774    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6775
 6776    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6777    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6778
 6779    // Ensure we can still save even if formatting hangs.
 6780    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6781        assert_eq!(
 6782            params.text_document.uri,
 6783            lsp::Url::from_file_path("/file.rs").unwrap()
 6784        );
 6785        futures::future::pending::<()>().await;
 6786        unreachable!()
 6787    });
 6788    let save = editor
 6789        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6790        .unwrap();
 6791    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6792    cx.executor().start_waiting();
 6793    save.await;
 6794    assert_eq!(
 6795        editor.update(cx, |editor, cx| editor.text(cx)),
 6796        "one\ntwo\nthree\n"
 6797    );
 6798    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6799
 6800    // For non-dirty buffer, no formatting request should be sent
 6801    let save = editor
 6802        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6803        .unwrap();
 6804    let _pending_format_request = fake_server
 6805        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6806            panic!("Should not be invoked on non-dirty buffer");
 6807        })
 6808        .next();
 6809    cx.executor().start_waiting();
 6810    save.await;
 6811
 6812    // Set rust language override and assert overridden tabsize is sent to language server
 6813    update_test_language_settings(cx, |settings| {
 6814        settings.languages.insert(
 6815            "Rust".into(),
 6816            LanguageSettingsContent {
 6817                tab_size: NonZeroU32::new(8),
 6818                ..Default::default()
 6819            },
 6820        );
 6821    });
 6822
 6823    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6824    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6825    let save = editor
 6826        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6827        .unwrap();
 6828    fake_server
 6829        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6830            assert_eq!(
 6831                params.text_document.uri,
 6832                lsp::Url::from_file_path("/file.rs").unwrap()
 6833            );
 6834            assert_eq!(params.options.tab_size, 8);
 6835            Ok(Some(vec![]))
 6836        })
 6837        .next()
 6838        .await;
 6839    cx.executor().start_waiting();
 6840    save.await;
 6841}
 6842
 6843#[gpui::test]
 6844async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6845    init_test(cx, |_| {});
 6846
 6847    let cols = 4;
 6848    let rows = 10;
 6849    let sample_text_1 = sample_text(rows, cols, 'a');
 6850    assert_eq!(
 6851        sample_text_1,
 6852        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6853    );
 6854    let sample_text_2 = sample_text(rows, cols, 'l');
 6855    assert_eq!(
 6856        sample_text_2,
 6857        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6858    );
 6859    let sample_text_3 = sample_text(rows, cols, 'v');
 6860    assert_eq!(
 6861        sample_text_3,
 6862        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6863    );
 6864
 6865    let fs = FakeFs::new(cx.executor());
 6866    fs.insert_tree(
 6867        "/a",
 6868        json!({
 6869            "main.rs": sample_text_1,
 6870            "other.rs": sample_text_2,
 6871            "lib.rs": sample_text_3,
 6872        }),
 6873    )
 6874    .await;
 6875
 6876    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6877    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6878    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6879
 6880    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6881    language_registry.add(rust_lang());
 6882    let mut fake_servers = language_registry.register_fake_lsp(
 6883        "Rust",
 6884        FakeLspAdapter {
 6885            capabilities: lsp::ServerCapabilities {
 6886                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6887                ..Default::default()
 6888            },
 6889            ..Default::default()
 6890        },
 6891    );
 6892
 6893    let worktree = project.update(cx, |project, cx| {
 6894        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6895        assert_eq!(worktrees.len(), 1);
 6896        worktrees.pop().unwrap()
 6897    });
 6898    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6899
 6900    let buffer_1 = project
 6901        .update(cx, |project, cx| {
 6902            project.open_buffer((worktree_id, "main.rs"), cx)
 6903        })
 6904        .await
 6905        .unwrap();
 6906    let buffer_2 = project
 6907        .update(cx, |project, cx| {
 6908            project.open_buffer((worktree_id, "other.rs"), cx)
 6909        })
 6910        .await
 6911        .unwrap();
 6912    let buffer_3 = project
 6913        .update(cx, |project, cx| {
 6914            project.open_buffer((worktree_id, "lib.rs"), cx)
 6915        })
 6916        .await
 6917        .unwrap();
 6918
 6919    let multi_buffer = cx.new_model(|cx| {
 6920        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6921        multi_buffer.push_excerpts(
 6922            buffer_1.clone(),
 6923            [
 6924                ExcerptRange {
 6925                    context: Point::new(0, 0)..Point::new(3, 0),
 6926                    primary: None,
 6927                },
 6928                ExcerptRange {
 6929                    context: Point::new(5, 0)..Point::new(7, 0),
 6930                    primary: None,
 6931                },
 6932                ExcerptRange {
 6933                    context: Point::new(9, 0)..Point::new(10, 4),
 6934                    primary: None,
 6935                },
 6936            ],
 6937            cx,
 6938        );
 6939        multi_buffer.push_excerpts(
 6940            buffer_2.clone(),
 6941            [
 6942                ExcerptRange {
 6943                    context: Point::new(0, 0)..Point::new(3, 0),
 6944                    primary: None,
 6945                },
 6946                ExcerptRange {
 6947                    context: Point::new(5, 0)..Point::new(7, 0),
 6948                    primary: None,
 6949                },
 6950                ExcerptRange {
 6951                    context: Point::new(9, 0)..Point::new(10, 4),
 6952                    primary: None,
 6953                },
 6954            ],
 6955            cx,
 6956        );
 6957        multi_buffer.push_excerpts(
 6958            buffer_3.clone(),
 6959            [
 6960                ExcerptRange {
 6961                    context: Point::new(0, 0)..Point::new(3, 0),
 6962                    primary: None,
 6963                },
 6964                ExcerptRange {
 6965                    context: Point::new(5, 0)..Point::new(7, 0),
 6966                    primary: None,
 6967                },
 6968                ExcerptRange {
 6969                    context: Point::new(9, 0)..Point::new(10, 4),
 6970                    primary: None,
 6971                },
 6972            ],
 6973            cx,
 6974        );
 6975        multi_buffer
 6976    });
 6977    let multi_buffer_editor = cx.new_view(|cx| {
 6978        Editor::new(
 6979            EditorMode::Full,
 6980            multi_buffer,
 6981            Some(project.clone()),
 6982            true,
 6983            cx,
 6984        )
 6985    });
 6986
 6987    multi_buffer_editor.update(cx, |editor, cx| {
 6988        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6989        editor.insert("|one|two|three|", cx);
 6990    });
 6991    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6992    multi_buffer_editor.update(cx, |editor, cx| {
 6993        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6994            s.select_ranges(Some(60..70))
 6995        });
 6996        editor.insert("|four|five|six|", cx);
 6997    });
 6998    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6999
 7000    // First two buffers should be edited, but not the third one.
 7001    assert_eq!(
 7002        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7003        "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}",
 7004    );
 7005    buffer_1.update(cx, |buffer, _| {
 7006        assert!(buffer.is_dirty());
 7007        assert_eq!(
 7008            buffer.text(),
 7009            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7010        )
 7011    });
 7012    buffer_2.update(cx, |buffer, _| {
 7013        assert!(buffer.is_dirty());
 7014        assert_eq!(
 7015            buffer.text(),
 7016            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7017        )
 7018    });
 7019    buffer_3.update(cx, |buffer, _| {
 7020        assert!(!buffer.is_dirty());
 7021        assert_eq!(buffer.text(), sample_text_3,)
 7022    });
 7023
 7024    cx.executor().start_waiting();
 7025    let save = multi_buffer_editor
 7026        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7027        .unwrap();
 7028
 7029    let fake_server = fake_servers.next().await.unwrap();
 7030    fake_server
 7031        .server
 7032        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7033            Ok(Some(vec![lsp::TextEdit::new(
 7034                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7035                format!("[{} formatted]", params.text_document.uri),
 7036            )]))
 7037        })
 7038        .detach();
 7039    save.await;
 7040
 7041    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7042    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7043    assert_eq!(
 7044        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7045        "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}",
 7046    );
 7047    buffer_1.update(cx, |buffer, _| {
 7048        assert!(!buffer.is_dirty());
 7049        assert_eq!(
 7050            buffer.text(),
 7051            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7052        )
 7053    });
 7054    buffer_2.update(cx, |buffer, _| {
 7055        assert!(!buffer.is_dirty());
 7056        assert_eq!(
 7057            buffer.text(),
 7058            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7059        )
 7060    });
 7061    buffer_3.update(cx, |buffer, _| {
 7062        assert!(!buffer.is_dirty());
 7063        assert_eq!(buffer.text(), sample_text_3,)
 7064    });
 7065}
 7066
 7067#[gpui::test]
 7068async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7069    init_test(cx, |_| {});
 7070
 7071    let fs = FakeFs::new(cx.executor());
 7072    fs.insert_file("/file.rs", Default::default()).await;
 7073
 7074    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7075
 7076    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7077    language_registry.add(rust_lang());
 7078    let mut fake_servers = language_registry.register_fake_lsp(
 7079        "Rust",
 7080        FakeLspAdapter {
 7081            capabilities: lsp::ServerCapabilities {
 7082                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7083                ..Default::default()
 7084            },
 7085            ..Default::default()
 7086        },
 7087    );
 7088
 7089    let buffer = project
 7090        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7091        .await
 7092        .unwrap();
 7093
 7094    cx.executor().start_waiting();
 7095    let fake_server = fake_servers.next().await.unwrap();
 7096
 7097    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7098    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7099    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7100    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7101
 7102    let save = editor
 7103        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7104        .unwrap();
 7105    fake_server
 7106        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7107            assert_eq!(
 7108                params.text_document.uri,
 7109                lsp::Url::from_file_path("/file.rs").unwrap()
 7110            );
 7111            assert_eq!(params.options.tab_size, 4);
 7112            Ok(Some(vec![lsp::TextEdit::new(
 7113                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7114                ", ".to_string(),
 7115            )]))
 7116        })
 7117        .next()
 7118        .await;
 7119    cx.executor().start_waiting();
 7120    save.await;
 7121    assert_eq!(
 7122        editor.update(cx, |editor, cx| editor.text(cx)),
 7123        "one, two\nthree\n"
 7124    );
 7125    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7126
 7127    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7128    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7129
 7130    // Ensure we can still save even if formatting hangs.
 7131    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7132        move |params, _| async move {
 7133            assert_eq!(
 7134                params.text_document.uri,
 7135                lsp::Url::from_file_path("/file.rs").unwrap()
 7136            );
 7137            futures::future::pending::<()>().await;
 7138            unreachable!()
 7139        },
 7140    );
 7141    let save = editor
 7142        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7143        .unwrap();
 7144    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7145    cx.executor().start_waiting();
 7146    save.await;
 7147    assert_eq!(
 7148        editor.update(cx, |editor, cx| editor.text(cx)),
 7149        "one\ntwo\nthree\n"
 7150    );
 7151    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7152
 7153    // For non-dirty buffer, no formatting request should be sent
 7154    let save = editor
 7155        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7156        .unwrap();
 7157    let _pending_format_request = fake_server
 7158        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7159            panic!("Should not be invoked on non-dirty buffer");
 7160        })
 7161        .next();
 7162    cx.executor().start_waiting();
 7163    save.await;
 7164
 7165    // Set Rust language override and assert overridden tabsize is sent to language server
 7166    update_test_language_settings(cx, |settings| {
 7167        settings.languages.insert(
 7168            "Rust".into(),
 7169            LanguageSettingsContent {
 7170                tab_size: NonZeroU32::new(8),
 7171                ..Default::default()
 7172            },
 7173        );
 7174    });
 7175
 7176    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7177    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7178    let save = editor
 7179        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7180        .unwrap();
 7181    fake_server
 7182        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7183            assert_eq!(
 7184                params.text_document.uri,
 7185                lsp::Url::from_file_path("/file.rs").unwrap()
 7186            );
 7187            assert_eq!(params.options.tab_size, 8);
 7188            Ok(Some(vec![]))
 7189        })
 7190        .next()
 7191        .await;
 7192    cx.executor().start_waiting();
 7193    save.await;
 7194}
 7195
 7196#[gpui::test]
 7197async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7198    init_test(cx, |settings| {
 7199        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7200            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7201        ))
 7202    });
 7203
 7204    let fs = FakeFs::new(cx.executor());
 7205    fs.insert_file("/file.rs", Default::default()).await;
 7206
 7207    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7208
 7209    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7210    language_registry.add(Arc::new(Language::new(
 7211        LanguageConfig {
 7212            name: "Rust".into(),
 7213            matcher: LanguageMatcher {
 7214                path_suffixes: vec!["rs".to_string()],
 7215                ..Default::default()
 7216            },
 7217            ..LanguageConfig::default()
 7218        },
 7219        Some(tree_sitter_rust::LANGUAGE.into()),
 7220    )));
 7221    update_test_language_settings(cx, |settings| {
 7222        // Enable Prettier formatting for the same buffer, and ensure
 7223        // LSP is called instead of Prettier.
 7224        settings.defaults.prettier = Some(PrettierSettings {
 7225            allowed: true,
 7226            ..PrettierSettings::default()
 7227        });
 7228    });
 7229    let mut fake_servers = language_registry.register_fake_lsp(
 7230        "Rust",
 7231        FakeLspAdapter {
 7232            capabilities: lsp::ServerCapabilities {
 7233                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7234                ..Default::default()
 7235            },
 7236            ..Default::default()
 7237        },
 7238    );
 7239
 7240    let buffer = project
 7241        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7242        .await
 7243        .unwrap();
 7244
 7245    cx.executor().start_waiting();
 7246    let fake_server = fake_servers.next().await.unwrap();
 7247
 7248    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7249    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7250    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7251
 7252    let format = editor
 7253        .update(cx, |editor, cx| {
 7254            editor.perform_format(
 7255                project.clone(),
 7256                FormatTrigger::Manual,
 7257                FormatTarget::Buffer,
 7258                cx,
 7259            )
 7260        })
 7261        .unwrap();
 7262    fake_server
 7263        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7264            assert_eq!(
 7265                params.text_document.uri,
 7266                lsp::Url::from_file_path("/file.rs").unwrap()
 7267            );
 7268            assert_eq!(params.options.tab_size, 4);
 7269            Ok(Some(vec![lsp::TextEdit::new(
 7270                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7271                ", ".to_string(),
 7272            )]))
 7273        })
 7274        .next()
 7275        .await;
 7276    cx.executor().start_waiting();
 7277    format.await;
 7278    assert_eq!(
 7279        editor.update(cx, |editor, cx| editor.text(cx)),
 7280        "one, two\nthree\n"
 7281    );
 7282
 7283    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7284    // Ensure we don't lock if formatting hangs.
 7285    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7286        assert_eq!(
 7287            params.text_document.uri,
 7288            lsp::Url::from_file_path("/file.rs").unwrap()
 7289        );
 7290        futures::future::pending::<()>().await;
 7291        unreachable!()
 7292    });
 7293    let format = editor
 7294        .update(cx, |editor, cx| {
 7295            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7296        })
 7297        .unwrap();
 7298    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7299    cx.executor().start_waiting();
 7300    format.await;
 7301    assert_eq!(
 7302        editor.update(cx, |editor, cx| editor.text(cx)),
 7303        "one\ntwo\nthree\n"
 7304    );
 7305}
 7306
 7307#[gpui::test]
 7308async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7309    init_test(cx, |_| {});
 7310
 7311    let mut cx = EditorLspTestContext::new_rust(
 7312        lsp::ServerCapabilities {
 7313            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7314            ..Default::default()
 7315        },
 7316        cx,
 7317    )
 7318    .await;
 7319
 7320    cx.set_state(indoc! {"
 7321        one.twoˇ
 7322    "});
 7323
 7324    // The format request takes a long time. When it completes, it inserts
 7325    // a newline and an indent before the `.`
 7326    cx.lsp
 7327        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7328            let executor = cx.background_executor().clone();
 7329            async move {
 7330                executor.timer(Duration::from_millis(100)).await;
 7331                Ok(Some(vec![lsp::TextEdit {
 7332                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7333                    new_text: "\n    ".into(),
 7334                }]))
 7335            }
 7336        });
 7337
 7338    // Submit a format request.
 7339    let format_1 = cx
 7340        .update_editor(|editor, cx| editor.format(&Format, cx))
 7341        .unwrap();
 7342    cx.executor().run_until_parked();
 7343
 7344    // Submit a second format request.
 7345    let format_2 = cx
 7346        .update_editor(|editor, cx| editor.format(&Format, cx))
 7347        .unwrap();
 7348    cx.executor().run_until_parked();
 7349
 7350    // Wait for both format requests to complete
 7351    cx.executor().advance_clock(Duration::from_millis(200));
 7352    cx.executor().start_waiting();
 7353    format_1.await.unwrap();
 7354    cx.executor().start_waiting();
 7355    format_2.await.unwrap();
 7356
 7357    // The formatting edits only happens once.
 7358    cx.assert_editor_state(indoc! {"
 7359        one
 7360            .twoˇ
 7361    "});
 7362}
 7363
 7364#[gpui::test]
 7365async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7366    init_test(cx, |settings| {
 7367        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7368    });
 7369
 7370    let mut cx = EditorLspTestContext::new_rust(
 7371        lsp::ServerCapabilities {
 7372            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7373            ..Default::default()
 7374        },
 7375        cx,
 7376    )
 7377    .await;
 7378
 7379    // Set up a buffer white some trailing whitespace and no trailing newline.
 7380    cx.set_state(
 7381        &[
 7382            "one ",   //
 7383            "twoˇ",   //
 7384            "three ", //
 7385            "four",   //
 7386        ]
 7387        .join("\n"),
 7388    );
 7389
 7390    // Submit a format request.
 7391    let format = cx
 7392        .update_editor(|editor, cx| editor.format(&Format, cx))
 7393        .unwrap();
 7394
 7395    // Record which buffer changes have been sent to the language server
 7396    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7397    cx.lsp
 7398        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7399            let buffer_changes = buffer_changes.clone();
 7400            move |params, _| {
 7401                buffer_changes.lock().extend(
 7402                    params
 7403                        .content_changes
 7404                        .into_iter()
 7405                        .map(|e| (e.range.unwrap(), e.text)),
 7406                );
 7407            }
 7408        });
 7409
 7410    // Handle formatting requests to the language server.
 7411    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7412        let buffer_changes = buffer_changes.clone();
 7413        move |_, _| {
 7414            // When formatting is requested, trailing whitespace has already been stripped,
 7415            // and the trailing newline has already been added.
 7416            assert_eq!(
 7417                &buffer_changes.lock()[1..],
 7418                &[
 7419                    (
 7420                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7421                        "".into()
 7422                    ),
 7423                    (
 7424                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7425                        "".into()
 7426                    ),
 7427                    (
 7428                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7429                        "\n".into()
 7430                    ),
 7431                ]
 7432            );
 7433
 7434            // Insert blank lines between each line of the buffer.
 7435            async move {
 7436                Ok(Some(vec![
 7437                    lsp::TextEdit {
 7438                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7439                        new_text: "\n".into(),
 7440                    },
 7441                    lsp::TextEdit {
 7442                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7443                        new_text: "\n".into(),
 7444                    },
 7445                ]))
 7446            }
 7447        }
 7448    });
 7449
 7450    // After formatting the buffer, the trailing whitespace is stripped,
 7451    // a newline is appended, and the edits provided by the language server
 7452    // have been applied.
 7453    format.await.unwrap();
 7454    cx.assert_editor_state(
 7455        &[
 7456            "one",   //
 7457            "",      //
 7458            "twoˇ",  //
 7459            "",      //
 7460            "three", //
 7461            "four",  //
 7462            "",      //
 7463        ]
 7464        .join("\n"),
 7465    );
 7466
 7467    // Undoing the formatting undoes the trailing whitespace removal, the
 7468    // trailing newline, and the LSP edits.
 7469    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7470    cx.assert_editor_state(
 7471        &[
 7472            "one ",   //
 7473            "twoˇ",   //
 7474            "three ", //
 7475            "four",   //
 7476        ]
 7477        .join("\n"),
 7478    );
 7479}
 7480
 7481#[gpui::test]
 7482async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7483    cx: &mut gpui::TestAppContext,
 7484) {
 7485    init_test(cx, |_| {});
 7486
 7487    cx.update(|cx| {
 7488        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7489            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7490                settings.auto_signature_help = Some(true);
 7491            });
 7492        });
 7493    });
 7494
 7495    let mut cx = EditorLspTestContext::new_rust(
 7496        lsp::ServerCapabilities {
 7497            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7498                ..Default::default()
 7499            }),
 7500            ..Default::default()
 7501        },
 7502        cx,
 7503    )
 7504    .await;
 7505
 7506    let language = Language::new(
 7507        LanguageConfig {
 7508            name: "Rust".into(),
 7509            brackets: BracketPairConfig {
 7510                pairs: vec![
 7511                    BracketPair {
 7512                        start: "{".to_string(),
 7513                        end: "}".to_string(),
 7514                        close: true,
 7515                        surround: true,
 7516                        newline: true,
 7517                    },
 7518                    BracketPair {
 7519                        start: "(".to_string(),
 7520                        end: ")".to_string(),
 7521                        close: true,
 7522                        surround: true,
 7523                        newline: true,
 7524                    },
 7525                    BracketPair {
 7526                        start: "/*".to_string(),
 7527                        end: " */".to_string(),
 7528                        close: true,
 7529                        surround: true,
 7530                        newline: true,
 7531                    },
 7532                    BracketPair {
 7533                        start: "[".to_string(),
 7534                        end: "]".to_string(),
 7535                        close: false,
 7536                        surround: false,
 7537                        newline: true,
 7538                    },
 7539                    BracketPair {
 7540                        start: "\"".to_string(),
 7541                        end: "\"".to_string(),
 7542                        close: true,
 7543                        surround: true,
 7544                        newline: false,
 7545                    },
 7546                    BracketPair {
 7547                        start: "<".to_string(),
 7548                        end: ">".to_string(),
 7549                        close: false,
 7550                        surround: true,
 7551                        newline: true,
 7552                    },
 7553                ],
 7554                ..Default::default()
 7555            },
 7556            autoclose_before: "})]".to_string(),
 7557            ..Default::default()
 7558        },
 7559        Some(tree_sitter_rust::LANGUAGE.into()),
 7560    );
 7561    let language = Arc::new(language);
 7562
 7563    cx.language_registry().add(language.clone());
 7564    cx.update_buffer(|buffer, cx| {
 7565        buffer.set_language(Some(language), cx);
 7566    });
 7567
 7568    cx.set_state(
 7569        &r#"
 7570            fn main() {
 7571                sampleˇ
 7572            }
 7573        "#
 7574        .unindent(),
 7575    );
 7576
 7577    cx.update_editor(|view, cx| {
 7578        view.handle_input("(", cx);
 7579    });
 7580    cx.assert_editor_state(
 7581        &"
 7582            fn main() {
 7583                sample(ˇ)
 7584            }
 7585        "
 7586        .unindent(),
 7587    );
 7588
 7589    let mocked_response = lsp::SignatureHelp {
 7590        signatures: vec![lsp::SignatureInformation {
 7591            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7592            documentation: None,
 7593            parameters: Some(vec![
 7594                lsp::ParameterInformation {
 7595                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7596                    documentation: None,
 7597                },
 7598                lsp::ParameterInformation {
 7599                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7600                    documentation: None,
 7601                },
 7602            ]),
 7603            active_parameter: None,
 7604        }],
 7605        active_signature: Some(0),
 7606        active_parameter: Some(0),
 7607    };
 7608    handle_signature_help_request(&mut cx, mocked_response).await;
 7609
 7610    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7611        .await;
 7612
 7613    cx.editor(|editor, _| {
 7614        let signature_help_state = editor.signature_help_state.popover().cloned();
 7615        assert!(signature_help_state.is_some());
 7616        let ParsedMarkdown {
 7617            text, highlights, ..
 7618        } = signature_help_state.unwrap().parsed_content;
 7619        assert_eq!(text, "param1: u8, param2: u8");
 7620        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7621    });
 7622}
 7623
 7624#[gpui::test]
 7625async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7626    init_test(cx, |_| {});
 7627
 7628    cx.update(|cx| {
 7629        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7630            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7631                settings.auto_signature_help = Some(false);
 7632                settings.show_signature_help_after_edits = Some(false);
 7633            });
 7634        });
 7635    });
 7636
 7637    let mut cx = EditorLspTestContext::new_rust(
 7638        lsp::ServerCapabilities {
 7639            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7640                ..Default::default()
 7641            }),
 7642            ..Default::default()
 7643        },
 7644        cx,
 7645    )
 7646    .await;
 7647
 7648    let language = Language::new(
 7649        LanguageConfig {
 7650            name: "Rust".into(),
 7651            brackets: BracketPairConfig {
 7652                pairs: vec![
 7653                    BracketPair {
 7654                        start: "{".to_string(),
 7655                        end: "}".to_string(),
 7656                        close: true,
 7657                        surround: true,
 7658                        newline: true,
 7659                    },
 7660                    BracketPair {
 7661                        start: "(".to_string(),
 7662                        end: ")".to_string(),
 7663                        close: true,
 7664                        surround: true,
 7665                        newline: true,
 7666                    },
 7667                    BracketPair {
 7668                        start: "/*".to_string(),
 7669                        end: " */".to_string(),
 7670                        close: true,
 7671                        surround: true,
 7672                        newline: true,
 7673                    },
 7674                    BracketPair {
 7675                        start: "[".to_string(),
 7676                        end: "]".to_string(),
 7677                        close: false,
 7678                        surround: false,
 7679                        newline: true,
 7680                    },
 7681                    BracketPair {
 7682                        start: "\"".to_string(),
 7683                        end: "\"".to_string(),
 7684                        close: true,
 7685                        surround: true,
 7686                        newline: false,
 7687                    },
 7688                    BracketPair {
 7689                        start: "<".to_string(),
 7690                        end: ">".to_string(),
 7691                        close: false,
 7692                        surround: true,
 7693                        newline: true,
 7694                    },
 7695                ],
 7696                ..Default::default()
 7697            },
 7698            autoclose_before: "})]".to_string(),
 7699            ..Default::default()
 7700        },
 7701        Some(tree_sitter_rust::LANGUAGE.into()),
 7702    );
 7703    let language = Arc::new(language);
 7704
 7705    cx.language_registry().add(language.clone());
 7706    cx.update_buffer(|buffer, cx| {
 7707        buffer.set_language(Some(language), cx);
 7708    });
 7709
 7710    // Ensure that signature_help is not called when no signature help is enabled.
 7711    cx.set_state(
 7712        &r#"
 7713            fn main() {
 7714                sampleˇ
 7715            }
 7716        "#
 7717        .unindent(),
 7718    );
 7719    cx.update_editor(|view, cx| {
 7720        view.handle_input("(", cx);
 7721    });
 7722    cx.assert_editor_state(
 7723        &"
 7724            fn main() {
 7725                sample(ˇ)
 7726            }
 7727        "
 7728        .unindent(),
 7729    );
 7730    cx.editor(|editor, _| {
 7731        assert!(editor.signature_help_state.task().is_none());
 7732    });
 7733
 7734    let mocked_response = lsp::SignatureHelp {
 7735        signatures: vec![lsp::SignatureInformation {
 7736            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7737            documentation: None,
 7738            parameters: Some(vec![
 7739                lsp::ParameterInformation {
 7740                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7741                    documentation: None,
 7742                },
 7743                lsp::ParameterInformation {
 7744                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7745                    documentation: None,
 7746                },
 7747            ]),
 7748            active_parameter: None,
 7749        }],
 7750        active_signature: Some(0),
 7751        active_parameter: Some(0),
 7752    };
 7753
 7754    // Ensure that signature_help is called when enabled afte edits
 7755    cx.update(|cx| {
 7756        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7757            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7758                settings.auto_signature_help = Some(false);
 7759                settings.show_signature_help_after_edits = Some(true);
 7760            });
 7761        });
 7762    });
 7763    cx.set_state(
 7764        &r#"
 7765            fn main() {
 7766                sampleˇ
 7767            }
 7768        "#
 7769        .unindent(),
 7770    );
 7771    cx.update_editor(|view, cx| {
 7772        view.handle_input("(", cx);
 7773    });
 7774    cx.assert_editor_state(
 7775        &"
 7776            fn main() {
 7777                sample(ˇ)
 7778            }
 7779        "
 7780        .unindent(),
 7781    );
 7782    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7783    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7784        .await;
 7785    cx.update_editor(|editor, _| {
 7786        let signature_help_state = editor.signature_help_state.popover().cloned();
 7787        assert!(signature_help_state.is_some());
 7788        let ParsedMarkdown {
 7789            text, highlights, ..
 7790        } = signature_help_state.unwrap().parsed_content;
 7791        assert_eq!(text, "param1: u8, param2: u8");
 7792        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7793        editor.signature_help_state = SignatureHelpState::default();
 7794    });
 7795
 7796    // Ensure that signature_help is called when auto signature help override is enabled
 7797    cx.update(|cx| {
 7798        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7799            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7800                settings.auto_signature_help = Some(true);
 7801                settings.show_signature_help_after_edits = Some(false);
 7802            });
 7803        });
 7804    });
 7805    cx.set_state(
 7806        &r#"
 7807            fn main() {
 7808                sampleˇ
 7809            }
 7810        "#
 7811        .unindent(),
 7812    );
 7813    cx.update_editor(|view, cx| {
 7814        view.handle_input("(", cx);
 7815    });
 7816    cx.assert_editor_state(
 7817        &"
 7818            fn main() {
 7819                sample(ˇ)
 7820            }
 7821        "
 7822        .unindent(),
 7823    );
 7824    handle_signature_help_request(&mut cx, mocked_response).await;
 7825    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7826        .await;
 7827    cx.editor(|editor, _| {
 7828        let signature_help_state = editor.signature_help_state.popover().cloned();
 7829        assert!(signature_help_state.is_some());
 7830        let ParsedMarkdown {
 7831            text, highlights, ..
 7832        } = signature_help_state.unwrap().parsed_content;
 7833        assert_eq!(text, "param1: u8, param2: u8");
 7834        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7835    });
 7836}
 7837
 7838#[gpui::test]
 7839async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7840    init_test(cx, |_| {});
 7841    cx.update(|cx| {
 7842        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7843            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7844                settings.auto_signature_help = Some(true);
 7845            });
 7846        });
 7847    });
 7848
 7849    let mut cx = EditorLspTestContext::new_rust(
 7850        lsp::ServerCapabilities {
 7851            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7852                ..Default::default()
 7853            }),
 7854            ..Default::default()
 7855        },
 7856        cx,
 7857    )
 7858    .await;
 7859
 7860    // A test that directly calls `show_signature_help`
 7861    cx.update_editor(|editor, cx| {
 7862        editor.show_signature_help(&ShowSignatureHelp, cx);
 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).await;
 7885
 7886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7887        .await;
 7888
 7889    cx.editor(|editor, _| {
 7890        let signature_help_state = editor.signature_help_state.popover().cloned();
 7891        assert!(signature_help_state.is_some());
 7892        let ParsedMarkdown {
 7893            text, highlights, ..
 7894        } = signature_help_state.unwrap().parsed_content;
 7895        assert_eq!(text, "param1: u8, param2: u8");
 7896        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7897    });
 7898
 7899    // When exiting outside from inside the brackets, `signature_help` is closed.
 7900    cx.set_state(indoc! {"
 7901        fn main() {
 7902            sample(ˇ);
 7903        }
 7904
 7905        fn sample(param1: u8, param2: u8) {}
 7906    "});
 7907
 7908    cx.update_editor(|editor, cx| {
 7909        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7910    });
 7911
 7912    let mocked_response = lsp::SignatureHelp {
 7913        signatures: Vec::new(),
 7914        active_signature: None,
 7915        active_parameter: None,
 7916    };
 7917    handle_signature_help_request(&mut cx, mocked_response).await;
 7918
 7919    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7920        .await;
 7921
 7922    cx.editor(|editor, _| {
 7923        assert!(!editor.signature_help_state.is_shown());
 7924    });
 7925
 7926    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7927    cx.set_state(indoc! {"
 7928        fn main() {
 7929            sample(ˇ);
 7930        }
 7931
 7932        fn sample(param1: u8, param2: u8) {}
 7933    "});
 7934
 7935    let mocked_response = lsp::SignatureHelp {
 7936        signatures: vec![lsp::SignatureInformation {
 7937            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7938            documentation: None,
 7939            parameters: Some(vec![
 7940                lsp::ParameterInformation {
 7941                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7942                    documentation: None,
 7943                },
 7944                lsp::ParameterInformation {
 7945                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7946                    documentation: None,
 7947                },
 7948            ]),
 7949            active_parameter: None,
 7950        }],
 7951        active_signature: Some(0),
 7952        active_parameter: Some(0),
 7953    };
 7954    handle_signature_help_request(&mut cx, mocked_response.clone()).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    // Restore the popover with more parameter input
 7962    cx.set_state(indoc! {"
 7963        fn main() {
 7964            sample(param1, param2ˇ);
 7965        }
 7966
 7967        fn sample(param1: u8, param2: u8) {}
 7968    "});
 7969
 7970    let mocked_response = lsp::SignatureHelp {
 7971        signatures: vec![lsp::SignatureInformation {
 7972            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7973            documentation: None,
 7974            parameters: Some(vec![
 7975                lsp::ParameterInformation {
 7976                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7977                    documentation: None,
 7978                },
 7979                lsp::ParameterInformation {
 7980                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7981                    documentation: None,
 7982                },
 7983            ]),
 7984            active_parameter: None,
 7985        }],
 7986        active_signature: Some(0),
 7987        active_parameter: Some(1),
 7988    };
 7989    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7990    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7991        .await;
 7992
 7993    // When selecting a range, the popover is gone.
 7994    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7995    cx.update_editor(|editor, cx| {
 7996        editor.change_selections(None, cx, |s| {
 7997            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7998        })
 7999    });
 8000    cx.assert_editor_state(indoc! {"
 8001        fn main() {
 8002            sample(param1, «ˇparam2»);
 8003        }
 8004
 8005        fn sample(param1: u8, param2: u8) {}
 8006    "});
 8007    cx.editor(|editor, _| {
 8008        assert!(!editor.signature_help_state.is_shown());
 8009    });
 8010
 8011    // When unselecting again, the popover is back if within the brackets.
 8012    cx.update_editor(|editor, cx| {
 8013        editor.change_selections(None, cx, |s| {
 8014            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8015        })
 8016    });
 8017    cx.assert_editor_state(indoc! {"
 8018        fn main() {
 8019            sample(param1, ˇparam2);
 8020        }
 8021
 8022        fn sample(param1: u8, param2: u8) {}
 8023    "});
 8024    handle_signature_help_request(&mut cx, mocked_response).await;
 8025    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8026        .await;
 8027    cx.editor(|editor, _| {
 8028        assert!(editor.signature_help_state.is_shown());
 8029    });
 8030
 8031    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8032    cx.update_editor(|editor, cx| {
 8033        editor.change_selections(None, cx, |s| {
 8034            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8035            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8036        })
 8037    });
 8038    cx.assert_editor_state(indoc! {"
 8039        fn main() {
 8040            sample(param1, ˇparam2);
 8041        }
 8042
 8043        fn sample(param1: u8, param2: u8) {}
 8044    "});
 8045
 8046    let mocked_response = lsp::SignatureHelp {
 8047        signatures: vec![lsp::SignatureInformation {
 8048            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8049            documentation: None,
 8050            parameters: Some(vec![
 8051                lsp::ParameterInformation {
 8052                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8053                    documentation: None,
 8054                },
 8055                lsp::ParameterInformation {
 8056                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8057                    documentation: None,
 8058                },
 8059            ]),
 8060            active_parameter: None,
 8061        }],
 8062        active_signature: Some(0),
 8063        active_parameter: Some(1),
 8064    };
 8065    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8066    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8067        .await;
 8068    cx.update_editor(|editor, cx| {
 8069        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8070    });
 8071    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8072        .await;
 8073    cx.update_editor(|editor, cx| {
 8074        editor.change_selections(None, cx, |s| {
 8075            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8076        })
 8077    });
 8078    cx.assert_editor_state(indoc! {"
 8079        fn main() {
 8080            sample(param1, «ˇparam2»);
 8081        }
 8082
 8083        fn sample(param1: u8, param2: u8) {}
 8084    "});
 8085    cx.update_editor(|editor, cx| {
 8086        editor.change_selections(None, cx, |s| {
 8087            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8088        })
 8089    });
 8090    cx.assert_editor_state(indoc! {"
 8091        fn main() {
 8092            sample(param1, ˇparam2);
 8093        }
 8094
 8095        fn sample(param1: u8, param2: u8) {}
 8096    "});
 8097    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8098        .await;
 8099}
 8100
 8101#[gpui::test]
 8102async fn test_completion(cx: &mut gpui::TestAppContext) {
 8103    init_test(cx, |_| {});
 8104
 8105    let mut cx = EditorLspTestContext::new_rust(
 8106        lsp::ServerCapabilities {
 8107            completion_provider: Some(lsp::CompletionOptions {
 8108                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8109                resolve_provider: Some(true),
 8110                ..Default::default()
 8111            }),
 8112            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8113            ..Default::default()
 8114        },
 8115        cx,
 8116    )
 8117    .await;
 8118    let counter = Arc::new(AtomicUsize::new(0));
 8119
 8120    cx.set_state(indoc! {"
 8121        oneˇ
 8122        two
 8123        three
 8124    "});
 8125    cx.simulate_keystroke(".");
 8126    handle_completion_request(
 8127        &mut cx,
 8128        indoc! {"
 8129            one.|<>
 8130            two
 8131            three
 8132        "},
 8133        vec!["first_completion", "second_completion"],
 8134        counter.clone(),
 8135    )
 8136    .await;
 8137    cx.condition(|editor, _| editor.context_menu_visible())
 8138        .await;
 8139    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8140
 8141    let _handler = handle_signature_help_request(
 8142        &mut cx,
 8143        lsp::SignatureHelp {
 8144            signatures: vec![lsp::SignatureInformation {
 8145                label: "test signature".to_string(),
 8146                documentation: None,
 8147                parameters: Some(vec![lsp::ParameterInformation {
 8148                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8149                    documentation: None,
 8150                }]),
 8151                active_parameter: None,
 8152            }],
 8153            active_signature: None,
 8154            active_parameter: None,
 8155        },
 8156    );
 8157    cx.update_editor(|editor, cx| {
 8158        assert!(
 8159            !editor.signature_help_state.is_shown(),
 8160            "No signature help was called for"
 8161        );
 8162        editor.show_signature_help(&ShowSignatureHelp, cx);
 8163    });
 8164    cx.run_until_parked();
 8165    cx.update_editor(|editor, _| {
 8166        assert!(
 8167            !editor.signature_help_state.is_shown(),
 8168            "No signature help should be shown when completions menu is open"
 8169        );
 8170    });
 8171
 8172    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8173        editor.context_menu_next(&Default::default(), cx);
 8174        editor
 8175            .confirm_completion(&ConfirmCompletion::default(), cx)
 8176            .unwrap()
 8177    });
 8178    cx.assert_editor_state(indoc! {"
 8179        one.second_completionˇ
 8180        two
 8181        three
 8182    "});
 8183
 8184    handle_resolve_completion_request(
 8185        &mut cx,
 8186        Some(vec![
 8187            (
 8188                //This overlaps with the primary completion edit which is
 8189                //misbehavior from the LSP spec, test that we filter it out
 8190                indoc! {"
 8191                    one.second_ˇcompletion
 8192                    two
 8193                    threeˇ
 8194                "},
 8195                "overlapping additional edit",
 8196            ),
 8197            (
 8198                indoc! {"
 8199                    one.second_completion
 8200                    two
 8201                    threeˇ
 8202                "},
 8203                "\nadditional edit",
 8204            ),
 8205        ]),
 8206    )
 8207    .await;
 8208    apply_additional_edits.await.unwrap();
 8209    cx.assert_editor_state(indoc! {"
 8210        one.second_completionˇ
 8211        two
 8212        three
 8213        additional edit
 8214    "});
 8215
 8216    cx.set_state(indoc! {"
 8217        one.second_completion
 8218        twoˇ
 8219        threeˇ
 8220        additional edit
 8221    "});
 8222    cx.simulate_keystroke(" ");
 8223    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8224    cx.simulate_keystroke("s");
 8225    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8226
 8227    cx.assert_editor_state(indoc! {"
 8228        one.second_completion
 8229        two sˇ
 8230        three sˇ
 8231        additional edit
 8232    "});
 8233    handle_completion_request(
 8234        &mut cx,
 8235        indoc! {"
 8236            one.second_completion
 8237            two s
 8238            three <s|>
 8239            additional edit
 8240        "},
 8241        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8242        counter.clone(),
 8243    )
 8244    .await;
 8245    cx.condition(|editor, _| editor.context_menu_visible())
 8246        .await;
 8247    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8248
 8249    cx.simulate_keystroke("i");
 8250
 8251    handle_completion_request(
 8252        &mut cx,
 8253        indoc! {"
 8254            one.second_completion
 8255            two si
 8256            three <si|>
 8257            additional edit
 8258        "},
 8259        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8260        counter.clone(),
 8261    )
 8262    .await;
 8263    cx.condition(|editor, _| editor.context_menu_visible())
 8264        .await;
 8265    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8266
 8267    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8268        editor
 8269            .confirm_completion(&ConfirmCompletion::default(), cx)
 8270            .unwrap()
 8271    });
 8272    cx.assert_editor_state(indoc! {"
 8273        one.second_completion
 8274        two sixth_completionˇ
 8275        three sixth_completionˇ
 8276        additional edit
 8277    "});
 8278
 8279    handle_resolve_completion_request(&mut cx, None).await;
 8280    apply_additional_edits.await.unwrap();
 8281
 8282    cx.update(|cx| {
 8283        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8284            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8285                settings.show_completions_on_input = Some(false);
 8286            });
 8287        })
 8288    });
 8289    cx.set_state("editorˇ");
 8290    cx.simulate_keystroke(".");
 8291    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8292    cx.simulate_keystroke("c");
 8293    cx.simulate_keystroke("l");
 8294    cx.simulate_keystroke("o");
 8295    cx.assert_editor_state("editor.cloˇ");
 8296    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8297    cx.update_editor(|editor, cx| {
 8298        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8299    });
 8300    handle_completion_request(
 8301        &mut cx,
 8302        "editor.<clo|>",
 8303        vec!["close", "clobber"],
 8304        counter.clone(),
 8305    )
 8306    .await;
 8307    cx.condition(|editor, _| editor.context_menu_visible())
 8308        .await;
 8309    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8310
 8311    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8312        editor
 8313            .confirm_completion(&ConfirmCompletion::default(), cx)
 8314            .unwrap()
 8315    });
 8316    cx.assert_editor_state("editor.closeˇ");
 8317    handle_resolve_completion_request(&mut cx, None).await;
 8318    apply_additional_edits.await.unwrap();
 8319}
 8320
 8321#[gpui::test]
 8322async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8323    init_test(cx, |_| {});
 8324    let mut cx = EditorLspTestContext::new_rust(
 8325        lsp::ServerCapabilities {
 8326            completion_provider: Some(lsp::CompletionOptions {
 8327                trigger_characters: Some(vec![".".to_string()]),
 8328                ..Default::default()
 8329            }),
 8330            ..Default::default()
 8331        },
 8332        cx,
 8333    )
 8334    .await;
 8335    cx.lsp
 8336        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8337            Ok(Some(lsp::CompletionResponse::Array(vec![
 8338                lsp::CompletionItem {
 8339                    label: "first".into(),
 8340                    ..Default::default()
 8341                },
 8342                lsp::CompletionItem {
 8343                    label: "last".into(),
 8344                    ..Default::default()
 8345                },
 8346            ])))
 8347        });
 8348    cx.set_state("variableˇ");
 8349    cx.simulate_keystroke(".");
 8350    cx.executor().run_until_parked();
 8351
 8352    cx.update_editor(|editor, _| {
 8353        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8354            assert_eq!(
 8355                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8356                &["first", "last"]
 8357            );
 8358        } else {
 8359            panic!("expected completion menu to be open");
 8360        }
 8361    });
 8362
 8363    cx.update_editor(|editor, cx| {
 8364        editor.move_page_down(&MovePageDown::default(), cx);
 8365        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8366            assert!(
 8367                menu.selected_item == 1,
 8368                "expected PageDown to select the last item from the context menu"
 8369            );
 8370        } else {
 8371            panic!("expected completion menu to stay open after PageDown");
 8372        }
 8373    });
 8374
 8375    cx.update_editor(|editor, cx| {
 8376        editor.move_page_up(&MovePageUp::default(), cx);
 8377        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8378            assert!(
 8379                menu.selected_item == 0,
 8380                "expected PageUp to select the first item from the context menu"
 8381            );
 8382        } else {
 8383            panic!("expected completion menu to stay open after PageUp");
 8384        }
 8385    });
 8386}
 8387
 8388#[gpui::test]
 8389async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8390    init_test(cx, |_| {});
 8391    let mut cx = EditorLspTestContext::new_rust(
 8392        lsp::ServerCapabilities {
 8393            completion_provider: Some(lsp::CompletionOptions {
 8394                trigger_characters: Some(vec![".".to_string()]),
 8395                ..Default::default()
 8396            }),
 8397            ..Default::default()
 8398        },
 8399        cx,
 8400    )
 8401    .await;
 8402    cx.lsp
 8403        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8404            Ok(Some(lsp::CompletionResponse::Array(vec![
 8405                lsp::CompletionItem {
 8406                    label: "Range".into(),
 8407                    sort_text: Some("a".into()),
 8408                    ..Default::default()
 8409                },
 8410                lsp::CompletionItem {
 8411                    label: "r".into(),
 8412                    sort_text: Some("b".into()),
 8413                    ..Default::default()
 8414                },
 8415                lsp::CompletionItem {
 8416                    label: "ret".into(),
 8417                    sort_text: Some("c".into()),
 8418                    ..Default::default()
 8419                },
 8420                lsp::CompletionItem {
 8421                    label: "return".into(),
 8422                    sort_text: Some("d".into()),
 8423                    ..Default::default()
 8424                },
 8425                lsp::CompletionItem {
 8426                    label: "slice".into(),
 8427                    sort_text: Some("d".into()),
 8428                    ..Default::default()
 8429                },
 8430            ])))
 8431        });
 8432    cx.set_state("");
 8433    cx.executor().run_until_parked();
 8434    cx.update_editor(|editor, cx| {
 8435        editor.show_completions(
 8436            &ShowCompletions {
 8437                trigger: Some("r".into()),
 8438            },
 8439            cx,
 8440        );
 8441    });
 8442    cx.executor().run_until_parked();
 8443
 8444    cx.update_editor(|editor, _| {
 8445        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8446            assert_eq!(
 8447                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8448                &["r", "ret", "Range", "return"]
 8449            );
 8450        } else {
 8451            panic!("expected completion menu to be open");
 8452        }
 8453    });
 8454}
 8455
 8456#[gpui::test]
 8457async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8458    init_test(cx, |_| {});
 8459
 8460    let mut cx = EditorLspTestContext::new_rust(
 8461        lsp::ServerCapabilities {
 8462            completion_provider: Some(lsp::CompletionOptions {
 8463                trigger_characters: Some(vec![".".to_string()]),
 8464                resolve_provider: Some(true),
 8465                ..Default::default()
 8466            }),
 8467            ..Default::default()
 8468        },
 8469        cx,
 8470    )
 8471    .await;
 8472
 8473    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8474    cx.simulate_keystroke(".");
 8475    let completion_item = lsp::CompletionItem {
 8476        label: "Some".into(),
 8477        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8478        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8479        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8480            kind: lsp::MarkupKind::Markdown,
 8481            value: "```rust\nSome(2)\n```".to_string(),
 8482        })),
 8483        deprecated: Some(false),
 8484        sort_text: Some("Some".to_string()),
 8485        filter_text: Some("Some".to_string()),
 8486        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8487        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8488            range: lsp::Range {
 8489                start: lsp::Position {
 8490                    line: 0,
 8491                    character: 22,
 8492                },
 8493                end: lsp::Position {
 8494                    line: 0,
 8495                    character: 22,
 8496                },
 8497            },
 8498            new_text: "Some(2)".to_string(),
 8499        })),
 8500        additional_text_edits: Some(vec![lsp::TextEdit {
 8501            range: lsp::Range {
 8502                start: lsp::Position {
 8503                    line: 0,
 8504                    character: 20,
 8505                },
 8506                end: lsp::Position {
 8507                    line: 0,
 8508                    character: 22,
 8509                },
 8510            },
 8511            new_text: "".to_string(),
 8512        }]),
 8513        ..Default::default()
 8514    };
 8515
 8516    let closure_completion_item = completion_item.clone();
 8517    let counter = Arc::new(AtomicUsize::new(0));
 8518    let counter_clone = counter.clone();
 8519    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8520        let task_completion_item = closure_completion_item.clone();
 8521        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8522        async move {
 8523            Ok(Some(lsp::CompletionResponse::Array(vec![
 8524                task_completion_item,
 8525            ])))
 8526        }
 8527    });
 8528
 8529    cx.condition(|editor, _| editor.context_menu_visible())
 8530        .await;
 8531    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8532    assert!(request.next().await.is_some());
 8533    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8534
 8535    cx.simulate_keystroke("S");
 8536    cx.simulate_keystroke("o");
 8537    cx.simulate_keystroke("m");
 8538    cx.condition(|editor, _| editor.context_menu_visible())
 8539        .await;
 8540    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8541    assert!(request.next().await.is_some());
 8542    assert!(request.next().await.is_some());
 8543    assert!(request.next().await.is_some());
 8544    request.close();
 8545    assert!(request.next().await.is_none());
 8546    assert_eq!(
 8547        counter.load(atomic::Ordering::Acquire),
 8548        4,
 8549        "With the completions menu open, only one LSP request should happen per input"
 8550    );
 8551}
 8552
 8553#[gpui::test]
 8554async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8555    init_test(cx, |_| {});
 8556    let mut cx = EditorTestContext::new(cx).await;
 8557    let language = Arc::new(Language::new(
 8558        LanguageConfig {
 8559            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8560            ..Default::default()
 8561        },
 8562        Some(tree_sitter_rust::LANGUAGE.into()),
 8563    ));
 8564    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8565
 8566    // If multiple selections intersect a line, the line is only toggled once.
 8567    cx.set_state(indoc! {"
 8568        fn a() {
 8569            «//b();
 8570            ˇ»// «c();
 8571            //ˇ»  d();
 8572        }
 8573    "});
 8574
 8575    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8576
 8577    cx.assert_editor_state(indoc! {"
 8578        fn a() {
 8579            «b();
 8580            c();
 8581            ˇ» d();
 8582        }
 8583    "});
 8584
 8585    // The comment prefix is inserted at the same column for every line in a
 8586    // selection.
 8587    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8588
 8589    cx.assert_editor_state(indoc! {"
 8590        fn a() {
 8591            // «b();
 8592            // c();
 8593            ˇ»//  d();
 8594        }
 8595    "});
 8596
 8597    // If a selection ends at the beginning of a line, that line is not toggled.
 8598    cx.set_selections_state(indoc! {"
 8599        fn a() {
 8600            // b();
 8601            «// c();
 8602        ˇ»    //  d();
 8603        }
 8604    "});
 8605
 8606    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8607
 8608    cx.assert_editor_state(indoc! {"
 8609        fn a() {
 8610            // b();
 8611            «c();
 8612        ˇ»    //  d();
 8613        }
 8614    "});
 8615
 8616    // If a selection span a single line and is empty, the line is toggled.
 8617    cx.set_state(indoc! {"
 8618        fn a() {
 8619            a();
 8620            b();
 8621        ˇ
 8622        }
 8623    "});
 8624
 8625    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8626
 8627    cx.assert_editor_state(indoc! {"
 8628        fn a() {
 8629            a();
 8630            b();
 8631        //•ˇ
 8632        }
 8633    "});
 8634
 8635    // If a selection span multiple lines, empty lines are not toggled.
 8636    cx.set_state(indoc! {"
 8637        fn a() {
 8638            «a();
 8639
 8640            c();ˇ»
 8641        }
 8642    "});
 8643
 8644    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8645
 8646    cx.assert_editor_state(indoc! {"
 8647        fn a() {
 8648            // «a();
 8649
 8650            // c();ˇ»
 8651        }
 8652    "});
 8653
 8654    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8655    cx.set_state(indoc! {"
 8656        fn a() {
 8657            «// a();
 8658            /// b();
 8659            //! c();ˇ»
 8660        }
 8661    "});
 8662
 8663    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8664
 8665    cx.assert_editor_state(indoc! {"
 8666        fn a() {
 8667            «a();
 8668            b();
 8669            c();ˇ»
 8670        }
 8671    "});
 8672}
 8673
 8674#[gpui::test]
 8675async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8676    init_test(cx, |_| {});
 8677    let mut cx = EditorTestContext::new(cx).await;
 8678    let language = Arc::new(Language::new(
 8679        LanguageConfig {
 8680            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8681            ..Default::default()
 8682        },
 8683        Some(tree_sitter_rust::LANGUAGE.into()),
 8684    ));
 8685    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8686
 8687    let toggle_comments = &ToggleComments {
 8688        advance_downwards: false,
 8689        ignore_indent: true,
 8690    };
 8691
 8692    // If multiple selections intersect a line, the line is only toggled once.
 8693    cx.set_state(indoc! {"
 8694        fn a() {
 8695        //    «b();
 8696        //    c();
 8697        //    ˇ» d();
 8698        }
 8699    "});
 8700
 8701    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8702
 8703    cx.assert_editor_state(indoc! {"
 8704        fn a() {
 8705            «b();
 8706            c();
 8707            ˇ» d();
 8708        }
 8709    "});
 8710
 8711    // The comment prefix is inserted at the beginning of each line
 8712    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8713
 8714    cx.assert_editor_state(indoc! {"
 8715        fn a() {
 8716        //    «b();
 8717        //    c();
 8718        //    ˇ» d();
 8719        }
 8720    "});
 8721
 8722    // If a selection ends at the beginning of a line, that line is not toggled.
 8723    cx.set_selections_state(indoc! {"
 8724        fn a() {
 8725        //    b();
 8726        //    «c();
 8727        ˇ»//     d();
 8728        }
 8729    "});
 8730
 8731    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8732
 8733    cx.assert_editor_state(indoc! {"
 8734        fn a() {
 8735        //    b();
 8736            «c();
 8737        ˇ»//     d();
 8738        }
 8739    "});
 8740
 8741    // If a selection span a single line and is empty, the line is toggled.
 8742    cx.set_state(indoc! {"
 8743        fn a() {
 8744            a();
 8745            b();
 8746        ˇ
 8747        }
 8748    "});
 8749
 8750    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8751
 8752    cx.assert_editor_state(indoc! {"
 8753        fn a() {
 8754            a();
 8755            b();
 8756        //ˇ
 8757        }
 8758    "});
 8759
 8760    // If a selection span multiple lines, empty lines are not toggled.
 8761    cx.set_state(indoc! {"
 8762        fn a() {
 8763            «a();
 8764
 8765            c();ˇ»
 8766        }
 8767    "});
 8768
 8769    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8770
 8771    cx.assert_editor_state(indoc! {"
 8772        fn a() {
 8773        //    «a();
 8774
 8775        //    c();ˇ»
 8776        }
 8777    "});
 8778
 8779    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8780    cx.set_state(indoc! {"
 8781        fn a() {
 8782        //    «a();
 8783        ///    b();
 8784        //!    c();ˇ»
 8785        }
 8786    "});
 8787
 8788    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8789
 8790    cx.assert_editor_state(indoc! {"
 8791        fn a() {
 8792            «a();
 8793            b();
 8794            c();ˇ»
 8795        }
 8796    "});
 8797}
 8798
 8799#[gpui::test]
 8800async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8801    init_test(cx, |_| {});
 8802
 8803    let language = Arc::new(Language::new(
 8804        LanguageConfig {
 8805            line_comments: vec!["// ".into()],
 8806            ..Default::default()
 8807        },
 8808        Some(tree_sitter_rust::LANGUAGE.into()),
 8809    ));
 8810
 8811    let mut cx = EditorTestContext::new(cx).await;
 8812
 8813    cx.language_registry().add(language.clone());
 8814    cx.update_buffer(|buffer, cx| {
 8815        buffer.set_language(Some(language), cx);
 8816    });
 8817
 8818    let toggle_comments = &ToggleComments {
 8819        advance_downwards: true,
 8820        ignore_indent: false,
 8821    };
 8822
 8823    // Single cursor on one line -> advance
 8824    // Cursor moves horizontally 3 characters as well on non-blank line
 8825    cx.set_state(indoc!(
 8826        "fn a() {
 8827             ˇdog();
 8828             cat();
 8829        }"
 8830    ));
 8831    cx.update_editor(|editor, cx| {
 8832        editor.toggle_comments(toggle_comments, cx);
 8833    });
 8834    cx.assert_editor_state(indoc!(
 8835        "fn a() {
 8836             // dog();
 8837             catˇ();
 8838        }"
 8839    ));
 8840
 8841    // Single selection on one line -> don't advance
 8842    cx.set_state(indoc!(
 8843        "fn a() {
 8844             «dog()ˇ»;
 8845             cat();
 8846        }"
 8847    ));
 8848    cx.update_editor(|editor, cx| {
 8849        editor.toggle_comments(toggle_comments, cx);
 8850    });
 8851    cx.assert_editor_state(indoc!(
 8852        "fn a() {
 8853             // «dog()ˇ»;
 8854             cat();
 8855        }"
 8856    ));
 8857
 8858    // Multiple cursors on one line -> advance
 8859    cx.set_state(indoc!(
 8860        "fn a() {
 8861             ˇdˇog();
 8862             cat();
 8863        }"
 8864    ));
 8865    cx.update_editor(|editor, cx| {
 8866        editor.toggle_comments(toggle_comments, cx);
 8867    });
 8868    cx.assert_editor_state(indoc!(
 8869        "fn a() {
 8870             // dog();
 8871             catˇ(ˇ);
 8872        }"
 8873    ));
 8874
 8875    // Multiple cursors on one line, with selection -> don't advance
 8876    cx.set_state(indoc!(
 8877        "fn a() {
 8878             ˇdˇog«()ˇ»;
 8879             cat();
 8880        }"
 8881    ));
 8882    cx.update_editor(|editor, cx| {
 8883        editor.toggle_comments(toggle_comments, cx);
 8884    });
 8885    cx.assert_editor_state(indoc!(
 8886        "fn a() {
 8887             // ˇdˇog«()ˇ»;
 8888             cat();
 8889        }"
 8890    ));
 8891
 8892    // Single cursor on one line -> advance
 8893    // Cursor moves to column 0 on blank line
 8894    cx.set_state(indoc!(
 8895        "fn a() {
 8896             ˇdog();
 8897
 8898             cat();
 8899        }"
 8900    ));
 8901    cx.update_editor(|editor, cx| {
 8902        editor.toggle_comments(toggle_comments, cx);
 8903    });
 8904    cx.assert_editor_state(indoc!(
 8905        "fn a() {
 8906             // dog();
 8907        ˇ
 8908             cat();
 8909        }"
 8910    ));
 8911
 8912    // Single cursor on one line -> advance
 8913    // Cursor starts and ends at column 0
 8914    cx.set_state(indoc!(
 8915        "fn a() {
 8916         ˇ    dog();
 8917             cat();
 8918        }"
 8919    ));
 8920    cx.update_editor(|editor, cx| {
 8921        editor.toggle_comments(toggle_comments, cx);
 8922    });
 8923    cx.assert_editor_state(indoc!(
 8924        "fn a() {
 8925             // dog();
 8926         ˇ    cat();
 8927        }"
 8928    ));
 8929}
 8930
 8931#[gpui::test]
 8932async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8933    init_test(cx, |_| {});
 8934
 8935    let mut cx = EditorTestContext::new(cx).await;
 8936
 8937    let html_language = Arc::new(
 8938        Language::new(
 8939            LanguageConfig {
 8940                name: "HTML".into(),
 8941                block_comment: Some(("<!-- ".into(), " -->".into())),
 8942                ..Default::default()
 8943            },
 8944            Some(tree_sitter_html::language()),
 8945        )
 8946        .with_injection_query(
 8947            r#"
 8948            (script_element
 8949                (raw_text) @content
 8950                (#set! "language" "javascript"))
 8951            "#,
 8952        )
 8953        .unwrap(),
 8954    );
 8955
 8956    let javascript_language = Arc::new(Language::new(
 8957        LanguageConfig {
 8958            name: "JavaScript".into(),
 8959            line_comments: vec!["// ".into()],
 8960            ..Default::default()
 8961        },
 8962        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8963    ));
 8964
 8965    cx.language_registry().add(html_language.clone());
 8966    cx.language_registry().add(javascript_language.clone());
 8967    cx.update_buffer(|buffer, cx| {
 8968        buffer.set_language(Some(html_language), cx);
 8969    });
 8970
 8971    // Toggle comments for empty selections
 8972    cx.set_state(
 8973        &r#"
 8974            <p>A</p>ˇ
 8975            <p>B</p>ˇ
 8976            <p>C</p>ˇ
 8977        "#
 8978        .unindent(),
 8979    );
 8980    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8981    cx.assert_editor_state(
 8982        &r#"
 8983            <!-- <p>A</p>ˇ -->
 8984            <!-- <p>B</p>ˇ -->
 8985            <!-- <p>C</p>ˇ -->
 8986        "#
 8987        .unindent(),
 8988    );
 8989    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8990    cx.assert_editor_state(
 8991        &r#"
 8992            <p>A</p>ˇ
 8993            <p>B</p>ˇ
 8994            <p>C</p>ˇ
 8995        "#
 8996        .unindent(),
 8997    );
 8998
 8999    // Toggle comments for mixture of empty and non-empty selections, where
 9000    // multiple selections occupy a given line.
 9001    cx.set_state(
 9002        &r#"
 9003            <p>A«</p>
 9004            <p>ˇ»B</p>ˇ
 9005            <p>C«</p>
 9006            <p>ˇ»D</p>ˇ
 9007        "#
 9008        .unindent(),
 9009    );
 9010
 9011    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9012    cx.assert_editor_state(
 9013        &r#"
 9014            <!-- <p>A«</p>
 9015            <p>ˇ»B</p>ˇ -->
 9016            <!-- <p>C«</p>
 9017            <p>ˇ»D</p>ˇ -->
 9018        "#
 9019        .unindent(),
 9020    );
 9021    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9022    cx.assert_editor_state(
 9023        &r#"
 9024            <p>A«</p>
 9025            <p>ˇ»B</p>ˇ
 9026            <p>C«</p>
 9027            <p>ˇ»D</p>ˇ
 9028        "#
 9029        .unindent(),
 9030    );
 9031
 9032    // Toggle comments when different languages are active for different
 9033    // selections.
 9034    cx.set_state(
 9035        &r#"
 9036            ˇ<script>
 9037                ˇvar x = new Y();
 9038            ˇ</script>
 9039        "#
 9040        .unindent(),
 9041    );
 9042    cx.executor().run_until_parked();
 9043    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9044    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9045    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9046    cx.assert_editor_state(
 9047        &r#"
 9048            <!-- ˇ<script> -->
 9049                // ˇvar x = new Y();
 9050            // ˇ</script>
 9051        "#
 9052        .unindent(),
 9053    );
 9054}
 9055
 9056#[gpui::test]
 9057fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9058    init_test(cx, |_| {});
 9059
 9060    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9061    let multibuffer = cx.new_model(|cx| {
 9062        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9063        multibuffer.push_excerpts(
 9064            buffer.clone(),
 9065            [
 9066                ExcerptRange {
 9067                    context: Point::new(0, 0)..Point::new(0, 4),
 9068                    primary: None,
 9069                },
 9070                ExcerptRange {
 9071                    context: Point::new(1, 0)..Point::new(1, 4),
 9072                    primary: None,
 9073                },
 9074            ],
 9075            cx,
 9076        );
 9077        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9078        multibuffer
 9079    });
 9080
 9081    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9082    view.update(cx, |view, cx| {
 9083        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9084        view.change_selections(None, cx, |s| {
 9085            s.select_ranges([
 9086                Point::new(0, 0)..Point::new(0, 0),
 9087                Point::new(1, 0)..Point::new(1, 0),
 9088            ])
 9089        });
 9090
 9091        view.handle_input("X", cx);
 9092        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9093        assert_eq!(
 9094            view.selections.ranges(cx),
 9095            [
 9096                Point::new(0, 1)..Point::new(0, 1),
 9097                Point::new(1, 1)..Point::new(1, 1),
 9098            ]
 9099        );
 9100
 9101        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9102        view.change_selections(None, cx, |s| {
 9103            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9104        });
 9105        view.backspace(&Default::default(), cx);
 9106        assert_eq!(view.text(cx), "Xa\nbbb");
 9107        assert_eq!(
 9108            view.selections.ranges(cx),
 9109            [Point::new(1, 0)..Point::new(1, 0)]
 9110        );
 9111
 9112        view.change_selections(None, cx, |s| {
 9113            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9114        });
 9115        view.backspace(&Default::default(), cx);
 9116        assert_eq!(view.text(cx), "X\nbb");
 9117        assert_eq!(
 9118            view.selections.ranges(cx),
 9119            [Point::new(0, 1)..Point::new(0, 1)]
 9120        );
 9121    });
 9122}
 9123
 9124#[gpui::test]
 9125fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9126    init_test(cx, |_| {});
 9127
 9128    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9129    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9130        indoc! {"
 9131            [aaaa
 9132            (bbbb]
 9133            cccc)",
 9134        },
 9135        markers.clone(),
 9136    );
 9137    let excerpt_ranges = markers.into_iter().map(|marker| {
 9138        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9139        ExcerptRange {
 9140            context,
 9141            primary: None,
 9142        }
 9143    });
 9144    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9145    let multibuffer = cx.new_model(|cx| {
 9146        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9147        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9148        multibuffer
 9149    });
 9150
 9151    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9152    view.update(cx, |view, cx| {
 9153        let (expected_text, selection_ranges) = marked_text_ranges(
 9154            indoc! {"
 9155                aaaa
 9156                bˇbbb
 9157                bˇbbˇb
 9158                cccc"
 9159            },
 9160            true,
 9161        );
 9162        assert_eq!(view.text(cx), expected_text);
 9163        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9164
 9165        view.handle_input("X", cx);
 9166
 9167        let (expected_text, expected_selections) = marked_text_ranges(
 9168            indoc! {"
 9169                aaaa
 9170                bXˇbbXb
 9171                bXˇbbXˇb
 9172                cccc"
 9173            },
 9174            false,
 9175        );
 9176        assert_eq!(view.text(cx), expected_text);
 9177        assert_eq!(view.selections.ranges(cx), expected_selections);
 9178
 9179        view.newline(&Newline, cx);
 9180        let (expected_text, expected_selections) = marked_text_ranges(
 9181            indoc! {"
 9182                aaaa
 9183                bX
 9184                ˇbbX
 9185                b
 9186                bX
 9187                ˇbbX
 9188                ˇb
 9189                cccc"
 9190            },
 9191            false,
 9192        );
 9193        assert_eq!(view.text(cx), expected_text);
 9194        assert_eq!(view.selections.ranges(cx), expected_selections);
 9195    });
 9196}
 9197
 9198#[gpui::test]
 9199fn test_refresh_selections(cx: &mut TestAppContext) {
 9200    init_test(cx, |_| {});
 9201
 9202    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9203    let mut excerpt1_id = None;
 9204    let multibuffer = cx.new_model(|cx| {
 9205        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9206        excerpt1_id = multibuffer
 9207            .push_excerpts(
 9208                buffer.clone(),
 9209                [
 9210                    ExcerptRange {
 9211                        context: Point::new(0, 0)..Point::new(1, 4),
 9212                        primary: None,
 9213                    },
 9214                    ExcerptRange {
 9215                        context: Point::new(1, 0)..Point::new(2, 4),
 9216                        primary: None,
 9217                    },
 9218                ],
 9219                cx,
 9220            )
 9221            .into_iter()
 9222            .next();
 9223        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9224        multibuffer
 9225    });
 9226
 9227    let editor = cx.add_window(|cx| {
 9228        let mut editor = build_editor(multibuffer.clone(), cx);
 9229        let snapshot = editor.snapshot(cx);
 9230        editor.change_selections(None, cx, |s| {
 9231            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9232        });
 9233        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9234        assert_eq!(
 9235            editor.selections.ranges(cx),
 9236            [
 9237                Point::new(1, 3)..Point::new(1, 3),
 9238                Point::new(2, 1)..Point::new(2, 1),
 9239            ]
 9240        );
 9241        editor
 9242    });
 9243
 9244    // Refreshing selections is a no-op when excerpts haven't changed.
 9245    _ = editor.update(cx, |editor, cx| {
 9246        editor.change_selections(None, cx, |s| s.refresh());
 9247        assert_eq!(
 9248            editor.selections.ranges(cx),
 9249            [
 9250                Point::new(1, 3)..Point::new(1, 3),
 9251                Point::new(2, 1)..Point::new(2, 1),
 9252            ]
 9253        );
 9254    });
 9255
 9256    multibuffer.update(cx, |multibuffer, cx| {
 9257        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9258    });
 9259    _ = editor.update(cx, |editor, cx| {
 9260        // Removing an excerpt causes the first selection to become degenerate.
 9261        assert_eq!(
 9262            editor.selections.ranges(cx),
 9263            [
 9264                Point::new(0, 0)..Point::new(0, 0),
 9265                Point::new(0, 1)..Point::new(0, 1)
 9266            ]
 9267        );
 9268
 9269        // Refreshing selections will relocate the first selection to the original buffer
 9270        // location.
 9271        editor.change_selections(None, cx, |s| s.refresh());
 9272        assert_eq!(
 9273            editor.selections.ranges(cx),
 9274            [
 9275                Point::new(0, 1)..Point::new(0, 1),
 9276                Point::new(0, 3)..Point::new(0, 3)
 9277            ]
 9278        );
 9279        assert!(editor.selections.pending_anchor().is_some());
 9280    });
 9281}
 9282
 9283#[gpui::test]
 9284fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9285    init_test(cx, |_| {});
 9286
 9287    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9288    let mut excerpt1_id = None;
 9289    let multibuffer = cx.new_model(|cx| {
 9290        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9291        excerpt1_id = multibuffer
 9292            .push_excerpts(
 9293                buffer.clone(),
 9294                [
 9295                    ExcerptRange {
 9296                        context: Point::new(0, 0)..Point::new(1, 4),
 9297                        primary: None,
 9298                    },
 9299                    ExcerptRange {
 9300                        context: Point::new(1, 0)..Point::new(2, 4),
 9301                        primary: None,
 9302                    },
 9303                ],
 9304                cx,
 9305            )
 9306            .into_iter()
 9307            .next();
 9308        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9309        multibuffer
 9310    });
 9311
 9312    let editor = cx.add_window(|cx| {
 9313        let mut editor = build_editor(multibuffer.clone(), cx);
 9314        let snapshot = editor.snapshot(cx);
 9315        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9316        assert_eq!(
 9317            editor.selections.ranges(cx),
 9318            [Point::new(1, 3)..Point::new(1, 3)]
 9319        );
 9320        editor
 9321    });
 9322
 9323    multibuffer.update(cx, |multibuffer, cx| {
 9324        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9325    });
 9326    _ = editor.update(cx, |editor, cx| {
 9327        assert_eq!(
 9328            editor.selections.ranges(cx),
 9329            [Point::new(0, 0)..Point::new(0, 0)]
 9330        );
 9331
 9332        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9333        editor.change_selections(None, cx, |s| s.refresh());
 9334        assert_eq!(
 9335            editor.selections.ranges(cx),
 9336            [Point::new(0, 3)..Point::new(0, 3)]
 9337        );
 9338        assert!(editor.selections.pending_anchor().is_some());
 9339    });
 9340}
 9341
 9342#[gpui::test]
 9343async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9344    init_test(cx, |_| {});
 9345
 9346    let language = Arc::new(
 9347        Language::new(
 9348            LanguageConfig {
 9349                brackets: BracketPairConfig {
 9350                    pairs: vec![
 9351                        BracketPair {
 9352                            start: "{".to_string(),
 9353                            end: "}".to_string(),
 9354                            close: true,
 9355                            surround: true,
 9356                            newline: true,
 9357                        },
 9358                        BracketPair {
 9359                            start: "/* ".to_string(),
 9360                            end: " */".to_string(),
 9361                            close: true,
 9362                            surround: true,
 9363                            newline: true,
 9364                        },
 9365                    ],
 9366                    ..Default::default()
 9367                },
 9368                ..Default::default()
 9369            },
 9370            Some(tree_sitter_rust::LANGUAGE.into()),
 9371        )
 9372        .with_indents_query("")
 9373        .unwrap(),
 9374    );
 9375
 9376    let text = concat!(
 9377        "{   }\n",     //
 9378        "  x\n",       //
 9379        "  /*   */\n", //
 9380        "x\n",         //
 9381        "{{} }\n",     //
 9382    );
 9383
 9384    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9385    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9386    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9387    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9388        .await;
 9389
 9390    view.update(cx, |view, cx| {
 9391        view.change_selections(None, cx, |s| {
 9392            s.select_display_ranges([
 9393                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9394                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9395                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9396            ])
 9397        });
 9398        view.newline(&Newline, cx);
 9399
 9400        assert_eq!(
 9401            view.buffer().read(cx).read(cx).text(),
 9402            concat!(
 9403                "{ \n",    // Suppress rustfmt
 9404                "\n",      //
 9405                "}\n",     //
 9406                "  x\n",   //
 9407                "  /* \n", //
 9408                "  \n",    //
 9409                "  */\n",  //
 9410                "x\n",     //
 9411                "{{} \n",  //
 9412                "}\n",     //
 9413            )
 9414        );
 9415    });
 9416}
 9417
 9418#[gpui::test]
 9419fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9420    init_test(cx, |_| {});
 9421
 9422    let editor = cx.add_window(|cx| {
 9423        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9424        build_editor(buffer.clone(), cx)
 9425    });
 9426
 9427    _ = editor.update(cx, |editor, cx| {
 9428        struct Type1;
 9429        struct Type2;
 9430
 9431        let buffer = editor.buffer.read(cx).snapshot(cx);
 9432
 9433        let anchor_range =
 9434            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9435
 9436        editor.highlight_background::<Type1>(
 9437            &[
 9438                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9439                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9440                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9441                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9442            ],
 9443            |_| Hsla::red(),
 9444            cx,
 9445        );
 9446        editor.highlight_background::<Type2>(
 9447            &[
 9448                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9449                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9450                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9451                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9452            ],
 9453            |_| Hsla::green(),
 9454            cx,
 9455        );
 9456
 9457        let snapshot = editor.snapshot(cx);
 9458        let mut highlighted_ranges = editor.background_highlights_in_range(
 9459            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9460            &snapshot,
 9461            cx.theme().colors(),
 9462        );
 9463        // Enforce a consistent ordering based on color without relying on the ordering of the
 9464        // highlight's `TypeId` which is non-executor.
 9465        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9466        assert_eq!(
 9467            highlighted_ranges,
 9468            &[
 9469                (
 9470                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9471                    Hsla::red(),
 9472                ),
 9473                (
 9474                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9475                    Hsla::red(),
 9476                ),
 9477                (
 9478                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9479                    Hsla::green(),
 9480                ),
 9481                (
 9482                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9483                    Hsla::green(),
 9484                ),
 9485            ]
 9486        );
 9487        assert_eq!(
 9488            editor.background_highlights_in_range(
 9489                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9490                &snapshot,
 9491                cx.theme().colors(),
 9492            ),
 9493            &[(
 9494                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9495                Hsla::red(),
 9496            )]
 9497        );
 9498    });
 9499}
 9500
 9501#[gpui::test]
 9502async fn test_following(cx: &mut gpui::TestAppContext) {
 9503    init_test(cx, |_| {});
 9504
 9505    let fs = FakeFs::new(cx.executor());
 9506    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9507
 9508    let buffer = project.update(cx, |project, cx| {
 9509        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9510        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9511    });
 9512    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9513    let follower = cx.update(|cx| {
 9514        cx.open_window(
 9515            WindowOptions {
 9516                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9517                    gpui::Point::new(px(0.), px(0.)),
 9518                    gpui::Point::new(px(10.), px(80.)),
 9519                ))),
 9520                ..Default::default()
 9521            },
 9522            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9523        )
 9524        .unwrap()
 9525    });
 9526
 9527    let is_still_following = Rc::new(RefCell::new(true));
 9528    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9529    let pending_update = Rc::new(RefCell::new(None));
 9530    _ = follower.update(cx, {
 9531        let update = pending_update.clone();
 9532        let is_still_following = is_still_following.clone();
 9533        let follower_edit_event_count = follower_edit_event_count.clone();
 9534        |_, cx| {
 9535            cx.subscribe(
 9536                &leader.root_view(cx).unwrap(),
 9537                move |_, leader, event, cx| {
 9538                    leader
 9539                        .read(cx)
 9540                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9541                },
 9542            )
 9543            .detach();
 9544
 9545            cx.subscribe(
 9546                &follower.root_view(cx).unwrap(),
 9547                move |_, _, event: &EditorEvent, _cx| {
 9548                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9549                        *is_still_following.borrow_mut() = false;
 9550                    }
 9551
 9552                    if let EditorEvent::BufferEdited = event {
 9553                        *follower_edit_event_count.borrow_mut() += 1;
 9554                    }
 9555                },
 9556            )
 9557            .detach();
 9558        }
 9559    });
 9560
 9561    // Update the selections only
 9562    _ = leader.update(cx, |leader, cx| {
 9563        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9564    });
 9565    follower
 9566        .update(cx, |follower, cx| {
 9567            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9568        })
 9569        .unwrap()
 9570        .await
 9571        .unwrap();
 9572    _ = follower.update(cx, |follower, cx| {
 9573        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9574    });
 9575    assert!(*is_still_following.borrow());
 9576    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9577
 9578    // Update the scroll position only
 9579    _ = leader.update(cx, |leader, cx| {
 9580        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9581    });
 9582    follower
 9583        .update(cx, |follower, cx| {
 9584            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9585        })
 9586        .unwrap()
 9587        .await
 9588        .unwrap();
 9589    assert_eq!(
 9590        follower
 9591            .update(cx, |follower, cx| follower.scroll_position(cx))
 9592            .unwrap(),
 9593        gpui::Point::new(1.5, 3.5)
 9594    );
 9595    assert!(*is_still_following.borrow());
 9596    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9597
 9598    // Update the selections and scroll position. The follower's scroll position is updated
 9599    // via autoscroll, not via the leader's exact scroll position.
 9600    _ = leader.update(cx, |leader, cx| {
 9601        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9602        leader.request_autoscroll(Autoscroll::newest(), cx);
 9603        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9604    });
 9605    follower
 9606        .update(cx, |follower, cx| {
 9607            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9608        })
 9609        .unwrap()
 9610        .await
 9611        .unwrap();
 9612    _ = follower.update(cx, |follower, cx| {
 9613        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9614        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9615    });
 9616    assert!(*is_still_following.borrow());
 9617
 9618    // Creating a pending selection that precedes another selection
 9619    _ = leader.update(cx, |leader, cx| {
 9620        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9621        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9622    });
 9623    follower
 9624        .update(cx, |follower, cx| {
 9625            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9626        })
 9627        .unwrap()
 9628        .await
 9629        .unwrap();
 9630    _ = follower.update(cx, |follower, cx| {
 9631        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9632    });
 9633    assert!(*is_still_following.borrow());
 9634
 9635    // Extend the pending selection so that it surrounds another selection
 9636    _ = leader.update(cx, |leader, cx| {
 9637        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9638    });
 9639    follower
 9640        .update(cx, |follower, cx| {
 9641            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9642        })
 9643        .unwrap()
 9644        .await
 9645        .unwrap();
 9646    _ = follower.update(cx, |follower, cx| {
 9647        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9648    });
 9649
 9650    // Scrolling locally breaks the follow
 9651    _ = follower.update(cx, |follower, cx| {
 9652        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9653        follower.set_scroll_anchor(
 9654            ScrollAnchor {
 9655                anchor: top_anchor,
 9656                offset: gpui::Point::new(0.0, 0.5),
 9657            },
 9658            cx,
 9659        );
 9660    });
 9661    assert!(!(*is_still_following.borrow()));
 9662}
 9663
 9664#[gpui::test]
 9665async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9666    init_test(cx, |_| {});
 9667
 9668    let fs = FakeFs::new(cx.executor());
 9669    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9670    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9671    let pane = workspace
 9672        .update(cx, |workspace, _| workspace.active_pane().clone())
 9673        .unwrap();
 9674
 9675    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9676
 9677    let leader = pane.update(cx, |_, cx| {
 9678        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9679        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9680    });
 9681
 9682    // Start following the editor when it has no excerpts.
 9683    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9684    let follower_1 = cx
 9685        .update_window(*workspace.deref(), |_, cx| {
 9686            Editor::from_state_proto(
 9687                workspace.root_view(cx).unwrap(),
 9688                ViewId {
 9689                    creator: Default::default(),
 9690                    id: 0,
 9691                },
 9692                &mut state_message,
 9693                cx,
 9694            )
 9695        })
 9696        .unwrap()
 9697        .unwrap()
 9698        .await
 9699        .unwrap();
 9700
 9701    let update_message = Rc::new(RefCell::new(None));
 9702    follower_1.update(cx, {
 9703        let update = update_message.clone();
 9704        |_, cx| {
 9705            cx.subscribe(&leader, move |_, leader, event, cx| {
 9706                leader
 9707                    .read(cx)
 9708                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9709            })
 9710            .detach();
 9711        }
 9712    });
 9713
 9714    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9715        (
 9716            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9717            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9718        )
 9719    });
 9720
 9721    // Insert some excerpts.
 9722    leader.update(cx, |leader, cx| {
 9723        leader.buffer.update(cx, |multibuffer, cx| {
 9724            let excerpt_ids = multibuffer.push_excerpts(
 9725                buffer_1.clone(),
 9726                [
 9727                    ExcerptRange {
 9728                        context: 1..6,
 9729                        primary: None,
 9730                    },
 9731                    ExcerptRange {
 9732                        context: 12..15,
 9733                        primary: None,
 9734                    },
 9735                    ExcerptRange {
 9736                        context: 0..3,
 9737                        primary: None,
 9738                    },
 9739                ],
 9740                cx,
 9741            );
 9742            multibuffer.insert_excerpts_after(
 9743                excerpt_ids[0],
 9744                buffer_2.clone(),
 9745                [
 9746                    ExcerptRange {
 9747                        context: 8..12,
 9748                        primary: None,
 9749                    },
 9750                    ExcerptRange {
 9751                        context: 0..6,
 9752                        primary: None,
 9753                    },
 9754                ],
 9755                cx,
 9756            );
 9757        });
 9758    });
 9759
 9760    // Apply the update of adding the excerpts.
 9761    follower_1
 9762        .update(cx, |follower, cx| {
 9763            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9764        })
 9765        .await
 9766        .unwrap();
 9767    assert_eq!(
 9768        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9769        leader.update(cx, |editor, cx| editor.text(cx))
 9770    );
 9771    update_message.borrow_mut().take();
 9772
 9773    // Start following separately after it already has excerpts.
 9774    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9775    let follower_2 = cx
 9776        .update_window(*workspace.deref(), |_, cx| {
 9777            Editor::from_state_proto(
 9778                workspace.root_view(cx).unwrap().clone(),
 9779                ViewId {
 9780                    creator: Default::default(),
 9781                    id: 0,
 9782                },
 9783                &mut state_message,
 9784                cx,
 9785            )
 9786        })
 9787        .unwrap()
 9788        .unwrap()
 9789        .await
 9790        .unwrap();
 9791    assert_eq!(
 9792        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9793        leader.update(cx, |editor, cx| editor.text(cx))
 9794    );
 9795
 9796    // Remove some excerpts.
 9797    leader.update(cx, |leader, cx| {
 9798        leader.buffer.update(cx, |multibuffer, cx| {
 9799            let excerpt_ids = multibuffer.excerpt_ids();
 9800            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9801            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9802        });
 9803    });
 9804
 9805    // Apply the update of removing the excerpts.
 9806    follower_1
 9807        .update(cx, |follower, cx| {
 9808            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9809        })
 9810        .await
 9811        .unwrap();
 9812    follower_2
 9813        .update(cx, |follower, cx| {
 9814            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9815        })
 9816        .await
 9817        .unwrap();
 9818    update_message.borrow_mut().take();
 9819    assert_eq!(
 9820        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9821        leader.update(cx, |editor, cx| editor.text(cx))
 9822    );
 9823}
 9824
 9825#[gpui::test]
 9826async fn go_to_prev_overlapping_diagnostic(
 9827    executor: BackgroundExecutor,
 9828    cx: &mut gpui::TestAppContext,
 9829) {
 9830    init_test(cx, |_| {});
 9831
 9832    let mut cx = EditorTestContext::new(cx).await;
 9833    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9834
 9835    cx.set_state(indoc! {"
 9836        ˇfn func(abc def: i32) -> u32 {
 9837        }
 9838    "});
 9839
 9840    cx.update(|cx| {
 9841        project.update(cx, |project, cx| {
 9842            project
 9843                .update_diagnostics(
 9844                    LanguageServerId(0),
 9845                    lsp::PublishDiagnosticsParams {
 9846                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9847                        version: None,
 9848                        diagnostics: vec![
 9849                            lsp::Diagnostic {
 9850                                range: lsp::Range::new(
 9851                                    lsp::Position::new(0, 11),
 9852                                    lsp::Position::new(0, 12),
 9853                                ),
 9854                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9855                                ..Default::default()
 9856                            },
 9857                            lsp::Diagnostic {
 9858                                range: lsp::Range::new(
 9859                                    lsp::Position::new(0, 12),
 9860                                    lsp::Position::new(0, 15),
 9861                                ),
 9862                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9863                                ..Default::default()
 9864                            },
 9865                            lsp::Diagnostic {
 9866                                range: lsp::Range::new(
 9867                                    lsp::Position::new(0, 25),
 9868                                    lsp::Position::new(0, 28),
 9869                                ),
 9870                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9871                                ..Default::default()
 9872                            },
 9873                        ],
 9874                    },
 9875                    &[],
 9876                    cx,
 9877                )
 9878                .unwrap()
 9879        });
 9880    });
 9881
 9882    executor.run_until_parked();
 9883
 9884    cx.update_editor(|editor, cx| {
 9885        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9886    });
 9887
 9888    cx.assert_editor_state(indoc! {"
 9889        fn func(abc def: i32) -> ˇu32 {
 9890        }
 9891    "});
 9892
 9893    cx.update_editor(|editor, cx| {
 9894        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9895    });
 9896
 9897    cx.assert_editor_state(indoc! {"
 9898        fn func(abc ˇdef: i32) -> u32 {
 9899        }
 9900    "});
 9901
 9902    cx.update_editor(|editor, cx| {
 9903        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9904    });
 9905
 9906    cx.assert_editor_state(indoc! {"
 9907        fn func(abcˇ def: i32) -> u32 {
 9908        }
 9909    "});
 9910
 9911    cx.update_editor(|editor, cx| {
 9912        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9913    });
 9914
 9915    cx.assert_editor_state(indoc! {"
 9916        fn func(abc def: i32) -> ˇu32 {
 9917        }
 9918    "});
 9919}
 9920
 9921#[gpui::test]
 9922async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9923    init_test(cx, |_| {});
 9924
 9925    let mut cx = EditorTestContext::new(cx).await;
 9926
 9927    cx.set_state(indoc! {"
 9928        fn func(abˇc def: i32) -> u32 {
 9929        }
 9930    "});
 9931    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9932
 9933    cx.update(|cx| {
 9934        project.update(cx, |project, cx| {
 9935            project.update_diagnostics(
 9936                LanguageServerId(0),
 9937                lsp::PublishDiagnosticsParams {
 9938                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9939                    version: None,
 9940                    diagnostics: vec![lsp::Diagnostic {
 9941                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9942                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9943                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9944                        ..Default::default()
 9945                    }],
 9946                },
 9947                &[],
 9948                cx,
 9949            )
 9950        })
 9951    }).unwrap();
 9952    cx.run_until_parked();
 9953    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9954    cx.run_until_parked();
 9955    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9956}
 9957
 9958#[gpui::test]
 9959async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9960    init_test(cx, |_| {});
 9961
 9962    let mut cx = EditorTestContext::new(cx).await;
 9963
 9964    let diff_base = r#"
 9965        use some::mod;
 9966
 9967        const A: u32 = 42;
 9968
 9969        fn main() {
 9970            println!("hello");
 9971
 9972            println!("world");
 9973        }
 9974        "#
 9975    .unindent();
 9976
 9977    // Edits are modified, removed, modified, added
 9978    cx.set_state(
 9979        &r#"
 9980        use some::modified;
 9981
 9982        ˇ
 9983        fn main() {
 9984            println!("hello there");
 9985
 9986            println!("around the");
 9987            println!("world");
 9988        }
 9989        "#
 9990        .unindent(),
 9991    );
 9992
 9993    cx.set_diff_base(Some(&diff_base));
 9994    executor.run_until_parked();
 9995
 9996    cx.update_editor(|editor, cx| {
 9997        //Wrap around the bottom of the buffer
 9998        for _ in 0..3 {
 9999            editor.go_to_next_hunk(&GoToHunk, cx);
10000        }
10001    });
10002
10003    cx.assert_editor_state(
10004        &r#"
10005        ˇuse some::modified;
10006
10007
10008        fn main() {
10009            println!("hello there");
10010
10011            println!("around the");
10012            println!("world");
10013        }
10014        "#
10015        .unindent(),
10016    );
10017
10018    cx.update_editor(|editor, cx| {
10019        //Wrap around the top of the buffer
10020        for _ in 0..2 {
10021            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10022        }
10023    });
10024
10025    cx.assert_editor_state(
10026        &r#"
10027        use some::modified;
10028
10029
10030        fn main() {
10031        ˇ    println!("hello there");
10032
10033            println!("around the");
10034            println!("world");
10035        }
10036        "#
10037        .unindent(),
10038    );
10039
10040    cx.update_editor(|editor, cx| {
10041        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10042    });
10043
10044    cx.assert_editor_state(
10045        &r#"
10046        use some::modified;
10047
10048        ˇ
10049        fn main() {
10050            println!("hello there");
10051
10052            println!("around the");
10053            println!("world");
10054        }
10055        "#
10056        .unindent(),
10057    );
10058
10059    cx.update_editor(|editor, cx| {
10060        for _ in 0..3 {
10061            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10062        }
10063    });
10064
10065    cx.assert_editor_state(
10066        &r#"
10067        use some::modified;
10068
10069
10070        fn main() {
10071        ˇ    println!("hello there");
10072
10073            println!("around the");
10074            println!("world");
10075        }
10076        "#
10077        .unindent(),
10078    );
10079
10080    cx.update_editor(|editor, cx| {
10081        editor.fold(&Fold, cx);
10082
10083        //Make sure that the fold only gets one hunk
10084        for _ in 0..4 {
10085            editor.go_to_next_hunk(&GoToHunk, cx);
10086        }
10087    });
10088
10089    cx.assert_editor_state(
10090        &r#"
10091        ˇuse some::modified;
10092
10093
10094        fn main() {
10095            println!("hello there");
10096
10097            println!("around the");
10098            println!("world");
10099        }
10100        "#
10101        .unindent(),
10102    );
10103}
10104
10105#[test]
10106fn test_split_words() {
10107    fn split(text: &str) -> Vec<&str> {
10108        split_words(text).collect()
10109    }
10110
10111    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10112    assert_eq!(split("hello_world"), &["hello_", "world"]);
10113    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10114    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10115    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10116    assert_eq!(split("helloworld"), &["helloworld"]);
10117
10118    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10119}
10120
10121#[gpui::test]
10122async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10123    init_test(cx, |_| {});
10124
10125    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10126    let mut assert = |before, after| {
10127        let _state_context = cx.set_state(before);
10128        cx.update_editor(|editor, cx| {
10129            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10130        });
10131        cx.assert_editor_state(after);
10132    };
10133
10134    // Outside bracket jumps to outside of matching bracket
10135    assert("console.logˇ(var);", "console.log(var)ˇ;");
10136    assert("console.log(var)ˇ;", "console.logˇ(var);");
10137
10138    // Inside bracket jumps to inside of matching bracket
10139    assert("console.log(ˇvar);", "console.log(varˇ);");
10140    assert("console.log(varˇ);", "console.log(ˇvar);");
10141
10142    // When outside a bracket and inside, favor jumping to the inside bracket
10143    assert(
10144        "console.log('foo', [1, 2, 3]ˇ);",
10145        "console.log(ˇ'foo', [1, 2, 3]);",
10146    );
10147    assert(
10148        "console.log(ˇ'foo', [1, 2, 3]);",
10149        "console.log('foo', [1, 2, 3]ˇ);",
10150    );
10151
10152    // Bias forward if two options are equally likely
10153    assert(
10154        "let result = curried_fun()ˇ();",
10155        "let result = curried_fun()()ˇ;",
10156    );
10157
10158    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10159    assert(
10160        indoc! {"
10161            function test() {
10162                console.log('test')ˇ
10163            }"},
10164        indoc! {"
10165            function test() {
10166                console.logˇ('test')
10167            }"},
10168    );
10169}
10170
10171#[gpui::test]
10172async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10173    init_test(cx, |_| {});
10174
10175    let fs = FakeFs::new(cx.executor());
10176    fs.insert_tree(
10177        "/a",
10178        json!({
10179            "main.rs": "fn main() { let a = 5; }",
10180            "other.rs": "// Test file",
10181        }),
10182    )
10183    .await;
10184    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10185
10186    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10187    language_registry.add(Arc::new(Language::new(
10188        LanguageConfig {
10189            name: "Rust".into(),
10190            matcher: LanguageMatcher {
10191                path_suffixes: vec!["rs".to_string()],
10192                ..Default::default()
10193            },
10194            brackets: BracketPairConfig {
10195                pairs: vec![BracketPair {
10196                    start: "{".to_string(),
10197                    end: "}".to_string(),
10198                    close: true,
10199                    surround: true,
10200                    newline: true,
10201                }],
10202                disabled_scopes_by_bracket_ix: Vec::new(),
10203            },
10204            ..Default::default()
10205        },
10206        Some(tree_sitter_rust::LANGUAGE.into()),
10207    )));
10208    let mut fake_servers = language_registry.register_fake_lsp(
10209        "Rust",
10210        FakeLspAdapter {
10211            capabilities: lsp::ServerCapabilities {
10212                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10213                    first_trigger_character: "{".to_string(),
10214                    more_trigger_character: None,
10215                }),
10216                ..Default::default()
10217            },
10218            ..Default::default()
10219        },
10220    );
10221
10222    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10223
10224    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10225
10226    let worktree_id = workspace
10227        .update(cx, |workspace, cx| {
10228            workspace.project().update(cx, |project, cx| {
10229                project.worktrees(cx).next().unwrap().read(cx).id()
10230            })
10231        })
10232        .unwrap();
10233
10234    let buffer = project
10235        .update(cx, |project, cx| {
10236            project.open_local_buffer("/a/main.rs", cx)
10237        })
10238        .await
10239        .unwrap();
10240    cx.executor().run_until_parked();
10241    cx.executor().start_waiting();
10242    let fake_server = fake_servers.next().await.unwrap();
10243    let editor_handle = workspace
10244        .update(cx, |workspace, cx| {
10245            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10246        })
10247        .unwrap()
10248        .await
10249        .unwrap()
10250        .downcast::<Editor>()
10251        .unwrap();
10252
10253    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10254        assert_eq!(
10255            params.text_document_position.text_document.uri,
10256            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10257        );
10258        assert_eq!(
10259            params.text_document_position.position,
10260            lsp::Position::new(0, 21),
10261        );
10262
10263        Ok(Some(vec![lsp::TextEdit {
10264            new_text: "]".to_string(),
10265            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10266        }]))
10267    });
10268
10269    editor_handle.update(cx, |editor, cx| {
10270        editor.focus(cx);
10271        editor.change_selections(None, cx, |s| {
10272            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10273        });
10274        editor.handle_input("{", cx);
10275    });
10276
10277    cx.executor().run_until_parked();
10278
10279    buffer.update(cx, |buffer, _| {
10280        assert_eq!(
10281            buffer.text(),
10282            "fn main() { let a = {5}; }",
10283            "No extra braces from on type formatting should appear in the buffer"
10284        )
10285    });
10286}
10287
10288#[gpui::test]
10289async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10290    init_test(cx, |_| {});
10291
10292    let fs = FakeFs::new(cx.executor());
10293    fs.insert_tree(
10294        "/a",
10295        json!({
10296            "main.rs": "fn main() { let a = 5; }",
10297            "other.rs": "// Test file",
10298        }),
10299    )
10300    .await;
10301
10302    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10303
10304    let server_restarts = Arc::new(AtomicUsize::new(0));
10305    let closure_restarts = Arc::clone(&server_restarts);
10306    let language_server_name = "test language server";
10307    let language_name: LanguageName = "Rust".into();
10308
10309    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10310    language_registry.add(Arc::new(Language::new(
10311        LanguageConfig {
10312            name: language_name.clone(),
10313            matcher: LanguageMatcher {
10314                path_suffixes: vec!["rs".to_string()],
10315                ..Default::default()
10316            },
10317            ..Default::default()
10318        },
10319        Some(tree_sitter_rust::LANGUAGE.into()),
10320    )));
10321    let mut fake_servers = language_registry.register_fake_lsp(
10322        "Rust",
10323        FakeLspAdapter {
10324            name: language_server_name,
10325            initialization_options: Some(json!({
10326                "testOptionValue": true
10327            })),
10328            initializer: Some(Box::new(move |fake_server| {
10329                let task_restarts = Arc::clone(&closure_restarts);
10330                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10331                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10332                    futures::future::ready(Ok(()))
10333                });
10334            })),
10335            ..Default::default()
10336        },
10337    );
10338
10339    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10340    let _buffer = project
10341        .update(cx, |project, cx| {
10342            project.open_local_buffer("/a/main.rs", cx)
10343        })
10344        .await
10345        .unwrap();
10346    let _fake_server = fake_servers.next().await.unwrap();
10347    update_test_language_settings(cx, |language_settings| {
10348        language_settings.languages.insert(
10349            language_name.clone(),
10350            LanguageSettingsContent {
10351                tab_size: NonZeroU32::new(8),
10352                ..Default::default()
10353            },
10354        );
10355    });
10356    cx.executor().run_until_parked();
10357    assert_eq!(
10358        server_restarts.load(atomic::Ordering::Acquire),
10359        0,
10360        "Should not restart LSP server on an unrelated change"
10361    );
10362
10363    update_test_project_settings(cx, |project_settings| {
10364        project_settings.lsp.insert(
10365            "Some other server name".into(),
10366            LspSettings {
10367                binary: None,
10368                settings: None,
10369                initialization_options: Some(json!({
10370                    "some other init value": false
10371                })),
10372            },
10373        );
10374    });
10375    cx.executor().run_until_parked();
10376    assert_eq!(
10377        server_restarts.load(atomic::Ordering::Acquire),
10378        0,
10379        "Should not restart LSP server on an unrelated LSP settings change"
10380    );
10381
10382    update_test_project_settings(cx, |project_settings| {
10383        project_settings.lsp.insert(
10384            language_server_name.into(),
10385            LspSettings {
10386                binary: None,
10387                settings: None,
10388                initialization_options: Some(json!({
10389                    "anotherInitValue": false
10390                })),
10391            },
10392        );
10393    });
10394    cx.executor().run_until_parked();
10395    assert_eq!(
10396        server_restarts.load(atomic::Ordering::Acquire),
10397        1,
10398        "Should restart LSP server on a related LSP settings change"
10399    );
10400
10401    update_test_project_settings(cx, |project_settings| {
10402        project_settings.lsp.insert(
10403            language_server_name.into(),
10404            LspSettings {
10405                binary: None,
10406                settings: None,
10407                initialization_options: Some(json!({
10408                    "anotherInitValue": false
10409                })),
10410            },
10411        );
10412    });
10413    cx.executor().run_until_parked();
10414    assert_eq!(
10415        server_restarts.load(atomic::Ordering::Acquire),
10416        1,
10417        "Should not restart LSP server on a related LSP settings change that is the same"
10418    );
10419
10420    update_test_project_settings(cx, |project_settings| {
10421        project_settings.lsp.insert(
10422            language_server_name.into(),
10423            LspSettings {
10424                binary: None,
10425                settings: None,
10426                initialization_options: None,
10427            },
10428        );
10429    });
10430    cx.executor().run_until_parked();
10431    assert_eq!(
10432        server_restarts.load(atomic::Ordering::Acquire),
10433        2,
10434        "Should restart LSP server on another related LSP settings change"
10435    );
10436}
10437
10438#[gpui::test]
10439async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10440    init_test(cx, |_| {});
10441
10442    let mut cx = EditorLspTestContext::new_rust(
10443        lsp::ServerCapabilities {
10444            completion_provider: Some(lsp::CompletionOptions {
10445                trigger_characters: Some(vec![".".to_string()]),
10446                resolve_provider: Some(true),
10447                ..Default::default()
10448            }),
10449            ..Default::default()
10450        },
10451        cx,
10452    )
10453    .await;
10454
10455    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10456    cx.simulate_keystroke(".");
10457    let completion_item = lsp::CompletionItem {
10458        label: "some".into(),
10459        kind: Some(lsp::CompletionItemKind::SNIPPET),
10460        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10461        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10462            kind: lsp::MarkupKind::Markdown,
10463            value: "```rust\nSome(2)\n```".to_string(),
10464        })),
10465        deprecated: Some(false),
10466        sort_text: Some("fffffff2".to_string()),
10467        filter_text: Some("some".to_string()),
10468        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10469        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10470            range: lsp::Range {
10471                start: lsp::Position {
10472                    line: 0,
10473                    character: 22,
10474                },
10475                end: lsp::Position {
10476                    line: 0,
10477                    character: 22,
10478                },
10479            },
10480            new_text: "Some(2)".to_string(),
10481        })),
10482        additional_text_edits: Some(vec![lsp::TextEdit {
10483            range: lsp::Range {
10484                start: lsp::Position {
10485                    line: 0,
10486                    character: 20,
10487                },
10488                end: lsp::Position {
10489                    line: 0,
10490                    character: 22,
10491                },
10492            },
10493            new_text: "".to_string(),
10494        }]),
10495        ..Default::default()
10496    };
10497
10498    let closure_completion_item = completion_item.clone();
10499    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10500        let task_completion_item = closure_completion_item.clone();
10501        async move {
10502            Ok(Some(lsp::CompletionResponse::Array(vec![
10503                task_completion_item,
10504            ])))
10505        }
10506    });
10507
10508    request.next().await;
10509
10510    cx.condition(|editor, _| editor.context_menu_visible())
10511        .await;
10512    let apply_additional_edits = cx.update_editor(|editor, cx| {
10513        editor
10514            .confirm_completion(&ConfirmCompletion::default(), cx)
10515            .unwrap()
10516    });
10517    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10518
10519    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10520        let task_completion_item = completion_item.clone();
10521        async move { Ok(task_completion_item) }
10522    })
10523    .next()
10524    .await
10525    .unwrap();
10526    apply_additional_edits.await.unwrap();
10527    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10528}
10529
10530#[gpui::test]
10531async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10532    init_test(cx, |_| {});
10533
10534    let mut cx = EditorLspTestContext::new(
10535        Language::new(
10536            LanguageConfig {
10537                matcher: LanguageMatcher {
10538                    path_suffixes: vec!["jsx".into()],
10539                    ..Default::default()
10540                },
10541                overrides: [(
10542                    "element".into(),
10543                    LanguageConfigOverride {
10544                        word_characters: Override::Set(['-'].into_iter().collect()),
10545                        ..Default::default()
10546                    },
10547                )]
10548                .into_iter()
10549                .collect(),
10550                ..Default::default()
10551            },
10552            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10553        )
10554        .with_override_query("(jsx_self_closing_element) @element")
10555        .unwrap(),
10556        lsp::ServerCapabilities {
10557            completion_provider: Some(lsp::CompletionOptions {
10558                trigger_characters: Some(vec![":".to_string()]),
10559                ..Default::default()
10560            }),
10561            ..Default::default()
10562        },
10563        cx,
10564    )
10565    .await;
10566
10567    cx.lsp
10568        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10569            Ok(Some(lsp::CompletionResponse::Array(vec![
10570                lsp::CompletionItem {
10571                    label: "bg-blue".into(),
10572                    ..Default::default()
10573                },
10574                lsp::CompletionItem {
10575                    label: "bg-red".into(),
10576                    ..Default::default()
10577                },
10578                lsp::CompletionItem {
10579                    label: "bg-yellow".into(),
10580                    ..Default::default()
10581                },
10582            ])))
10583        });
10584
10585    cx.set_state(r#"<p class="bgˇ" />"#);
10586
10587    // Trigger completion when typing a dash, because the dash is an extra
10588    // word character in the 'element' scope, which contains the cursor.
10589    cx.simulate_keystroke("-");
10590    cx.executor().run_until_parked();
10591    cx.update_editor(|editor, _| {
10592        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10593            assert_eq!(
10594                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10595                &["bg-red", "bg-blue", "bg-yellow"]
10596            );
10597        } else {
10598            panic!("expected completion menu to be open");
10599        }
10600    });
10601
10602    cx.simulate_keystroke("l");
10603    cx.executor().run_until_parked();
10604    cx.update_editor(|editor, _| {
10605        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10606            assert_eq!(
10607                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10608                &["bg-blue", "bg-yellow"]
10609            );
10610        } else {
10611            panic!("expected completion menu to be open");
10612        }
10613    });
10614
10615    // When filtering completions, consider the character after the '-' to
10616    // be the start of a subword.
10617    cx.set_state(r#"<p class="yelˇ" />"#);
10618    cx.simulate_keystroke("l");
10619    cx.executor().run_until_parked();
10620    cx.update_editor(|editor, _| {
10621        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10622            assert_eq!(
10623                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10624                &["bg-yellow"]
10625            );
10626        } else {
10627            panic!("expected completion menu to be open");
10628        }
10629    });
10630}
10631
10632#[gpui::test]
10633async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10634    init_test(cx, |settings| {
10635        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10636            FormatterList(vec![Formatter::Prettier].into()),
10637        ))
10638    });
10639
10640    let fs = FakeFs::new(cx.executor());
10641    fs.insert_file("/file.ts", Default::default()).await;
10642
10643    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10644    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10645
10646    language_registry.add(Arc::new(Language::new(
10647        LanguageConfig {
10648            name: "TypeScript".into(),
10649            matcher: LanguageMatcher {
10650                path_suffixes: vec!["ts".to_string()],
10651                ..Default::default()
10652            },
10653            ..Default::default()
10654        },
10655        Some(tree_sitter_rust::LANGUAGE.into()),
10656    )));
10657    update_test_language_settings(cx, |settings| {
10658        settings.defaults.prettier = Some(PrettierSettings {
10659            allowed: true,
10660            ..PrettierSettings::default()
10661        });
10662    });
10663
10664    let test_plugin = "test_plugin";
10665    let _ = language_registry.register_fake_lsp(
10666        "TypeScript",
10667        FakeLspAdapter {
10668            prettier_plugins: vec![test_plugin],
10669            ..Default::default()
10670        },
10671    );
10672
10673    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10674    let buffer = project
10675        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10676        .await
10677        .unwrap();
10678
10679    let buffer_text = "one\ntwo\nthree\n";
10680    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10681    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10682    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10683
10684    editor
10685        .update(cx, |editor, cx| {
10686            editor.perform_format(
10687                project.clone(),
10688                FormatTrigger::Manual,
10689                FormatTarget::Buffer,
10690                cx,
10691            )
10692        })
10693        .unwrap()
10694        .await;
10695    assert_eq!(
10696        editor.update(cx, |editor, cx| editor.text(cx)),
10697        buffer_text.to_string() + prettier_format_suffix,
10698        "Test prettier formatting was not applied to the original buffer text",
10699    );
10700
10701    update_test_language_settings(cx, |settings| {
10702        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10703    });
10704    let format = editor.update(cx, |editor, cx| {
10705        editor.perform_format(
10706            project.clone(),
10707            FormatTrigger::Manual,
10708            FormatTarget::Buffer,
10709            cx,
10710        )
10711    });
10712    format.await.unwrap();
10713    assert_eq!(
10714        editor.update(cx, |editor, cx| editor.text(cx)),
10715        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10716        "Autoformatting (via test prettier) was not applied to the original buffer text",
10717    );
10718}
10719
10720#[gpui::test]
10721async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10722    init_test(cx, |_| {});
10723    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10724    let base_text = indoc! {r#"struct Row;
10725struct Row1;
10726struct Row2;
10727
10728struct Row4;
10729struct Row5;
10730struct Row6;
10731
10732struct Row8;
10733struct Row9;
10734struct Row10;"#};
10735
10736    // When addition hunks are not adjacent to carets, no hunk revert is performed
10737    assert_hunk_revert(
10738        indoc! {r#"struct Row;
10739                   struct Row1;
10740                   struct Row1.1;
10741                   struct Row1.2;
10742                   struct Row2;ˇ
10743
10744                   struct Row4;
10745                   struct Row5;
10746                   struct Row6;
10747
10748                   struct Row8;
10749                   ˇstruct Row9;
10750                   struct Row9.1;
10751                   struct Row9.2;
10752                   struct Row9.3;
10753                   struct Row10;"#},
10754        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10755        indoc! {r#"struct Row;
10756                   struct Row1;
10757                   struct Row1.1;
10758                   struct Row1.2;
10759                   struct Row2;ˇ
10760
10761                   struct Row4;
10762                   struct Row5;
10763                   struct Row6;
10764
10765                   struct Row8;
10766                   ˇstruct Row9;
10767                   struct Row9.1;
10768                   struct Row9.2;
10769                   struct Row9.3;
10770                   struct Row10;"#},
10771        base_text,
10772        &mut cx,
10773    );
10774    // Same for selections
10775    assert_hunk_revert(
10776        indoc! {r#"struct Row;
10777                   struct Row1;
10778                   struct Row2;
10779                   struct Row2.1;
10780                   struct Row2.2;
10781                   «ˇ
10782                   struct Row4;
10783                   struct» Row5;
10784                   «struct Row6;
10785                   ˇ»
10786                   struct Row9.1;
10787                   struct Row9.2;
10788                   struct Row9.3;
10789                   struct Row8;
10790                   struct Row9;
10791                   struct Row10;"#},
10792        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10793        indoc! {r#"struct Row;
10794                   struct Row1;
10795                   struct Row2;
10796                   struct Row2.1;
10797                   struct Row2.2;
10798                   «ˇ
10799                   struct Row4;
10800                   struct» Row5;
10801                   «struct Row6;
10802                   ˇ»
10803                   struct Row9.1;
10804                   struct Row9.2;
10805                   struct Row9.3;
10806                   struct Row8;
10807                   struct Row9;
10808                   struct Row10;"#},
10809        base_text,
10810        &mut cx,
10811    );
10812
10813    // When carets and selections intersect the addition hunks, those are reverted.
10814    // Adjacent carets got merged.
10815    assert_hunk_revert(
10816        indoc! {r#"struct Row;
10817                   ˇ// something on the top
10818                   struct Row1;
10819                   struct Row2;
10820                   struct Roˇw3.1;
10821                   struct Row2.2;
10822                   struct Row2.3;ˇ
10823
10824                   struct Row4;
10825                   struct ˇRow5.1;
10826                   struct Row5.2;
10827                   struct «Rowˇ»5.3;
10828                   struct Row5;
10829                   struct Row6;
10830                   ˇ
10831                   struct Row9.1;
10832                   struct «Rowˇ»9.2;
10833                   struct «ˇRow»9.3;
10834                   struct Row8;
10835                   struct Row9;
10836                   «ˇ// something on bottom»
10837                   struct Row10;"#},
10838        vec![
10839            DiffHunkStatus::Added,
10840            DiffHunkStatus::Added,
10841            DiffHunkStatus::Added,
10842            DiffHunkStatus::Added,
10843            DiffHunkStatus::Added,
10844        ],
10845        indoc! {r#"struct Row;
10846                   ˇstruct Row1;
10847                   struct Row2;
10848                   ˇ
10849                   struct Row4;
10850                   ˇstruct Row5;
10851                   struct Row6;
10852                   ˇ
10853                   ˇstruct Row8;
10854                   struct Row9;
10855                   ˇstruct Row10;"#},
10856        base_text,
10857        &mut cx,
10858    );
10859}
10860
10861#[gpui::test]
10862async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10863    init_test(cx, |_| {});
10864    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10865    let base_text = indoc! {r#"struct Row;
10866struct Row1;
10867struct Row2;
10868
10869struct Row4;
10870struct Row5;
10871struct Row6;
10872
10873struct Row8;
10874struct Row9;
10875struct Row10;"#};
10876
10877    // Modification hunks behave the same as the addition ones.
10878    assert_hunk_revert(
10879        indoc! {r#"struct Row;
10880                   struct Row1;
10881                   struct Row33;
10882                   ˇ
10883                   struct Row4;
10884                   struct Row5;
10885                   struct Row6;
10886                   ˇ
10887                   struct Row99;
10888                   struct Row9;
10889                   struct Row10;"#},
10890        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10891        indoc! {r#"struct Row;
10892                   struct Row1;
10893                   struct Row33;
10894                   ˇ
10895                   struct Row4;
10896                   struct Row5;
10897                   struct Row6;
10898                   ˇ
10899                   struct Row99;
10900                   struct Row9;
10901                   struct Row10;"#},
10902        base_text,
10903        &mut cx,
10904    );
10905    assert_hunk_revert(
10906        indoc! {r#"struct Row;
10907                   struct Row1;
10908                   struct Row33;
10909                   «ˇ
10910                   struct Row4;
10911                   struct» Row5;
10912                   «struct Row6;
10913                   ˇ»
10914                   struct Row99;
10915                   struct Row9;
10916                   struct Row10;"#},
10917        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10918        indoc! {r#"struct Row;
10919                   struct Row1;
10920                   struct Row33;
10921                   «ˇ
10922                   struct Row4;
10923                   struct» Row5;
10924                   «struct Row6;
10925                   ˇ»
10926                   struct Row99;
10927                   struct Row9;
10928                   struct Row10;"#},
10929        base_text,
10930        &mut cx,
10931    );
10932
10933    assert_hunk_revert(
10934        indoc! {r#"ˇstruct Row1.1;
10935                   struct Row1;
10936                   «ˇstr»uct Row22;
10937
10938                   struct ˇRow44;
10939                   struct Row5;
10940                   struct «Rˇ»ow66;ˇ
10941
10942                   «struˇ»ct Row88;
10943                   struct Row9;
10944                   struct Row1011;ˇ"#},
10945        vec![
10946            DiffHunkStatus::Modified,
10947            DiffHunkStatus::Modified,
10948            DiffHunkStatus::Modified,
10949            DiffHunkStatus::Modified,
10950            DiffHunkStatus::Modified,
10951            DiffHunkStatus::Modified,
10952        ],
10953        indoc! {r#"struct Row;
10954                   ˇstruct Row1;
10955                   struct Row2;
10956                   ˇ
10957                   struct Row4;
10958                   ˇstruct Row5;
10959                   struct Row6;
10960                   ˇ
10961                   struct Row8;
10962                   ˇstruct Row9;
10963                   struct Row10;ˇ"#},
10964        base_text,
10965        &mut cx,
10966    );
10967}
10968
10969#[gpui::test]
10970async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10971    init_test(cx, |_| {});
10972    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10973    let base_text = indoc! {r#"struct Row;
10974struct Row1;
10975struct Row2;
10976
10977struct Row4;
10978struct Row5;
10979struct Row6;
10980
10981struct Row8;
10982struct Row9;
10983struct Row10;"#};
10984
10985    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10986    assert_hunk_revert(
10987        indoc! {r#"struct Row;
10988                   struct Row2;
10989
10990                   ˇstruct Row4;
10991                   struct Row5;
10992                   struct Row6;
10993                   ˇ
10994                   struct Row8;
10995                   struct Row10;"#},
10996        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10997        indoc! {r#"struct Row;
10998                   struct Row2;
10999
11000                   ˇstruct Row4;
11001                   struct Row5;
11002                   struct Row6;
11003                   ˇ
11004                   struct Row8;
11005                   struct Row10;"#},
11006        base_text,
11007        &mut cx,
11008    );
11009    assert_hunk_revert(
11010        indoc! {r#"struct Row;
11011                   struct Row2;
11012
11013                   «ˇstruct Row4;
11014                   struct» Row5;
11015                   «struct Row6;
11016                   ˇ»
11017                   struct Row8;
11018                   struct Row10;"#},
11019        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11020        indoc! {r#"struct Row;
11021                   struct Row2;
11022
11023                   «ˇstruct Row4;
11024                   struct» Row5;
11025                   «struct Row6;
11026                   ˇ»
11027                   struct Row8;
11028                   struct Row10;"#},
11029        base_text,
11030        &mut cx,
11031    );
11032
11033    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11034    assert_hunk_revert(
11035        indoc! {r#"struct Row;
11036                   ˇstruct Row2;
11037
11038                   struct Row4;
11039                   struct Row5;
11040                   struct Row6;
11041
11042                   struct Row8;ˇ
11043                   struct Row10;"#},
11044        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11045        indoc! {r#"struct Row;
11046                   struct Row1;
11047                   ˇstruct Row2;
11048
11049                   struct Row4;
11050                   struct Row5;
11051                   struct Row6;
11052
11053                   struct Row8;ˇ
11054                   struct Row9;
11055                   struct Row10;"#},
11056        base_text,
11057        &mut cx,
11058    );
11059    assert_hunk_revert(
11060        indoc! {r#"struct Row;
11061                   struct Row2«ˇ;
11062                   struct Row4;
11063                   struct» Row5;
11064                   «struct Row6;
11065
11066                   struct Row8;ˇ»
11067                   struct Row10;"#},
11068        vec![
11069            DiffHunkStatus::Removed,
11070            DiffHunkStatus::Removed,
11071            DiffHunkStatus::Removed,
11072        ],
11073        indoc! {r#"struct Row;
11074                   struct Row1;
11075                   struct Row2«ˇ;
11076
11077                   struct Row4;
11078                   struct» Row5;
11079                   «struct Row6;
11080
11081                   struct Row8;ˇ»
11082                   struct Row9;
11083                   struct Row10;"#},
11084        base_text,
11085        &mut cx,
11086    );
11087}
11088
11089#[gpui::test]
11090async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11091    init_test(cx, |_| {});
11092
11093    let cols = 4;
11094    let rows = 10;
11095    let sample_text_1 = sample_text(rows, cols, 'a');
11096    assert_eq!(
11097        sample_text_1,
11098        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11099    );
11100    let sample_text_2 = sample_text(rows, cols, 'l');
11101    assert_eq!(
11102        sample_text_2,
11103        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11104    );
11105    let sample_text_3 = sample_text(rows, cols, 'v');
11106    assert_eq!(
11107        sample_text_3,
11108        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11109    );
11110
11111    fn diff_every_buffer_row(
11112        buffer: &Model<Buffer>,
11113        sample_text: String,
11114        cols: usize,
11115        cx: &mut gpui::TestAppContext,
11116    ) {
11117        // revert first character in each row, creating one large diff hunk per buffer
11118        let is_first_char = |offset: usize| offset % cols == 0;
11119        buffer.update(cx, |buffer, cx| {
11120            buffer.set_text(
11121                sample_text
11122                    .chars()
11123                    .enumerate()
11124                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11125                    .collect::<String>(),
11126                cx,
11127            );
11128            buffer.set_diff_base(Some(sample_text), cx);
11129        });
11130        cx.executor().run_until_parked();
11131    }
11132
11133    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11134    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11135
11136    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11137    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11138
11139    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11140    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11141
11142    let multibuffer = cx.new_model(|cx| {
11143        let mut multibuffer = MultiBuffer::new(ReadWrite);
11144        multibuffer.push_excerpts(
11145            buffer_1.clone(),
11146            [
11147                ExcerptRange {
11148                    context: Point::new(0, 0)..Point::new(3, 0),
11149                    primary: None,
11150                },
11151                ExcerptRange {
11152                    context: Point::new(5, 0)..Point::new(7, 0),
11153                    primary: None,
11154                },
11155                ExcerptRange {
11156                    context: Point::new(9, 0)..Point::new(10, 4),
11157                    primary: None,
11158                },
11159            ],
11160            cx,
11161        );
11162        multibuffer.push_excerpts(
11163            buffer_2.clone(),
11164            [
11165                ExcerptRange {
11166                    context: Point::new(0, 0)..Point::new(3, 0),
11167                    primary: None,
11168                },
11169                ExcerptRange {
11170                    context: Point::new(5, 0)..Point::new(7, 0),
11171                    primary: None,
11172                },
11173                ExcerptRange {
11174                    context: Point::new(9, 0)..Point::new(10, 4),
11175                    primary: None,
11176                },
11177            ],
11178            cx,
11179        );
11180        multibuffer.push_excerpts(
11181            buffer_3.clone(),
11182            [
11183                ExcerptRange {
11184                    context: Point::new(0, 0)..Point::new(3, 0),
11185                    primary: None,
11186                },
11187                ExcerptRange {
11188                    context: Point::new(5, 0)..Point::new(7, 0),
11189                    primary: None,
11190                },
11191                ExcerptRange {
11192                    context: Point::new(9, 0)..Point::new(10, 4),
11193                    primary: None,
11194                },
11195            ],
11196            cx,
11197        );
11198        multibuffer
11199    });
11200
11201    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11202    editor.update(cx, |editor, cx| {
11203        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");
11204        editor.select_all(&SelectAll, cx);
11205        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11206    });
11207    cx.executor().run_until_parked();
11208    // When all ranges are selected, all buffer hunks are reverted.
11209    editor.update(cx, |editor, cx| {
11210        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");
11211    });
11212    buffer_1.update(cx, |buffer, _| {
11213        assert_eq!(buffer.text(), sample_text_1);
11214    });
11215    buffer_2.update(cx, |buffer, _| {
11216        assert_eq!(buffer.text(), sample_text_2);
11217    });
11218    buffer_3.update(cx, |buffer, _| {
11219        assert_eq!(buffer.text(), sample_text_3);
11220    });
11221
11222    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11223    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11224    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11225    editor.update(cx, |editor, cx| {
11226        editor.change_selections(None, cx, |s| {
11227            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11228        });
11229        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11230    });
11231    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11232    // but not affect buffer_2 and its related excerpts.
11233    editor.update(cx, |editor, cx| {
11234        assert_eq!(
11235            editor.text(cx),
11236            "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"
11237        );
11238    });
11239    buffer_1.update(cx, |buffer, _| {
11240        assert_eq!(buffer.text(), sample_text_1);
11241    });
11242    buffer_2.update(cx, |buffer, _| {
11243        assert_eq!(
11244            buffer.text(),
11245            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11246        );
11247    });
11248    buffer_3.update(cx, |buffer, _| {
11249        assert_eq!(
11250            buffer.text(),
11251            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11252        );
11253    });
11254}
11255
11256#[gpui::test]
11257async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11258    init_test(cx, |_| {});
11259
11260    let cols = 4;
11261    let rows = 10;
11262    let sample_text_1 = sample_text(rows, cols, 'a');
11263    assert_eq!(
11264        sample_text_1,
11265        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11266    );
11267    let sample_text_2 = sample_text(rows, cols, 'l');
11268    assert_eq!(
11269        sample_text_2,
11270        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11271    );
11272    let sample_text_3 = sample_text(rows, cols, 'v');
11273    assert_eq!(
11274        sample_text_3,
11275        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11276    );
11277
11278    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11279    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11280    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11281
11282    let multi_buffer = cx.new_model(|cx| {
11283        let mut multibuffer = MultiBuffer::new(ReadWrite);
11284        multibuffer.push_excerpts(
11285            buffer_1.clone(),
11286            [
11287                ExcerptRange {
11288                    context: Point::new(0, 0)..Point::new(3, 0),
11289                    primary: None,
11290                },
11291                ExcerptRange {
11292                    context: Point::new(5, 0)..Point::new(7, 0),
11293                    primary: None,
11294                },
11295                ExcerptRange {
11296                    context: Point::new(9, 0)..Point::new(10, 4),
11297                    primary: None,
11298                },
11299            ],
11300            cx,
11301        );
11302        multibuffer.push_excerpts(
11303            buffer_2.clone(),
11304            [
11305                ExcerptRange {
11306                    context: Point::new(0, 0)..Point::new(3, 0),
11307                    primary: None,
11308                },
11309                ExcerptRange {
11310                    context: Point::new(5, 0)..Point::new(7, 0),
11311                    primary: None,
11312                },
11313                ExcerptRange {
11314                    context: Point::new(9, 0)..Point::new(10, 4),
11315                    primary: None,
11316                },
11317            ],
11318            cx,
11319        );
11320        multibuffer.push_excerpts(
11321            buffer_3.clone(),
11322            [
11323                ExcerptRange {
11324                    context: Point::new(0, 0)..Point::new(3, 0),
11325                    primary: None,
11326                },
11327                ExcerptRange {
11328                    context: Point::new(5, 0)..Point::new(7, 0),
11329                    primary: None,
11330                },
11331                ExcerptRange {
11332                    context: Point::new(9, 0)..Point::new(10, 4),
11333                    primary: None,
11334                },
11335            ],
11336            cx,
11337        );
11338        multibuffer
11339    });
11340
11341    let fs = FakeFs::new(cx.executor());
11342    fs.insert_tree(
11343        "/a",
11344        json!({
11345            "main.rs": sample_text_1,
11346            "other.rs": sample_text_2,
11347            "lib.rs": sample_text_3,
11348        }),
11349    )
11350    .await;
11351    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11352    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11353    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11354    let multi_buffer_editor = cx.new_view(|cx| {
11355        Editor::new(
11356            EditorMode::Full,
11357            multi_buffer,
11358            Some(project.clone()),
11359            true,
11360            cx,
11361        )
11362    });
11363    let multibuffer_item_id = workspace
11364        .update(cx, |workspace, cx| {
11365            assert!(
11366                workspace.active_item(cx).is_none(),
11367                "active item should be None before the first item is added"
11368            );
11369            workspace.add_item_to_active_pane(
11370                Box::new(multi_buffer_editor.clone()),
11371                None,
11372                true,
11373                cx,
11374            );
11375            let active_item = workspace
11376                .active_item(cx)
11377                .expect("should have an active item after adding the multi buffer");
11378            assert!(
11379                !active_item.is_singleton(cx),
11380                "A multi buffer was expected to active after adding"
11381            );
11382            active_item.item_id()
11383        })
11384        .unwrap();
11385    cx.executor().run_until_parked();
11386
11387    multi_buffer_editor.update(cx, |editor, cx| {
11388        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11389        editor.open_excerpts(&OpenExcerpts, cx);
11390    });
11391    cx.executor().run_until_parked();
11392    let first_item_id = workspace
11393        .update(cx, |workspace, cx| {
11394            let active_item = workspace
11395                .active_item(cx)
11396                .expect("should have an active item after navigating into the 1st buffer");
11397            let first_item_id = active_item.item_id();
11398            assert_ne!(
11399                first_item_id, multibuffer_item_id,
11400                "Should navigate into the 1st buffer and activate it"
11401            );
11402            assert!(
11403                active_item.is_singleton(cx),
11404                "New active item should be a singleton buffer"
11405            );
11406            assert_eq!(
11407                active_item
11408                    .act_as::<Editor>(cx)
11409                    .expect("should have navigated into an editor for the 1st buffer")
11410                    .read(cx)
11411                    .text(cx),
11412                sample_text_1
11413            );
11414
11415            workspace
11416                .go_back(workspace.active_pane().downgrade(), cx)
11417                .detach_and_log_err(cx);
11418
11419            first_item_id
11420        })
11421        .unwrap();
11422    cx.executor().run_until_parked();
11423    workspace
11424        .update(cx, |workspace, cx| {
11425            let active_item = workspace
11426                .active_item(cx)
11427                .expect("should have an active item after navigating back");
11428            assert_eq!(
11429                active_item.item_id(),
11430                multibuffer_item_id,
11431                "Should navigate back to the multi buffer"
11432            );
11433            assert!(!active_item.is_singleton(cx));
11434        })
11435        .unwrap();
11436
11437    multi_buffer_editor.update(cx, |editor, cx| {
11438        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11439            s.select_ranges(Some(39..40))
11440        });
11441        editor.open_excerpts(&OpenExcerpts, cx);
11442    });
11443    cx.executor().run_until_parked();
11444    let second_item_id = workspace
11445        .update(cx, |workspace, cx| {
11446            let active_item = workspace
11447                .active_item(cx)
11448                .expect("should have an active item after navigating into the 2nd buffer");
11449            let second_item_id = active_item.item_id();
11450            assert_ne!(
11451                second_item_id, multibuffer_item_id,
11452                "Should navigate away from the multibuffer"
11453            );
11454            assert_ne!(
11455                second_item_id, first_item_id,
11456                "Should navigate into the 2nd buffer and activate it"
11457            );
11458            assert!(
11459                active_item.is_singleton(cx),
11460                "New active item should be a singleton buffer"
11461            );
11462            assert_eq!(
11463                active_item
11464                    .act_as::<Editor>(cx)
11465                    .expect("should have navigated into an editor")
11466                    .read(cx)
11467                    .text(cx),
11468                sample_text_2
11469            );
11470
11471            workspace
11472                .go_back(workspace.active_pane().downgrade(), cx)
11473                .detach_and_log_err(cx);
11474
11475            second_item_id
11476        })
11477        .unwrap();
11478    cx.executor().run_until_parked();
11479    workspace
11480        .update(cx, |workspace, cx| {
11481            let active_item = workspace
11482                .active_item(cx)
11483                .expect("should have an active item after navigating back from the 2nd buffer");
11484            assert_eq!(
11485                active_item.item_id(),
11486                multibuffer_item_id,
11487                "Should navigate back from the 2nd buffer to the multi buffer"
11488            );
11489            assert!(!active_item.is_singleton(cx));
11490        })
11491        .unwrap();
11492
11493    multi_buffer_editor.update(cx, |editor, cx| {
11494        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11495            s.select_ranges(Some(60..70))
11496        });
11497        editor.open_excerpts(&OpenExcerpts, cx);
11498    });
11499    cx.executor().run_until_parked();
11500    workspace
11501        .update(cx, |workspace, cx| {
11502            let active_item = workspace
11503                .active_item(cx)
11504                .expect("should have an active item after navigating into the 3rd buffer");
11505            let third_item_id = active_item.item_id();
11506            assert_ne!(
11507                third_item_id, multibuffer_item_id,
11508                "Should navigate into the 3rd buffer and activate it"
11509            );
11510            assert_ne!(third_item_id, first_item_id);
11511            assert_ne!(third_item_id, second_item_id);
11512            assert!(
11513                active_item.is_singleton(cx),
11514                "New active item should be a singleton buffer"
11515            );
11516            assert_eq!(
11517                active_item
11518                    .act_as::<Editor>(cx)
11519                    .expect("should have navigated into an editor")
11520                    .read(cx)
11521                    .text(cx),
11522                sample_text_3
11523            );
11524
11525            workspace
11526                .go_back(workspace.active_pane().downgrade(), cx)
11527                .detach_and_log_err(cx);
11528        })
11529        .unwrap();
11530    cx.executor().run_until_parked();
11531    workspace
11532        .update(cx, |workspace, cx| {
11533            let active_item = workspace
11534                .active_item(cx)
11535                .expect("should have an active item after navigating back from the 3rd buffer");
11536            assert_eq!(
11537                active_item.item_id(),
11538                multibuffer_item_id,
11539                "Should navigate back from the 3rd buffer to the multi buffer"
11540            );
11541            assert!(!active_item.is_singleton(cx));
11542        })
11543        .unwrap();
11544}
11545
11546#[gpui::test]
11547async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11548    init_test(cx, |_| {});
11549
11550    let mut cx = EditorTestContext::new(cx).await;
11551
11552    let diff_base = r#"
11553        use some::mod;
11554
11555        const A: u32 = 42;
11556
11557        fn main() {
11558            println!("hello");
11559
11560            println!("world");
11561        }
11562        "#
11563    .unindent();
11564
11565    cx.set_state(
11566        &r#"
11567        use some::modified;
11568
11569        ˇ
11570        fn main() {
11571            println!("hello there");
11572
11573            println!("around the");
11574            println!("world");
11575        }
11576        "#
11577        .unindent(),
11578    );
11579
11580    cx.set_diff_base(Some(&diff_base));
11581    executor.run_until_parked();
11582
11583    cx.update_editor(|editor, cx| {
11584        editor.go_to_next_hunk(&GoToHunk, cx);
11585        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11586    });
11587    executor.run_until_parked();
11588    cx.assert_diff_hunks(
11589        r#"
11590          use some::modified;
11591
11592
11593          fn main() {
11594        -     println!("hello");
11595        +     println!("hello there");
11596
11597              println!("around the");
11598              println!("world");
11599          }
11600        "#
11601        .unindent(),
11602    );
11603
11604    cx.update_editor(|editor, cx| {
11605        for _ in 0..3 {
11606            editor.go_to_next_hunk(&GoToHunk, cx);
11607            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11608        }
11609    });
11610    executor.run_until_parked();
11611    cx.assert_editor_state(
11612        &r#"
11613        use some::modified;
11614
11615        ˇ
11616        fn main() {
11617            println!("hello there");
11618
11619            println!("around the");
11620            println!("world");
11621        }
11622        "#
11623        .unindent(),
11624    );
11625
11626    cx.assert_diff_hunks(
11627        r#"
11628        - use some::mod;
11629        + use some::modified;
11630
11631        - const A: u32 = 42;
11632
11633          fn main() {
11634        -     println!("hello");
11635        +     println!("hello there");
11636
11637        +     println!("around the");
11638              println!("world");
11639          }
11640        "#
11641        .unindent(),
11642    );
11643
11644    cx.update_editor(|editor, cx| {
11645        editor.cancel(&Cancel, cx);
11646    });
11647
11648    cx.assert_diff_hunks(
11649        r#"
11650          use some::modified;
11651
11652
11653          fn main() {
11654              println!("hello there");
11655
11656              println!("around the");
11657              println!("world");
11658          }
11659        "#
11660        .unindent(),
11661    );
11662}
11663
11664#[gpui::test]
11665async fn test_diff_base_change_with_expanded_diff_hunks(
11666    executor: BackgroundExecutor,
11667    cx: &mut gpui::TestAppContext,
11668) {
11669    init_test(cx, |_| {});
11670
11671    let mut cx = EditorTestContext::new(cx).await;
11672
11673    let diff_base = r#"
11674        use some::mod1;
11675        use some::mod2;
11676
11677        const A: u32 = 42;
11678        const B: u32 = 42;
11679        const C: u32 = 42;
11680
11681        fn main() {
11682            println!("hello");
11683
11684            println!("world");
11685        }
11686        "#
11687    .unindent();
11688
11689    cx.set_state(
11690        &r#"
11691        use some::mod2;
11692
11693        const A: u32 = 42;
11694        const C: u32 = 42;
11695
11696        fn main(ˇ) {
11697            //println!("hello");
11698
11699            println!("world");
11700            //
11701            //
11702        }
11703        "#
11704        .unindent(),
11705    );
11706
11707    cx.set_diff_base(Some(&diff_base));
11708    executor.run_until_parked();
11709
11710    cx.update_editor(|editor, cx| {
11711        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11712    });
11713    executor.run_until_parked();
11714    cx.assert_diff_hunks(
11715        r#"
11716        - use some::mod1;
11717          use some::mod2;
11718
11719          const A: u32 = 42;
11720        - const B: u32 = 42;
11721          const C: u32 = 42;
11722
11723          fn main() {
11724        -     println!("hello");
11725        +     //println!("hello");
11726
11727              println!("world");
11728        +     //
11729        +     //
11730          }
11731        "#
11732        .unindent(),
11733    );
11734
11735    cx.set_diff_base(Some("new diff base!"));
11736    executor.run_until_parked();
11737    cx.assert_diff_hunks(
11738        r#"
11739          use some::mod2;
11740
11741          const A: u32 = 42;
11742          const C: u32 = 42;
11743
11744          fn main() {
11745              //println!("hello");
11746
11747              println!("world");
11748              //
11749              //
11750          }
11751        "#
11752        .unindent(),
11753    );
11754
11755    cx.update_editor(|editor, cx| {
11756        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11757    });
11758    executor.run_until_parked();
11759    cx.assert_diff_hunks(
11760        r#"
11761        - new diff base!
11762        + use some::mod2;
11763        +
11764        + const A: u32 = 42;
11765        + const C: u32 = 42;
11766        +
11767        + fn main() {
11768        +     //println!("hello");
11769        +
11770        +     println!("world");
11771        +     //
11772        +     //
11773        + }
11774        "#
11775        .unindent(),
11776    );
11777}
11778
11779#[gpui::test]
11780async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11781    init_test(cx, |_| {});
11782
11783    let mut cx = EditorTestContext::new(cx).await;
11784
11785    let diff_base = r#"
11786        use some::mod1;
11787        use some::mod2;
11788
11789        const A: u32 = 42;
11790        const B: u32 = 42;
11791        const C: u32 = 42;
11792
11793        fn main() {
11794            println!("hello");
11795
11796            println!("world");
11797        }
11798
11799        fn another() {
11800            println!("another");
11801        }
11802
11803        fn another2() {
11804            println!("another2");
11805        }
11806        "#
11807    .unindent();
11808
11809    cx.set_state(
11810        &r#"
11811        «use some::mod2;
11812
11813        const A: u32 = 42;
11814        const C: u32 = 42;
11815
11816        fn main() {
11817            //println!("hello");
11818
11819            println!("world");
11820            //
11821            //ˇ»
11822        }
11823
11824        fn another() {
11825            println!("another");
11826            println!("another");
11827        }
11828
11829            println!("another2");
11830        }
11831        "#
11832        .unindent(),
11833    );
11834
11835    cx.set_diff_base(Some(&diff_base));
11836    executor.run_until_parked();
11837
11838    cx.update_editor(|editor, cx| {
11839        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11840    });
11841    executor.run_until_parked();
11842
11843    cx.assert_diff_hunks(
11844        r#"
11845        - use some::mod1;
11846          use some::mod2;
11847
11848          const A: u32 = 42;
11849        - const B: u32 = 42;
11850          const C: u32 = 42;
11851
11852          fn main() {
11853        -     println!("hello");
11854        +     //println!("hello");
11855
11856              println!("world");
11857        +     //
11858        +     //
11859          }
11860
11861          fn another() {
11862              println!("another");
11863        +     println!("another");
11864          }
11865
11866        - fn another2() {
11867              println!("another2");
11868          }
11869        "#
11870        .unindent(),
11871    );
11872
11873    // Fold across some of the diff hunks. They should no longer appear expanded.
11874    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11875    cx.executor().run_until_parked();
11876
11877    // Hunks are not shown if their position is within a fold
11878    cx.assert_diff_hunks(
11879        r#"
11880          use some::mod2;
11881
11882          const A: u32 = 42;
11883          const C: u32 = 42;
11884
11885          fn main() {
11886              //println!("hello");
11887
11888              println!("world");
11889              //
11890              //
11891          }
11892
11893          fn another() {
11894              println!("another");
11895        +     println!("another");
11896          }
11897
11898        - fn another2() {
11899              println!("another2");
11900          }
11901        "#
11902        .unindent(),
11903    );
11904
11905    cx.update_editor(|editor, cx| {
11906        editor.select_all(&SelectAll, cx);
11907        editor.unfold_lines(&UnfoldLines, cx);
11908    });
11909    cx.executor().run_until_parked();
11910
11911    // The deletions reappear when unfolding.
11912    cx.assert_diff_hunks(
11913        r#"
11914        - use some::mod1;
11915          use some::mod2;
11916
11917          const A: u32 = 42;
11918        - const B: u32 = 42;
11919          const C: u32 = 42;
11920
11921          fn main() {
11922        -     println!("hello");
11923        +     //println!("hello");
11924
11925              println!("world");
11926        +     //
11927        +     //
11928          }
11929
11930          fn another() {
11931              println!("another");
11932        +     println!("another");
11933          }
11934
11935        - fn another2() {
11936              println!("another2");
11937          }
11938        "#
11939        .unindent(),
11940    );
11941}
11942
11943#[gpui::test]
11944async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11945    init_test(cx, |_| {});
11946
11947    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11948    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11949    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11950    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11951    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11952    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11953
11954    let buffer_1 = cx.new_model(|cx| {
11955        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11956        buffer.set_diff_base(Some(file_1_old.into()), cx);
11957        buffer
11958    });
11959    let buffer_2 = cx.new_model(|cx| {
11960        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11961        buffer.set_diff_base(Some(file_2_old.into()), cx);
11962        buffer
11963    });
11964    let buffer_3 = cx.new_model(|cx| {
11965        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11966        buffer.set_diff_base(Some(file_3_old.into()), cx);
11967        buffer
11968    });
11969
11970    let multi_buffer = cx.new_model(|cx| {
11971        let mut multibuffer = MultiBuffer::new(ReadWrite);
11972        multibuffer.push_excerpts(
11973            buffer_1.clone(),
11974            [
11975                ExcerptRange {
11976                    context: Point::new(0, 0)..Point::new(3, 0),
11977                    primary: None,
11978                },
11979                ExcerptRange {
11980                    context: Point::new(5, 0)..Point::new(7, 0),
11981                    primary: None,
11982                },
11983                ExcerptRange {
11984                    context: Point::new(9, 0)..Point::new(10, 3),
11985                    primary: None,
11986                },
11987            ],
11988            cx,
11989        );
11990        multibuffer.push_excerpts(
11991            buffer_2.clone(),
11992            [
11993                ExcerptRange {
11994                    context: Point::new(0, 0)..Point::new(3, 0),
11995                    primary: None,
11996                },
11997                ExcerptRange {
11998                    context: Point::new(5, 0)..Point::new(7, 0),
11999                    primary: None,
12000                },
12001                ExcerptRange {
12002                    context: Point::new(9, 0)..Point::new(10, 3),
12003                    primary: None,
12004                },
12005            ],
12006            cx,
12007        );
12008        multibuffer.push_excerpts(
12009            buffer_3.clone(),
12010            [
12011                ExcerptRange {
12012                    context: Point::new(0, 0)..Point::new(3, 0),
12013                    primary: None,
12014                },
12015                ExcerptRange {
12016                    context: Point::new(5, 0)..Point::new(7, 0),
12017                    primary: None,
12018                },
12019                ExcerptRange {
12020                    context: Point::new(9, 0)..Point::new(10, 3),
12021                    primary: None,
12022                },
12023            ],
12024            cx,
12025        );
12026        multibuffer
12027    });
12028
12029    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12030    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12031    cx.run_until_parked();
12032
12033    cx.assert_editor_state(
12034        &"
12035            ˇaaa
12036            ccc
12037            ddd
12038
12039            ggg
12040            hhh
12041
12042
12043            lll
12044            mmm
12045            NNN
12046
12047            qqq
12048            rrr
12049
12050            uuu
12051            111
12052            222
12053            333
12054
12055            666
12056            777
12057
12058            000
12059            !!!"
12060        .unindent(),
12061    );
12062
12063    cx.update_editor(|editor, cx| {
12064        editor.select_all(&SelectAll, cx);
12065        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12066    });
12067    cx.executor().run_until_parked();
12068
12069    cx.assert_diff_hunks(
12070        "
12071            aaa
12072          - bbb
12073            ccc
12074            ddd
12075
12076            ggg
12077            hhh
12078
12079
12080            lll
12081            mmm
12082          - nnn
12083          + NNN
12084
12085            qqq
12086            rrr
12087
12088            uuu
12089            111
12090            222
12091            333
12092
12093          + 666
12094            777
12095
12096            000
12097            !!!"
12098        .unindent(),
12099    );
12100}
12101
12102#[gpui::test]
12103async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12104    init_test(cx, |_| {});
12105
12106    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12107    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12108
12109    let buffer = cx.new_model(|cx| {
12110        let mut buffer = Buffer::local(text.to_string(), cx);
12111        buffer.set_diff_base(Some(base.into()), cx);
12112        buffer
12113    });
12114
12115    let multi_buffer = cx.new_model(|cx| {
12116        let mut multibuffer = MultiBuffer::new(ReadWrite);
12117        multibuffer.push_excerpts(
12118            buffer.clone(),
12119            [
12120                ExcerptRange {
12121                    context: Point::new(0, 0)..Point::new(2, 0),
12122                    primary: None,
12123                },
12124                ExcerptRange {
12125                    context: Point::new(5, 0)..Point::new(7, 0),
12126                    primary: None,
12127                },
12128            ],
12129            cx,
12130        );
12131        multibuffer
12132    });
12133
12134    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12135    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12136    cx.run_until_parked();
12137
12138    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12139    cx.executor().run_until_parked();
12140
12141    cx.assert_diff_hunks(
12142        "
12143            aaa
12144          - bbb
12145          + BBB
12146
12147          - ddd
12148          - eee
12149          + EEE
12150            fff
12151        "
12152        .unindent(),
12153    );
12154}
12155
12156#[gpui::test]
12157async fn test_edits_around_expanded_insertion_hunks(
12158    executor: BackgroundExecutor,
12159    cx: &mut gpui::TestAppContext,
12160) {
12161    init_test(cx, |_| {});
12162
12163    let mut cx = EditorTestContext::new(cx).await;
12164
12165    let diff_base = r#"
12166        use some::mod1;
12167        use some::mod2;
12168
12169        const A: u32 = 42;
12170
12171        fn main() {
12172            println!("hello");
12173
12174            println!("world");
12175        }
12176        "#
12177    .unindent();
12178    executor.run_until_parked();
12179    cx.set_state(
12180        &r#"
12181        use some::mod1;
12182        use some::mod2;
12183
12184        const A: u32 = 42;
12185        const B: u32 = 42;
12186        const C: u32 = 42;
12187        ˇ
12188
12189        fn main() {
12190            println!("hello");
12191
12192            println!("world");
12193        }
12194        "#
12195        .unindent(),
12196    );
12197
12198    cx.set_diff_base(Some(&diff_base));
12199    executor.run_until_parked();
12200
12201    cx.update_editor(|editor, cx| {
12202        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12203    });
12204    executor.run_until_parked();
12205
12206    cx.assert_diff_hunks(
12207        r#"
12208        use some::mod1;
12209        use some::mod2;
12210
12211        const A: u32 = 42;
12212      + const B: u32 = 42;
12213      + const C: u32 = 42;
12214      +
12215
12216        fn main() {
12217            println!("hello");
12218
12219            println!("world");
12220        }
12221        "#
12222        .unindent(),
12223    );
12224
12225    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12226    executor.run_until_parked();
12227
12228    cx.assert_diff_hunks(
12229        r#"
12230        use some::mod1;
12231        use some::mod2;
12232
12233        const A: u32 = 42;
12234      + const B: u32 = 42;
12235      + const C: u32 = 42;
12236      + const D: u32 = 42;
12237      +
12238
12239        fn main() {
12240            println!("hello");
12241
12242            println!("world");
12243        }
12244        "#
12245        .unindent(),
12246    );
12247
12248    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12249    executor.run_until_parked();
12250
12251    cx.assert_diff_hunks(
12252        r#"
12253        use some::mod1;
12254        use some::mod2;
12255
12256        const A: u32 = 42;
12257      + const B: u32 = 42;
12258      + const C: u32 = 42;
12259      + const D: u32 = 42;
12260      + const E: u32 = 42;
12261      +
12262
12263        fn main() {
12264            println!("hello");
12265
12266            println!("world");
12267        }
12268        "#
12269        .unindent(),
12270    );
12271
12272    cx.update_editor(|editor, cx| {
12273        editor.delete_line(&DeleteLine, cx);
12274    });
12275    executor.run_until_parked();
12276
12277    cx.assert_diff_hunks(
12278        r#"
12279        use some::mod1;
12280        use some::mod2;
12281
12282        const A: u32 = 42;
12283      + const B: u32 = 42;
12284      + const C: u32 = 42;
12285      + const D: u32 = 42;
12286      + const E: u32 = 42;
12287
12288        fn main() {
12289            println!("hello");
12290
12291            println!("world");
12292        }
12293        "#
12294        .unindent(),
12295    );
12296
12297    cx.update_editor(|editor, cx| {
12298        editor.move_up(&MoveUp, cx);
12299        editor.delete_line(&DeleteLine, cx);
12300        editor.move_up(&MoveUp, cx);
12301        editor.delete_line(&DeleteLine, cx);
12302        editor.move_up(&MoveUp, cx);
12303        editor.delete_line(&DeleteLine, cx);
12304    });
12305    executor.run_until_parked();
12306    cx.assert_editor_state(
12307        &r#"
12308        use some::mod1;
12309        use some::mod2;
12310
12311        const A: u32 = 42;
12312        const B: u32 = 42;
12313        ˇ
12314        fn main() {
12315            println!("hello");
12316
12317            println!("world");
12318        }
12319        "#
12320        .unindent(),
12321    );
12322
12323    cx.assert_diff_hunks(
12324        r#"
12325        use some::mod1;
12326        use some::mod2;
12327
12328        const A: u32 = 42;
12329      + const B: u32 = 42;
12330
12331        fn main() {
12332            println!("hello");
12333
12334            println!("world");
12335        }
12336        "#
12337        .unindent(),
12338    );
12339
12340    cx.update_editor(|editor, cx| {
12341        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12342        editor.delete_line(&DeleteLine, cx);
12343    });
12344    executor.run_until_parked();
12345    cx.assert_diff_hunks(
12346        r#"
12347        use some::mod1;
12348      - use some::mod2;
12349      -
12350      - const A: u32 = 42;
12351
12352        fn main() {
12353            println!("hello");
12354
12355            println!("world");
12356        }
12357        "#
12358        .unindent(),
12359    );
12360}
12361
12362#[gpui::test]
12363async fn test_edits_around_expanded_deletion_hunks(
12364    executor: BackgroundExecutor,
12365    cx: &mut gpui::TestAppContext,
12366) {
12367    init_test(cx, |_| {});
12368
12369    let mut cx = EditorTestContext::new(cx).await;
12370
12371    let diff_base = r#"
12372        use some::mod1;
12373        use some::mod2;
12374
12375        const A: u32 = 42;
12376        const B: u32 = 42;
12377        const C: u32 = 42;
12378
12379
12380        fn main() {
12381            println!("hello");
12382
12383            println!("world");
12384        }
12385    "#
12386    .unindent();
12387    executor.run_until_parked();
12388    cx.set_state(
12389        &r#"
12390        use some::mod1;
12391        use some::mod2;
12392
12393        ˇconst B: u32 = 42;
12394        const C: u32 = 42;
12395
12396
12397        fn main() {
12398            println!("hello");
12399
12400            println!("world");
12401        }
12402        "#
12403        .unindent(),
12404    );
12405
12406    cx.set_diff_base(Some(&diff_base));
12407    executor.run_until_parked();
12408
12409    cx.update_editor(|editor, cx| {
12410        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12411    });
12412    executor.run_until_parked();
12413
12414    cx.assert_diff_hunks(
12415        r#"
12416        use some::mod1;
12417        use some::mod2;
12418
12419      - const A: u32 = 42;
12420        const B: u32 = 42;
12421        const C: u32 = 42;
12422
12423
12424        fn main() {
12425            println!("hello");
12426
12427            println!("world");
12428        }
12429        "#
12430        .unindent(),
12431    );
12432
12433    cx.update_editor(|editor, cx| {
12434        editor.delete_line(&DeleteLine, cx);
12435    });
12436    executor.run_until_parked();
12437    cx.assert_editor_state(
12438        &r#"
12439        use some::mod1;
12440        use some::mod2;
12441
12442        ˇconst C: u32 = 42;
12443
12444
12445        fn main() {
12446            println!("hello");
12447
12448            println!("world");
12449        }
12450        "#
12451        .unindent(),
12452    );
12453    cx.assert_diff_hunks(
12454        r#"
12455        use some::mod1;
12456        use some::mod2;
12457
12458      - const A: u32 = 42;
12459      - const B: u32 = 42;
12460        const C: u32 = 42;
12461
12462
12463        fn main() {
12464            println!("hello");
12465
12466            println!("world");
12467        }
12468        "#
12469        .unindent(),
12470    );
12471
12472    cx.update_editor(|editor, cx| {
12473        editor.delete_line(&DeleteLine, cx);
12474    });
12475    executor.run_until_parked();
12476    cx.assert_editor_state(
12477        &r#"
12478        use some::mod1;
12479        use some::mod2;
12480
12481        ˇ
12482
12483        fn main() {
12484            println!("hello");
12485
12486            println!("world");
12487        }
12488        "#
12489        .unindent(),
12490    );
12491    cx.assert_diff_hunks(
12492        r#"
12493        use some::mod1;
12494        use some::mod2;
12495
12496      - const A: u32 = 42;
12497      - const B: u32 = 42;
12498      - const C: u32 = 42;
12499
12500
12501        fn main() {
12502            println!("hello");
12503
12504            println!("world");
12505        }
12506        "#
12507        .unindent(),
12508    );
12509
12510    cx.update_editor(|editor, cx| {
12511        editor.handle_input("replacement", cx);
12512    });
12513    executor.run_until_parked();
12514    cx.assert_editor_state(
12515        &r#"
12516        use some::mod1;
12517        use some::mod2;
12518
12519        replacementˇ
12520
12521        fn main() {
12522            println!("hello");
12523
12524            println!("world");
12525        }
12526        "#
12527        .unindent(),
12528    );
12529    cx.assert_diff_hunks(
12530        r#"
12531        use some::mod1;
12532        use some::mod2;
12533
12534      - const A: u32 = 42;
12535      - const B: u32 = 42;
12536      - const C: u32 = 42;
12537      -
12538      + replacement
12539
12540        fn main() {
12541            println!("hello");
12542
12543            println!("world");
12544        }
12545        "#
12546        .unindent(),
12547    );
12548}
12549
12550#[gpui::test]
12551async fn test_edit_after_expanded_modification_hunk(
12552    executor: BackgroundExecutor,
12553    cx: &mut gpui::TestAppContext,
12554) {
12555    init_test(cx, |_| {});
12556
12557    let mut cx = EditorTestContext::new(cx).await;
12558
12559    let diff_base = r#"
12560        use some::mod1;
12561        use some::mod2;
12562
12563        const A: u32 = 42;
12564        const B: u32 = 42;
12565        const C: u32 = 42;
12566        const D: u32 = 42;
12567
12568
12569        fn main() {
12570            println!("hello");
12571
12572            println!("world");
12573        }"#
12574    .unindent();
12575
12576    cx.set_state(
12577        &r#"
12578        use some::mod1;
12579        use some::mod2;
12580
12581        const A: u32 = 42;
12582        const B: u32 = 42;
12583        const C: u32 = 43ˇ
12584        const D: u32 = 42;
12585
12586
12587        fn main() {
12588            println!("hello");
12589
12590            println!("world");
12591        }"#
12592        .unindent(),
12593    );
12594
12595    cx.set_diff_base(Some(&diff_base));
12596    executor.run_until_parked();
12597    cx.update_editor(|editor, cx| {
12598        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12599    });
12600    executor.run_until_parked();
12601
12602    cx.assert_diff_hunks(
12603        r#"
12604        use some::mod1;
12605        use some::mod2;
12606
12607        const A: u32 = 42;
12608        const B: u32 = 42;
12609      - const C: u32 = 42;
12610      + const C: u32 = 43
12611        const D: u32 = 42;
12612
12613
12614        fn main() {
12615            println!("hello");
12616
12617            println!("world");
12618        }"#
12619        .unindent(),
12620    );
12621
12622    cx.update_editor(|editor, cx| {
12623        editor.handle_input("\nnew_line\n", cx);
12624    });
12625    executor.run_until_parked();
12626
12627    cx.assert_diff_hunks(
12628        r#"
12629        use some::mod1;
12630        use some::mod2;
12631
12632        const A: u32 = 42;
12633        const B: u32 = 42;
12634      - const C: u32 = 42;
12635      + const C: u32 = 43
12636      + new_line
12637      +
12638        const D: u32 = 42;
12639
12640
12641        fn main() {
12642            println!("hello");
12643
12644            println!("world");
12645        }"#
12646        .unindent(),
12647    );
12648}
12649
12650async fn setup_indent_guides_editor(
12651    text: &str,
12652    cx: &mut gpui::TestAppContext,
12653) -> (BufferId, EditorTestContext) {
12654    init_test(cx, |_| {});
12655
12656    let mut cx = EditorTestContext::new(cx).await;
12657
12658    let buffer_id = cx.update_editor(|editor, cx| {
12659        editor.set_text(text, cx);
12660        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12661
12662        buffer_ids[0]
12663    });
12664
12665    (buffer_id, cx)
12666}
12667
12668fn assert_indent_guides(
12669    range: Range<u32>,
12670    expected: Vec<IndentGuide>,
12671    active_indices: Option<Vec<usize>>,
12672    cx: &mut EditorTestContext,
12673) {
12674    let indent_guides = cx.update_editor(|editor, cx| {
12675        let snapshot = editor.snapshot(cx).display_snapshot;
12676        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12677            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12678            true,
12679            &snapshot,
12680            cx,
12681        );
12682
12683        indent_guides.sort_by(|a, b| {
12684            a.depth.cmp(&b.depth).then(
12685                a.start_row
12686                    .cmp(&b.start_row)
12687                    .then(a.end_row.cmp(&b.end_row)),
12688            )
12689        });
12690        indent_guides
12691    });
12692
12693    if let Some(expected) = active_indices {
12694        let active_indices = cx.update_editor(|editor, cx| {
12695            let snapshot = editor.snapshot(cx).display_snapshot;
12696            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12697        });
12698
12699        assert_eq!(
12700            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12701            expected,
12702            "Active indent guide indices do not match"
12703        );
12704    }
12705
12706    let expected: Vec<_> = expected
12707        .into_iter()
12708        .map(|guide| MultiBufferIndentGuide {
12709            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12710            buffer: guide,
12711        })
12712        .collect();
12713
12714    assert_eq!(indent_guides, expected, "Indent guides do not match");
12715}
12716
12717fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12718    IndentGuide {
12719        buffer_id,
12720        start_row,
12721        end_row,
12722        depth,
12723        tab_size: 4,
12724        settings: IndentGuideSettings {
12725            enabled: true,
12726            line_width: 1,
12727            active_line_width: 1,
12728            ..Default::default()
12729        },
12730    }
12731}
12732
12733#[gpui::test]
12734async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12735    let (buffer_id, mut cx) = setup_indent_guides_editor(
12736        &"
12737    fn main() {
12738        let a = 1;
12739    }"
12740        .unindent(),
12741        cx,
12742    )
12743    .await;
12744
12745    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12746}
12747
12748#[gpui::test]
12749async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12750    let (buffer_id, mut cx) = setup_indent_guides_editor(
12751        &"
12752    fn main() {
12753        let a = 1;
12754        let b = 2;
12755    }"
12756        .unindent(),
12757        cx,
12758    )
12759    .await;
12760
12761    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12762}
12763
12764#[gpui::test]
12765async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12766    let (buffer_id, mut cx) = setup_indent_guides_editor(
12767        &"
12768    fn main() {
12769        let a = 1;
12770        if a == 3 {
12771            let b = 2;
12772        } else {
12773            let c = 3;
12774        }
12775    }"
12776        .unindent(),
12777        cx,
12778    )
12779    .await;
12780
12781    assert_indent_guides(
12782        0..8,
12783        vec![
12784            indent_guide(buffer_id, 1, 6, 0),
12785            indent_guide(buffer_id, 3, 3, 1),
12786            indent_guide(buffer_id, 5, 5, 1),
12787        ],
12788        None,
12789        &mut cx,
12790    );
12791}
12792
12793#[gpui::test]
12794async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12795    let (buffer_id, mut cx) = setup_indent_guides_editor(
12796        &"
12797    fn main() {
12798        let a = 1;
12799            let b = 2;
12800        let c = 3;
12801    }"
12802        .unindent(),
12803        cx,
12804    )
12805    .await;
12806
12807    assert_indent_guides(
12808        0..5,
12809        vec![
12810            indent_guide(buffer_id, 1, 3, 0),
12811            indent_guide(buffer_id, 2, 2, 1),
12812        ],
12813        None,
12814        &mut cx,
12815    );
12816}
12817
12818#[gpui::test]
12819async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12820    let (buffer_id, mut cx) = setup_indent_guides_editor(
12821        &"
12822        fn main() {
12823            let a = 1;
12824
12825            let c = 3;
12826        }"
12827        .unindent(),
12828        cx,
12829    )
12830    .await;
12831
12832    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12833}
12834
12835#[gpui::test]
12836async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12837    let (buffer_id, mut cx) = setup_indent_guides_editor(
12838        &"
12839        fn main() {
12840            let a = 1;
12841
12842            let c = 3;
12843
12844            if a == 3 {
12845                let b = 2;
12846            } else {
12847                let c = 3;
12848            }
12849        }"
12850        .unindent(),
12851        cx,
12852    )
12853    .await;
12854
12855    assert_indent_guides(
12856        0..11,
12857        vec![
12858            indent_guide(buffer_id, 1, 9, 0),
12859            indent_guide(buffer_id, 6, 6, 1),
12860            indent_guide(buffer_id, 8, 8, 1),
12861        ],
12862        None,
12863        &mut cx,
12864    );
12865}
12866
12867#[gpui::test]
12868async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12869    let (buffer_id, mut cx) = setup_indent_guides_editor(
12870        &"
12871        fn main() {
12872            let a = 1;
12873
12874            let c = 3;
12875
12876            if a == 3 {
12877                let b = 2;
12878            } else {
12879                let c = 3;
12880            }
12881        }"
12882        .unindent(),
12883        cx,
12884    )
12885    .await;
12886
12887    assert_indent_guides(
12888        1..11,
12889        vec![
12890            indent_guide(buffer_id, 1, 9, 0),
12891            indent_guide(buffer_id, 6, 6, 1),
12892            indent_guide(buffer_id, 8, 8, 1),
12893        ],
12894        None,
12895        &mut cx,
12896    );
12897}
12898
12899#[gpui::test]
12900async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12901    let (buffer_id, mut cx) = setup_indent_guides_editor(
12902        &"
12903        fn main() {
12904            let a = 1;
12905
12906            let c = 3;
12907
12908            if a == 3 {
12909                let b = 2;
12910            } else {
12911                let c = 3;
12912            }
12913        }"
12914        .unindent(),
12915        cx,
12916    )
12917    .await;
12918
12919    assert_indent_guides(
12920        1..10,
12921        vec![
12922            indent_guide(buffer_id, 1, 9, 0),
12923            indent_guide(buffer_id, 6, 6, 1),
12924            indent_guide(buffer_id, 8, 8, 1),
12925        ],
12926        None,
12927        &mut cx,
12928    );
12929}
12930
12931#[gpui::test]
12932async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12933    let (buffer_id, mut cx) = setup_indent_guides_editor(
12934        &"
12935        block1
12936            block2
12937                block3
12938                    block4
12939            block2
12940        block1
12941        block1"
12942            .unindent(),
12943        cx,
12944    )
12945    .await;
12946
12947    assert_indent_guides(
12948        1..10,
12949        vec![
12950            indent_guide(buffer_id, 1, 4, 0),
12951            indent_guide(buffer_id, 2, 3, 1),
12952            indent_guide(buffer_id, 3, 3, 2),
12953        ],
12954        None,
12955        &mut cx,
12956    );
12957}
12958
12959#[gpui::test]
12960async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12961    let (buffer_id, mut cx) = setup_indent_guides_editor(
12962        &"
12963        block1
12964            block2
12965                block3
12966
12967        block1
12968        block1"
12969            .unindent(),
12970        cx,
12971    )
12972    .await;
12973
12974    assert_indent_guides(
12975        0..6,
12976        vec![
12977            indent_guide(buffer_id, 1, 2, 0),
12978            indent_guide(buffer_id, 2, 2, 1),
12979        ],
12980        None,
12981        &mut cx,
12982    );
12983}
12984
12985#[gpui::test]
12986async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12987    let (buffer_id, mut cx) = setup_indent_guides_editor(
12988        &"
12989        block1
12990
12991
12992
12993            block2
12994        "
12995        .unindent(),
12996        cx,
12997    )
12998    .await;
12999
13000    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13001}
13002
13003#[gpui::test]
13004async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13005    let (buffer_id, mut cx) = setup_indent_guides_editor(
13006        &"
13007        def a:
13008        \tb = 3
13009        \tif True:
13010        \t\tc = 4
13011        \t\td = 5
13012        \tprint(b)
13013        "
13014        .unindent(),
13015        cx,
13016    )
13017    .await;
13018
13019    assert_indent_guides(
13020        0..6,
13021        vec![
13022            indent_guide(buffer_id, 1, 6, 0),
13023            indent_guide(buffer_id, 3, 4, 1),
13024        ],
13025        None,
13026        &mut cx,
13027    );
13028}
13029
13030#[gpui::test]
13031async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13032    let (buffer_id, mut cx) = setup_indent_guides_editor(
13033        &"
13034    fn main() {
13035        let a = 1;
13036    }"
13037        .unindent(),
13038        cx,
13039    )
13040    .await;
13041
13042    cx.update_editor(|editor, cx| {
13043        editor.change_selections(None, cx, |s| {
13044            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13045        });
13046    });
13047
13048    assert_indent_guides(
13049        0..3,
13050        vec![indent_guide(buffer_id, 1, 1, 0)],
13051        Some(vec![0]),
13052        &mut cx,
13053    );
13054}
13055
13056#[gpui::test]
13057async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13058    let (buffer_id, mut cx) = setup_indent_guides_editor(
13059        &"
13060    fn main() {
13061        if 1 == 2 {
13062            let a = 1;
13063        }
13064    }"
13065        .unindent(),
13066        cx,
13067    )
13068    .await;
13069
13070    cx.update_editor(|editor, cx| {
13071        editor.change_selections(None, cx, |s| {
13072            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13073        });
13074    });
13075
13076    assert_indent_guides(
13077        0..4,
13078        vec![
13079            indent_guide(buffer_id, 1, 3, 0),
13080            indent_guide(buffer_id, 2, 2, 1),
13081        ],
13082        Some(vec![1]),
13083        &mut cx,
13084    );
13085
13086    cx.update_editor(|editor, cx| {
13087        editor.change_selections(None, cx, |s| {
13088            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13089        });
13090    });
13091
13092    assert_indent_guides(
13093        0..4,
13094        vec![
13095            indent_guide(buffer_id, 1, 3, 0),
13096            indent_guide(buffer_id, 2, 2, 1),
13097        ],
13098        Some(vec![1]),
13099        &mut cx,
13100    );
13101
13102    cx.update_editor(|editor, cx| {
13103        editor.change_selections(None, cx, |s| {
13104            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13105        });
13106    });
13107
13108    assert_indent_guides(
13109        0..4,
13110        vec![
13111            indent_guide(buffer_id, 1, 3, 0),
13112            indent_guide(buffer_id, 2, 2, 1),
13113        ],
13114        Some(vec![0]),
13115        &mut cx,
13116    );
13117}
13118
13119#[gpui::test]
13120async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13121    let (buffer_id, mut cx) = setup_indent_guides_editor(
13122        &"
13123    fn main() {
13124        let a = 1;
13125
13126        let b = 2;
13127    }"
13128        .unindent(),
13129        cx,
13130    )
13131    .await;
13132
13133    cx.update_editor(|editor, cx| {
13134        editor.change_selections(None, cx, |s| {
13135            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13136        });
13137    });
13138
13139    assert_indent_guides(
13140        0..5,
13141        vec![indent_guide(buffer_id, 1, 3, 0)],
13142        Some(vec![0]),
13143        &mut cx,
13144    );
13145}
13146
13147#[gpui::test]
13148async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13149    let (buffer_id, mut cx) = setup_indent_guides_editor(
13150        &"
13151    def m:
13152        a = 1
13153        pass"
13154            .unindent(),
13155        cx,
13156    )
13157    .await;
13158
13159    cx.update_editor(|editor, cx| {
13160        editor.change_selections(None, cx, |s| {
13161            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13162        });
13163    });
13164
13165    assert_indent_guides(
13166        0..3,
13167        vec![indent_guide(buffer_id, 1, 2, 0)],
13168        Some(vec![0]),
13169        &mut cx,
13170    );
13171}
13172
13173#[gpui::test]
13174fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13175    init_test(cx, |_| {});
13176
13177    let editor = cx.add_window(|cx| {
13178        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13179        build_editor(buffer, cx)
13180    });
13181
13182    let render_args = Arc::new(Mutex::new(None));
13183    let snapshot = editor
13184        .update(cx, |editor, cx| {
13185            let snapshot = editor.buffer().read(cx).snapshot(cx);
13186            let range =
13187                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13188
13189            struct RenderArgs {
13190                row: MultiBufferRow,
13191                folded: bool,
13192                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13193            }
13194
13195            let crease = Crease::new(
13196                range,
13197                FoldPlaceholder::test(),
13198                {
13199                    let toggle_callback = render_args.clone();
13200                    move |row, folded, callback, _cx| {
13201                        *toggle_callback.lock() = Some(RenderArgs {
13202                            row,
13203                            folded,
13204                            callback,
13205                        });
13206                        div()
13207                    }
13208                },
13209                |_row, _folded, _cx| div(),
13210            );
13211
13212            editor.insert_creases(Some(crease), cx);
13213            let snapshot = editor.snapshot(cx);
13214            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13215            snapshot
13216        })
13217        .unwrap();
13218
13219    let render_args = render_args.lock().take().unwrap();
13220    assert_eq!(render_args.row, MultiBufferRow(1));
13221    assert!(!render_args.folded);
13222    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13223
13224    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13225        .unwrap();
13226    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13227    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13228
13229    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13230        .unwrap();
13231    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13232    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13233}
13234
13235#[gpui::test]
13236async fn test_input_text(cx: &mut gpui::TestAppContext) {
13237    init_test(cx, |_| {});
13238    let mut cx = EditorTestContext::new(cx).await;
13239
13240    cx.set_state(
13241        &r#"ˇone
13242        two
13243
13244        three
13245        fourˇ
13246        five
13247
13248        siˇx"#
13249            .unindent(),
13250    );
13251
13252    cx.dispatch_action(HandleInput(String::new()));
13253    cx.assert_editor_state(
13254        &r#"ˇone
13255        two
13256
13257        three
13258        fourˇ
13259        five
13260
13261        siˇx"#
13262            .unindent(),
13263    );
13264
13265    cx.dispatch_action(HandleInput("AAAA".to_string()));
13266    cx.assert_editor_state(
13267        &r#"AAAAˇone
13268        two
13269
13270        three
13271        fourAAAAˇ
13272        five
13273
13274        siAAAAˇx"#
13275            .unindent(),
13276    );
13277}
13278
13279#[gpui::test]
13280async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13281    init_test(cx, |_| {});
13282
13283    let mut cx = EditorTestContext::new(cx).await;
13284    cx.set_state(
13285        r#"let foo = 1;
13286let foo = 2;
13287let foo = 3;
13288let fooˇ = 4;
13289let foo = 5;
13290let foo = 6;
13291let foo = 7;
13292let foo = 8;
13293let foo = 9;
13294let foo = 10;
13295let foo = 11;
13296let foo = 12;
13297let foo = 13;
13298let foo = 14;
13299let foo = 15;"#,
13300    );
13301
13302    cx.update_editor(|e, cx| {
13303        assert_eq!(
13304            e.next_scroll_position,
13305            NextScrollCursorCenterTopBottom::Center,
13306            "Default next scroll direction is center",
13307        );
13308
13309        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13310        assert_eq!(
13311            e.next_scroll_position,
13312            NextScrollCursorCenterTopBottom::Top,
13313            "After center, next scroll direction should be top",
13314        );
13315
13316        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13317        assert_eq!(
13318            e.next_scroll_position,
13319            NextScrollCursorCenterTopBottom::Bottom,
13320            "After top, next scroll direction should be bottom",
13321        );
13322
13323        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13324        assert_eq!(
13325            e.next_scroll_position,
13326            NextScrollCursorCenterTopBottom::Center,
13327            "After bottom, scrolling should start over",
13328        );
13329
13330        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13331        assert_eq!(
13332            e.next_scroll_position,
13333            NextScrollCursorCenterTopBottom::Top,
13334            "Scrolling continues if retriggered fast enough"
13335        );
13336    });
13337
13338    cx.executor()
13339        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13340    cx.executor().run_until_parked();
13341    cx.update_editor(|e, _| {
13342        assert_eq!(
13343            e.next_scroll_position,
13344            NextScrollCursorCenterTopBottom::Center,
13345            "If scrolling is not triggered fast enough, it should reset"
13346        );
13347    });
13348}
13349
13350#[gpui::test]
13351async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13352    init_test(cx, |_| {});
13353    let mut cx = EditorLspTestContext::new_rust(
13354        lsp::ServerCapabilities {
13355            definition_provider: Some(lsp::OneOf::Left(true)),
13356            references_provider: Some(lsp::OneOf::Left(true)),
13357            ..lsp::ServerCapabilities::default()
13358        },
13359        cx,
13360    )
13361    .await;
13362
13363    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13364        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13365            move |params, _| async move {
13366                if empty_go_to_definition {
13367                    Ok(None)
13368                } else {
13369                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13370                        uri: params.text_document_position_params.text_document.uri,
13371                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13372                    })))
13373                }
13374            },
13375        );
13376        let references =
13377            cx.lsp
13378                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13379                    Ok(Some(vec![lsp::Location {
13380                        uri: params.text_document_position.text_document.uri,
13381                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13382                    }]))
13383                });
13384        (go_to_definition, references)
13385    };
13386
13387    cx.set_state(
13388        &r#"fn one() {
13389            let mut a = ˇtwo();
13390        }
13391
13392        fn two() {}"#
13393            .unindent(),
13394    );
13395    set_up_lsp_handlers(false, &mut cx);
13396    let navigated = cx
13397        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13398        .await
13399        .expect("Failed to navigate to definition");
13400    assert_eq!(
13401        navigated,
13402        Navigated::Yes,
13403        "Should have navigated to definition from the GetDefinition response"
13404    );
13405    cx.assert_editor_state(
13406        &r#"fn one() {
13407            let mut a = two();
13408        }
13409
13410        fn «twoˇ»() {}"#
13411            .unindent(),
13412    );
13413
13414    let editors = cx.update_workspace(|workspace, cx| {
13415        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13416    });
13417    cx.update_editor(|_, test_editor_cx| {
13418        assert_eq!(
13419            editors.len(),
13420            1,
13421            "Initially, only one, test, editor should be open in the workspace"
13422        );
13423        assert_eq!(
13424            test_editor_cx.view(),
13425            editors.last().expect("Asserted len is 1")
13426        );
13427    });
13428
13429    set_up_lsp_handlers(true, &mut cx);
13430    let navigated = cx
13431        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13432        .await
13433        .expect("Failed to navigate to lookup references");
13434    assert_eq!(
13435        navigated,
13436        Navigated::Yes,
13437        "Should have navigated to references as a fallback after empty GoToDefinition response"
13438    );
13439    // We should not change the selections in the existing file,
13440    // if opening another milti buffer with the references
13441    cx.assert_editor_state(
13442        &r#"fn one() {
13443            let mut a = two();
13444        }
13445
13446        fn «twoˇ»() {}"#
13447            .unindent(),
13448    );
13449    let editors = cx.update_workspace(|workspace, cx| {
13450        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13451    });
13452    cx.update_editor(|_, test_editor_cx| {
13453        assert_eq!(
13454            editors.len(),
13455            2,
13456            "After falling back to references search, we open a new editor with the results"
13457        );
13458        let references_fallback_text = editors
13459            .into_iter()
13460            .find(|new_editor| new_editor != test_editor_cx.view())
13461            .expect("Should have one non-test editor now")
13462            .read(test_editor_cx)
13463            .text(test_editor_cx);
13464        assert_eq!(
13465            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13466            "Should use the range from the references response and not the GoToDefinition one"
13467        );
13468    });
13469}
13470
13471#[gpui::test]
13472async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13473    init_test(cx, |_| {});
13474
13475    let language = Arc::new(Language::new(
13476        LanguageConfig::default(),
13477        Some(tree_sitter_rust::LANGUAGE.into()),
13478    ));
13479
13480    let text = r#"
13481        #[cfg(test)]
13482        mod tests() {
13483            #[test]
13484            fn runnable_1() {
13485                let a = 1;
13486            }
13487
13488            #[test]
13489            fn runnable_2() {
13490                let a = 1;
13491                let b = 2;
13492            }
13493        }
13494    "#
13495    .unindent();
13496
13497    let fs = FakeFs::new(cx.executor());
13498    fs.insert_file("/file.rs", Default::default()).await;
13499
13500    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13501    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13502    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13503    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13504    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13505
13506    let editor = cx.new_view(|cx| {
13507        Editor::new(
13508            EditorMode::Full,
13509            multi_buffer,
13510            Some(project.clone()),
13511            true,
13512            cx,
13513        )
13514    });
13515
13516    editor.update(cx, |editor, cx| {
13517        editor.tasks.insert(
13518            (buffer.read(cx).remote_id(), 3),
13519            RunnableTasks {
13520                templates: vec![],
13521                offset: MultiBufferOffset(43),
13522                column: 0,
13523                extra_variables: HashMap::default(),
13524                context_range: BufferOffset(43)..BufferOffset(85),
13525            },
13526        );
13527        editor.tasks.insert(
13528            (buffer.read(cx).remote_id(), 8),
13529            RunnableTasks {
13530                templates: vec![],
13531                offset: MultiBufferOffset(86),
13532                column: 0,
13533                extra_variables: HashMap::default(),
13534                context_range: BufferOffset(86)..BufferOffset(191),
13535            },
13536        );
13537
13538        // Test finding task when cursor is inside function body
13539        editor.change_selections(None, cx, |s| {
13540            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13541        });
13542        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13543        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13544
13545        // Test finding task when cursor is on function name
13546        editor.change_selections(None, cx, |s| {
13547            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13548        });
13549        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13550        assert_eq!(row, 8, "Should find task when cursor is on function name");
13551    });
13552}
13553
13554fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13555    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13556    point..point
13557}
13558
13559fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13560    let (text, ranges) = marked_text_ranges(marked_text, true);
13561    assert_eq!(view.text(cx), text);
13562    assert_eq!(
13563        view.selections.ranges(cx),
13564        ranges,
13565        "Assert selections are {}",
13566        marked_text
13567    );
13568}
13569
13570pub fn handle_signature_help_request(
13571    cx: &mut EditorLspTestContext,
13572    mocked_response: lsp::SignatureHelp,
13573) -> impl Future<Output = ()> {
13574    let mut request =
13575        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13576            let mocked_response = mocked_response.clone();
13577            async move { Ok(Some(mocked_response)) }
13578        });
13579
13580    async move {
13581        request.next().await;
13582    }
13583}
13584
13585/// Handle completion request passing a marked string specifying where the completion
13586/// should be triggered from using '|' character, what range should be replaced, and what completions
13587/// should be returned using '<' and '>' to delimit the range
13588pub fn handle_completion_request(
13589    cx: &mut EditorLspTestContext,
13590    marked_string: &str,
13591    completions: Vec<&'static str>,
13592    counter: Arc<AtomicUsize>,
13593) -> impl Future<Output = ()> {
13594    let complete_from_marker: TextRangeMarker = '|'.into();
13595    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13596    let (_, mut marked_ranges) = marked_text_ranges_by(
13597        marked_string,
13598        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13599    );
13600
13601    let complete_from_position =
13602        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13603    let replace_range =
13604        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13605
13606    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13607        let completions = completions.clone();
13608        counter.fetch_add(1, atomic::Ordering::Release);
13609        async move {
13610            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13611            assert_eq!(
13612                params.text_document_position.position,
13613                complete_from_position
13614            );
13615            Ok(Some(lsp::CompletionResponse::Array(
13616                completions
13617                    .iter()
13618                    .map(|completion_text| lsp::CompletionItem {
13619                        label: completion_text.to_string(),
13620                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13621                            range: replace_range,
13622                            new_text: completion_text.to_string(),
13623                        })),
13624                        ..Default::default()
13625                    })
13626                    .collect(),
13627            )))
13628        }
13629    });
13630
13631    async move {
13632        request.next().await;
13633    }
13634}
13635
13636fn handle_resolve_completion_request(
13637    cx: &mut EditorLspTestContext,
13638    edits: Option<Vec<(&'static str, &'static str)>>,
13639) -> impl Future<Output = ()> {
13640    let edits = edits.map(|edits| {
13641        edits
13642            .iter()
13643            .map(|(marked_string, new_text)| {
13644                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13645                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13646                lsp::TextEdit::new(replace_range, new_text.to_string())
13647            })
13648            .collect::<Vec<_>>()
13649    });
13650
13651    let mut request =
13652        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13653            let edits = edits.clone();
13654            async move {
13655                Ok(lsp::CompletionItem {
13656                    additional_text_edits: edits,
13657                    ..Default::default()
13658                })
13659            }
13660        });
13661
13662    async move {
13663        request.next().await;
13664    }
13665}
13666
13667pub(crate) fn update_test_language_settings(
13668    cx: &mut TestAppContext,
13669    f: impl Fn(&mut AllLanguageSettingsContent),
13670) {
13671    cx.update(|cx| {
13672        SettingsStore::update_global(cx, |store, cx| {
13673            store.update_user_settings::<AllLanguageSettings>(cx, f);
13674        });
13675    });
13676}
13677
13678pub(crate) fn update_test_project_settings(
13679    cx: &mut TestAppContext,
13680    f: impl Fn(&mut ProjectSettings),
13681) {
13682    cx.update(|cx| {
13683        SettingsStore::update_global(cx, |store, cx| {
13684            store.update_user_settings::<ProjectSettings>(cx, f);
13685        });
13686    });
13687}
13688
13689pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13690    cx.update(|cx| {
13691        assets::Assets.load_test_fonts(cx);
13692        let store = SettingsStore::test(cx);
13693        cx.set_global(store);
13694        theme::init(theme::LoadThemes::JustBase, cx);
13695        release_channel::init(SemanticVersion::default(), cx);
13696        client::init_settings(cx);
13697        language::init(cx);
13698        Project::init_settings(cx);
13699        workspace::init_settings(cx);
13700        crate::init(cx);
13701    });
13702
13703    update_test_language_settings(cx, f);
13704}
13705
13706pub(crate) fn rust_lang() -> Arc<Language> {
13707    Arc::new(Language::new(
13708        LanguageConfig {
13709            name: "Rust".into(),
13710            matcher: LanguageMatcher {
13711                path_suffixes: vec!["rs".to_string()],
13712                ..Default::default()
13713            },
13714            ..Default::default()
13715        },
13716        Some(tree_sitter_rust::LANGUAGE.into()),
13717    ))
13718}
13719
13720#[track_caller]
13721fn assert_hunk_revert(
13722    not_reverted_text_with_selections: &str,
13723    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13724    expected_reverted_text_with_selections: &str,
13725    base_text: &str,
13726    cx: &mut EditorLspTestContext,
13727) {
13728    cx.set_state(not_reverted_text_with_selections);
13729    cx.update_editor(|editor, cx| {
13730        editor
13731            .buffer()
13732            .read(cx)
13733            .as_singleton()
13734            .unwrap()
13735            .update(cx, |buffer, cx| {
13736                buffer.set_diff_base(Some(base_text.into()), cx);
13737            });
13738    });
13739    cx.executor().run_until_parked();
13740
13741    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13742        let snapshot = editor.buffer().read(cx).snapshot(cx);
13743        let reverted_hunk_statuses = snapshot
13744            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13745            .map(|hunk| hunk_status(&hunk))
13746            .collect::<Vec<_>>();
13747
13748        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13749        reverted_hunk_statuses
13750    });
13751    cx.executor().run_until_parked();
13752    cx.assert_editor_state(expected_reverted_text_with_selections);
13753    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13754}