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 group_interval = Duration::from_millis(1);
  173    let buffer = cx.new_model(|cx| {
  174        let mut buf = language::Buffer::local("123456", cx);
  175        buf.set_group_interval(group_interval);
  176        buf
  177    });
  178    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  179    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  180
  181    _ = editor.update(cx, |editor, cx| {
  182        editor.start_transaction_at(now, cx);
  183        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  184
  185        editor.insert("cd", cx);
  186        editor.end_transaction_at(now, cx);
  187        assert_eq!(editor.text(cx), "12cd56");
  188        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  189
  190        editor.start_transaction_at(now, cx);
  191        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  192        editor.insert("e", cx);
  193        editor.end_transaction_at(now, cx);
  194        assert_eq!(editor.text(cx), "12cde6");
  195        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  196
  197        now += group_interval + Duration::from_millis(1);
  198        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  199
  200        // Simulate an edit in another editor
  201        buffer.update(cx, |buffer, cx| {
  202            buffer.start_transaction_at(now, cx);
  203            buffer.edit([(0..1, "a")], None, cx);
  204            buffer.edit([(1..1, "b")], None, cx);
  205            buffer.end_transaction_at(now, cx);
  206        });
  207
  208        assert_eq!(editor.text(cx), "ab2cde6");
  209        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  210
  211        // Last transaction happened past the group interval in a different editor.
  212        // Undo it individually and don't restore selections.
  213        editor.undo(&Undo, cx);
  214        assert_eq!(editor.text(cx), "12cde6");
  215        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  216
  217        // First two transactions happened within the group interval in this editor.
  218        // Undo them together and restore selections.
  219        editor.undo(&Undo, cx);
  220        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  221        assert_eq!(editor.text(cx), "123456");
  222        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  223
  224        // Redo the first two transactions together.
  225        editor.redo(&Redo, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  228
  229        // Redo the last transaction on its own.
  230        editor.redo(&Redo, cx);
  231        assert_eq!(editor.text(cx), "ab2cde6");
  232        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  233
  234        // Test empty transactions.
  235        editor.start_transaction_at(now, cx);
  236        editor.end_transaction_at(now, cx);
  237        editor.undo(&Undo, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239    });
  240}
  241
  242#[gpui::test]
  243fn test_ime_composition(cx: &mut TestAppContext) {
  244    init_test(cx, |_| {});
  245
  246    let buffer = cx.new_model(|cx| {
  247        let mut buffer = language::Buffer::local("abcde", cx);
  248        // Ensure automatic grouping doesn't occur.
  249        buffer.set_group_interval(Duration::ZERO);
  250        buffer
  251    });
  252
  253    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  254    cx.add_window(|cx| {
  255        let mut editor = build_editor(buffer.clone(), cx);
  256
  257        // Start a new IME composition.
  258        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  259        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  260        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  261        assert_eq!(editor.text(cx), "äbcde");
  262        assert_eq!(
  263            editor.marked_text_ranges(cx),
  264            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  265        );
  266
  267        // Finalize IME composition.
  268        editor.replace_text_in_range(None, "ā", cx);
  269        assert_eq!(editor.text(cx), "ābcde");
  270        assert_eq!(editor.marked_text_ranges(cx), None);
  271
  272        // IME composition edits are grouped and are undone/redone at once.
  273        editor.undo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "abcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276        editor.redo(&Default::default(), cx);
  277        assert_eq!(editor.text(cx), "ābcde");
  278        assert_eq!(editor.marked_text_ranges(cx), None);
  279
  280        // Start a new IME composition.
  281        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  282        assert_eq!(
  283            editor.marked_text_ranges(cx),
  284            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  285        );
  286
  287        // Undoing during an IME composition cancels it.
  288        editor.undo(&Default::default(), cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  293        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  294        assert_eq!(editor.text(cx), "ābcdè");
  295        assert_eq!(
  296            editor.marked_text_ranges(cx),
  297            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  298        );
  299
  300        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  301        editor.replace_text_in_range(Some(4..999), "ę", cx);
  302        assert_eq!(editor.text(cx), "ābcdę");
  303        assert_eq!(editor.marked_text_ranges(cx), None);
  304
  305        // Start a new IME composition with multiple cursors.
  306        editor.change_selections(None, cx, |s| {
  307            s.select_ranges([
  308                OffsetUtf16(1)..OffsetUtf16(1),
  309                OffsetUtf16(3)..OffsetUtf16(3),
  310                OffsetUtf16(5)..OffsetUtf16(5),
  311            ])
  312        });
  313        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  314        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  315        assert_eq!(
  316            editor.marked_text_ranges(cx),
  317            Some(vec![
  318                OffsetUtf16(0)..OffsetUtf16(3),
  319                OffsetUtf16(4)..OffsetUtf16(7),
  320                OffsetUtf16(8)..OffsetUtf16(11)
  321            ])
  322        );
  323
  324        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  325        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  326        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(1)..OffsetUtf16(2),
  331                OffsetUtf16(5)..OffsetUtf16(6),
  332                OffsetUtf16(9)..OffsetUtf16(10)
  333            ])
  334        );
  335
  336        // Finalize IME composition with multiple cursors.
  337        editor.replace_text_in_range(Some(9..10), "2", cx);
  338        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  339        assert_eq!(editor.marked_text_ranges(cx), None);
  340
  341        editor
  342    });
  343}
  344
  345#[gpui::test]
  346fn test_selection_with_mouse(cx: &mut TestAppContext) {
  347    init_test(cx, |_| {});
  348
  349    let editor = cx.add_window(|cx| {
  350        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  351        build_editor(buffer, cx)
  352    });
  353
  354    _ = editor.update(cx, |view, cx| {
  355        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  356    });
  357    assert_eq!(
  358        editor
  359            .update(cx, |view, cx| view.selections.display_ranges(cx))
  360            .unwrap(),
  361        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  362    );
  363
  364    _ = editor.update(cx, |view, cx| {
  365        view.update_selection(
  366            DisplayPoint::new(DisplayRow(3), 3),
  367            0,
  368            gpui::Point::<f32>::default(),
  369            cx,
  370        );
  371    });
  372
  373    assert_eq!(
  374        editor
  375            .update(cx, |view, cx| view.selections.display_ranges(cx))
  376            .unwrap(),
  377        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  378    );
  379
  380    _ = editor.update(cx, |view, cx| {
  381        view.update_selection(
  382            DisplayPoint::new(DisplayRow(1), 1),
  383            0,
  384            gpui::Point::<f32>::default(),
  385            cx,
  386        );
  387    });
  388
  389    assert_eq!(
  390        editor
  391            .update(cx, |view, cx| view.selections.display_ranges(cx))
  392            .unwrap(),
  393        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  394    );
  395
  396    _ = editor.update(cx, |view, cx| {
  397        view.end_selection(cx);
  398        view.update_selection(
  399            DisplayPoint::new(DisplayRow(3), 3),
  400            0,
  401            gpui::Point::<f32>::default(),
  402            cx,
  403        );
  404    });
  405
  406    assert_eq!(
  407        editor
  408            .update(cx, |view, cx| view.selections.display_ranges(cx))
  409            .unwrap(),
  410        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  411    );
  412
  413    _ = editor.update(cx, |view, cx| {
  414        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  415        view.update_selection(
  416            DisplayPoint::new(DisplayRow(0), 0),
  417            0,
  418            gpui::Point::<f32>::default(),
  419            cx,
  420        );
  421    });
  422
  423    assert_eq!(
  424        editor
  425            .update(cx, |view, cx| view.selections.display_ranges(cx))
  426            .unwrap(),
  427        [
  428            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  429            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  430        ]
  431    );
  432
  433    _ = editor.update(cx, |view, cx| {
  434        view.end_selection(cx);
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |view, cx| view.selections.display_ranges(cx))
  440            .unwrap(),
  441        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  442    );
  443}
  444
  445#[gpui::test]
  446fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  447    init_test(cx, |_| {});
  448
  449    let editor = cx.add_window(|cx| {
  450        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  451        build_editor(buffer, cx)
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.end_selection(cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.end_selection(cx);
  468    });
  469
  470    assert_eq!(
  471        editor
  472            .update(cx, |view, cx| view.selections.display_ranges(cx))
  473            .unwrap(),
  474        [
  475            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  476            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  477        ]
  478    );
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  482    });
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.end_selection(cx);
  486    });
  487
  488    assert_eq!(
  489        editor
  490            .update(cx, |view, cx| view.selections.display_ranges(cx))
  491            .unwrap(),
  492        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  493    );
  494}
  495
  496#[gpui::test]
  497fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  498    init_test(cx, |_| {});
  499
  500    let view = cx.add_window(|cx| {
  501        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  502        build_editor(buffer, cx)
  503    });
  504
  505    _ = view.update(cx, |view, cx| {
  506        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  507        assert_eq!(
  508            view.selections.display_ranges(cx),
  509            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  510        );
  511    });
  512
  513    _ = view.update(cx, |view, cx| {
  514        view.update_selection(
  515            DisplayPoint::new(DisplayRow(3), 3),
  516            0,
  517            gpui::Point::<f32>::default(),
  518            cx,
  519        );
  520        assert_eq!(
  521            view.selections.display_ranges(cx),
  522            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  523        );
  524    });
  525
  526    _ = view.update(cx, |view, cx| {
  527        view.cancel(&Cancel, cx);
  528        view.update_selection(
  529            DisplayPoint::new(DisplayRow(1), 1),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            cx,
  533        );
  534        assert_eq!(
  535            view.selections.display_ranges(cx),
  536            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  537        );
  538    });
  539}
  540
  541#[gpui::test]
  542fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  543    init_test(cx, |_| {});
  544
  545    let view = cx.add_window(|cx| {
  546        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  547        build_editor(buffer, cx)
  548    });
  549
  550    _ = view.update(cx, |view, cx| {
  551        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  552        assert_eq!(
  553            view.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  555        );
  556
  557        view.move_down(&Default::default(), cx);
  558        assert_eq!(
  559            view.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  561        );
  562
  563        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  564        assert_eq!(
  565            view.selections.display_ranges(cx),
  566            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  567        );
  568
  569        view.move_up(&Default::default(), cx);
  570        assert_eq!(
  571            view.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  573        );
  574    });
  575}
  576
  577#[gpui::test]
  578fn test_clone(cx: &mut TestAppContext) {
  579    init_test(cx, |_| {});
  580
  581    let (text, selection_ranges) = marked_text_ranges(
  582        indoc! {"
  583            one
  584            two
  585            threeˇ
  586            four
  587            fiveˇ
  588        "},
  589        true,
  590    );
  591
  592    let editor = cx.add_window(|cx| {
  593        let buffer = MultiBuffer::build_simple(&text, cx);
  594        build_editor(buffer, cx)
  595    });
  596
  597    _ = editor.update(cx, |editor, cx| {
  598        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  599        editor.fold_creases(
  600            vec![
  601                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  602                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  603            ],
  604            true,
  605            cx,
  606        );
  607    });
  608
  609    let cloned_editor = editor
  610        .update(cx, |editor, cx| {
  611            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  612        })
  613        .unwrap()
  614        .unwrap();
  615
  616    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  617    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  618
  619    assert_eq!(
  620        cloned_editor
  621            .update(cx, |e, cx| e.display_text(cx))
  622            .unwrap(),
  623        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  624    );
  625    assert_eq!(
  626        cloned_snapshot
  627            .folds_in_range(0..text.len())
  628            .collect::<Vec<_>>(),
  629        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  630    );
  631    assert_set_eq!(
  632        cloned_editor
  633            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  634            .unwrap(),
  635        editor
  636            .update(cx, |editor, cx| editor.selections.ranges(cx))
  637            .unwrap()
  638    );
  639    assert_set_eq!(
  640        cloned_editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap(),
  643        editor
  644            .update(cx, |e, cx| e.selections.display_ranges(cx))
  645            .unwrap()
  646    );
  647}
  648
  649#[gpui::test]
  650async fn test_navigation_history(cx: &mut TestAppContext) {
  651    init_test(cx, |_| {});
  652
  653    use workspace::item::Item;
  654
  655    let fs = FakeFs::new(cx.executor());
  656    let project = Project::test(fs, [], cx).await;
  657    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  658    let pane = workspace
  659        .update(cx, |workspace, _| workspace.active_pane().clone())
  660        .unwrap();
  661
  662    _ = workspace.update(cx, |_v, cx| {
  663        cx.new_view(|cx| {
  664            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  665            let mut editor = build_editor(buffer.clone(), cx);
  666            let handle = cx.view();
  667            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  668
  669            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  670                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  671            }
  672
  673            // Move the cursor a small distance.
  674            // Nothing is added to the navigation history.
  675            editor.change_selections(None, cx, |s| {
  676                s.select_display_ranges([
  677                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  678                ])
  679            });
  680            editor.change_selections(None, cx, |s| {
  681                s.select_display_ranges([
  682                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  683                ])
  684            });
  685            assert!(pop_history(&mut editor, cx).is_none());
  686
  687            // Move the cursor a large distance.
  688            // The history can jump back to the previous position.
  689            editor.change_selections(None, cx, |s| {
  690                s.select_display_ranges([
  691                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  692                ])
  693            });
  694            let nav_entry = pop_history(&mut editor, cx).unwrap();
  695            editor.navigate(nav_entry.data.unwrap(), cx);
  696            assert_eq!(nav_entry.item.id(), cx.entity_id());
  697            assert_eq!(
  698                editor.selections.display_ranges(cx),
  699                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  700            );
  701            assert!(pop_history(&mut editor, cx).is_none());
  702
  703            // Move the cursor a small distance via the mouse.
  704            // Nothing is added to the navigation history.
  705            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  706            editor.end_selection(cx);
  707            assert_eq!(
  708                editor.selections.display_ranges(cx),
  709                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  710            );
  711            assert!(pop_history(&mut editor, cx).is_none());
  712
  713            // Move the cursor a large distance via the mouse.
  714            // The history can jump back to the previous position.
  715            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  716            editor.end_selection(cx);
  717            assert_eq!(
  718                editor.selections.display_ranges(cx),
  719                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  720            );
  721            let nav_entry = pop_history(&mut editor, cx).unwrap();
  722            editor.navigate(nav_entry.data.unwrap(), cx);
  723            assert_eq!(nav_entry.item.id(), cx.entity_id());
  724            assert_eq!(
  725                editor.selections.display_ranges(cx),
  726                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  727            );
  728            assert!(pop_history(&mut editor, cx).is_none());
  729
  730            // Set scroll position to check later
  731            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  732            let original_scroll_position = editor.scroll_manager.anchor();
  733
  734            // Jump to the end of the document and adjust scroll
  735            editor.move_to_end(&MoveToEnd, cx);
  736            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  737            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            let nav_entry = pop_history(&mut editor, cx).unwrap();
  740            editor.navigate(nav_entry.data.unwrap(), cx);
  741            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  742
  743            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  744            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  745            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  746            let invalid_point = Point::new(9999, 0);
  747            editor.navigate(
  748                Box::new(NavigationData {
  749                    cursor_anchor: invalid_anchor,
  750                    cursor_position: invalid_point,
  751                    scroll_anchor: ScrollAnchor {
  752                        anchor: invalid_anchor,
  753                        offset: Default::default(),
  754                    },
  755                    scroll_top_row: invalid_point.row,
  756                }),
  757                cx,
  758            );
  759            assert_eq!(
  760                editor.selections.display_ranges(cx),
  761                &[editor.max_point(cx)..editor.max_point(cx)]
  762            );
  763            assert_eq!(
  764                editor.scroll_position(cx),
  765                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  766            );
  767
  768            editor
  769        })
  770    });
  771}
  772
  773#[gpui::test]
  774fn test_cancel(cx: &mut TestAppContext) {
  775    init_test(cx, |_| {});
  776
  777    let view = cx.add_window(|cx| {
  778        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  779        build_editor(buffer, cx)
  780    });
  781
  782    _ = view.update(cx, |view, cx| {
  783        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  784        view.update_selection(
  785            DisplayPoint::new(DisplayRow(1), 1),
  786            0,
  787            gpui::Point::<f32>::default(),
  788            cx,
  789        );
  790        view.end_selection(cx);
  791
  792        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  793        view.update_selection(
  794            DisplayPoint::new(DisplayRow(0), 3),
  795            0,
  796            gpui::Point::<f32>::default(),
  797            cx,
  798        );
  799        view.end_selection(cx);
  800        assert_eq!(
  801            view.selections.display_ranges(cx),
  802            [
  803                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  804                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  805            ]
  806        );
  807    });
  808
  809    _ = view.update(cx, |view, cx| {
  810        view.cancel(&Cancel, cx);
  811        assert_eq!(
  812            view.selections.display_ranges(cx),
  813            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  814        );
  815    });
  816
  817    _ = view.update(cx, |view, cx| {
  818        view.cancel(&Cancel, cx);
  819        assert_eq!(
  820            view.selections.display_ranges(cx),
  821            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  822        );
  823    });
  824}
  825
  826#[gpui::test]
  827fn test_fold_action(cx: &mut TestAppContext) {
  828    init_test(cx, |_| {});
  829
  830    let view = cx.add_window(|cx| {
  831        let buffer = MultiBuffer::build_simple(
  832            &"
  833                impl Foo {
  834                    // Hello!
  835
  836                    fn a() {
  837                        1
  838                    }
  839
  840                    fn b() {
  841                        2
  842                    }
  843
  844                    fn c() {
  845                        3
  846                    }
  847                }
  848            "
  849            .unindent(),
  850            cx,
  851        );
  852        build_editor(buffer.clone(), cx)
  853    });
  854
  855    _ = view.update(cx, |view, cx| {
  856        view.change_selections(None, cx, |s| {
  857            s.select_display_ranges([
  858                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  859            ]);
  860        });
  861        view.fold(&Fold, cx);
  862        assert_eq!(
  863            view.display_text(cx),
  864            "
  865                impl Foo {
  866                    // Hello!
  867
  868                    fn a() {
  869                        1
  870                    }
  871
  872                    fn b() {⋯
  873                    }
  874
  875                    fn c() {⋯
  876                    }
  877                }
  878            "
  879            .unindent(),
  880        );
  881
  882        view.fold(&Fold, cx);
  883        assert_eq!(
  884            view.display_text(cx),
  885            "
  886                impl Foo {⋯
  887                }
  888            "
  889            .unindent(),
  890        );
  891
  892        view.unfold_lines(&UnfoldLines, cx);
  893        assert_eq!(
  894            view.display_text(cx),
  895            "
  896                impl Foo {
  897                    // Hello!
  898
  899                    fn a() {
  900                        1
  901                    }
  902
  903                    fn b() {⋯
  904                    }
  905
  906                    fn c() {⋯
  907                    }
  908                }
  909            "
  910            .unindent(),
  911        );
  912
  913        view.unfold_lines(&UnfoldLines, cx);
  914        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  915    });
  916}
  917
  918#[gpui::test]
  919fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  920    init_test(cx, |_| {});
  921
  922    let view = cx.add_window(|cx| {
  923        let buffer = MultiBuffer::build_simple(
  924            &"
  925                class Foo:
  926                    # Hello!
  927
  928                    def a():
  929                        print(1)
  930
  931                    def b():
  932                        print(2)
  933
  934                    def c():
  935                        print(3)
  936            "
  937            .unindent(),
  938            cx,
  939        );
  940        build_editor(buffer.clone(), cx)
  941    });
  942
  943    _ = view.update(cx, |view, cx| {
  944        view.change_selections(None, cx, |s| {
  945            s.select_display_ranges([
  946                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  947            ]);
  948        });
  949        view.fold(&Fold, cx);
  950        assert_eq!(
  951            view.display_text(cx),
  952            "
  953                class Foo:
  954                    # Hello!
  955
  956                    def a():
  957                        print(1)
  958
  959                    def b():⋯
  960
  961                    def c():⋯
  962            "
  963            .unindent(),
  964        );
  965
  966        view.fold(&Fold, cx);
  967        assert_eq!(
  968            view.display_text(cx),
  969            "
  970                class Foo:⋯
  971            "
  972            .unindent(),
  973        );
  974
  975        view.unfold_lines(&UnfoldLines, cx);
  976        assert_eq!(
  977            view.display_text(cx),
  978            "
  979                class Foo:
  980                    # Hello!
  981
  982                    def a():
  983                        print(1)
  984
  985                    def b():⋯
  986
  987                    def c():⋯
  988            "
  989            .unindent(),
  990        );
  991
  992        view.unfold_lines(&UnfoldLines, cx);
  993        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  994    });
  995}
  996
  997#[gpui::test]
  998fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  999    init_test(cx, |_| {});
 1000
 1001    let view = cx.add_window(|cx| {
 1002        let buffer = MultiBuffer::build_simple(
 1003            &"
 1004                class Foo:
 1005                    # Hello!
 1006
 1007                    def a():
 1008                        print(1)
 1009
 1010                    def b():
 1011                        print(2)
 1012
 1013
 1014                    def c():
 1015                        print(3)
 1016
 1017
 1018            "
 1019            .unindent(),
 1020            cx,
 1021        );
 1022        build_editor(buffer.clone(), cx)
 1023    });
 1024
 1025    _ = view.update(cx, |view, cx| {
 1026        view.change_selections(None, cx, |s| {
 1027            s.select_display_ranges([
 1028                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1029            ]);
 1030        });
 1031        view.fold(&Fold, cx);
 1032        assert_eq!(
 1033            view.display_text(cx),
 1034            "
 1035                class Foo:
 1036                    # Hello!
 1037
 1038                    def a():
 1039                        print(1)
 1040
 1041                    def b():⋯
 1042
 1043
 1044                    def c():⋯
 1045
 1046
 1047            "
 1048            .unindent(),
 1049        );
 1050
 1051        view.fold(&Fold, cx);
 1052        assert_eq!(
 1053            view.display_text(cx),
 1054            "
 1055                class Foo:⋯
 1056
 1057
 1058            "
 1059            .unindent(),
 1060        );
 1061
 1062        view.unfold_lines(&UnfoldLines, cx);
 1063        assert_eq!(
 1064            view.display_text(cx),
 1065            "
 1066                class Foo:
 1067                    # Hello!
 1068
 1069                    def a():
 1070                        print(1)
 1071
 1072                    def b():⋯
 1073
 1074
 1075                    def c():⋯
 1076
 1077
 1078            "
 1079            .unindent(),
 1080        );
 1081
 1082        view.unfold_lines(&UnfoldLines, cx);
 1083        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1084    });
 1085}
 1086
 1087#[gpui::test]
 1088fn test_fold_at_level(cx: &mut TestAppContext) {
 1089    init_test(cx, |_| {});
 1090
 1091    let view = cx.add_window(|cx| {
 1092        let buffer = MultiBuffer::build_simple(
 1093            &"
 1094                class Foo:
 1095                    # Hello!
 1096
 1097                    def a():
 1098                        print(1)
 1099
 1100                    def b():
 1101                        print(2)
 1102
 1103
 1104                class Bar:
 1105                    # World!
 1106
 1107                    def a():
 1108                        print(1)
 1109
 1110                    def b():
 1111                        print(2)
 1112
 1113
 1114            "
 1115            .unindent(),
 1116            cx,
 1117        );
 1118        build_editor(buffer.clone(), cx)
 1119    });
 1120
 1121    _ = view.update(cx, |view, cx| {
 1122        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1123        assert_eq!(
 1124            view.display_text(cx),
 1125            "
 1126                class Foo:
 1127                    # Hello!
 1128
 1129                    def a():⋯
 1130
 1131                    def b():⋯
 1132
 1133
 1134                class Bar:
 1135                    # World!
 1136
 1137                    def a():⋯
 1138
 1139                    def b():⋯
 1140
 1141
 1142            "
 1143            .unindent(),
 1144        );
 1145
 1146        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1147        assert_eq!(
 1148            view.display_text(cx),
 1149            "
 1150                class Foo:⋯
 1151
 1152
 1153                class Bar:⋯
 1154
 1155
 1156            "
 1157            .unindent(),
 1158        );
 1159
 1160        view.unfold_all(&UnfoldAll, cx);
 1161        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1162        assert_eq!(
 1163            view.display_text(cx),
 1164            "
 1165                class Foo:
 1166                    # Hello!
 1167
 1168                    def a():
 1169                        print(1)
 1170
 1171                    def b():
 1172                        print(2)
 1173
 1174
 1175                class Bar:
 1176                    # World!
 1177
 1178                    def a():
 1179                        print(1)
 1180
 1181                    def b():
 1182                        print(2)
 1183
 1184
 1185            "
 1186            .unindent(),
 1187        );
 1188
 1189        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1190    });
 1191}
 1192
 1193#[gpui::test]
 1194fn test_move_cursor(cx: &mut TestAppContext) {
 1195    init_test(cx, |_| {});
 1196
 1197    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1198    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1199
 1200    buffer.update(cx, |buffer, cx| {
 1201        buffer.edit(
 1202            vec![
 1203                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1204                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1205            ],
 1206            None,
 1207            cx,
 1208        );
 1209    });
 1210    _ = view.update(cx, |view, cx| {
 1211        assert_eq!(
 1212            view.selections.display_ranges(cx),
 1213            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1214        );
 1215
 1216        view.move_down(&MoveDown, cx);
 1217        assert_eq!(
 1218            view.selections.display_ranges(cx),
 1219            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1220        );
 1221
 1222        view.move_right(&MoveRight, cx);
 1223        assert_eq!(
 1224            view.selections.display_ranges(cx),
 1225            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1226        );
 1227
 1228        view.move_left(&MoveLeft, cx);
 1229        assert_eq!(
 1230            view.selections.display_ranges(cx),
 1231            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1232        );
 1233
 1234        view.move_up(&MoveUp, cx);
 1235        assert_eq!(
 1236            view.selections.display_ranges(cx),
 1237            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1238        );
 1239
 1240        view.move_to_end(&MoveToEnd, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1244        );
 1245
 1246        view.move_to_beginning(&MoveToBeginning, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1250        );
 1251
 1252        view.change_selections(None, cx, |s| {
 1253            s.select_display_ranges([
 1254                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1255            ]);
 1256        });
 1257        view.select_to_beginning(&SelectToBeginning, cx);
 1258        assert_eq!(
 1259            view.selections.display_ranges(cx),
 1260            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1261        );
 1262
 1263        view.select_to_end(&SelectToEnd, cx);
 1264        assert_eq!(
 1265            view.selections.display_ranges(cx),
 1266            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1267        );
 1268    });
 1269}
 1270
 1271// TODO: Re-enable this test
 1272#[cfg(target_os = "macos")]
 1273#[gpui::test]
 1274fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1275    init_test(cx, |_| {});
 1276
 1277    let view = cx.add_window(|cx| {
 1278        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1279        build_editor(buffer.clone(), cx)
 1280    });
 1281
 1282    assert_eq!('ⓐ'.len_utf8(), 3);
 1283    assert_eq!('α'.len_utf8(), 2);
 1284
 1285    _ = view.update(cx, |view, cx| {
 1286        view.fold_creases(
 1287            vec![
 1288                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1289                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1290                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1291            ],
 1292            true,
 1293            cx,
 1294        );
 1295        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1296
 1297        view.move_right(&MoveRight, cx);
 1298        assert_eq!(
 1299            view.selections.display_ranges(cx),
 1300            &[empty_range(0, "".len())]
 1301        );
 1302        view.move_right(&MoveRight, cx);
 1303        assert_eq!(
 1304            view.selections.display_ranges(cx),
 1305            &[empty_range(0, "ⓐⓑ".len())]
 1306        );
 1307        view.move_right(&MoveRight, cx);
 1308        assert_eq!(
 1309            view.selections.display_ranges(cx),
 1310            &[empty_range(0, "ⓐⓑ⋯".len())]
 1311        );
 1312
 1313        view.move_down(&MoveDown, cx);
 1314        assert_eq!(
 1315            view.selections.display_ranges(cx),
 1316            &[empty_range(1, "ab⋯e".len())]
 1317        );
 1318        view.move_left(&MoveLeft, cx);
 1319        assert_eq!(
 1320            view.selections.display_ranges(cx),
 1321            &[empty_range(1, "ab⋯".len())]
 1322        );
 1323        view.move_left(&MoveLeft, cx);
 1324        assert_eq!(
 1325            view.selections.display_ranges(cx),
 1326            &[empty_range(1, "ab".len())]
 1327        );
 1328        view.move_left(&MoveLeft, cx);
 1329        assert_eq!(
 1330            view.selections.display_ranges(cx),
 1331            &[empty_range(1, "a".len())]
 1332        );
 1333
 1334        view.move_down(&MoveDown, cx);
 1335        assert_eq!(
 1336            view.selections.display_ranges(cx),
 1337            &[empty_range(2, "α".len())]
 1338        );
 1339        view.move_right(&MoveRight, cx);
 1340        assert_eq!(
 1341            view.selections.display_ranges(cx),
 1342            &[empty_range(2, "αβ".len())]
 1343        );
 1344        view.move_right(&MoveRight, cx);
 1345        assert_eq!(
 1346            view.selections.display_ranges(cx),
 1347            &[empty_range(2, "αβ⋯".len())]
 1348        );
 1349        view.move_right(&MoveRight, cx);
 1350        assert_eq!(
 1351            view.selections.display_ranges(cx),
 1352            &[empty_range(2, "αβ⋯ε".len())]
 1353        );
 1354
 1355        view.move_up(&MoveUp, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[empty_range(1, "ab⋯e".len())]
 1359        );
 1360        view.move_down(&MoveDown, cx);
 1361        assert_eq!(
 1362            view.selections.display_ranges(cx),
 1363            &[empty_range(2, "αβ⋯ε".len())]
 1364        );
 1365        view.move_up(&MoveUp, cx);
 1366        assert_eq!(
 1367            view.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯e".len())]
 1369        );
 1370
 1371        view.move_up(&MoveUp, cx);
 1372        assert_eq!(
 1373            view.selections.display_ranges(cx),
 1374            &[empty_range(0, "ⓐⓑ".len())]
 1375        );
 1376        view.move_left(&MoveLeft, cx);
 1377        assert_eq!(
 1378            view.selections.display_ranges(cx),
 1379            &[empty_range(0, "".len())]
 1380        );
 1381        view.move_left(&MoveLeft, cx);
 1382        assert_eq!(
 1383            view.selections.display_ranges(cx),
 1384            &[empty_range(0, "".len())]
 1385        );
 1386    });
 1387}
 1388
 1389#[gpui::test]
 1390fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1391    init_test(cx, |_| {});
 1392
 1393    let view = cx.add_window(|cx| {
 1394        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1395        build_editor(buffer.clone(), cx)
 1396    });
 1397    _ = view.update(cx, |view, cx| {
 1398        view.change_selections(None, cx, |s| {
 1399            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1400        });
 1401        view.move_down(&MoveDown, cx);
 1402        assert_eq!(
 1403            view.selections.display_ranges(cx),
 1404            &[empty_range(1, "abcd".len())]
 1405        );
 1406
 1407        view.move_down(&MoveDown, cx);
 1408        assert_eq!(
 1409            view.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβγ".len())]
 1411        );
 1412
 1413        view.move_down(&MoveDown, cx);
 1414        assert_eq!(
 1415            view.selections.display_ranges(cx),
 1416            &[empty_range(3, "abcd".len())]
 1417        );
 1418
 1419        view.move_down(&MoveDown, cx);
 1420        assert_eq!(
 1421            view.selections.display_ranges(cx),
 1422            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1423        );
 1424
 1425        view.move_up(&MoveUp, cx);
 1426        assert_eq!(
 1427            view.selections.display_ranges(cx),
 1428            &[empty_range(3, "abcd".len())]
 1429        );
 1430
 1431        view.move_up(&MoveUp, cx);
 1432        assert_eq!(
 1433            view.selections.display_ranges(cx),
 1434            &[empty_range(2, "αβγ".len())]
 1435        );
 1436    });
 1437}
 1438
 1439#[gpui::test]
 1440fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1441    init_test(cx, |_| {});
 1442    let move_to_beg = MoveToBeginningOfLine {
 1443        stop_at_soft_wraps: true,
 1444    };
 1445
 1446    let move_to_end = MoveToEndOfLine {
 1447        stop_at_soft_wraps: true,
 1448    };
 1449
 1450    let view = cx.add_window(|cx| {
 1451        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1452        build_editor(buffer, cx)
 1453    });
 1454    _ = view.update(cx, |view, cx| {
 1455        view.change_selections(None, cx, |s| {
 1456            s.select_display_ranges([
 1457                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1458                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1459            ]);
 1460        });
 1461    });
 1462
 1463    _ = view.update(cx, |view, cx| {
 1464        view.move_to_beginning_of_line(&move_to_beg, cx);
 1465        assert_eq!(
 1466            view.selections.display_ranges(cx),
 1467            &[
 1468                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1469                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1470            ]
 1471        );
 1472    });
 1473
 1474    _ = view.update(cx, |view, cx| {
 1475        view.move_to_beginning_of_line(&move_to_beg, cx);
 1476        assert_eq!(
 1477            view.selections.display_ranges(cx),
 1478            &[
 1479                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1480                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1481            ]
 1482        );
 1483    });
 1484
 1485    _ = view.update(cx, |view, cx| {
 1486        view.move_to_beginning_of_line(&move_to_beg, cx);
 1487        assert_eq!(
 1488            view.selections.display_ranges(cx),
 1489            &[
 1490                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1491                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1492            ]
 1493        );
 1494    });
 1495
 1496    _ = view.update(cx, |view, cx| {
 1497        view.move_to_end_of_line(&move_to_end, cx);
 1498        assert_eq!(
 1499            view.selections.display_ranges(cx),
 1500            &[
 1501                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1502                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1503            ]
 1504        );
 1505    });
 1506
 1507    // Moving to the end of line again is a no-op.
 1508    _ = view.update(cx, |view, cx| {
 1509        view.move_to_end_of_line(&move_to_end, cx);
 1510        assert_eq!(
 1511            view.selections.display_ranges(cx),
 1512            &[
 1513                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1514                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1515            ]
 1516        );
 1517    });
 1518
 1519    _ = view.update(cx, |view, cx| {
 1520        view.move_left(&MoveLeft, cx);
 1521        view.select_to_beginning_of_line(
 1522            &SelectToBeginningOfLine {
 1523                stop_at_soft_wraps: true,
 1524            },
 1525            cx,
 1526        );
 1527        assert_eq!(
 1528            view.selections.display_ranges(cx),
 1529            &[
 1530                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1531                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1532            ]
 1533        );
 1534    });
 1535
 1536    _ = view.update(cx, |view, cx| {
 1537        view.select_to_beginning_of_line(
 1538            &SelectToBeginningOfLine {
 1539                stop_at_soft_wraps: true,
 1540            },
 1541            cx,
 1542        );
 1543        assert_eq!(
 1544            view.selections.display_ranges(cx),
 1545            &[
 1546                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1547                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1548            ]
 1549        );
 1550    });
 1551
 1552    _ = view.update(cx, |view, cx| {
 1553        view.select_to_beginning_of_line(
 1554            &SelectToBeginningOfLine {
 1555                stop_at_soft_wraps: true,
 1556            },
 1557            cx,
 1558        );
 1559        assert_eq!(
 1560            view.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = view.update(cx, |view, cx| {
 1569        view.select_to_end_of_line(
 1570            &SelectToEndOfLine {
 1571                stop_at_soft_wraps: true,
 1572            },
 1573            cx,
 1574        );
 1575        assert_eq!(
 1576            view.selections.display_ranges(cx),
 1577            &[
 1578                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1579                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1580            ]
 1581        );
 1582    });
 1583
 1584    _ = view.update(cx, |view, cx| {
 1585        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1586        assert_eq!(view.display_text(cx), "ab\n  de");
 1587        assert_eq!(
 1588            view.selections.display_ranges(cx),
 1589            &[
 1590                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1591                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1592            ]
 1593        );
 1594    });
 1595
 1596    _ = view.update(cx, |view, cx| {
 1597        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1598        assert_eq!(view.display_text(cx), "\n");
 1599        assert_eq!(
 1600            view.selections.display_ranges(cx),
 1601            &[
 1602                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1603                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1604            ]
 1605        );
 1606    });
 1607}
 1608
 1609#[gpui::test]
 1610fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1611    init_test(cx, |_| {});
 1612    let move_to_beg = MoveToBeginningOfLine {
 1613        stop_at_soft_wraps: false,
 1614    };
 1615
 1616    let move_to_end = MoveToEndOfLine {
 1617        stop_at_soft_wraps: false,
 1618    };
 1619
 1620    let view = cx.add_window(|cx| {
 1621        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1622        build_editor(buffer, cx)
 1623    });
 1624
 1625    _ = view.update(cx, |view, cx| {
 1626        view.set_wrap_width(Some(140.0.into()), cx);
 1627
 1628        // We expect the following lines after wrapping
 1629        // ```
 1630        // thequickbrownfox
 1631        // jumpedoverthelazydo
 1632        // gs
 1633        // ```
 1634        // The final `gs` was soft-wrapped onto a new line.
 1635        assert_eq!(
 1636            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1637            view.display_text(cx),
 1638        );
 1639
 1640        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1641        // Start the cursor at the `k` on the first line
 1642        view.change_selections(None, cx, |s| {
 1643            s.select_display_ranges([
 1644                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1645            ]);
 1646        });
 1647
 1648        // Moving to the beginning of the line should put us at the beginning of the line.
 1649        view.move_to_beginning_of_line(&move_to_beg, cx);
 1650        assert_eq!(
 1651            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1652            view.selections.display_ranges(cx)
 1653        );
 1654
 1655        // Moving to the end of the line should put us at the end of the line.
 1656        view.move_to_end_of_line(&move_to_end, cx);
 1657        assert_eq!(
 1658            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1659            view.selections.display_ranges(cx)
 1660        );
 1661
 1662        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1663        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1664        view.change_selections(None, cx, |s| {
 1665            s.select_display_ranges([
 1666                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1667            ]);
 1668        });
 1669
 1670        // Moving to the beginning of the line should put us at the start of the second line of
 1671        // display text, i.e., the `j`.
 1672        view.move_to_beginning_of_line(&move_to_beg, cx);
 1673        assert_eq!(
 1674            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1675            view.selections.display_ranges(cx)
 1676        );
 1677
 1678        // Moving to the beginning of the line again should be a no-op.
 1679        view.move_to_beginning_of_line(&move_to_beg, cx);
 1680        assert_eq!(
 1681            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1682            view.selections.display_ranges(cx)
 1683        );
 1684
 1685        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1686        // next display line.
 1687        view.move_to_end_of_line(&move_to_end, cx);
 1688        assert_eq!(
 1689            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1690            view.selections.display_ranges(cx)
 1691        );
 1692
 1693        // Moving to the end of the line again should be a no-op.
 1694        view.move_to_end_of_line(&move_to_end, cx);
 1695        assert_eq!(
 1696            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1697            view.selections.display_ranges(cx)
 1698        );
 1699    });
 1700}
 1701
 1702#[gpui::test]
 1703fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1704    init_test(cx, |_| {});
 1705
 1706    let view = cx.add_window(|cx| {
 1707        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1708        build_editor(buffer, cx)
 1709    });
 1710    _ = view.update(cx, |view, cx| {
 1711        view.change_selections(None, cx, |s| {
 1712            s.select_display_ranges([
 1713                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1714                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1715            ])
 1716        });
 1717
 1718        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1719        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1720
 1721        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1722        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1723
 1724        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1725        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1726
 1727        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1728        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1729
 1730        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1731        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1732
 1733        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1734        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1735
 1736        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1737        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1738
 1739        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1740        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1741
 1742        view.move_right(&MoveRight, cx);
 1743        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1744        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1745
 1746        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1747        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1748
 1749        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1750        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1751    });
 1752}
 1753
 1754#[gpui::test]
 1755fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1756    init_test(cx, |_| {});
 1757
 1758    let view = cx.add_window(|cx| {
 1759        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1760        build_editor(buffer, cx)
 1761    });
 1762
 1763    _ = view.update(cx, |view, cx| {
 1764        view.set_wrap_width(Some(140.0.into()), cx);
 1765        assert_eq!(
 1766            view.display_text(cx),
 1767            "use one::{\n    two::three::\n    four::five\n};"
 1768        );
 1769
 1770        view.change_selections(None, cx, |s| {
 1771            s.select_display_ranges([
 1772                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1773            ]);
 1774        });
 1775
 1776        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1777        assert_eq!(
 1778            view.selections.display_ranges(cx),
 1779            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1780        );
 1781
 1782        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1783        assert_eq!(
 1784            view.selections.display_ranges(cx),
 1785            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1786        );
 1787
 1788        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1789        assert_eq!(
 1790            view.selections.display_ranges(cx),
 1791            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1792        );
 1793
 1794        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1795        assert_eq!(
 1796            view.selections.display_ranges(cx),
 1797            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1798        );
 1799
 1800        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1801        assert_eq!(
 1802            view.selections.display_ranges(cx),
 1803            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1804        );
 1805
 1806        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1807        assert_eq!(
 1808            view.selections.display_ranges(cx),
 1809            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1810        );
 1811    });
 1812}
 1813
 1814#[gpui::test]
 1815async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1816    init_test(cx, |_| {});
 1817    let mut cx = EditorTestContext::new(cx).await;
 1818
 1819    let line_height = cx.editor(|editor, cx| {
 1820        editor
 1821            .style()
 1822            .unwrap()
 1823            .text
 1824            .line_height_in_pixels(cx.rem_size())
 1825    });
 1826    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1827
 1828    cx.set_state(
 1829        &r#"ˇone
 1830        two
 1831
 1832        three
 1833        fourˇ
 1834        five
 1835
 1836        six"#
 1837            .unindent(),
 1838    );
 1839
 1840    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1841    cx.assert_editor_state(
 1842        &r#"one
 1843        two
 1844        ˇ
 1845        three
 1846        four
 1847        five
 1848        ˇ
 1849        six"#
 1850            .unindent(),
 1851    );
 1852
 1853    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1854    cx.assert_editor_state(
 1855        &r#"one
 1856        two
 1857
 1858        three
 1859        four
 1860        five
 1861        ˇ
 1862        sixˇ"#
 1863            .unindent(),
 1864    );
 1865
 1866    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1867    cx.assert_editor_state(
 1868        &r#"one
 1869        two
 1870
 1871        three
 1872        four
 1873        five
 1874
 1875        sixˇ"#
 1876            .unindent(),
 1877    );
 1878
 1879    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1880    cx.assert_editor_state(
 1881        &r#"one
 1882        two
 1883
 1884        three
 1885        four
 1886        five
 1887        ˇ
 1888        six"#
 1889            .unindent(),
 1890    );
 1891
 1892    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1893    cx.assert_editor_state(
 1894        &r#"one
 1895        two
 1896        ˇ
 1897        three
 1898        four
 1899        five
 1900
 1901        six"#
 1902            .unindent(),
 1903    );
 1904
 1905    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1906    cx.assert_editor_state(
 1907        &r#"ˇone
 1908        two
 1909
 1910        three
 1911        four
 1912        five
 1913
 1914        six"#
 1915            .unindent(),
 1916    );
 1917}
 1918
 1919#[gpui::test]
 1920async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1921    init_test(cx, |_| {});
 1922    let mut cx = EditorTestContext::new(cx).await;
 1923    let line_height = cx.editor(|editor, cx| {
 1924        editor
 1925            .style()
 1926            .unwrap()
 1927            .text
 1928            .line_height_in_pixels(cx.rem_size())
 1929    });
 1930    let window = cx.window;
 1931    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1932
 1933    cx.set_state(
 1934        r#"ˇone
 1935        two
 1936        three
 1937        four
 1938        five
 1939        six
 1940        seven
 1941        eight
 1942        nine
 1943        ten
 1944        "#,
 1945    );
 1946
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 0.)
 1951        );
 1952        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1953        assert_eq!(
 1954            editor.snapshot(cx).scroll_position(),
 1955            gpui::Point::new(0., 3.)
 1956        );
 1957        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1958        assert_eq!(
 1959            editor.snapshot(cx).scroll_position(),
 1960            gpui::Point::new(0., 6.)
 1961        );
 1962        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1963        assert_eq!(
 1964            editor.snapshot(cx).scroll_position(),
 1965            gpui::Point::new(0., 3.)
 1966        );
 1967
 1968        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1969        assert_eq!(
 1970            editor.snapshot(cx).scroll_position(),
 1971            gpui::Point::new(0., 1.)
 1972        );
 1973        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1974        assert_eq!(
 1975            editor.snapshot(cx).scroll_position(),
 1976            gpui::Point::new(0., 3.)
 1977        );
 1978    });
 1979}
 1980
 1981#[gpui::test]
 1982async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1983    init_test(cx, |_| {});
 1984    let mut cx = EditorTestContext::new(cx).await;
 1985
 1986    let line_height = cx.update_editor(|editor, cx| {
 1987        editor.set_vertical_scroll_margin(2, cx);
 1988        editor
 1989            .style()
 1990            .unwrap()
 1991            .text
 1992            .line_height_in_pixels(cx.rem_size())
 1993    });
 1994    let window = cx.window;
 1995    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1996
 1997    cx.set_state(
 1998        r#"ˇone
 1999            two
 2000            three
 2001            four
 2002            five
 2003            six
 2004            seven
 2005            eight
 2006            nine
 2007            ten
 2008        "#,
 2009    );
 2010    cx.update_editor(|editor, cx| {
 2011        assert_eq!(
 2012            editor.snapshot(cx).scroll_position(),
 2013            gpui::Point::new(0., 0.0)
 2014        );
 2015    });
 2016
 2017    // Add a cursor below the visible area. Since both cursors cannot fit
 2018    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2019    // allows the vertical scroll margin below that cursor.
 2020    cx.update_editor(|editor, cx| {
 2021        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2022            selections.select_ranges([
 2023                Point::new(0, 0)..Point::new(0, 0),
 2024                Point::new(6, 0)..Point::new(6, 0),
 2025            ]);
 2026        })
 2027    });
 2028    cx.update_editor(|editor, cx| {
 2029        assert_eq!(
 2030            editor.snapshot(cx).scroll_position(),
 2031            gpui::Point::new(0., 3.0)
 2032        );
 2033    });
 2034
 2035    // Move down. The editor cursor scrolls down to track the newest cursor.
 2036    cx.update_editor(|editor, cx| {
 2037        editor.move_down(&Default::default(), cx);
 2038    });
 2039    cx.update_editor(|editor, cx| {
 2040        assert_eq!(
 2041            editor.snapshot(cx).scroll_position(),
 2042            gpui::Point::new(0., 4.0)
 2043        );
 2044    });
 2045
 2046    // Add a cursor above the visible area. Since both cursors fit on screen,
 2047    // the editor scrolls to show both.
 2048    cx.update_editor(|editor, cx| {
 2049        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2050            selections.select_ranges([
 2051                Point::new(1, 0)..Point::new(1, 0),
 2052                Point::new(6, 0)..Point::new(6, 0),
 2053            ]);
 2054        })
 2055    });
 2056    cx.update_editor(|editor, cx| {
 2057        assert_eq!(
 2058            editor.snapshot(cx).scroll_position(),
 2059            gpui::Point::new(0., 1.0)
 2060        );
 2061    });
 2062}
 2063
 2064#[gpui::test]
 2065async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2066    init_test(cx, |_| {});
 2067    let mut cx = EditorTestContext::new(cx).await;
 2068
 2069    let line_height = cx.editor(|editor, cx| {
 2070        editor
 2071            .style()
 2072            .unwrap()
 2073            .text
 2074            .line_height_in_pixels(cx.rem_size())
 2075    });
 2076    let window = cx.window;
 2077    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2078    cx.set_state(
 2079        &r#"
 2080        ˇone
 2081        two
 2082        threeˇ
 2083        four
 2084        five
 2085        six
 2086        seven
 2087        eight
 2088        nine
 2089        ten
 2090        "#
 2091        .unindent(),
 2092    );
 2093
 2094    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2095    cx.assert_editor_state(
 2096        &r#"
 2097        one
 2098        two
 2099        three
 2100        ˇfour
 2101        five
 2102        sixˇ
 2103        seven
 2104        eight
 2105        nine
 2106        ten
 2107        "#
 2108        .unindent(),
 2109    );
 2110
 2111    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2112    cx.assert_editor_state(
 2113        &r#"
 2114        one
 2115        two
 2116        three
 2117        four
 2118        five
 2119        six
 2120        ˇseven
 2121        eight
 2122        nineˇ
 2123        ten
 2124        "#
 2125        .unindent(),
 2126    );
 2127
 2128    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2129    cx.assert_editor_state(
 2130        &r#"
 2131        one
 2132        two
 2133        three
 2134        ˇfour
 2135        five
 2136        sixˇ
 2137        seven
 2138        eight
 2139        nine
 2140        ten
 2141        "#
 2142        .unindent(),
 2143    );
 2144
 2145    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2146    cx.assert_editor_state(
 2147        &r#"
 2148        ˇone
 2149        two
 2150        threeˇ
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#
 2159        .unindent(),
 2160    );
 2161
 2162    // Test select collapsing
 2163    cx.update_editor(|editor, cx| {
 2164        editor.move_page_down(&MovePageDown::default(), cx);
 2165        editor.move_page_down(&MovePageDown::default(), cx);
 2166        editor.move_page_down(&MovePageDown::default(), cx);
 2167    });
 2168    cx.assert_editor_state(
 2169        &r#"
 2170        one
 2171        two
 2172        three
 2173        four
 2174        five
 2175        six
 2176        seven
 2177        eight
 2178        nine
 2179        ˇten
 2180        ˇ"#
 2181        .unindent(),
 2182    );
 2183}
 2184
 2185#[gpui::test]
 2186async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2187    init_test(cx, |_| {});
 2188    let mut cx = EditorTestContext::new(cx).await;
 2189    cx.set_state("one «two threeˇ» four");
 2190    cx.update_editor(|editor, cx| {
 2191        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2192        assert_eq!(editor.text(cx), " four");
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199
 2200    let view = cx.add_window(|cx| {
 2201        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2202        build_editor(buffer.clone(), cx)
 2203    });
 2204
 2205    _ = view.update(cx, |view, cx| {
 2206        view.change_selections(None, cx, |s| {
 2207            s.select_display_ranges([
 2208                // an empty selection - the preceding word fragment is deleted
 2209                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2210                // characters selected - they are deleted
 2211                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2212            ])
 2213        });
 2214        view.delete_to_previous_word_start(
 2215            &DeleteToPreviousWordStart {
 2216                ignore_newlines: false,
 2217            },
 2218            cx,
 2219        );
 2220        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2221    });
 2222
 2223    _ = view.update(cx, |view, cx| {
 2224        view.change_selections(None, cx, |s| {
 2225            s.select_display_ranges([
 2226                // an empty selection - the following word fragment is deleted
 2227                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2228                // characters selected - they are deleted
 2229                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2230            ])
 2231        });
 2232        view.delete_to_next_word_end(
 2233            &DeleteToNextWordEnd {
 2234                ignore_newlines: false,
 2235            },
 2236            cx,
 2237        );
 2238        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2239    });
 2240}
 2241
 2242#[gpui::test]
 2243fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2244    init_test(cx, |_| {});
 2245
 2246    let view = cx.add_window(|cx| {
 2247        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2248        build_editor(buffer.clone(), cx)
 2249    });
 2250    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2251        ignore_newlines: false,
 2252    };
 2253    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2254        ignore_newlines: true,
 2255    };
 2256
 2257    _ = view.update(cx, |view, cx| {
 2258        view.change_selections(None, cx, |s| {
 2259            s.select_display_ranges([
 2260                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2261            ])
 2262        });
 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\nthree\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\nthree");
 2267        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2268        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2269        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2271        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2272        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2273        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2274        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281
 2282    let view = cx.add_window(|cx| {
 2283        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2284        build_editor(buffer.clone(), cx)
 2285    });
 2286    let del_to_next_word_end = DeleteToNextWordEnd {
 2287        ignore_newlines: false,
 2288    };
 2289    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2290        ignore_newlines: true,
 2291    };
 2292
 2293    _ = view.update(cx, |view, cx| {
 2294        view.change_selections(None, cx, |s| {
 2295            s.select_display_ranges([
 2296                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2297            ])
 2298        });
 2299        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2300        assert_eq!(
 2301            view.buffer.read(cx).read(cx).text(),
 2302            "one\n   two\nthree\n   four"
 2303        );
 2304        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2305        assert_eq!(
 2306            view.buffer.read(cx).read(cx).text(),
 2307            "\n   two\nthree\n   four"
 2308        );
 2309        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2310        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2311        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2312        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2313        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2314        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2315        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2316        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2317    });
 2318}
 2319
 2320#[gpui::test]
 2321fn test_newline(cx: &mut TestAppContext) {
 2322    init_test(cx, |_| {});
 2323
 2324    let view = cx.add_window(|cx| {
 2325        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2326        build_editor(buffer.clone(), cx)
 2327    });
 2328
 2329    _ = view.update(cx, |view, cx| {
 2330        view.change_selections(None, cx, |s| {
 2331            s.select_display_ranges([
 2332                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2333                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2334                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2335            ])
 2336        });
 2337
 2338        view.newline(&Newline, cx);
 2339        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2340    });
 2341}
 2342
 2343#[gpui::test]
 2344fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2345    init_test(cx, |_| {});
 2346
 2347    let editor = cx.add_window(|cx| {
 2348        let buffer = MultiBuffer::build_simple(
 2349            "
 2350                a
 2351                b(
 2352                    X
 2353                )
 2354                c(
 2355                    X
 2356                )
 2357            "
 2358            .unindent()
 2359            .as_str(),
 2360            cx,
 2361        );
 2362        let mut editor = build_editor(buffer.clone(), cx);
 2363        editor.change_selections(None, cx, |s| {
 2364            s.select_ranges([
 2365                Point::new(2, 4)..Point::new(2, 5),
 2366                Point::new(5, 4)..Point::new(5, 5),
 2367            ])
 2368        });
 2369        editor
 2370    });
 2371
 2372    _ = editor.update(cx, |editor, cx| {
 2373        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2374        editor.buffer.update(cx, |buffer, cx| {
 2375            buffer.edit(
 2376                [
 2377                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2378                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2379                ],
 2380                None,
 2381                cx,
 2382            );
 2383            assert_eq!(
 2384                buffer.read(cx).text(),
 2385                "
 2386                    a
 2387                    b()
 2388                    c()
 2389                "
 2390                .unindent()
 2391            );
 2392        });
 2393        assert_eq!(
 2394            editor.selections.ranges(cx),
 2395            &[
 2396                Point::new(1, 2)..Point::new(1, 2),
 2397                Point::new(2, 2)..Point::new(2, 2),
 2398            ],
 2399        );
 2400
 2401        editor.newline(&Newline, cx);
 2402        assert_eq!(
 2403            editor.text(cx),
 2404            "
 2405                a
 2406                b(
 2407                )
 2408                c(
 2409                )
 2410            "
 2411            .unindent()
 2412        );
 2413
 2414        // The selections are moved after the inserted newlines
 2415        assert_eq!(
 2416            editor.selections.ranges(cx),
 2417            &[
 2418                Point::new(2, 0)..Point::new(2, 0),
 2419                Point::new(4, 0)..Point::new(4, 0),
 2420            ],
 2421        );
 2422    });
 2423}
 2424
 2425#[gpui::test]
 2426async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2427    init_test(cx, |settings| {
 2428        settings.defaults.tab_size = NonZeroU32::new(4)
 2429    });
 2430
 2431    let language = Arc::new(
 2432        Language::new(
 2433            LanguageConfig::default(),
 2434            Some(tree_sitter_rust::LANGUAGE.into()),
 2435        )
 2436        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2437        .unwrap(),
 2438    );
 2439
 2440    let mut cx = EditorTestContext::new(cx).await;
 2441    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2442    cx.set_state(indoc! {"
 2443        const a: ˇA = (
 2444 2445                «const_functionˇ»(ˇ),
 2446                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2447 2448        ˇ);ˇ
 2449    "});
 2450
 2451    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2452    cx.assert_editor_state(indoc! {"
 2453        ˇ
 2454        const a: A = (
 2455            ˇ
 2456            (
 2457                ˇ
 2458                ˇ
 2459                const_function(),
 2460                ˇ
 2461                ˇ
 2462                ˇ
 2463                ˇ
 2464                something_else,
 2465                ˇ
 2466            )
 2467            ˇ
 2468            ˇ
 2469        );
 2470    "});
 2471}
 2472
 2473#[gpui::test]
 2474async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2475    init_test(cx, |settings| {
 2476        settings.defaults.tab_size = NonZeroU32::new(4)
 2477    });
 2478
 2479    let language = Arc::new(
 2480        Language::new(
 2481            LanguageConfig::default(),
 2482            Some(tree_sitter_rust::LANGUAGE.into()),
 2483        )
 2484        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2485        .unwrap(),
 2486    );
 2487
 2488    let mut cx = EditorTestContext::new(cx).await;
 2489    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2490    cx.set_state(indoc! {"
 2491        const a: ˇA = (
 2492 2493                «const_functionˇ»(ˇ),
 2494                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2495 2496        ˇ);ˇ
 2497    "});
 2498
 2499    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2500    cx.assert_editor_state(indoc! {"
 2501        const a: A = (
 2502            ˇ
 2503            (
 2504                ˇ
 2505                const_function(),
 2506                ˇ
 2507                ˇ
 2508                something_else,
 2509                ˇ
 2510                ˇ
 2511                ˇ
 2512                ˇ
 2513            )
 2514            ˇ
 2515        );
 2516        ˇ
 2517        ˇ
 2518    "});
 2519}
 2520
 2521#[gpui::test]
 2522async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2523    init_test(cx, |settings| {
 2524        settings.defaults.tab_size = NonZeroU32::new(4)
 2525    });
 2526
 2527    let language = Arc::new(Language::new(
 2528        LanguageConfig {
 2529            line_comments: vec!["//".into()],
 2530            ..LanguageConfig::default()
 2531        },
 2532        None,
 2533    ));
 2534    {
 2535        let mut cx = EditorTestContext::new(cx).await;
 2536        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2537        cx.set_state(indoc! {"
 2538        // Fooˇ
 2539    "});
 2540
 2541        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2542        cx.assert_editor_state(indoc! {"
 2543        // Foo
 2544        //ˇ
 2545    "});
 2546        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2547        cx.set_state(indoc! {"
 2548        ˇ// Foo
 2549    "});
 2550        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2551        cx.assert_editor_state(indoc! {"
 2552
 2553        ˇ// Foo
 2554    "});
 2555    }
 2556    // Ensure that comment continuations can be disabled.
 2557    update_test_language_settings(cx, |settings| {
 2558        settings.defaults.extend_comment_on_newline = Some(false);
 2559    });
 2560    let mut cx = EditorTestContext::new(cx).await;
 2561    cx.set_state(indoc! {"
 2562        // Fooˇ
 2563    "});
 2564    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2565    cx.assert_editor_state(indoc! {"
 2566        // Foo
 2567        ˇ
 2568    "});
 2569}
 2570
 2571#[gpui::test]
 2572fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|cx| {
 2576        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2577        let mut editor = build_editor(buffer.clone(), cx);
 2578        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2579        editor
 2580    });
 2581
 2582    _ = editor.update(cx, |editor, cx| {
 2583        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2584        editor.buffer.update(cx, |buffer, cx| {
 2585            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2586            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2587        });
 2588        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2589
 2590        editor.insert("Z", cx);
 2591        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2592
 2593        // The selections are moved after the inserted characters
 2594        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2595    });
 2596}
 2597
 2598#[gpui::test]
 2599async fn test_tab(cx: &mut gpui::TestAppContext) {
 2600    init_test(cx, |settings| {
 2601        settings.defaults.tab_size = NonZeroU32::new(3)
 2602    });
 2603
 2604    let mut cx = EditorTestContext::new(cx).await;
 2605    cx.set_state(indoc! {"
 2606        ˇabˇc
 2607        ˇ🏀ˇ🏀ˇefg
 2608 2609    "});
 2610    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2611    cx.assert_editor_state(indoc! {"
 2612           ˇab ˇc
 2613           ˇ🏀  ˇ🏀  ˇefg
 2614        d  ˇ
 2615    "});
 2616
 2617    cx.set_state(indoc! {"
 2618        a
 2619        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2620    "});
 2621    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2622    cx.assert_editor_state(indoc! {"
 2623        a
 2624           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2625    "});
 2626}
 2627
 2628#[gpui::test]
 2629async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2630    init_test(cx, |_| {});
 2631
 2632    let mut cx = EditorTestContext::new(cx).await;
 2633    let language = Arc::new(
 2634        Language::new(
 2635            LanguageConfig::default(),
 2636            Some(tree_sitter_rust::LANGUAGE.into()),
 2637        )
 2638        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2639        .unwrap(),
 2640    );
 2641    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2642
 2643    // cursors that are already at the suggested indent level insert
 2644    // a soft tab. cursors that are to the left of the suggested indent
 2645    // auto-indent their line.
 2646    cx.set_state(indoc! {"
 2647        ˇ
 2648        const a: B = (
 2649            c(
 2650                d(
 2651        ˇ
 2652                )
 2653        ˇ
 2654        ˇ    )
 2655        );
 2656    "});
 2657    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2658    cx.assert_editor_state(indoc! {"
 2659            ˇ
 2660        const a: B = (
 2661            c(
 2662                d(
 2663                    ˇ
 2664                )
 2665                ˇ
 2666            ˇ)
 2667        );
 2668    "});
 2669
 2670    // handle auto-indent when there are multiple cursors on the same line
 2671    cx.set_state(indoc! {"
 2672        const a: B = (
 2673            c(
 2674        ˇ    ˇ
 2675        ˇ    )
 2676        );
 2677    "});
 2678    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2679    cx.assert_editor_state(indoc! {"
 2680        const a: B = (
 2681            c(
 2682                ˇ
 2683            ˇ)
 2684        );
 2685    "});
 2686}
 2687
 2688#[gpui::test]
 2689async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2690    init_test(cx, |settings| {
 2691        settings.defaults.tab_size = NonZeroU32::new(4)
 2692    });
 2693
 2694    let language = Arc::new(
 2695        Language::new(
 2696            LanguageConfig::default(),
 2697            Some(tree_sitter_rust::LANGUAGE.into()),
 2698        )
 2699        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2700        .unwrap(),
 2701    );
 2702
 2703    let mut cx = EditorTestContext::new(cx).await;
 2704    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2705    cx.set_state(indoc! {"
 2706        fn a() {
 2707            if b {
 2708        \t ˇc
 2709            }
 2710        }
 2711    "});
 2712
 2713    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2714    cx.assert_editor_state(indoc! {"
 2715        fn a() {
 2716            if b {
 2717                ˇc
 2718            }
 2719        }
 2720    "});
 2721}
 2722
 2723#[gpui::test]
 2724async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2725    init_test(cx, |settings| {
 2726        settings.defaults.tab_size = NonZeroU32::new(4);
 2727    });
 2728
 2729    let mut cx = EditorTestContext::new(cx).await;
 2730
 2731    cx.set_state(indoc! {"
 2732          «oneˇ» «twoˇ»
 2733        three
 2734         four
 2735    "});
 2736    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2737    cx.assert_editor_state(indoc! {"
 2738            «oneˇ» «twoˇ»
 2739        three
 2740         four
 2741    "});
 2742
 2743    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2744    cx.assert_editor_state(indoc! {"
 2745        «oneˇ» «twoˇ»
 2746        three
 2747         four
 2748    "});
 2749
 2750    // select across line ending
 2751    cx.set_state(indoc! {"
 2752        one two
 2753        t«hree
 2754        ˇ» four
 2755    "});
 2756    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2757    cx.assert_editor_state(indoc! {"
 2758        one two
 2759            t«hree
 2760        ˇ» four
 2761    "});
 2762
 2763    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2764    cx.assert_editor_state(indoc! {"
 2765        one two
 2766        t«hree
 2767        ˇ» four
 2768    "});
 2769
 2770    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2771    cx.set_state(indoc! {"
 2772        one two
 2773        ˇthree
 2774            four
 2775    "});
 2776    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2777    cx.assert_editor_state(indoc! {"
 2778        one two
 2779            ˇthree
 2780            four
 2781    "});
 2782
 2783    cx.set_state(indoc! {"
 2784        one two
 2785        ˇ    three
 2786            four
 2787    "});
 2788    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        one two
 2791        ˇthree
 2792            four
 2793    "});
 2794}
 2795
 2796#[gpui::test]
 2797async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2798    init_test(cx, |settings| {
 2799        settings.defaults.hard_tabs = Some(true);
 2800    });
 2801
 2802    let mut cx = EditorTestContext::new(cx).await;
 2803
 2804    // select two ranges on one line
 2805    cx.set_state(indoc! {"
 2806        «oneˇ» «twoˇ»
 2807        three
 2808        four
 2809    "});
 2810    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2811    cx.assert_editor_state(indoc! {"
 2812        \t«oneˇ» «twoˇ»
 2813        three
 2814        four
 2815    "});
 2816    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2817    cx.assert_editor_state(indoc! {"
 2818        \t\t«oneˇ» «twoˇ»
 2819        three
 2820        four
 2821    "});
 2822    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2823    cx.assert_editor_state(indoc! {"
 2824        \t«oneˇ» «twoˇ»
 2825        three
 2826        four
 2827    "});
 2828    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2829    cx.assert_editor_state(indoc! {"
 2830        «oneˇ» «twoˇ»
 2831        three
 2832        four
 2833    "});
 2834
 2835    // select across a line ending
 2836    cx.set_state(indoc! {"
 2837        one two
 2838        t«hree
 2839        ˇ»four
 2840    "});
 2841    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843        one two
 2844        \tt«hree
 2845        ˇ»four
 2846    "});
 2847    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2848    cx.assert_editor_state(indoc! {"
 2849        one two
 2850        \t\tt«hree
 2851        ˇ»four
 2852    "});
 2853    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        one two
 2856        \tt«hree
 2857        ˇ»four
 2858    "});
 2859    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2860    cx.assert_editor_state(indoc! {"
 2861        one two
 2862        t«hree
 2863        ˇ»four
 2864    "});
 2865
 2866    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2867    cx.set_state(indoc! {"
 2868        one two
 2869        ˇthree
 2870        four
 2871    "});
 2872    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2873    cx.assert_editor_state(indoc! {"
 2874        one two
 2875        ˇthree
 2876        four
 2877    "});
 2878    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2879    cx.assert_editor_state(indoc! {"
 2880        one two
 2881        \tˇthree
 2882        four
 2883    "});
 2884    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2885    cx.assert_editor_state(indoc! {"
 2886        one two
 2887        ˇthree
 2888        four
 2889    "});
 2890}
 2891
 2892#[gpui::test]
 2893fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2894    init_test(cx, |settings| {
 2895        settings.languages.extend([
 2896            (
 2897                "TOML".into(),
 2898                LanguageSettingsContent {
 2899                    tab_size: NonZeroU32::new(2),
 2900                    ..Default::default()
 2901                },
 2902            ),
 2903            (
 2904                "Rust".into(),
 2905                LanguageSettingsContent {
 2906                    tab_size: NonZeroU32::new(4),
 2907                    ..Default::default()
 2908                },
 2909            ),
 2910        ]);
 2911    });
 2912
 2913    let toml_language = Arc::new(Language::new(
 2914        LanguageConfig {
 2915            name: "TOML".into(),
 2916            ..Default::default()
 2917        },
 2918        None,
 2919    ));
 2920    let rust_language = Arc::new(Language::new(
 2921        LanguageConfig {
 2922            name: "Rust".into(),
 2923            ..Default::default()
 2924        },
 2925        None,
 2926    ));
 2927
 2928    let toml_buffer =
 2929        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2930    let rust_buffer = cx.new_model(|cx| {
 2931        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2932    });
 2933    let multibuffer = cx.new_model(|cx| {
 2934        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2935        multibuffer.push_excerpts(
 2936            toml_buffer.clone(),
 2937            [ExcerptRange {
 2938                context: Point::new(0, 0)..Point::new(2, 0),
 2939                primary: None,
 2940            }],
 2941            cx,
 2942        );
 2943        multibuffer.push_excerpts(
 2944            rust_buffer.clone(),
 2945            [ExcerptRange {
 2946                context: Point::new(0, 0)..Point::new(1, 0),
 2947                primary: None,
 2948            }],
 2949            cx,
 2950        );
 2951        multibuffer
 2952    });
 2953
 2954    cx.add_window(|cx| {
 2955        let mut editor = build_editor(multibuffer, cx);
 2956
 2957        assert_eq!(
 2958            editor.text(cx),
 2959            indoc! {"
 2960                a = 1
 2961                b = 2
 2962
 2963                const c: usize = 3;
 2964            "}
 2965        );
 2966
 2967        select_ranges(
 2968            &mut editor,
 2969            indoc! {"
 2970                «aˇ» = 1
 2971                b = 2
 2972
 2973                «const c:ˇ» usize = 3;
 2974            "},
 2975            cx,
 2976        );
 2977
 2978        editor.tab(&Tab, cx);
 2979        assert_text_with_selections(
 2980            &mut editor,
 2981            indoc! {"
 2982                  «aˇ» = 1
 2983                b = 2
 2984
 2985                    «const c:ˇ» usize = 3;
 2986            "},
 2987            cx,
 2988        );
 2989        editor.tab_prev(&TabPrev, cx);
 2990        assert_text_with_selections(
 2991            &mut editor,
 2992            indoc! {"
 2993                «aˇ» = 1
 2994                b = 2
 2995
 2996                «const c:ˇ» usize = 3;
 2997            "},
 2998            cx,
 2999        );
 3000
 3001        editor
 3002    });
 3003}
 3004
 3005#[gpui::test]
 3006async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3007    init_test(cx, |_| {});
 3008
 3009    let mut cx = EditorTestContext::new(cx).await;
 3010
 3011    // Basic backspace
 3012    cx.set_state(indoc! {"
 3013        onˇe two three
 3014        fou«rˇ» five six
 3015        seven «ˇeight nine
 3016        »ten
 3017    "});
 3018    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        oˇe two three
 3021        fouˇ five six
 3022        seven ˇten
 3023    "});
 3024
 3025    // Test backspace inside and around indents
 3026    cx.set_state(indoc! {"
 3027        zero
 3028            ˇone
 3029                ˇtwo
 3030            ˇ ˇ ˇ  three
 3031        ˇ  ˇ  four
 3032    "});
 3033    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3034    cx.assert_editor_state(indoc! {"
 3035        zero
 3036        ˇone
 3037            ˇtwo
 3038        ˇ  threeˇ  four
 3039    "});
 3040
 3041    // Test backspace with line_mode set to true
 3042    cx.update_editor(|e, _| e.selections.line_mode = true);
 3043    cx.set_state(indoc! {"
 3044        The ˇquick ˇbrown
 3045        fox jumps over
 3046        the lazy dog
 3047        ˇThe qu«ick bˇ»rown"});
 3048    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        ˇfox jumps over
 3051        the lazy dogˇ"});
 3052}
 3053
 3054#[gpui::test]
 3055async fn test_delete(cx: &mut gpui::TestAppContext) {
 3056    init_test(cx, |_| {});
 3057
 3058    let mut cx = EditorTestContext::new(cx).await;
 3059    cx.set_state(indoc! {"
 3060        onˇe two three
 3061        fou«rˇ» five six
 3062        seven «ˇeight nine
 3063        »ten
 3064    "});
 3065    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        onˇ two three
 3068        fouˇ five six
 3069        seven ˇten
 3070    "});
 3071
 3072    // Test backspace with line_mode set to true
 3073    cx.update_editor(|e, _| e.selections.line_mode = true);
 3074    cx.set_state(indoc! {"
 3075        The ˇquick ˇbrown
 3076        fox «ˇjum»ps over
 3077        the lazy dog
 3078        ˇThe qu«ick bˇ»rown"});
 3079    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3080    cx.assert_editor_state("ˇthe lazy dogˇ");
 3081}
 3082
 3083#[gpui::test]
 3084fn test_delete_line(cx: &mut TestAppContext) {
 3085    init_test(cx, |_| {});
 3086
 3087    let view = cx.add_window(|cx| {
 3088        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3089        build_editor(buffer, cx)
 3090    });
 3091    _ = view.update(cx, |view, cx| {
 3092        view.change_selections(None, cx, |s| {
 3093            s.select_display_ranges([
 3094                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3095                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3096                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3097            ])
 3098        });
 3099        view.delete_line(&DeleteLine, cx);
 3100        assert_eq!(view.display_text(cx), "ghi");
 3101        assert_eq!(
 3102            view.selections.display_ranges(cx),
 3103            vec![
 3104                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3105                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3106            ]
 3107        );
 3108    });
 3109
 3110    let view = cx.add_window(|cx| {
 3111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3112        build_editor(buffer, cx)
 3113    });
 3114    _ = view.update(cx, |view, cx| {
 3115        view.change_selections(None, cx, |s| {
 3116            s.select_display_ranges([
 3117                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3118            ])
 3119        });
 3120        view.delete_line(&DeleteLine, cx);
 3121        assert_eq!(view.display_text(cx), "ghi\n");
 3122        assert_eq!(
 3123            view.selections.display_ranges(cx),
 3124            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3125        );
 3126    });
 3127}
 3128
 3129#[gpui::test]
 3130fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3131    init_test(cx, |_| {});
 3132
 3133    cx.add_window(|cx| {
 3134        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3135        let mut editor = build_editor(buffer.clone(), cx);
 3136        let buffer = buffer.read(cx).as_singleton().unwrap();
 3137
 3138        assert_eq!(
 3139            editor.selections.ranges::<Point>(cx),
 3140            &[Point::new(0, 0)..Point::new(0, 0)]
 3141        );
 3142
 3143        // When on single line, replace newline at end by space
 3144        editor.join_lines(&JoinLines, cx);
 3145        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3146        assert_eq!(
 3147            editor.selections.ranges::<Point>(cx),
 3148            &[Point::new(0, 3)..Point::new(0, 3)]
 3149        );
 3150
 3151        // When multiple lines are selected, remove newlines that are spanned by the selection
 3152        editor.change_selections(None, cx, |s| {
 3153            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3154        });
 3155        editor.join_lines(&JoinLines, cx);
 3156        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3157        assert_eq!(
 3158            editor.selections.ranges::<Point>(cx),
 3159            &[Point::new(0, 11)..Point::new(0, 11)]
 3160        );
 3161
 3162        // Undo should be transactional
 3163        editor.undo(&Undo, cx);
 3164        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3165        assert_eq!(
 3166            editor.selections.ranges::<Point>(cx),
 3167            &[Point::new(0, 5)..Point::new(2, 2)]
 3168        );
 3169
 3170        // When joining an empty line don't insert a space
 3171        editor.change_selections(None, cx, |s| {
 3172            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3173        });
 3174        editor.join_lines(&JoinLines, cx);
 3175        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3176        assert_eq!(
 3177            editor.selections.ranges::<Point>(cx),
 3178            [Point::new(2, 3)..Point::new(2, 3)]
 3179        );
 3180
 3181        // We can remove trailing newlines
 3182        editor.join_lines(&JoinLines, cx);
 3183        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3184        assert_eq!(
 3185            editor.selections.ranges::<Point>(cx),
 3186            [Point::new(2, 3)..Point::new(2, 3)]
 3187        );
 3188
 3189        // We don't blow up on the last line
 3190        editor.join_lines(&JoinLines, cx);
 3191        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3192        assert_eq!(
 3193            editor.selections.ranges::<Point>(cx),
 3194            [Point::new(2, 3)..Point::new(2, 3)]
 3195        );
 3196
 3197        // reset to test indentation
 3198        editor.buffer.update(cx, |buffer, cx| {
 3199            buffer.edit(
 3200                [
 3201                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3202                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3203                ],
 3204                None,
 3205                cx,
 3206            )
 3207        });
 3208
 3209        // We remove any leading spaces
 3210        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3211        editor.change_selections(None, cx, |s| {
 3212            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3213        });
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3216
 3217        // We don't insert a space for a line containing only spaces
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3220
 3221        // We ignore any leading tabs
 3222        editor.join_lines(&JoinLines, cx);
 3223        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3224
 3225        editor
 3226    });
 3227}
 3228
 3229#[gpui::test]
 3230fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3231    init_test(cx, |_| {});
 3232
 3233    cx.add_window(|cx| {
 3234        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3235        let mut editor = build_editor(buffer.clone(), cx);
 3236        let buffer = buffer.read(cx).as_singleton().unwrap();
 3237
 3238        editor.change_selections(None, cx, |s| {
 3239            s.select_ranges([
 3240                Point::new(0, 2)..Point::new(1, 1),
 3241                Point::new(1, 2)..Point::new(1, 2),
 3242                Point::new(3, 1)..Point::new(3, 2),
 3243            ])
 3244        });
 3245
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3248
 3249        assert_eq!(
 3250            editor.selections.ranges::<Point>(cx),
 3251            [
 3252                Point::new(0, 7)..Point::new(0, 7),
 3253                Point::new(1, 3)..Point::new(1, 3)
 3254            ]
 3255        );
 3256        editor
 3257    });
 3258}
 3259
 3260#[gpui::test]
 3261async fn test_join_lines_with_git_diff_base(
 3262    executor: BackgroundExecutor,
 3263    cx: &mut gpui::TestAppContext,
 3264) {
 3265    init_test(cx, |_| {});
 3266
 3267    let mut cx = EditorTestContext::new(cx).await;
 3268
 3269    let diff_base = r#"
 3270        Line 0
 3271        Line 1
 3272        Line 2
 3273        Line 3
 3274        "#
 3275    .unindent();
 3276
 3277    cx.set_state(
 3278        &r#"
 3279        ˇLine 0
 3280        Line 1
 3281        Line 2
 3282        Line 3
 3283        "#
 3284        .unindent(),
 3285    );
 3286
 3287    cx.set_diff_base(Some(&diff_base));
 3288    executor.run_until_parked();
 3289
 3290    // Join lines
 3291    cx.update_editor(|editor, cx| {
 3292        editor.join_lines(&JoinLines, cx);
 3293    });
 3294    executor.run_until_parked();
 3295
 3296    cx.assert_editor_state(
 3297        &r#"
 3298        Line 0ˇ Line 1
 3299        Line 2
 3300        Line 3
 3301        "#
 3302        .unindent(),
 3303    );
 3304    // Join again
 3305    cx.update_editor(|editor, cx| {
 3306        editor.join_lines(&JoinLines, cx);
 3307    });
 3308    executor.run_until_parked();
 3309
 3310    cx.assert_editor_state(
 3311        &r#"
 3312        Line 0 Line 1ˇ Line 2
 3313        Line 3
 3314        "#
 3315        .unindent(),
 3316    );
 3317}
 3318
 3319#[gpui::test]
 3320async fn test_custom_newlines_cause_no_false_positive_diffs(
 3321    executor: BackgroundExecutor,
 3322    cx: &mut gpui::TestAppContext,
 3323) {
 3324    init_test(cx, |_| {});
 3325    let mut cx = EditorTestContext::new(cx).await;
 3326    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3327    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3328    executor.run_until_parked();
 3329
 3330    cx.update_editor(|editor, cx| {
 3331        assert_eq!(
 3332            editor
 3333                .buffer()
 3334                .read(cx)
 3335                .snapshot(cx)
 3336                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3337                .collect::<Vec<_>>(),
 3338            Vec::new(),
 3339            "Should not have any diffs for files with custom newlines"
 3340        );
 3341    });
 3342}
 3343
 3344#[gpui::test]
 3345async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3346    init_test(cx, |_| {});
 3347
 3348    let mut cx = EditorTestContext::new(cx).await;
 3349
 3350    // Test sort_lines_case_insensitive()
 3351    cx.set_state(indoc! {"
 3352        «z
 3353        y
 3354        x
 3355        Z
 3356        Y
 3357        Xˇ»
 3358    "});
 3359    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3360    cx.assert_editor_state(indoc! {"
 3361        «x
 3362        X
 3363        y
 3364        Y
 3365        z
 3366        Zˇ»
 3367    "});
 3368
 3369    // Test reverse_lines()
 3370    cx.set_state(indoc! {"
 3371        «5
 3372        4
 3373        3
 3374        2
 3375        1ˇ»
 3376    "});
 3377    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3378    cx.assert_editor_state(indoc! {"
 3379        «1
 3380        2
 3381        3
 3382        4
 3383        5ˇ»
 3384    "});
 3385
 3386    // Skip testing shuffle_line()
 3387
 3388    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3389    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3390
 3391    // Don't manipulate when cursor is on single line, but expand the selection
 3392    cx.set_state(indoc! {"
 3393        ddˇdd
 3394        ccc
 3395        bb
 3396        a
 3397    "});
 3398    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3399    cx.assert_editor_state(indoc! {"
 3400        «ddddˇ»
 3401        ccc
 3402        bb
 3403        a
 3404    "});
 3405
 3406    // Basic manipulate case
 3407    // Start selection moves to column 0
 3408    // End of selection shrinks to fit shorter line
 3409    cx.set_state(indoc! {"
 3410        dd«d
 3411        ccc
 3412        bb
 3413        aaaaaˇ»
 3414    "});
 3415    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3416    cx.assert_editor_state(indoc! {"
 3417        «aaaaa
 3418        bb
 3419        ccc
 3420        dddˇ»
 3421    "});
 3422
 3423    // Manipulate case with newlines
 3424    cx.set_state(indoc! {"
 3425        dd«d
 3426        ccc
 3427
 3428        bb
 3429        aaaaa
 3430
 3431        ˇ»
 3432    "});
 3433    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3434    cx.assert_editor_state(indoc! {"
 3435        «
 3436
 3437        aaaaa
 3438        bb
 3439        ccc
 3440        dddˇ»
 3441
 3442    "});
 3443
 3444    // Adding new line
 3445    cx.set_state(indoc! {"
 3446        aa«a
 3447        bbˇ»b
 3448    "});
 3449    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3450    cx.assert_editor_state(indoc! {"
 3451        «aaa
 3452        bbb
 3453        added_lineˇ»
 3454    "});
 3455
 3456    // Removing line
 3457    cx.set_state(indoc! {"
 3458        aa«a
 3459        bbbˇ»
 3460    "});
 3461    cx.update_editor(|e, cx| {
 3462        e.manipulate_lines(cx, |lines| {
 3463            lines.pop();
 3464        })
 3465    });
 3466    cx.assert_editor_state(indoc! {"
 3467        «aaaˇ»
 3468    "});
 3469
 3470    // Removing all lines
 3471    cx.set_state(indoc! {"
 3472        aa«a
 3473        bbbˇ»
 3474    "});
 3475    cx.update_editor(|e, cx| {
 3476        e.manipulate_lines(cx, |lines| {
 3477            lines.drain(..);
 3478        })
 3479    });
 3480    cx.assert_editor_state(indoc! {"
 3481        ˇ
 3482    "});
 3483}
 3484
 3485#[gpui::test]
 3486async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3487    init_test(cx, |_| {});
 3488
 3489    let mut cx = EditorTestContext::new(cx).await;
 3490
 3491    // Consider continuous selection as single selection
 3492    cx.set_state(indoc! {"
 3493        Aaa«aa
 3494        cˇ»c«c
 3495        bb
 3496        aaaˇ»aa
 3497    "});
 3498    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3499    cx.assert_editor_state(indoc! {"
 3500        «Aaaaa
 3501        ccc
 3502        bb
 3503        aaaaaˇ»
 3504    "});
 3505
 3506    cx.set_state(indoc! {"
 3507        Aaa«aa
 3508        cˇ»c«c
 3509        bb
 3510        aaaˇ»aa
 3511    "});
 3512    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3513    cx.assert_editor_state(indoc! {"
 3514        «Aaaaa
 3515        ccc
 3516        bbˇ»
 3517    "});
 3518
 3519    // Consider non continuous selection as distinct dedup operations
 3520    cx.set_state(indoc! {"
 3521        «aaaaa
 3522        bb
 3523        aaaaa
 3524        aaaaaˇ»
 3525
 3526        aaa«aaˇ»
 3527    "});
 3528    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3529    cx.assert_editor_state(indoc! {"
 3530        «aaaaa
 3531        bbˇ»
 3532
 3533        «aaaaaˇ»
 3534    "});
 3535}
 3536
 3537#[gpui::test]
 3538async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3539    init_test(cx, |_| {});
 3540
 3541    let mut cx = EditorTestContext::new(cx).await;
 3542
 3543    cx.set_state(indoc! {"
 3544        «Aaa
 3545        aAa
 3546        Aaaˇ»
 3547    "});
 3548    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3549    cx.assert_editor_state(indoc! {"
 3550        «Aaa
 3551        aAaˇ»
 3552    "});
 3553
 3554    cx.set_state(indoc! {"
 3555        «Aaa
 3556        aAa
 3557        aaAˇ»
 3558    "});
 3559    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3560    cx.assert_editor_state(indoc! {"
 3561        «Aaaˇ»
 3562    "});
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    // Manipulate with multiple selections on a single line
 3572    cx.set_state(indoc! {"
 3573        dd«dd
 3574        cˇ»c«c
 3575        bb
 3576        aaaˇ»aa
 3577    "});
 3578    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3579    cx.assert_editor_state(indoc! {"
 3580        «aaaaa
 3581        bb
 3582        ccc
 3583        ddddˇ»
 3584    "});
 3585
 3586    // Manipulate with multiple disjoin selections
 3587    cx.set_state(indoc! {"
 3588 3589        4
 3590        3
 3591        2
 3592        1ˇ»
 3593
 3594        dd«dd
 3595        ccc
 3596        bb
 3597        aaaˇ»aa
 3598    "});
 3599    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3600    cx.assert_editor_state(indoc! {"
 3601        «1
 3602        2
 3603        3
 3604        4
 3605        5ˇ»
 3606
 3607        «aaaaa
 3608        bb
 3609        ccc
 3610        ddddˇ»
 3611    "});
 3612
 3613    // Adding lines on each selection
 3614    cx.set_state(indoc! {"
 3615 3616        1ˇ»
 3617
 3618        bb«bb
 3619        aaaˇ»aa
 3620    "});
 3621    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3622    cx.assert_editor_state(indoc! {"
 3623        «2
 3624        1
 3625        added lineˇ»
 3626
 3627        «bbbb
 3628        aaaaa
 3629        added lineˇ»
 3630    "});
 3631
 3632    // Removing lines on each selection
 3633    cx.set_state(indoc! {"
 3634 3635        1ˇ»
 3636
 3637        bb«bb
 3638        aaaˇ»aa
 3639    "});
 3640    cx.update_editor(|e, cx| {
 3641        e.manipulate_lines(cx, |lines| {
 3642            lines.pop();
 3643        })
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «2ˇ»
 3647
 3648        «bbbbˇ»
 3649    "});
 3650}
 3651
 3652#[gpui::test]
 3653async fn test_manipulate_text(cx: &mut TestAppContext) {
 3654    init_test(cx, |_| {});
 3655
 3656    let mut cx = EditorTestContext::new(cx).await;
 3657
 3658    // Test convert_to_upper_case()
 3659    cx.set_state(indoc! {"
 3660        «hello worldˇ»
 3661    "});
 3662    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3663    cx.assert_editor_state(indoc! {"
 3664        «HELLO WORLDˇ»
 3665    "});
 3666
 3667    // Test convert_to_lower_case()
 3668    cx.set_state(indoc! {"
 3669        «HELLO WORLDˇ»
 3670    "});
 3671    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3672    cx.assert_editor_state(indoc! {"
 3673        «hello worldˇ»
 3674    "});
 3675
 3676    // Test multiple line, single selection case
 3677    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3678    cx.set_state(indoc! {"
 3679        «The quick brown
 3680        fox jumps over
 3681        the lazy dogˇ»
 3682    "});
 3683    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3684    cx.assert_editor_state(indoc! {"
 3685        «The Quick Brown
 3686        Fox Jumps Over
 3687        The Lazy Dogˇ»
 3688    "});
 3689
 3690    // Test multiple line, single selection case
 3691    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3692    cx.set_state(indoc! {"
 3693        «The quick brown
 3694        fox jumps over
 3695        the lazy dogˇ»
 3696    "});
 3697    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3698    cx.assert_editor_state(indoc! {"
 3699        «TheQuickBrown
 3700        FoxJumpsOver
 3701        TheLazyDogˇ»
 3702    "});
 3703
 3704    // From here on out, test more complex cases of manipulate_text()
 3705
 3706    // Test no selection case - should affect words cursors are in
 3707    // Cursor at beginning, middle, and end of word
 3708    cx.set_state(indoc! {"
 3709        ˇhello big beauˇtiful worldˇ
 3710    "});
 3711    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3712    cx.assert_editor_state(indoc! {"
 3713        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3714    "});
 3715
 3716    // Test multiple selections on a single line and across multiple lines
 3717    cx.set_state(indoc! {"
 3718        «Theˇ» quick «brown
 3719        foxˇ» jumps «overˇ»
 3720        the «lazyˇ» dog
 3721    "});
 3722    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3723    cx.assert_editor_state(indoc! {"
 3724        «THEˇ» quick «BROWN
 3725        FOXˇ» jumps «OVERˇ»
 3726        the «LAZYˇ» dog
 3727    "});
 3728
 3729    // Test case where text length grows
 3730    cx.set_state(indoc! {"
 3731        «tschüߡ»
 3732    "});
 3733    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3734    cx.assert_editor_state(indoc! {"
 3735        «TSCHÜSSˇ»
 3736    "});
 3737
 3738    // Test to make sure we don't crash when text shrinks
 3739    cx.set_state(indoc! {"
 3740        aaa_bbbˇ
 3741    "});
 3742    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3743    cx.assert_editor_state(indoc! {"
 3744        «aaaBbbˇ»
 3745    "});
 3746
 3747    // Test to make sure we all aware of the fact that each word can grow and shrink
 3748    // Final selections should be aware of this fact
 3749    cx.set_state(indoc! {"
 3750        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3751    "});
 3752    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3753    cx.assert_editor_state(indoc! {"
 3754        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3755    "});
 3756
 3757    cx.set_state(indoc! {"
 3758        «hElLo, WoRld!ˇ»
 3759    "});
 3760    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3761    cx.assert_editor_state(indoc! {"
 3762        «HeLlO, wOrLD!ˇ»
 3763    "});
 3764}
 3765
 3766#[gpui::test]
 3767fn test_duplicate_line(cx: &mut TestAppContext) {
 3768    init_test(cx, |_| {});
 3769
 3770    let view = cx.add_window(|cx| {
 3771        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3772        build_editor(buffer, cx)
 3773    });
 3774    _ = view.update(cx, |view, cx| {
 3775        view.change_selections(None, cx, |s| {
 3776            s.select_display_ranges([
 3777                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3778                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3779                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3780                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3781            ])
 3782        });
 3783        view.duplicate_line_down(&DuplicateLineDown, cx);
 3784        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3785        assert_eq!(
 3786            view.selections.display_ranges(cx),
 3787            vec![
 3788                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3789                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3790                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3791                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3792            ]
 3793        );
 3794    });
 3795
 3796    let view = cx.add_window(|cx| {
 3797        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3798        build_editor(buffer, cx)
 3799    });
 3800    _ = view.update(cx, |view, cx| {
 3801        view.change_selections(None, cx, |s| {
 3802            s.select_display_ranges([
 3803                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3804                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3805            ])
 3806        });
 3807        view.duplicate_line_down(&DuplicateLineDown, cx);
 3808        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3809        assert_eq!(
 3810            view.selections.display_ranges(cx),
 3811            vec![
 3812                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3813                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3814            ]
 3815        );
 3816    });
 3817
 3818    // With `move_upwards` the selections stay in place, except for
 3819    // the lines inserted above them
 3820    let view = cx.add_window(|cx| {
 3821        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3822        build_editor(buffer, cx)
 3823    });
 3824    _ = view.update(cx, |view, cx| {
 3825        view.change_selections(None, cx, |s| {
 3826            s.select_display_ranges([
 3827                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3828                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3829                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3830                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3831            ])
 3832        });
 3833        view.duplicate_line_up(&DuplicateLineUp, cx);
 3834        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3835        assert_eq!(
 3836            view.selections.display_ranges(cx),
 3837            vec![
 3838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3839                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3840                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3841                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3842            ]
 3843        );
 3844    });
 3845
 3846    let view = cx.add_window(|cx| {
 3847        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3848        build_editor(buffer, cx)
 3849    });
 3850    _ = view.update(cx, |view, cx| {
 3851        view.change_selections(None, cx, |s| {
 3852            s.select_display_ranges([
 3853                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3855            ])
 3856        });
 3857        view.duplicate_line_up(&DuplicateLineUp, cx);
 3858        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3859        assert_eq!(
 3860            view.selections.display_ranges(cx),
 3861            vec![
 3862                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3863                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3864            ]
 3865        );
 3866    });
 3867}
 3868
 3869#[gpui::test]
 3870fn test_move_line_up_down(cx: &mut TestAppContext) {
 3871    init_test(cx, |_| {});
 3872
 3873    let view = cx.add_window(|cx| {
 3874        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3875        build_editor(buffer, cx)
 3876    });
 3877    _ = view.update(cx, |view, cx| {
 3878        view.fold_creases(
 3879            vec![
 3880                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3881                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3882                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3883            ],
 3884            true,
 3885            cx,
 3886        );
 3887        view.change_selections(None, cx, |s| {
 3888            s.select_display_ranges([
 3889                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3890                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3891                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3892                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3893            ])
 3894        });
 3895        assert_eq!(
 3896            view.display_text(cx),
 3897            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3898        );
 3899
 3900        view.move_line_up(&MoveLineUp, cx);
 3901        assert_eq!(
 3902            view.display_text(cx),
 3903            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3904        );
 3905        assert_eq!(
 3906            view.selections.display_ranges(cx),
 3907            vec![
 3908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3909                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3910                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3911                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3912            ]
 3913        );
 3914    });
 3915
 3916    _ = view.update(cx, |view, cx| {
 3917        view.move_line_down(&MoveLineDown, cx);
 3918        assert_eq!(
 3919            view.display_text(cx),
 3920            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3921        );
 3922        assert_eq!(
 3923            view.selections.display_ranges(cx),
 3924            vec![
 3925                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3926                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3927                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3928                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3929            ]
 3930        );
 3931    });
 3932
 3933    _ = view.update(cx, |view, cx| {
 3934        view.move_line_down(&MoveLineDown, cx);
 3935        assert_eq!(
 3936            view.display_text(cx),
 3937            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3938        );
 3939        assert_eq!(
 3940            view.selections.display_ranges(cx),
 3941            vec![
 3942                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3943                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3944                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3945                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3946            ]
 3947        );
 3948    });
 3949
 3950    _ = view.update(cx, |view, cx| {
 3951        view.move_line_up(&MoveLineUp, cx);
 3952        assert_eq!(
 3953            view.display_text(cx),
 3954            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3955        );
 3956        assert_eq!(
 3957            view.selections.display_ranges(cx),
 3958            vec![
 3959                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3960                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3961                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3962                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3963            ]
 3964        );
 3965    });
 3966}
 3967
 3968#[gpui::test]
 3969fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3970    init_test(cx, |_| {});
 3971
 3972    let editor = cx.add_window(|cx| {
 3973        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3974        build_editor(buffer, cx)
 3975    });
 3976    _ = editor.update(cx, |editor, cx| {
 3977        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3978        editor.insert_blocks(
 3979            [BlockProperties {
 3980                style: BlockStyle::Fixed,
 3981                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 3982                height: 1,
 3983                render: Arc::new(|_| div().into_any()),
 3984                priority: 0,
 3985            }],
 3986            Some(Autoscroll::fit()),
 3987            cx,
 3988        );
 3989        editor.change_selections(None, cx, |s| {
 3990            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3991        });
 3992        editor.move_line_down(&MoveLineDown, cx);
 3993    });
 3994}
 3995
 3996#[gpui::test]
 3997async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 3998    init_test(cx, |_| {});
 3999
 4000    let mut cx = EditorTestContext::new(cx).await;
 4001    cx.set_state(
 4002        &"
 4003            ˇzero
 4004            one
 4005            two
 4006            three
 4007            four
 4008            five
 4009        "
 4010        .unindent(),
 4011    );
 4012
 4013    // Create a four-line block that replaces three lines of text.
 4014    cx.update_editor(|editor, cx| {
 4015        let snapshot = editor.snapshot(cx);
 4016        let snapshot = &snapshot.buffer_snapshot;
 4017        let placement = BlockPlacement::Replace(
 4018            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4019        );
 4020        editor.insert_blocks(
 4021            [BlockProperties {
 4022                placement,
 4023                height: 4,
 4024                style: BlockStyle::Sticky,
 4025                render: Arc::new(|_| gpui::div().into_any_element()),
 4026                priority: 0,
 4027            }],
 4028            None,
 4029            cx,
 4030        );
 4031    });
 4032
 4033    // Move down so that the cursor touches the block.
 4034    cx.update_editor(|editor, cx| {
 4035        editor.move_down(&Default::default(), cx);
 4036    });
 4037    cx.assert_editor_state(
 4038        &"
 4039            zero
 4040            «one
 4041            two
 4042            threeˇ»
 4043            four
 4044            five
 4045        "
 4046        .unindent(),
 4047    );
 4048
 4049    // Move down past the block.
 4050    cx.update_editor(|editor, cx| {
 4051        editor.move_down(&Default::default(), cx);
 4052    });
 4053    cx.assert_editor_state(
 4054        &"
 4055            zero
 4056            one
 4057            two
 4058            three
 4059            ˇfour
 4060            five
 4061        "
 4062        .unindent(),
 4063    );
 4064}
 4065
 4066#[gpui::test]
 4067fn test_transpose(cx: &mut TestAppContext) {
 4068    init_test(cx, |_| {});
 4069
 4070    _ = cx.add_window(|cx| {
 4071        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4072        editor.set_style(EditorStyle::default(), cx);
 4073        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "bac");
 4076        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "bca");
 4080        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4081
 4082        editor.transpose(&Default::default(), cx);
 4083        assert_eq!(editor.text(cx), "bac");
 4084        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4085
 4086        editor
 4087    });
 4088
 4089    _ = cx.add_window(|cx| {
 4090        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4091        editor.set_style(EditorStyle::default(), cx);
 4092        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4093        editor.transpose(&Default::default(), cx);
 4094        assert_eq!(editor.text(cx), "acb\nde");
 4095        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4096
 4097        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4098        editor.transpose(&Default::default(), cx);
 4099        assert_eq!(editor.text(cx), "acbd\ne");
 4100        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4101
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "acbde\n");
 4104        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4105
 4106        editor.transpose(&Default::default(), cx);
 4107        assert_eq!(editor.text(cx), "acbd\ne");
 4108        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4109
 4110        editor
 4111    });
 4112
 4113    _ = cx.add_window(|cx| {
 4114        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4115        editor.set_style(EditorStyle::default(), cx);
 4116        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4117        editor.transpose(&Default::default(), cx);
 4118        assert_eq!(editor.text(cx), "bacd\ne");
 4119        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4120
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "bcade\n");
 4123        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4124
 4125        editor.transpose(&Default::default(), cx);
 4126        assert_eq!(editor.text(cx), "bcda\ne");
 4127        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4128
 4129        editor.transpose(&Default::default(), cx);
 4130        assert_eq!(editor.text(cx), "bcade\n");
 4131        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4132
 4133        editor.transpose(&Default::default(), cx);
 4134        assert_eq!(editor.text(cx), "bcaed\n");
 4135        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4136
 4137        editor
 4138    });
 4139
 4140    _ = cx.add_window(|cx| {
 4141        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4142        editor.set_style(EditorStyle::default(), cx);
 4143        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4144        editor.transpose(&Default::default(), cx);
 4145        assert_eq!(editor.text(cx), "🏀🍐✋");
 4146        assert_eq!(editor.selections.ranges(cx), [8..8]);
 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.transpose(&Default::default(), cx);
 4153        assert_eq!(editor.text(cx), "🏀🍐✋");
 4154        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4155
 4156        editor
 4157    });
 4158}
 4159
 4160#[gpui::test]
 4161async fn test_rewrap(cx: &mut TestAppContext) {
 4162    init_test(cx, |_| {});
 4163
 4164    let mut cx = EditorTestContext::new(cx).await;
 4165
 4166    let language_with_c_comments = Arc::new(Language::new(
 4167        LanguageConfig {
 4168            line_comments: vec!["// ".into()],
 4169            ..LanguageConfig::default()
 4170        },
 4171        None,
 4172    ));
 4173    let language_with_pound_comments = Arc::new(Language::new(
 4174        LanguageConfig {
 4175            line_comments: vec!["# ".into()],
 4176            ..LanguageConfig::default()
 4177        },
 4178        None,
 4179    ));
 4180    let markdown_language = Arc::new(Language::new(
 4181        LanguageConfig {
 4182            name: "Markdown".into(),
 4183            ..LanguageConfig::default()
 4184        },
 4185        None,
 4186    ));
 4187    let language_with_doc_comments = Arc::new(Language::new(
 4188        LanguageConfig {
 4189            line_comments: vec!["// ".into(), "/// ".into()],
 4190            ..LanguageConfig::default()
 4191        },
 4192        Some(tree_sitter_rust::LANGUAGE.into()),
 4193    ));
 4194
 4195    let plaintext_language = Arc::new(Language::new(
 4196        LanguageConfig {
 4197            name: "Plain Text".into(),
 4198            ..LanguageConfig::default()
 4199        },
 4200        None,
 4201    ));
 4202
 4203    assert_rewrap(
 4204        indoc! {"
 4205            // ˇ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.
 4206        "},
 4207        indoc! {"
 4208            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4209            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4210            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4211            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4212            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4213            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4214            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4215            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4216            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4217            // porttitor id. Aliquam id accumsan eros.
 4218        "},
 4219        language_with_c_comments.clone(),
 4220        &mut cx,
 4221    );
 4222
 4223    // Test that rewrapping works inside of a selection
 4224    assert_rewrap(
 4225        indoc! {"
 4226            «// 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.ˇ»
 4227        "},
 4228        indoc! {"
 4229            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4230            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4231            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4232            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4233            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4234            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4235            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4236            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4237            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4238            // porttitor id. Aliquam id accumsan eros.ˇ»
 4239        "},
 4240        language_with_c_comments.clone(),
 4241        &mut cx,
 4242    );
 4243
 4244    // Test that cursors that expand to the same region are collapsed.
 4245    assert_rewrap(
 4246        indoc! {"
 4247            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4248            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4249            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4250            // ˇ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.
 4251        "},
 4252        indoc! {"
 4253            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4254            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4255            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4256            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4257            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4258            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4259            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4260            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4261            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4262            // porttitor id. Aliquam id accumsan eros.
 4263        "},
 4264        language_with_c_comments.clone(),
 4265        &mut cx,
 4266    );
 4267
 4268    // Test that non-contiguous selections are treated separately.
 4269    assert_rewrap(
 4270        indoc! {"
 4271            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4272            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4273            //
 4274            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4275            // ˇ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.
 4276        "},
 4277        indoc! {"
 4278            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4279            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4280            // auctor, eu lacinia sapien scelerisque.
 4281            //
 4282            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4283            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4284            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4285            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4286            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4287            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4288            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4289        "},
 4290        language_with_c_comments.clone(),
 4291        &mut cx,
 4292    );
 4293
 4294    // Test that different comment prefixes are supported.
 4295    assert_rewrap(
 4296        indoc! {"
 4297            # ˇ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.
 4298        "},
 4299        indoc! {"
 4300            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4301            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4302            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4303            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4304            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4305            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4306            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4307            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4308            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4309            # accumsan eros.
 4310        "},
 4311        language_with_pound_comments.clone(),
 4312        &mut cx,
 4313    );
 4314
 4315    // Test that rewrapping is ignored outside of comments in most languages.
 4316    assert_rewrap(
 4317        indoc! {"
 4318            /// Adds two numbers.
 4319            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4320            fn add(a: u32, b: u32) -> u32 {
 4321                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ˇ
 4322            }
 4323        "},
 4324        indoc! {"
 4325            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4326            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4327            fn add(a: u32, b: u32) -> u32 {
 4328                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ˇ
 4329            }
 4330        "},
 4331        language_with_doc_comments.clone(),
 4332        &mut cx,
 4333    );
 4334
 4335    // Test that rewrapping works in Markdown and Plain Text languages.
 4336    assert_rewrap(
 4337        indoc! {"
 4338            # Hello
 4339
 4340            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.
 4341        "},
 4342        indoc! {"
 4343            # Hello
 4344
 4345            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4346            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4347            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4348            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4349            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4350            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4351            Integer sit amet scelerisque nisi.
 4352        "},
 4353        markdown_language,
 4354        &mut cx,
 4355    );
 4356
 4357    assert_rewrap(
 4358        indoc! {"
 4359            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.
 4360        "},
 4361        indoc! {"
 4362            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4363            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4364            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4365            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4366            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4367            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4368            Integer sit amet scelerisque nisi.
 4369        "},
 4370        plaintext_language,
 4371        &mut cx,
 4372    );
 4373
 4374    // Test rewrapping unaligned comments in a selection.
 4375    assert_rewrap(
 4376        indoc! {"
 4377            fn foo() {
 4378                if true {
 4379            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4380            // Praesent semper egestas tellus id dignissim.ˇ»
 4381                    do_something();
 4382                } else {
 4383                    //
 4384                }
 4385            }
 4386        "},
 4387        indoc! {"
 4388            fn foo() {
 4389                if true {
 4390            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4391                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4392                    // egestas tellus id dignissim.ˇ»
 4393                    do_something();
 4394                } else {
 4395                    //
 4396                }
 4397            }
 4398        "},
 4399        language_with_doc_comments.clone(),
 4400        &mut cx,
 4401    );
 4402
 4403    assert_rewrap(
 4404        indoc! {"
 4405            fn foo() {
 4406                if true {
 4407            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4408            // Praesent semper egestas tellus id dignissim.»
 4409                    do_something();
 4410                } else {
 4411                    //
 4412                }
 4413
 4414            }
 4415        "},
 4416        indoc! {"
 4417            fn foo() {
 4418                if true {
 4419            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4420                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4421                    // egestas tellus id dignissim.»
 4422                    do_something();
 4423                } else {
 4424                    //
 4425                }
 4426
 4427            }
 4428        "},
 4429        language_with_doc_comments.clone(),
 4430        &mut cx,
 4431    );
 4432
 4433    #[track_caller]
 4434    fn assert_rewrap(
 4435        unwrapped_text: &str,
 4436        wrapped_text: &str,
 4437        language: Arc<Language>,
 4438        cx: &mut EditorTestContext,
 4439    ) {
 4440        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4441        cx.set_state(unwrapped_text);
 4442        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4443        cx.assert_editor_state(wrapped_text);
 4444    }
 4445}
 4446
 4447#[gpui::test]
 4448async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4449    init_test(cx, |_| {});
 4450
 4451    let mut cx = EditorTestContext::new(cx).await;
 4452
 4453    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4454    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4455    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4456
 4457    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4458    cx.set_state("two ˇfour ˇsix ˇ");
 4459    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4460    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4461
 4462    // Paste again but with only two cursors. Since the number of cursors doesn't
 4463    // match the number of slices in the clipboard, the entire clipboard text
 4464    // is pasted at each cursor.
 4465    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4466    cx.update_editor(|e, cx| {
 4467        e.handle_input("( ", cx);
 4468        e.paste(&Paste, cx);
 4469        e.handle_input(") ", cx);
 4470    });
 4471    cx.assert_editor_state(
 4472        &([
 4473            "( one✅ ",
 4474            "three ",
 4475            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4476            "three ",
 4477            "five ) ˇ",
 4478        ]
 4479        .join("\n")),
 4480    );
 4481
 4482    // Cut with three selections, one of which is full-line.
 4483    cx.set_state(indoc! {"
 4484        1«2ˇ»3
 4485        4ˇ567
 4486        «8ˇ»9"});
 4487    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4488    cx.assert_editor_state(indoc! {"
 4489        1ˇ3
 4490        ˇ9"});
 4491
 4492    // Paste with three selections, noticing how the copied selection that was full-line
 4493    // gets inserted before the second cursor.
 4494    cx.set_state(indoc! {"
 4495        1ˇ3
 4496 4497        «oˇ»ne"});
 4498    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4499    cx.assert_editor_state(indoc! {"
 4500        12ˇ3
 4501        4567
 4502 4503        8ˇne"});
 4504
 4505    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4506    cx.set_state(indoc! {"
 4507        The quick brown
 4508        fox juˇmps over
 4509        the lazy dog"});
 4510    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4511    assert_eq!(
 4512        cx.read_from_clipboard()
 4513            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4514        Some("fox jumps over\n".to_string())
 4515    );
 4516
 4517    // Paste with three selections, noticing how the copied full-line selection is inserted
 4518    // before the empty selections but replaces the selection that is non-empty.
 4519    cx.set_state(indoc! {"
 4520        Tˇhe quick brown
 4521        «foˇ»x jumps over
 4522        tˇhe lazy dog"});
 4523    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4524    cx.assert_editor_state(indoc! {"
 4525        fox jumps over
 4526        Tˇhe quick brown
 4527        fox jumps over
 4528        ˇx jumps over
 4529        fox jumps over
 4530        tˇhe lazy dog"});
 4531}
 4532
 4533#[gpui::test]
 4534async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4535    init_test(cx, |_| {});
 4536
 4537    let mut cx = EditorTestContext::new(cx).await;
 4538    let language = Arc::new(Language::new(
 4539        LanguageConfig::default(),
 4540        Some(tree_sitter_rust::LANGUAGE.into()),
 4541    ));
 4542    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4543
 4544    // Cut an indented block, without the leading whitespace.
 4545    cx.set_state(indoc! {"
 4546        const a: B = (
 4547            c(),
 4548            «d(
 4549                e,
 4550                f
 4551            )ˇ»
 4552        );
 4553    "});
 4554    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4555    cx.assert_editor_state(indoc! {"
 4556        const a: B = (
 4557            c(),
 4558            ˇ
 4559        );
 4560    "});
 4561
 4562    // Paste it at the same position.
 4563    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4564    cx.assert_editor_state(indoc! {"
 4565        const a: B = (
 4566            c(),
 4567            d(
 4568                e,
 4569                f
 4570 4571        );
 4572    "});
 4573
 4574    // Paste it at a line with a lower indent level.
 4575    cx.set_state(indoc! {"
 4576        ˇ
 4577        const a: B = (
 4578            c(),
 4579        );
 4580    "});
 4581    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4582    cx.assert_editor_state(indoc! {"
 4583        d(
 4584            e,
 4585            f
 4586 4587        const a: B = (
 4588            c(),
 4589        );
 4590    "});
 4591
 4592    // Cut an indented block, with the leading whitespace.
 4593    cx.set_state(indoc! {"
 4594        const a: B = (
 4595            c(),
 4596        «    d(
 4597                e,
 4598                f
 4599            )
 4600        ˇ»);
 4601    "});
 4602    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4603    cx.assert_editor_state(indoc! {"
 4604        const a: B = (
 4605            c(),
 4606        ˇ);
 4607    "});
 4608
 4609    // Paste it at the same position.
 4610    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4611    cx.assert_editor_state(indoc! {"
 4612        const a: B = (
 4613            c(),
 4614            d(
 4615                e,
 4616                f
 4617            )
 4618        ˇ);
 4619    "});
 4620
 4621    // Paste it at a line with a higher indent level.
 4622    cx.set_state(indoc! {"
 4623        const a: B = (
 4624            c(),
 4625            d(
 4626                e,
 4627 4628            )
 4629        );
 4630    "});
 4631    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4632    cx.assert_editor_state(indoc! {"
 4633        const a: B = (
 4634            c(),
 4635            d(
 4636                e,
 4637                f    d(
 4638                    e,
 4639                    f
 4640                )
 4641        ˇ
 4642            )
 4643        );
 4644    "});
 4645}
 4646
 4647#[gpui::test]
 4648fn test_select_all(cx: &mut TestAppContext) {
 4649    init_test(cx, |_| {});
 4650
 4651    let view = cx.add_window(|cx| {
 4652        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4653        build_editor(buffer, cx)
 4654    });
 4655    _ = view.update(cx, |view, cx| {
 4656        view.select_all(&SelectAll, cx);
 4657        assert_eq!(
 4658            view.selections.display_ranges(cx),
 4659            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4660        );
 4661    });
 4662}
 4663
 4664#[gpui::test]
 4665fn test_select_line(cx: &mut TestAppContext) {
 4666    init_test(cx, |_| {});
 4667
 4668    let view = cx.add_window(|cx| {
 4669        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4670        build_editor(buffer, cx)
 4671    });
 4672    _ = view.update(cx, |view, cx| {
 4673        view.change_selections(None, cx, |s| {
 4674            s.select_display_ranges([
 4675                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4676                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4677                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4678                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4679            ])
 4680        });
 4681        view.select_line(&SelectLine, cx);
 4682        assert_eq!(
 4683            view.selections.display_ranges(cx),
 4684            vec![
 4685                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4686                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4687            ]
 4688        );
 4689    });
 4690
 4691    _ = view.update(cx, |view, cx| {
 4692        view.select_line(&SelectLine, cx);
 4693        assert_eq!(
 4694            view.selections.display_ranges(cx),
 4695            vec![
 4696                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4697                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4698            ]
 4699        );
 4700    });
 4701
 4702    _ = view.update(cx, |view, cx| {
 4703        view.select_line(&SelectLine, cx);
 4704        assert_eq!(
 4705            view.selections.display_ranges(cx),
 4706            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4707        );
 4708    });
 4709}
 4710
 4711#[gpui::test]
 4712fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4713    init_test(cx, |_| {});
 4714
 4715    let view = cx.add_window(|cx| {
 4716        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4717        build_editor(buffer, cx)
 4718    });
 4719    _ = view.update(cx, |view, cx| {
 4720        view.fold_creases(
 4721            vec![
 4722                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4723                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4724                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4725            ],
 4726            true,
 4727            cx,
 4728        );
 4729        view.change_selections(None, cx, |s| {
 4730            s.select_display_ranges([
 4731                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4732                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4733                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4734                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4735            ])
 4736        });
 4737        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4738    });
 4739
 4740    _ = view.update(cx, |view, cx| {
 4741        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4742        assert_eq!(
 4743            view.display_text(cx),
 4744            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4745        );
 4746        assert_eq!(
 4747            view.selections.display_ranges(cx),
 4748            [
 4749                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4750                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4751                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4752                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4753            ]
 4754        );
 4755    });
 4756
 4757    _ = view.update(cx, |view, cx| {
 4758        view.change_selections(None, cx, |s| {
 4759            s.select_display_ranges([
 4760                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4761            ])
 4762        });
 4763        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4764        assert_eq!(
 4765            view.display_text(cx),
 4766            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4767        );
 4768        assert_eq!(
 4769            view.selections.display_ranges(cx),
 4770            [
 4771                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4772                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4773                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4774                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4775                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4776                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4777                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4778                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4779            ]
 4780        );
 4781    });
 4782}
 4783
 4784#[gpui::test]
 4785async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4786    init_test(cx, |_| {});
 4787
 4788    let mut cx = EditorTestContext::new(cx).await;
 4789
 4790    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4791    cx.set_state(indoc!(
 4792        r#"abc
 4793           defˇghi
 4794
 4795           jk
 4796           nlmo
 4797           "#
 4798    ));
 4799
 4800    cx.update_editor(|editor, cx| {
 4801        editor.add_selection_above(&Default::default(), cx);
 4802    });
 4803
 4804    cx.assert_editor_state(indoc!(
 4805        r#"abcˇ
 4806           defˇghi
 4807
 4808           jk
 4809           nlmo
 4810           "#
 4811    ));
 4812
 4813    cx.update_editor(|editor, cx| {
 4814        editor.add_selection_above(&Default::default(), cx);
 4815    });
 4816
 4817    cx.assert_editor_state(indoc!(
 4818        r#"abcˇ
 4819            defˇghi
 4820
 4821            jk
 4822            nlmo
 4823            "#
 4824    ));
 4825
 4826    cx.update_editor(|view, cx| {
 4827        view.add_selection_below(&Default::default(), cx);
 4828    });
 4829
 4830    cx.assert_editor_state(indoc!(
 4831        r#"abc
 4832           defˇghi
 4833
 4834           jk
 4835           nlmo
 4836           "#
 4837    ));
 4838
 4839    cx.update_editor(|view, cx| {
 4840        view.undo_selection(&Default::default(), cx);
 4841    });
 4842
 4843    cx.assert_editor_state(indoc!(
 4844        r#"abcˇ
 4845           defˇghi
 4846
 4847           jk
 4848           nlmo
 4849           "#
 4850    ));
 4851
 4852    cx.update_editor(|view, cx| {
 4853        view.redo_selection(&Default::default(), cx);
 4854    });
 4855
 4856    cx.assert_editor_state(indoc!(
 4857        r#"abc
 4858           defˇghi
 4859
 4860           jk
 4861           nlmo
 4862           "#
 4863    ));
 4864
 4865    cx.update_editor(|view, cx| {
 4866        view.add_selection_below(&Default::default(), cx);
 4867    });
 4868
 4869    cx.assert_editor_state(indoc!(
 4870        r#"abc
 4871           defˇghi
 4872
 4873           jk
 4874           nlmˇo
 4875           "#
 4876    ));
 4877
 4878    cx.update_editor(|view, cx| {
 4879        view.add_selection_below(&Default::default(), cx);
 4880    });
 4881
 4882    cx.assert_editor_state(indoc!(
 4883        r#"abc
 4884           defˇghi
 4885
 4886           jk
 4887           nlmˇo
 4888           "#
 4889    ));
 4890
 4891    // change selections
 4892    cx.set_state(indoc!(
 4893        r#"abc
 4894           def«ˇg»hi
 4895
 4896           jk
 4897           nlmo
 4898           "#
 4899    ));
 4900
 4901    cx.update_editor(|view, cx| {
 4902        view.add_selection_below(&Default::default(), cx);
 4903    });
 4904
 4905    cx.assert_editor_state(indoc!(
 4906        r#"abc
 4907           def«ˇg»hi
 4908
 4909           jk
 4910           nlm«ˇo»
 4911           "#
 4912    ));
 4913
 4914    cx.update_editor(|view, cx| {
 4915        view.add_selection_below(&Default::default(), cx);
 4916    });
 4917
 4918    cx.assert_editor_state(indoc!(
 4919        r#"abc
 4920           def«ˇg»hi
 4921
 4922           jk
 4923           nlm«ˇo»
 4924           "#
 4925    ));
 4926
 4927    cx.update_editor(|view, cx| {
 4928        view.add_selection_above(&Default::default(), cx);
 4929    });
 4930
 4931    cx.assert_editor_state(indoc!(
 4932        r#"abc
 4933           def«ˇg»hi
 4934
 4935           jk
 4936           nlmo
 4937           "#
 4938    ));
 4939
 4940    cx.update_editor(|view, cx| {
 4941        view.add_selection_above(&Default::default(), cx);
 4942    });
 4943
 4944    cx.assert_editor_state(indoc!(
 4945        r#"abc
 4946           def«ˇg»hi
 4947
 4948           jk
 4949           nlmo
 4950           "#
 4951    ));
 4952
 4953    // Change selections again
 4954    cx.set_state(indoc!(
 4955        r#"a«bc
 4956           defgˇ»hi
 4957
 4958           jk
 4959           nlmo
 4960           "#
 4961    ));
 4962
 4963    cx.update_editor(|view, cx| {
 4964        view.add_selection_below(&Default::default(), cx);
 4965    });
 4966
 4967    cx.assert_editor_state(indoc!(
 4968        r#"a«bcˇ»
 4969           d«efgˇ»hi
 4970
 4971           j«kˇ»
 4972           nlmo
 4973           "#
 4974    ));
 4975
 4976    cx.update_editor(|view, cx| {
 4977        view.add_selection_below(&Default::default(), cx);
 4978    });
 4979    cx.assert_editor_state(indoc!(
 4980        r#"a«bcˇ»
 4981           d«efgˇ»hi
 4982
 4983           j«kˇ»
 4984           n«lmoˇ»
 4985           "#
 4986    ));
 4987    cx.update_editor(|view, cx| {
 4988        view.add_selection_above(&Default::default(), cx);
 4989    });
 4990
 4991    cx.assert_editor_state(indoc!(
 4992        r#"a«bcˇ»
 4993           d«efgˇ»hi
 4994
 4995           j«kˇ»
 4996           nlmo
 4997           "#
 4998    ));
 4999
 5000    // Change selections again
 5001    cx.set_state(indoc!(
 5002        r#"abc
 5003           d«ˇefghi
 5004
 5005           jk
 5006           nlm»o
 5007           "#
 5008    ));
 5009
 5010    cx.update_editor(|view, cx| {
 5011        view.add_selection_above(&Default::default(), cx);
 5012    });
 5013
 5014    cx.assert_editor_state(indoc!(
 5015        r#"a«ˇbc»
 5016           d«ˇef»ghi
 5017
 5018           j«ˇk»
 5019           n«ˇlm»o
 5020           "#
 5021    ));
 5022
 5023    cx.update_editor(|view, cx| {
 5024        view.add_selection_below(&Default::default(), cx);
 5025    });
 5026
 5027    cx.assert_editor_state(indoc!(
 5028        r#"abc
 5029           d«ˇef»ghi
 5030
 5031           j«ˇk»
 5032           n«ˇlm»o
 5033           "#
 5034    ));
 5035}
 5036
 5037#[gpui::test]
 5038async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5039    init_test(cx, |_| {});
 5040
 5041    let mut cx = EditorTestContext::new(cx).await;
 5042    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5043
 5044    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5045        .unwrap();
 5046    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5047
 5048    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5049        .unwrap();
 5050    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5051
 5052    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5053    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5054
 5055    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5056    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5057
 5058    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5059        .unwrap();
 5060    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5061
 5062    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5063        .unwrap();
 5064    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5065}
 5066
 5067#[gpui::test]
 5068async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5069    init_test(cx, |_| {});
 5070
 5071    let mut cx = EditorTestContext::new(cx).await;
 5072    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5073
 5074    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5075        .unwrap();
 5076    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5077}
 5078
 5079#[gpui::test]
 5080async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5081    init_test(cx, |_| {});
 5082
 5083    let mut cx = EditorTestContext::new(cx).await;
 5084    cx.set_state(
 5085        r#"let foo = 2;
 5086lˇet foo = 2;
 5087let fooˇ = 2;
 5088let foo = 2;
 5089let foo = ˇ2;"#,
 5090    );
 5091
 5092    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5093        .unwrap();
 5094    cx.assert_editor_state(
 5095        r#"let foo = 2;
 5096«letˇ» foo = 2;
 5097let «fooˇ» = 2;
 5098let foo = 2;
 5099let foo = «2ˇ»;"#,
 5100    );
 5101
 5102    // noop for multiple selections with different contents
 5103    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5104        .unwrap();
 5105    cx.assert_editor_state(
 5106        r#"let foo = 2;
 5107«letˇ» foo = 2;
 5108let «fooˇ» = 2;
 5109let foo = 2;
 5110let foo = «2ˇ»;"#,
 5111    );
 5112}
 5113
 5114#[gpui::test]
 5115async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5116    init_test(cx, |_| {});
 5117
 5118    let mut cx =
 5119        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5120
 5121    cx.assert_editor_state(indoc! {"
 5122        ˇbbb
 5123        ccc
 5124
 5125        bbb
 5126        ccc
 5127        "});
 5128    cx.dispatch_action(SelectPrevious::default());
 5129    cx.assert_editor_state(indoc! {"
 5130                «bbbˇ»
 5131                ccc
 5132
 5133                bbb
 5134                ccc
 5135                "});
 5136    cx.dispatch_action(SelectPrevious::default());
 5137    cx.assert_editor_state(indoc! {"
 5138                «bbbˇ»
 5139                ccc
 5140
 5141                «bbbˇ»
 5142                ccc
 5143                "});
 5144}
 5145
 5146#[gpui::test]
 5147async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5148    init_test(cx, |_| {});
 5149
 5150    let mut cx = EditorTestContext::new(cx).await;
 5151    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5152
 5153    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5154        .unwrap();
 5155    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5156
 5157    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5158        .unwrap();
 5159    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5160
 5161    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5162    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5163
 5164    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5165    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5166
 5167    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5168        .unwrap();
 5169    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5170
 5171    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5172        .unwrap();
 5173    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5174
 5175    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5176        .unwrap();
 5177    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5178}
 5179
 5180#[gpui::test]
 5181async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5182    init_test(cx, |_| {});
 5183
 5184    let mut cx = EditorTestContext::new(cx).await;
 5185    cx.set_state(
 5186        r#"let foo = 2;
 5187lˇet foo = 2;
 5188let fooˇ = 2;
 5189let foo = 2;
 5190let foo = ˇ2;"#,
 5191    );
 5192
 5193    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5194        .unwrap();
 5195    cx.assert_editor_state(
 5196        r#"let foo = 2;
 5197«letˇ» foo = 2;
 5198let «fooˇ» = 2;
 5199let foo = 2;
 5200let foo = «2ˇ»;"#,
 5201    );
 5202
 5203    // noop for multiple selections with different contents
 5204    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5205        .unwrap();
 5206    cx.assert_editor_state(
 5207        r#"let foo = 2;
 5208«letˇ» foo = 2;
 5209let «fooˇ» = 2;
 5210let foo = 2;
 5211let foo = «2ˇ»;"#,
 5212    );
 5213}
 5214
 5215#[gpui::test]
 5216async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5217    init_test(cx, |_| {});
 5218
 5219    let mut cx = EditorTestContext::new(cx).await;
 5220    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5221
 5222    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5223        .unwrap();
 5224    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5225
 5226    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5227        .unwrap();
 5228    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5229
 5230    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5231    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5232
 5233    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5234    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5235
 5236    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5237        .unwrap();
 5238    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5239
 5240    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5241        .unwrap();
 5242    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5243}
 5244
 5245#[gpui::test]
 5246async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5247    init_test(cx, |_| {});
 5248
 5249    let language = Arc::new(Language::new(
 5250        LanguageConfig::default(),
 5251        Some(tree_sitter_rust::LANGUAGE.into()),
 5252    ));
 5253
 5254    let text = r#"
 5255        use mod1::mod2::{mod3, mod4};
 5256
 5257        fn fn_1(param1: bool, param2: &str) {
 5258            let var1 = "text";
 5259        }
 5260    "#
 5261    .unindent();
 5262
 5263    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5264    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5265    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5266
 5267    editor
 5268        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5269        .await;
 5270
 5271    editor.update(cx, |view, cx| {
 5272        view.change_selections(None, cx, |s| {
 5273            s.select_display_ranges([
 5274                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5275                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5276                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5277            ]);
 5278        });
 5279        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5280    });
 5281    editor.update(cx, |editor, cx| {
 5282        assert_text_with_selections(
 5283            editor,
 5284            indoc! {r#"
 5285                use mod1::mod2::{mod3, «mod4ˇ»};
 5286
 5287                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5288                    let var1 = "«textˇ»";
 5289                }
 5290            "#},
 5291            cx,
 5292        );
 5293    });
 5294
 5295    editor.update(cx, |view, cx| {
 5296        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5297    });
 5298    editor.update(cx, |editor, cx| {
 5299        assert_text_with_selections(
 5300            editor,
 5301            indoc! {r#"
 5302                use mod1::mod2::«{mod3, mod4}ˇ»;
 5303
 5304                «ˇfn fn_1(param1: bool, param2: &str) {
 5305                    let var1 = "text";
 5306 5307            "#},
 5308            cx,
 5309        );
 5310    });
 5311
 5312    editor.update(cx, |view, cx| {
 5313        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5314    });
 5315    assert_eq!(
 5316        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5317        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5318    );
 5319
 5320    // Trying to expand the selected syntax node one more time has no effect.
 5321    editor.update(cx, |view, cx| {
 5322        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5323    });
 5324    assert_eq!(
 5325        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5326        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5327    );
 5328
 5329    editor.update(cx, |view, cx| {
 5330        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5331    });
 5332    editor.update(cx, |editor, cx| {
 5333        assert_text_with_selections(
 5334            editor,
 5335            indoc! {r#"
 5336                use mod1::mod2::«{mod3, mod4}ˇ»;
 5337
 5338                «ˇfn fn_1(param1: bool, param2: &str) {
 5339                    let var1 = "text";
 5340 5341            "#},
 5342            cx,
 5343        );
 5344    });
 5345
 5346    editor.update(cx, |view, cx| {
 5347        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5348    });
 5349    editor.update(cx, |editor, cx| {
 5350        assert_text_with_selections(
 5351            editor,
 5352            indoc! {r#"
 5353                use mod1::mod2::{mod3, «mod4ˇ»};
 5354
 5355                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5356                    let var1 = "«textˇ»";
 5357                }
 5358            "#},
 5359            cx,
 5360        );
 5361    });
 5362
 5363    editor.update(cx, |view, cx| {
 5364        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5365    });
 5366    editor.update(cx, |editor, cx| {
 5367        assert_text_with_selections(
 5368            editor,
 5369            indoc! {r#"
 5370                use mod1::mod2::{mod3, mo«ˇ»d4};
 5371
 5372                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5373                    let var1 = "te«ˇ»xt";
 5374                }
 5375            "#},
 5376            cx,
 5377        );
 5378    });
 5379
 5380    // Trying to shrink the selected syntax node one more time has no effect.
 5381    editor.update(cx, |view, cx| {
 5382        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5383    });
 5384    editor.update(cx, |editor, cx| {
 5385        assert_text_with_selections(
 5386            editor,
 5387            indoc! {r#"
 5388                use mod1::mod2::{mod3, mo«ˇ»d4};
 5389
 5390                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5391                    let var1 = "te«ˇ»xt";
 5392                }
 5393            "#},
 5394            cx,
 5395        );
 5396    });
 5397
 5398    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5399    // a fold.
 5400    editor.update(cx, |view, cx| {
 5401        view.fold_creases(
 5402            vec![
 5403                Crease::simple(
 5404                    Point::new(0, 21)..Point::new(0, 24),
 5405                    FoldPlaceholder::test(),
 5406                ),
 5407                Crease::simple(
 5408                    Point::new(3, 20)..Point::new(3, 22),
 5409                    FoldPlaceholder::test(),
 5410                ),
 5411            ],
 5412            true,
 5413            cx,
 5414        );
 5415        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5416    });
 5417    editor.update(cx, |editor, cx| {
 5418        assert_text_with_selections(
 5419            editor,
 5420            indoc! {r#"
 5421                use mod1::mod2::«{mod3, mod4}ˇ»;
 5422
 5423                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5424                    «let var1 = "text";ˇ»
 5425                }
 5426            "#},
 5427            cx,
 5428        );
 5429    });
 5430}
 5431
 5432#[gpui::test]
 5433async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5434    init_test(cx, |_| {});
 5435
 5436    let language = Arc::new(
 5437        Language::new(
 5438            LanguageConfig {
 5439                brackets: BracketPairConfig {
 5440                    pairs: vec![
 5441                        BracketPair {
 5442                            start: "{".to_string(),
 5443                            end: "}".to_string(),
 5444                            close: false,
 5445                            surround: false,
 5446                            newline: true,
 5447                        },
 5448                        BracketPair {
 5449                            start: "(".to_string(),
 5450                            end: ")".to_string(),
 5451                            close: false,
 5452                            surround: false,
 5453                            newline: true,
 5454                        },
 5455                    ],
 5456                    ..Default::default()
 5457                },
 5458                ..Default::default()
 5459            },
 5460            Some(tree_sitter_rust::LANGUAGE.into()),
 5461        )
 5462        .with_indents_query(
 5463            r#"
 5464                (_ "(" ")" @end) @indent
 5465                (_ "{" "}" @end) @indent
 5466            "#,
 5467        )
 5468        .unwrap(),
 5469    );
 5470
 5471    let text = "fn a() {}";
 5472
 5473    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5474    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5475    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5476    editor
 5477        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5478        .await;
 5479
 5480    editor.update(cx, |editor, cx| {
 5481        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5482        editor.newline(&Newline, cx);
 5483        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5484        assert_eq!(
 5485            editor.selections.ranges(cx),
 5486            &[
 5487                Point::new(1, 4)..Point::new(1, 4),
 5488                Point::new(3, 4)..Point::new(3, 4),
 5489                Point::new(5, 0)..Point::new(5, 0)
 5490            ]
 5491        );
 5492    });
 5493}
 5494
 5495#[gpui::test]
 5496async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5497    init_test(cx, |_| {});
 5498
 5499    let mut cx = EditorTestContext::new(cx).await;
 5500
 5501    let language = Arc::new(Language::new(
 5502        LanguageConfig {
 5503            brackets: BracketPairConfig {
 5504                pairs: vec![
 5505                    BracketPair {
 5506                        start: "{".to_string(),
 5507                        end: "}".to_string(),
 5508                        close: true,
 5509                        surround: true,
 5510                        newline: true,
 5511                    },
 5512                    BracketPair {
 5513                        start: "(".to_string(),
 5514                        end: ")".to_string(),
 5515                        close: true,
 5516                        surround: true,
 5517                        newline: true,
 5518                    },
 5519                    BracketPair {
 5520                        start: "/*".to_string(),
 5521                        end: " */".to_string(),
 5522                        close: true,
 5523                        surround: true,
 5524                        newline: true,
 5525                    },
 5526                    BracketPair {
 5527                        start: "[".to_string(),
 5528                        end: "]".to_string(),
 5529                        close: false,
 5530                        surround: false,
 5531                        newline: true,
 5532                    },
 5533                    BracketPair {
 5534                        start: "\"".to_string(),
 5535                        end: "\"".to_string(),
 5536                        close: true,
 5537                        surround: true,
 5538                        newline: false,
 5539                    },
 5540                    BracketPair {
 5541                        start: "<".to_string(),
 5542                        end: ">".to_string(),
 5543                        close: false,
 5544                        surround: true,
 5545                        newline: true,
 5546                    },
 5547                ],
 5548                ..Default::default()
 5549            },
 5550            autoclose_before: "})]".to_string(),
 5551            ..Default::default()
 5552        },
 5553        Some(tree_sitter_rust::LANGUAGE.into()),
 5554    ));
 5555
 5556    cx.language_registry().add(language.clone());
 5557    cx.update_buffer(|buffer, cx| {
 5558        buffer.set_language(Some(language), cx);
 5559    });
 5560
 5561    cx.set_state(
 5562        &r#"
 5563            🏀ˇ
 5564            εˇ
 5565            ❤️ˇ
 5566        "#
 5567        .unindent(),
 5568    );
 5569
 5570    // autoclose multiple nested brackets at multiple cursors
 5571    cx.update_editor(|view, cx| {
 5572        view.handle_input("{", cx);
 5573        view.handle_input("{", cx);
 5574        view.handle_input("{", cx);
 5575    });
 5576    cx.assert_editor_state(
 5577        &"
 5578            🏀{{{ˇ}}}
 5579            ε{{{ˇ}}}
 5580            ❤️{{{ˇ}}}
 5581        "
 5582        .unindent(),
 5583    );
 5584
 5585    // insert a different closing bracket
 5586    cx.update_editor(|view, cx| {
 5587        view.handle_input(")", cx);
 5588    });
 5589    cx.assert_editor_state(
 5590        &"
 5591            🏀{{{)ˇ}}}
 5592            ε{{{)ˇ}}}
 5593            ❤️{{{)ˇ}}}
 5594        "
 5595        .unindent(),
 5596    );
 5597
 5598    // skip over the auto-closed brackets when typing a closing bracket
 5599    cx.update_editor(|view, cx| {
 5600        view.move_right(&MoveRight, cx);
 5601        view.handle_input("}", cx);
 5602        view.handle_input("}", cx);
 5603        view.handle_input("}", cx);
 5604    });
 5605    cx.assert_editor_state(
 5606        &"
 5607            🏀{{{)}}}}ˇ
 5608            ε{{{)}}}}ˇ
 5609            ❤️{{{)}}}}ˇ
 5610        "
 5611        .unindent(),
 5612    );
 5613
 5614    // autoclose multi-character pairs
 5615    cx.set_state(
 5616        &"
 5617            ˇ
 5618            ˇ
 5619        "
 5620        .unindent(),
 5621    );
 5622    cx.update_editor(|view, cx| {
 5623        view.handle_input("/", cx);
 5624        view.handle_input("*", cx);
 5625    });
 5626    cx.assert_editor_state(
 5627        &"
 5628            /*ˇ */
 5629            /*ˇ */
 5630        "
 5631        .unindent(),
 5632    );
 5633
 5634    // one cursor autocloses a multi-character pair, one cursor
 5635    // does not autoclose.
 5636    cx.set_state(
 5637        &"
 5638 5639            ˇ
 5640        "
 5641        .unindent(),
 5642    );
 5643    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5644    cx.assert_editor_state(
 5645        &"
 5646            /*ˇ */
 5647 5648        "
 5649        .unindent(),
 5650    );
 5651
 5652    // Don't autoclose if the next character isn't whitespace and isn't
 5653    // listed in the language's "autoclose_before" section.
 5654    cx.set_state("ˇa b");
 5655    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5656    cx.assert_editor_state("{ˇa b");
 5657
 5658    // Don't autoclose if `close` is false for the bracket pair
 5659    cx.set_state("ˇ");
 5660    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5661    cx.assert_editor_state("");
 5662
 5663    // Surround with brackets if text is selected
 5664    cx.set_state("«aˇ» b");
 5665    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5666    cx.assert_editor_state("{«aˇ»} b");
 5667
 5668    // Autclose pair where the start and end characters are the same
 5669    cx.set_state("");
 5670    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5671    cx.assert_editor_state("a\"ˇ\"");
 5672    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5673    cx.assert_editor_state("a\"\"ˇ");
 5674
 5675    // Don't autoclose pair if autoclose is disabled
 5676    cx.set_state("ˇ");
 5677    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5678    cx.assert_editor_state("");
 5679
 5680    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5681    cx.set_state("«aˇ» b");
 5682    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5683    cx.assert_editor_state("<«aˇ»> b");
 5684}
 5685
 5686#[gpui::test]
 5687async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5688    init_test(cx, |settings| {
 5689        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5690    });
 5691
 5692    let mut cx = EditorTestContext::new(cx).await;
 5693
 5694    let language = Arc::new(Language::new(
 5695        LanguageConfig {
 5696            brackets: BracketPairConfig {
 5697                pairs: vec![
 5698                    BracketPair {
 5699                        start: "{".to_string(),
 5700                        end: "}".to_string(),
 5701                        close: true,
 5702                        surround: true,
 5703                        newline: true,
 5704                    },
 5705                    BracketPair {
 5706                        start: "(".to_string(),
 5707                        end: ")".to_string(),
 5708                        close: true,
 5709                        surround: true,
 5710                        newline: true,
 5711                    },
 5712                    BracketPair {
 5713                        start: "[".to_string(),
 5714                        end: "]".to_string(),
 5715                        close: false,
 5716                        surround: false,
 5717                        newline: true,
 5718                    },
 5719                ],
 5720                ..Default::default()
 5721            },
 5722            autoclose_before: "})]".to_string(),
 5723            ..Default::default()
 5724        },
 5725        Some(tree_sitter_rust::LANGUAGE.into()),
 5726    ));
 5727
 5728    cx.language_registry().add(language.clone());
 5729    cx.update_buffer(|buffer, cx| {
 5730        buffer.set_language(Some(language), cx);
 5731    });
 5732
 5733    cx.set_state(
 5734        &"
 5735            ˇ
 5736            ˇ
 5737            ˇ
 5738        "
 5739        .unindent(),
 5740    );
 5741
 5742    // ensure only matching closing brackets are skipped over
 5743    cx.update_editor(|view, cx| {
 5744        view.handle_input("}", cx);
 5745        view.move_left(&MoveLeft, cx);
 5746        view.handle_input(")", cx);
 5747        view.move_left(&MoveLeft, cx);
 5748    });
 5749    cx.assert_editor_state(
 5750        &"
 5751            ˇ)}
 5752            ˇ)}
 5753            ˇ)}
 5754        "
 5755        .unindent(),
 5756    );
 5757
 5758    // skip-over closing brackets at multiple cursors
 5759    cx.update_editor(|view, cx| {
 5760        view.handle_input(")", cx);
 5761        view.handle_input("}", cx);
 5762    });
 5763    cx.assert_editor_state(
 5764        &"
 5765            )}ˇ
 5766            )}ˇ
 5767            )}ˇ
 5768        "
 5769        .unindent(),
 5770    );
 5771
 5772    // ignore non-close brackets
 5773    cx.update_editor(|view, cx| {
 5774        view.handle_input("]", cx);
 5775        view.move_left(&MoveLeft, cx);
 5776        view.handle_input("]", cx);
 5777    });
 5778    cx.assert_editor_state(
 5779        &"
 5780            )}]ˇ]
 5781            )}]ˇ]
 5782            )}]ˇ]
 5783        "
 5784        .unindent(),
 5785    );
 5786}
 5787
 5788#[gpui::test]
 5789async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5790    init_test(cx, |_| {});
 5791
 5792    let mut cx = EditorTestContext::new(cx).await;
 5793
 5794    let html_language = Arc::new(
 5795        Language::new(
 5796            LanguageConfig {
 5797                name: "HTML".into(),
 5798                brackets: BracketPairConfig {
 5799                    pairs: vec![
 5800                        BracketPair {
 5801                            start: "<".into(),
 5802                            end: ">".into(),
 5803                            close: true,
 5804                            ..Default::default()
 5805                        },
 5806                        BracketPair {
 5807                            start: "{".into(),
 5808                            end: "}".into(),
 5809                            close: true,
 5810                            ..Default::default()
 5811                        },
 5812                        BracketPair {
 5813                            start: "(".into(),
 5814                            end: ")".into(),
 5815                            close: true,
 5816                            ..Default::default()
 5817                        },
 5818                    ],
 5819                    ..Default::default()
 5820                },
 5821                autoclose_before: "})]>".into(),
 5822                ..Default::default()
 5823            },
 5824            Some(tree_sitter_html::language()),
 5825        )
 5826        .with_injection_query(
 5827            r#"
 5828            (script_element
 5829                (raw_text) @content
 5830                (#set! "language" "javascript"))
 5831            "#,
 5832        )
 5833        .unwrap(),
 5834    );
 5835
 5836    let javascript_language = Arc::new(Language::new(
 5837        LanguageConfig {
 5838            name: "JavaScript".into(),
 5839            brackets: BracketPairConfig {
 5840                pairs: vec![
 5841                    BracketPair {
 5842                        start: "/*".into(),
 5843                        end: " */".into(),
 5844                        close: true,
 5845                        ..Default::default()
 5846                    },
 5847                    BracketPair {
 5848                        start: "{".into(),
 5849                        end: "}".into(),
 5850                        close: true,
 5851                        ..Default::default()
 5852                    },
 5853                    BracketPair {
 5854                        start: "(".into(),
 5855                        end: ")".into(),
 5856                        close: true,
 5857                        ..Default::default()
 5858                    },
 5859                ],
 5860                ..Default::default()
 5861            },
 5862            autoclose_before: "})]>".into(),
 5863            ..Default::default()
 5864        },
 5865        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5866    ));
 5867
 5868    cx.language_registry().add(html_language.clone());
 5869    cx.language_registry().add(javascript_language.clone());
 5870
 5871    cx.update_buffer(|buffer, cx| {
 5872        buffer.set_language(Some(html_language), cx);
 5873    });
 5874
 5875    cx.set_state(
 5876        &r#"
 5877            <body>ˇ
 5878                <script>
 5879                    var x = 1;ˇ
 5880                </script>
 5881            </body>ˇ
 5882        "#
 5883        .unindent(),
 5884    );
 5885
 5886    // Precondition: different languages are active at different locations.
 5887    cx.update_editor(|editor, cx| {
 5888        let snapshot = editor.snapshot(cx);
 5889        let cursors = editor.selections.ranges::<usize>(cx);
 5890        let languages = cursors
 5891            .iter()
 5892            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5893            .collect::<Vec<_>>();
 5894        assert_eq!(
 5895            languages,
 5896            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5897        );
 5898    });
 5899
 5900    // Angle brackets autoclose in HTML, but not JavaScript.
 5901    cx.update_editor(|editor, cx| {
 5902        editor.handle_input("<", cx);
 5903        editor.handle_input("a", cx);
 5904    });
 5905    cx.assert_editor_state(
 5906        &r#"
 5907            <body><aˇ>
 5908                <script>
 5909                    var x = 1;<aˇ
 5910                </script>
 5911            </body><aˇ>
 5912        "#
 5913        .unindent(),
 5914    );
 5915
 5916    // Curly braces and parens autoclose in both HTML and JavaScript.
 5917    cx.update_editor(|editor, cx| {
 5918        editor.handle_input(" b=", cx);
 5919        editor.handle_input("{", cx);
 5920        editor.handle_input("c", cx);
 5921        editor.handle_input("(", cx);
 5922    });
 5923    cx.assert_editor_state(
 5924        &r#"
 5925            <body><a b={c(ˇ)}>
 5926                <script>
 5927                    var x = 1;<a b={c(ˇ)}
 5928                </script>
 5929            </body><a b={c(ˇ)}>
 5930        "#
 5931        .unindent(),
 5932    );
 5933
 5934    // Brackets that were already autoclosed are skipped.
 5935    cx.update_editor(|editor, cx| {
 5936        editor.handle_input(")", cx);
 5937        editor.handle_input("d", cx);
 5938        editor.handle_input("}", cx);
 5939    });
 5940    cx.assert_editor_state(
 5941        &r#"
 5942            <body><a b={c()d}ˇ>
 5943                <script>
 5944                    var x = 1;<a b={c()d}ˇ
 5945                </script>
 5946            </body><a b={c()d}ˇ>
 5947        "#
 5948        .unindent(),
 5949    );
 5950    cx.update_editor(|editor, cx| {
 5951        editor.handle_input(">", cx);
 5952    });
 5953    cx.assert_editor_state(
 5954        &r#"
 5955            <body><a b={c()d}>ˇ
 5956                <script>
 5957                    var x = 1;<a b={c()d}>ˇ
 5958                </script>
 5959            </body><a b={c()d}>ˇ
 5960        "#
 5961        .unindent(),
 5962    );
 5963
 5964    // Reset
 5965    cx.set_state(
 5966        &r#"
 5967            <body>ˇ
 5968                <script>
 5969                    var x = 1;ˇ
 5970                </script>
 5971            </body>ˇ
 5972        "#
 5973        .unindent(),
 5974    );
 5975
 5976    cx.update_editor(|editor, cx| {
 5977        editor.handle_input("<", cx);
 5978    });
 5979    cx.assert_editor_state(
 5980        &r#"
 5981            <body><ˇ>
 5982                <script>
 5983                    var x = 1;<ˇ
 5984                </script>
 5985            </body><ˇ>
 5986        "#
 5987        .unindent(),
 5988    );
 5989
 5990    // When backspacing, the closing angle brackets are removed.
 5991    cx.update_editor(|editor, cx| {
 5992        editor.backspace(&Backspace, cx);
 5993    });
 5994    cx.assert_editor_state(
 5995        &r#"
 5996            <body>ˇ
 5997                <script>
 5998                    var x = 1;ˇ
 5999                </script>
 6000            </body>ˇ
 6001        "#
 6002        .unindent(),
 6003    );
 6004
 6005    // Block comments autoclose in JavaScript, but not HTML.
 6006    cx.update_editor(|editor, cx| {
 6007        editor.handle_input("/", cx);
 6008        editor.handle_input("*", cx);
 6009    });
 6010    cx.assert_editor_state(
 6011        &r#"
 6012            <body>/*ˇ
 6013                <script>
 6014                    var x = 1;/*ˇ */
 6015                </script>
 6016            </body>/*ˇ
 6017        "#
 6018        .unindent(),
 6019    );
 6020}
 6021
 6022#[gpui::test]
 6023async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6024    init_test(cx, |_| {});
 6025
 6026    let mut cx = EditorTestContext::new(cx).await;
 6027
 6028    let rust_language = Arc::new(
 6029        Language::new(
 6030            LanguageConfig {
 6031                name: "Rust".into(),
 6032                brackets: serde_json::from_value(json!([
 6033                    { "start": "{", "end": "}", "close": true, "newline": true },
 6034                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6035                ]))
 6036                .unwrap(),
 6037                autoclose_before: "})]>".into(),
 6038                ..Default::default()
 6039            },
 6040            Some(tree_sitter_rust::LANGUAGE.into()),
 6041        )
 6042        .with_override_query("(string_literal) @string")
 6043        .unwrap(),
 6044    );
 6045
 6046    cx.language_registry().add(rust_language.clone());
 6047    cx.update_buffer(|buffer, cx| {
 6048        buffer.set_language(Some(rust_language), cx);
 6049    });
 6050
 6051    cx.set_state(
 6052        &r#"
 6053            let x = ˇ
 6054        "#
 6055        .unindent(),
 6056    );
 6057
 6058    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6059    cx.update_editor(|editor, cx| {
 6060        editor.handle_input("\"", cx);
 6061    });
 6062    cx.assert_editor_state(
 6063        &r#"
 6064            let x = "ˇ"
 6065        "#
 6066        .unindent(),
 6067    );
 6068
 6069    // Inserting another quotation mark. The cursor moves across the existing
 6070    // automatically-inserted quotation mark.
 6071    cx.update_editor(|editor, cx| {
 6072        editor.handle_input("\"", cx);
 6073    });
 6074    cx.assert_editor_state(
 6075        &r#"
 6076            let x = ""ˇ
 6077        "#
 6078        .unindent(),
 6079    );
 6080
 6081    // Reset
 6082    cx.set_state(
 6083        &r#"
 6084            let x = ˇ
 6085        "#
 6086        .unindent(),
 6087    );
 6088
 6089    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6090    cx.update_editor(|editor, cx| {
 6091        editor.handle_input("\"", cx);
 6092        editor.handle_input(" ", cx);
 6093        editor.move_left(&Default::default(), cx);
 6094        editor.handle_input("\\", cx);
 6095        editor.handle_input("\"", cx);
 6096    });
 6097    cx.assert_editor_state(
 6098        &r#"
 6099            let x = "\"ˇ "
 6100        "#
 6101        .unindent(),
 6102    );
 6103
 6104    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6105    // mark. Nothing is inserted.
 6106    cx.update_editor(|editor, cx| {
 6107        editor.move_right(&Default::default(), cx);
 6108        editor.handle_input("\"", cx);
 6109    });
 6110    cx.assert_editor_state(
 6111        &r#"
 6112            let x = "\" "ˇ
 6113        "#
 6114        .unindent(),
 6115    );
 6116}
 6117
 6118#[gpui::test]
 6119async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6120    init_test(cx, |_| {});
 6121
 6122    let language = Arc::new(Language::new(
 6123        LanguageConfig {
 6124            brackets: BracketPairConfig {
 6125                pairs: vec![
 6126                    BracketPair {
 6127                        start: "{".to_string(),
 6128                        end: "}".to_string(),
 6129                        close: true,
 6130                        surround: true,
 6131                        newline: true,
 6132                    },
 6133                    BracketPair {
 6134                        start: "/* ".to_string(),
 6135                        end: "*/".to_string(),
 6136                        close: true,
 6137                        surround: true,
 6138                        ..Default::default()
 6139                    },
 6140                ],
 6141                ..Default::default()
 6142            },
 6143            ..Default::default()
 6144        },
 6145        Some(tree_sitter_rust::LANGUAGE.into()),
 6146    ));
 6147
 6148    let text = r#"
 6149        a
 6150        b
 6151        c
 6152    "#
 6153    .unindent();
 6154
 6155    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6156    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6157    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6158    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6159        .await;
 6160
 6161    view.update(cx, |view, cx| {
 6162        view.change_selections(None, cx, |s| {
 6163            s.select_display_ranges([
 6164                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6165                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6166                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6167            ])
 6168        });
 6169
 6170        view.handle_input("{", cx);
 6171        view.handle_input("{", cx);
 6172        view.handle_input("{", cx);
 6173        assert_eq!(
 6174            view.text(cx),
 6175            "
 6176                {{{a}}}
 6177                {{{b}}}
 6178                {{{c}}}
 6179            "
 6180            .unindent()
 6181        );
 6182        assert_eq!(
 6183            view.selections.display_ranges(cx),
 6184            [
 6185                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6186                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6187                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6188            ]
 6189        );
 6190
 6191        view.undo(&Undo, cx);
 6192        view.undo(&Undo, cx);
 6193        view.undo(&Undo, cx);
 6194        assert_eq!(
 6195            view.text(cx),
 6196            "
 6197                a
 6198                b
 6199                c
 6200            "
 6201            .unindent()
 6202        );
 6203        assert_eq!(
 6204            view.selections.display_ranges(cx),
 6205            [
 6206                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6207                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6208                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6209            ]
 6210        );
 6211
 6212        // Ensure inserting the first character of a multi-byte bracket pair
 6213        // doesn't surround the selections with the bracket.
 6214        view.handle_input("/", cx);
 6215        assert_eq!(
 6216            view.text(cx),
 6217            "
 6218                /
 6219                /
 6220                /
 6221            "
 6222            .unindent()
 6223        );
 6224        assert_eq!(
 6225            view.selections.display_ranges(cx),
 6226            [
 6227                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6228                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6229                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6230            ]
 6231        );
 6232
 6233        view.undo(&Undo, cx);
 6234        assert_eq!(
 6235            view.text(cx),
 6236            "
 6237                a
 6238                b
 6239                c
 6240            "
 6241            .unindent()
 6242        );
 6243        assert_eq!(
 6244            view.selections.display_ranges(cx),
 6245            [
 6246                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6247                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6248                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6249            ]
 6250        );
 6251
 6252        // Ensure inserting the last character of a multi-byte bracket pair
 6253        // doesn't surround the selections with the bracket.
 6254        view.handle_input("*", cx);
 6255        assert_eq!(
 6256            view.text(cx),
 6257            "
 6258                *
 6259                *
 6260                *
 6261            "
 6262            .unindent()
 6263        );
 6264        assert_eq!(
 6265            view.selections.display_ranges(cx),
 6266            [
 6267                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6268                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6269                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6270            ]
 6271        );
 6272    });
 6273}
 6274
 6275#[gpui::test]
 6276async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6277    init_test(cx, |_| {});
 6278
 6279    let language = Arc::new(Language::new(
 6280        LanguageConfig {
 6281            brackets: BracketPairConfig {
 6282                pairs: vec![BracketPair {
 6283                    start: "{".to_string(),
 6284                    end: "}".to_string(),
 6285                    close: true,
 6286                    surround: true,
 6287                    newline: true,
 6288                }],
 6289                ..Default::default()
 6290            },
 6291            autoclose_before: "}".to_string(),
 6292            ..Default::default()
 6293        },
 6294        Some(tree_sitter_rust::LANGUAGE.into()),
 6295    ));
 6296
 6297    let text = r#"
 6298        a
 6299        b
 6300        c
 6301    "#
 6302    .unindent();
 6303
 6304    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6305    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6306    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6307    editor
 6308        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6309        .await;
 6310
 6311    editor.update(cx, |editor, cx| {
 6312        editor.change_selections(None, cx, |s| {
 6313            s.select_ranges([
 6314                Point::new(0, 1)..Point::new(0, 1),
 6315                Point::new(1, 1)..Point::new(1, 1),
 6316                Point::new(2, 1)..Point::new(2, 1),
 6317            ])
 6318        });
 6319
 6320        editor.handle_input("{", cx);
 6321        editor.handle_input("{", cx);
 6322        editor.handle_input("_", cx);
 6323        assert_eq!(
 6324            editor.text(cx),
 6325            "
 6326                a{{_}}
 6327                b{{_}}
 6328                c{{_}}
 6329            "
 6330            .unindent()
 6331        );
 6332        assert_eq!(
 6333            editor.selections.ranges::<Point>(cx),
 6334            [
 6335                Point::new(0, 4)..Point::new(0, 4),
 6336                Point::new(1, 4)..Point::new(1, 4),
 6337                Point::new(2, 4)..Point::new(2, 4)
 6338            ]
 6339        );
 6340
 6341        editor.backspace(&Default::default(), cx);
 6342        editor.backspace(&Default::default(), cx);
 6343        assert_eq!(
 6344            editor.text(cx),
 6345            "
 6346                a{}
 6347                b{}
 6348                c{}
 6349            "
 6350            .unindent()
 6351        );
 6352        assert_eq!(
 6353            editor.selections.ranges::<Point>(cx),
 6354            [
 6355                Point::new(0, 2)..Point::new(0, 2),
 6356                Point::new(1, 2)..Point::new(1, 2),
 6357                Point::new(2, 2)..Point::new(2, 2)
 6358            ]
 6359        );
 6360
 6361        editor.delete_to_previous_word_start(&Default::default(), cx);
 6362        assert_eq!(
 6363            editor.text(cx),
 6364            "
 6365                a
 6366                b
 6367                c
 6368            "
 6369            .unindent()
 6370        );
 6371        assert_eq!(
 6372            editor.selections.ranges::<Point>(cx),
 6373            [
 6374                Point::new(0, 1)..Point::new(0, 1),
 6375                Point::new(1, 1)..Point::new(1, 1),
 6376                Point::new(2, 1)..Point::new(2, 1)
 6377            ]
 6378        );
 6379    });
 6380}
 6381
 6382#[gpui::test]
 6383async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6384    init_test(cx, |settings| {
 6385        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6386    });
 6387
 6388    let mut cx = EditorTestContext::new(cx).await;
 6389
 6390    let language = Arc::new(Language::new(
 6391        LanguageConfig {
 6392            brackets: BracketPairConfig {
 6393                pairs: vec![
 6394                    BracketPair {
 6395                        start: "{".to_string(),
 6396                        end: "}".to_string(),
 6397                        close: true,
 6398                        surround: true,
 6399                        newline: true,
 6400                    },
 6401                    BracketPair {
 6402                        start: "(".to_string(),
 6403                        end: ")".to_string(),
 6404                        close: true,
 6405                        surround: true,
 6406                        newline: true,
 6407                    },
 6408                    BracketPair {
 6409                        start: "[".to_string(),
 6410                        end: "]".to_string(),
 6411                        close: false,
 6412                        surround: true,
 6413                        newline: true,
 6414                    },
 6415                ],
 6416                ..Default::default()
 6417            },
 6418            autoclose_before: "})]".to_string(),
 6419            ..Default::default()
 6420        },
 6421        Some(tree_sitter_rust::LANGUAGE.into()),
 6422    ));
 6423
 6424    cx.language_registry().add(language.clone());
 6425    cx.update_buffer(|buffer, cx| {
 6426        buffer.set_language(Some(language), cx);
 6427    });
 6428
 6429    cx.set_state(
 6430        &"
 6431            {(ˇ)}
 6432            [[ˇ]]
 6433            {(ˇ)}
 6434        "
 6435        .unindent(),
 6436    );
 6437
 6438    cx.update_editor(|view, cx| {
 6439        view.backspace(&Default::default(), cx);
 6440        view.backspace(&Default::default(), cx);
 6441    });
 6442
 6443    cx.assert_editor_state(
 6444        &"
 6445            ˇ
 6446            ˇ]]
 6447            ˇ
 6448        "
 6449        .unindent(),
 6450    );
 6451
 6452    cx.update_editor(|view, cx| {
 6453        view.handle_input("{", cx);
 6454        view.handle_input("{", cx);
 6455        view.move_right(&MoveRight, cx);
 6456        view.move_right(&MoveRight, cx);
 6457        view.move_left(&MoveLeft, cx);
 6458        view.move_left(&MoveLeft, cx);
 6459        view.backspace(&Default::default(), cx);
 6460    });
 6461
 6462    cx.assert_editor_state(
 6463        &"
 6464            {ˇ}
 6465            {ˇ}]]
 6466            {ˇ}
 6467        "
 6468        .unindent(),
 6469    );
 6470
 6471    cx.update_editor(|view, cx| {
 6472        view.backspace(&Default::default(), cx);
 6473    });
 6474
 6475    cx.assert_editor_state(
 6476        &"
 6477            ˇ
 6478            ˇ]]
 6479            ˇ
 6480        "
 6481        .unindent(),
 6482    );
 6483}
 6484
 6485#[gpui::test]
 6486async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6487    init_test(cx, |_| {});
 6488
 6489    let language = Arc::new(Language::new(
 6490        LanguageConfig::default(),
 6491        Some(tree_sitter_rust::LANGUAGE.into()),
 6492    ));
 6493
 6494    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6495    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6496    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6497    editor
 6498        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6499        .await;
 6500
 6501    editor.update(cx, |editor, cx| {
 6502        editor.set_auto_replace_emoji_shortcode(true);
 6503
 6504        editor.handle_input("Hello ", cx);
 6505        editor.handle_input(":wave", cx);
 6506        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6507
 6508        editor.handle_input(":", cx);
 6509        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6510
 6511        editor.handle_input(" :smile", cx);
 6512        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6513
 6514        editor.handle_input(":", cx);
 6515        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6516
 6517        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6518        editor.handle_input(":wave", cx);
 6519        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6520
 6521        editor.handle_input(":", cx);
 6522        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6523
 6524        editor.handle_input(":1", cx);
 6525        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6526
 6527        editor.handle_input(":", cx);
 6528        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6529
 6530        // Ensure shortcode does not get replaced when it is part of a word
 6531        editor.handle_input(" Test:wave", cx);
 6532        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6533
 6534        editor.handle_input(":", cx);
 6535        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6536
 6537        editor.set_auto_replace_emoji_shortcode(false);
 6538
 6539        // Ensure shortcode does not get replaced when auto replace is off
 6540        editor.handle_input(" :wave", cx);
 6541        assert_eq!(
 6542            editor.text(cx),
 6543            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6544        );
 6545
 6546        editor.handle_input(":", cx);
 6547        assert_eq!(
 6548            editor.text(cx),
 6549            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6550        );
 6551    });
 6552}
 6553
 6554#[gpui::test]
 6555async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6556    init_test(cx, |_| {});
 6557
 6558    let (text, insertion_ranges) = marked_text_ranges(
 6559        indoc! {"
 6560            ˇ
 6561        "},
 6562        false,
 6563    );
 6564
 6565    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6566    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6567
 6568    _ = editor.update(cx, |editor, cx| {
 6569        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6570
 6571        editor
 6572            .insert_snippet(&insertion_ranges, snippet, cx)
 6573            .unwrap();
 6574
 6575        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6576            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6577            assert_eq!(editor.text(cx), expected_text);
 6578            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6579        }
 6580
 6581        assert(
 6582            editor,
 6583            cx,
 6584            indoc! {"
 6585            type «» =•
 6586            "},
 6587        );
 6588
 6589        assert!(editor.context_menu_visible(), "There should be a matches");
 6590    });
 6591}
 6592
 6593#[gpui::test]
 6594async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6595    init_test(cx, |_| {});
 6596
 6597    let (text, insertion_ranges) = marked_text_ranges(
 6598        indoc! {"
 6599            a.ˇ b
 6600            a.ˇ b
 6601            a.ˇ b
 6602        "},
 6603        false,
 6604    );
 6605
 6606    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6607    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6608
 6609    editor.update(cx, |editor, cx| {
 6610        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6611
 6612        editor
 6613            .insert_snippet(&insertion_ranges, snippet, cx)
 6614            .unwrap();
 6615
 6616        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6617            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6618            assert_eq!(editor.text(cx), expected_text);
 6619            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6620        }
 6621
 6622        assert(
 6623            editor,
 6624            cx,
 6625            indoc! {"
 6626                a.f(«one», two, «three») b
 6627                a.f(«one», two, «three») b
 6628                a.f(«one», two, «three») b
 6629            "},
 6630        );
 6631
 6632        // Can't move earlier than the first tab stop
 6633        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6634        assert(
 6635            editor,
 6636            cx,
 6637            indoc! {"
 6638                a.f(«one», two, «three») b
 6639                a.f(«one», two, «three») b
 6640                a.f(«one», two, «three») b
 6641            "},
 6642        );
 6643
 6644        assert!(editor.move_to_next_snippet_tabstop(cx));
 6645        assert(
 6646            editor,
 6647            cx,
 6648            indoc! {"
 6649                a.f(one, «two», three) b
 6650                a.f(one, «two», three) b
 6651                a.f(one, «two», three) b
 6652            "},
 6653        );
 6654
 6655        editor.move_to_prev_snippet_tabstop(cx);
 6656        assert(
 6657            editor,
 6658            cx,
 6659            indoc! {"
 6660                a.f(«one», two, «three») b
 6661                a.f(«one», two, «three») b
 6662                a.f(«one», two, «three») b
 6663            "},
 6664        );
 6665
 6666        assert!(editor.move_to_next_snippet_tabstop(cx));
 6667        assert(
 6668            editor,
 6669            cx,
 6670            indoc! {"
 6671                a.f(one, «two», three) b
 6672                a.f(one, «two», three) b
 6673                a.f(one, «two», three) b
 6674            "},
 6675        );
 6676        assert!(editor.move_to_next_snippet_tabstop(cx));
 6677        assert(
 6678            editor,
 6679            cx,
 6680            indoc! {"
 6681                a.f(one, two, three)ˇ b
 6682                a.f(one, two, three)ˇ b
 6683                a.f(one, two, three)ˇ b
 6684            "},
 6685        );
 6686
 6687        // As soon as the last tab stop is reached, snippet state is gone
 6688        editor.move_to_prev_snippet_tabstop(cx);
 6689        assert(
 6690            editor,
 6691            cx,
 6692            indoc! {"
 6693                a.f(one, two, three)ˇ b
 6694                a.f(one, two, three)ˇ b
 6695                a.f(one, two, three)ˇ b
 6696            "},
 6697        );
 6698    });
 6699}
 6700
 6701#[gpui::test]
 6702async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6703    init_test(cx, |_| {});
 6704
 6705    let fs = FakeFs::new(cx.executor());
 6706    fs.insert_file("/file.rs", Default::default()).await;
 6707
 6708    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6709
 6710    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6711    language_registry.add(rust_lang());
 6712    let mut fake_servers = language_registry.register_fake_lsp(
 6713        "Rust",
 6714        FakeLspAdapter {
 6715            capabilities: lsp::ServerCapabilities {
 6716                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6717                ..Default::default()
 6718            },
 6719            ..Default::default()
 6720        },
 6721    );
 6722
 6723    let buffer = project
 6724        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6725        .await
 6726        .unwrap();
 6727
 6728    cx.executor().start_waiting();
 6729    let fake_server = fake_servers.next().await.unwrap();
 6730
 6731    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6732    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6733    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6734    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6735
 6736    let save = editor
 6737        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6738        .unwrap();
 6739    fake_server
 6740        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6741            assert_eq!(
 6742                params.text_document.uri,
 6743                lsp::Url::from_file_path("/file.rs").unwrap()
 6744            );
 6745            assert_eq!(params.options.tab_size, 4);
 6746            Ok(Some(vec![lsp::TextEdit::new(
 6747                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6748                ", ".to_string(),
 6749            )]))
 6750        })
 6751        .next()
 6752        .await;
 6753    cx.executor().start_waiting();
 6754    save.await;
 6755
 6756    assert_eq!(
 6757        editor.update(cx, |editor, cx| editor.text(cx)),
 6758        "one, two\nthree\n"
 6759    );
 6760    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6761
 6762    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6763    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6764
 6765    // Ensure we can still save even if formatting hangs.
 6766    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6767        assert_eq!(
 6768            params.text_document.uri,
 6769            lsp::Url::from_file_path("/file.rs").unwrap()
 6770        );
 6771        futures::future::pending::<()>().await;
 6772        unreachable!()
 6773    });
 6774    let save = editor
 6775        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6776        .unwrap();
 6777    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6778    cx.executor().start_waiting();
 6779    save.await;
 6780    assert_eq!(
 6781        editor.update(cx, |editor, cx| editor.text(cx)),
 6782        "one\ntwo\nthree\n"
 6783    );
 6784    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6785
 6786    // For non-dirty buffer, no formatting request should be sent
 6787    let save = editor
 6788        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6789        .unwrap();
 6790    let _pending_format_request = fake_server
 6791        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6792            panic!("Should not be invoked on non-dirty buffer");
 6793        })
 6794        .next();
 6795    cx.executor().start_waiting();
 6796    save.await;
 6797
 6798    // Set rust language override and assert overridden tabsize is sent to language server
 6799    update_test_language_settings(cx, |settings| {
 6800        settings.languages.insert(
 6801            "Rust".into(),
 6802            LanguageSettingsContent {
 6803                tab_size: NonZeroU32::new(8),
 6804                ..Default::default()
 6805            },
 6806        );
 6807    });
 6808
 6809    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6810    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6811    let save = editor
 6812        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6813        .unwrap();
 6814    fake_server
 6815        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6816            assert_eq!(
 6817                params.text_document.uri,
 6818                lsp::Url::from_file_path("/file.rs").unwrap()
 6819            );
 6820            assert_eq!(params.options.tab_size, 8);
 6821            Ok(Some(vec![]))
 6822        })
 6823        .next()
 6824        .await;
 6825    cx.executor().start_waiting();
 6826    save.await;
 6827}
 6828
 6829#[gpui::test]
 6830async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6831    init_test(cx, |_| {});
 6832
 6833    let cols = 4;
 6834    let rows = 10;
 6835    let sample_text_1 = sample_text(rows, cols, 'a');
 6836    assert_eq!(
 6837        sample_text_1,
 6838        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6839    );
 6840    let sample_text_2 = sample_text(rows, cols, 'l');
 6841    assert_eq!(
 6842        sample_text_2,
 6843        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6844    );
 6845    let sample_text_3 = sample_text(rows, cols, 'v');
 6846    assert_eq!(
 6847        sample_text_3,
 6848        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6849    );
 6850
 6851    let fs = FakeFs::new(cx.executor());
 6852    fs.insert_tree(
 6853        "/a",
 6854        json!({
 6855            "main.rs": sample_text_1,
 6856            "other.rs": sample_text_2,
 6857            "lib.rs": sample_text_3,
 6858        }),
 6859    )
 6860    .await;
 6861
 6862    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6863    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6864    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6865
 6866    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6867    language_registry.add(rust_lang());
 6868    let mut fake_servers = language_registry.register_fake_lsp(
 6869        "Rust",
 6870        FakeLspAdapter {
 6871            capabilities: lsp::ServerCapabilities {
 6872                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6873                ..Default::default()
 6874            },
 6875            ..Default::default()
 6876        },
 6877    );
 6878
 6879    let worktree = project.update(cx, |project, cx| {
 6880        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6881        assert_eq!(worktrees.len(), 1);
 6882        worktrees.pop().unwrap()
 6883    });
 6884    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6885
 6886    let buffer_1 = project
 6887        .update(cx, |project, cx| {
 6888            project.open_buffer((worktree_id, "main.rs"), cx)
 6889        })
 6890        .await
 6891        .unwrap();
 6892    let buffer_2 = project
 6893        .update(cx, |project, cx| {
 6894            project.open_buffer((worktree_id, "other.rs"), cx)
 6895        })
 6896        .await
 6897        .unwrap();
 6898    let buffer_3 = project
 6899        .update(cx, |project, cx| {
 6900            project.open_buffer((worktree_id, "lib.rs"), cx)
 6901        })
 6902        .await
 6903        .unwrap();
 6904
 6905    let multi_buffer = cx.new_model(|cx| {
 6906        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6907        multi_buffer.push_excerpts(
 6908            buffer_1.clone(),
 6909            [
 6910                ExcerptRange {
 6911                    context: Point::new(0, 0)..Point::new(3, 0),
 6912                    primary: None,
 6913                },
 6914                ExcerptRange {
 6915                    context: Point::new(5, 0)..Point::new(7, 0),
 6916                    primary: None,
 6917                },
 6918                ExcerptRange {
 6919                    context: Point::new(9, 0)..Point::new(10, 4),
 6920                    primary: None,
 6921                },
 6922            ],
 6923            cx,
 6924        );
 6925        multi_buffer.push_excerpts(
 6926            buffer_2.clone(),
 6927            [
 6928                ExcerptRange {
 6929                    context: Point::new(0, 0)..Point::new(3, 0),
 6930                    primary: None,
 6931                },
 6932                ExcerptRange {
 6933                    context: Point::new(5, 0)..Point::new(7, 0),
 6934                    primary: None,
 6935                },
 6936                ExcerptRange {
 6937                    context: Point::new(9, 0)..Point::new(10, 4),
 6938                    primary: None,
 6939                },
 6940            ],
 6941            cx,
 6942        );
 6943        multi_buffer.push_excerpts(
 6944            buffer_3.clone(),
 6945            [
 6946                ExcerptRange {
 6947                    context: Point::new(0, 0)..Point::new(3, 0),
 6948                    primary: None,
 6949                },
 6950                ExcerptRange {
 6951                    context: Point::new(5, 0)..Point::new(7, 0),
 6952                    primary: None,
 6953                },
 6954                ExcerptRange {
 6955                    context: Point::new(9, 0)..Point::new(10, 4),
 6956                    primary: None,
 6957                },
 6958            ],
 6959            cx,
 6960        );
 6961        multi_buffer
 6962    });
 6963    let multi_buffer_editor = cx.new_view(|cx| {
 6964        Editor::new(
 6965            EditorMode::Full,
 6966            multi_buffer,
 6967            Some(project.clone()),
 6968            true,
 6969            cx,
 6970        )
 6971    });
 6972
 6973    multi_buffer_editor.update(cx, |editor, cx| {
 6974        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6975        editor.insert("|one|two|three|", cx);
 6976    });
 6977    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6978    multi_buffer_editor.update(cx, |editor, cx| {
 6979        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6980            s.select_ranges(Some(60..70))
 6981        });
 6982        editor.insert("|four|five|six|", cx);
 6983    });
 6984    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6985
 6986    // First two buffers should be edited, but not the third one.
 6987    assert_eq!(
 6988        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6989        "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}",
 6990    );
 6991    buffer_1.update(cx, |buffer, _| {
 6992        assert!(buffer.is_dirty());
 6993        assert_eq!(
 6994            buffer.text(),
 6995            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6996        )
 6997    });
 6998    buffer_2.update(cx, |buffer, _| {
 6999        assert!(buffer.is_dirty());
 7000        assert_eq!(
 7001            buffer.text(),
 7002            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7003        )
 7004    });
 7005    buffer_3.update(cx, |buffer, _| {
 7006        assert!(!buffer.is_dirty());
 7007        assert_eq!(buffer.text(), sample_text_3,)
 7008    });
 7009
 7010    cx.executor().start_waiting();
 7011    let save = multi_buffer_editor
 7012        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7013        .unwrap();
 7014
 7015    let fake_server = fake_servers.next().await.unwrap();
 7016    fake_server
 7017        .server
 7018        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7019            Ok(Some(vec![lsp::TextEdit::new(
 7020                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7021                format!("[{} formatted]", params.text_document.uri),
 7022            )]))
 7023        })
 7024        .detach();
 7025    save.await;
 7026
 7027    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7028    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7029    assert_eq!(
 7030        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7031        "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}",
 7032    );
 7033    buffer_1.update(cx, |buffer, _| {
 7034        assert!(!buffer.is_dirty());
 7035        assert_eq!(
 7036            buffer.text(),
 7037            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7038        )
 7039    });
 7040    buffer_2.update(cx, |buffer, _| {
 7041        assert!(!buffer.is_dirty());
 7042        assert_eq!(
 7043            buffer.text(),
 7044            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7045        )
 7046    });
 7047    buffer_3.update(cx, |buffer, _| {
 7048        assert!(!buffer.is_dirty());
 7049        assert_eq!(buffer.text(), sample_text_3,)
 7050    });
 7051}
 7052
 7053#[gpui::test]
 7054async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7055    init_test(cx, |_| {});
 7056
 7057    let fs = FakeFs::new(cx.executor());
 7058    fs.insert_file("/file.rs", Default::default()).await;
 7059
 7060    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7061
 7062    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7063    language_registry.add(rust_lang());
 7064    let mut fake_servers = language_registry.register_fake_lsp(
 7065        "Rust",
 7066        FakeLspAdapter {
 7067            capabilities: lsp::ServerCapabilities {
 7068                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7069                ..Default::default()
 7070            },
 7071            ..Default::default()
 7072        },
 7073    );
 7074
 7075    let buffer = project
 7076        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7077        .await
 7078        .unwrap();
 7079
 7080    cx.executor().start_waiting();
 7081    let fake_server = fake_servers.next().await.unwrap();
 7082
 7083    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7084    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7085    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7086    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7087
 7088    let save = editor
 7089        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7090        .unwrap();
 7091    fake_server
 7092        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7093            assert_eq!(
 7094                params.text_document.uri,
 7095                lsp::Url::from_file_path("/file.rs").unwrap()
 7096            );
 7097            assert_eq!(params.options.tab_size, 4);
 7098            Ok(Some(vec![lsp::TextEdit::new(
 7099                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7100                ", ".to_string(),
 7101            )]))
 7102        })
 7103        .next()
 7104        .await;
 7105    cx.executor().start_waiting();
 7106    save.await;
 7107    assert_eq!(
 7108        editor.update(cx, |editor, cx| editor.text(cx)),
 7109        "one, two\nthree\n"
 7110    );
 7111    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7112
 7113    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7114    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7115
 7116    // Ensure we can still save even if formatting hangs.
 7117    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7118        move |params, _| async move {
 7119            assert_eq!(
 7120                params.text_document.uri,
 7121                lsp::Url::from_file_path("/file.rs").unwrap()
 7122            );
 7123            futures::future::pending::<()>().await;
 7124            unreachable!()
 7125        },
 7126    );
 7127    let save = editor
 7128        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7129        .unwrap();
 7130    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7131    cx.executor().start_waiting();
 7132    save.await;
 7133    assert_eq!(
 7134        editor.update(cx, |editor, cx| editor.text(cx)),
 7135        "one\ntwo\nthree\n"
 7136    );
 7137    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7138
 7139    // For non-dirty buffer, no formatting request should be sent
 7140    let save = editor
 7141        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7142        .unwrap();
 7143    let _pending_format_request = fake_server
 7144        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7145            panic!("Should not be invoked on non-dirty buffer");
 7146        })
 7147        .next();
 7148    cx.executor().start_waiting();
 7149    save.await;
 7150
 7151    // Set Rust language override and assert overridden tabsize is sent to language server
 7152    update_test_language_settings(cx, |settings| {
 7153        settings.languages.insert(
 7154            "Rust".into(),
 7155            LanguageSettingsContent {
 7156                tab_size: NonZeroU32::new(8),
 7157                ..Default::default()
 7158            },
 7159        );
 7160    });
 7161
 7162    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7163    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7164    let save = editor
 7165        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7166        .unwrap();
 7167    fake_server
 7168        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7169            assert_eq!(
 7170                params.text_document.uri,
 7171                lsp::Url::from_file_path("/file.rs").unwrap()
 7172            );
 7173            assert_eq!(params.options.tab_size, 8);
 7174            Ok(Some(vec![]))
 7175        })
 7176        .next()
 7177        .await;
 7178    cx.executor().start_waiting();
 7179    save.await;
 7180}
 7181
 7182#[gpui::test]
 7183async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7184    init_test(cx, |settings| {
 7185        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7186            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7187        ))
 7188    });
 7189
 7190    let fs = FakeFs::new(cx.executor());
 7191    fs.insert_file("/file.rs", Default::default()).await;
 7192
 7193    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7194
 7195    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7196    language_registry.add(Arc::new(Language::new(
 7197        LanguageConfig {
 7198            name: "Rust".into(),
 7199            matcher: LanguageMatcher {
 7200                path_suffixes: vec!["rs".to_string()],
 7201                ..Default::default()
 7202            },
 7203            ..LanguageConfig::default()
 7204        },
 7205        Some(tree_sitter_rust::LANGUAGE.into()),
 7206    )));
 7207    update_test_language_settings(cx, |settings| {
 7208        // Enable Prettier formatting for the same buffer, and ensure
 7209        // LSP is called instead of Prettier.
 7210        settings.defaults.prettier = Some(PrettierSettings {
 7211            allowed: true,
 7212            ..PrettierSettings::default()
 7213        });
 7214    });
 7215    let mut fake_servers = language_registry.register_fake_lsp(
 7216        "Rust",
 7217        FakeLspAdapter {
 7218            capabilities: lsp::ServerCapabilities {
 7219                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7220                ..Default::default()
 7221            },
 7222            ..Default::default()
 7223        },
 7224    );
 7225
 7226    let buffer = project
 7227        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7228        .await
 7229        .unwrap();
 7230
 7231    cx.executor().start_waiting();
 7232    let fake_server = fake_servers.next().await.unwrap();
 7233
 7234    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7235    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7236    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7237
 7238    let format = editor
 7239        .update(cx, |editor, cx| {
 7240            editor.perform_format(
 7241                project.clone(),
 7242                FormatTrigger::Manual,
 7243                FormatTarget::Buffer,
 7244                cx,
 7245            )
 7246        })
 7247        .unwrap();
 7248    fake_server
 7249        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7250            assert_eq!(
 7251                params.text_document.uri,
 7252                lsp::Url::from_file_path("/file.rs").unwrap()
 7253            );
 7254            assert_eq!(params.options.tab_size, 4);
 7255            Ok(Some(vec![lsp::TextEdit::new(
 7256                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7257                ", ".to_string(),
 7258            )]))
 7259        })
 7260        .next()
 7261        .await;
 7262    cx.executor().start_waiting();
 7263    format.await;
 7264    assert_eq!(
 7265        editor.update(cx, |editor, cx| editor.text(cx)),
 7266        "one, two\nthree\n"
 7267    );
 7268
 7269    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7270    // Ensure we don't lock if formatting hangs.
 7271    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7272        assert_eq!(
 7273            params.text_document.uri,
 7274            lsp::Url::from_file_path("/file.rs").unwrap()
 7275        );
 7276        futures::future::pending::<()>().await;
 7277        unreachable!()
 7278    });
 7279    let format = editor
 7280        .update(cx, |editor, cx| {
 7281            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7282        })
 7283        .unwrap();
 7284    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7285    cx.executor().start_waiting();
 7286    format.await;
 7287    assert_eq!(
 7288        editor.update(cx, |editor, cx| editor.text(cx)),
 7289        "one\ntwo\nthree\n"
 7290    );
 7291}
 7292
 7293#[gpui::test]
 7294async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7295    init_test(cx, |_| {});
 7296
 7297    let mut cx = EditorLspTestContext::new_rust(
 7298        lsp::ServerCapabilities {
 7299            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7300            ..Default::default()
 7301        },
 7302        cx,
 7303    )
 7304    .await;
 7305
 7306    cx.set_state(indoc! {"
 7307        one.twoˇ
 7308    "});
 7309
 7310    // The format request takes a long time. When it completes, it inserts
 7311    // a newline and an indent before the `.`
 7312    cx.lsp
 7313        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7314            let executor = cx.background_executor().clone();
 7315            async move {
 7316                executor.timer(Duration::from_millis(100)).await;
 7317                Ok(Some(vec![lsp::TextEdit {
 7318                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7319                    new_text: "\n    ".into(),
 7320                }]))
 7321            }
 7322        });
 7323
 7324    // Submit a format request.
 7325    let format_1 = cx
 7326        .update_editor(|editor, cx| editor.format(&Format, cx))
 7327        .unwrap();
 7328    cx.executor().run_until_parked();
 7329
 7330    // Submit a second format request.
 7331    let format_2 = cx
 7332        .update_editor(|editor, cx| editor.format(&Format, cx))
 7333        .unwrap();
 7334    cx.executor().run_until_parked();
 7335
 7336    // Wait for both format requests to complete
 7337    cx.executor().advance_clock(Duration::from_millis(200));
 7338    cx.executor().start_waiting();
 7339    format_1.await.unwrap();
 7340    cx.executor().start_waiting();
 7341    format_2.await.unwrap();
 7342
 7343    // The formatting edits only happens once.
 7344    cx.assert_editor_state(indoc! {"
 7345        one
 7346            .twoˇ
 7347    "});
 7348}
 7349
 7350#[gpui::test]
 7351async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7352    init_test(cx, |settings| {
 7353        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7354    });
 7355
 7356    let mut cx = EditorLspTestContext::new_rust(
 7357        lsp::ServerCapabilities {
 7358            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7359            ..Default::default()
 7360        },
 7361        cx,
 7362    )
 7363    .await;
 7364
 7365    // Set up a buffer white some trailing whitespace and no trailing newline.
 7366    cx.set_state(
 7367        &[
 7368            "one ",   //
 7369            "twoˇ",   //
 7370            "three ", //
 7371            "four",   //
 7372        ]
 7373        .join("\n"),
 7374    );
 7375
 7376    // Submit a format request.
 7377    let format = cx
 7378        .update_editor(|editor, cx| editor.format(&Format, cx))
 7379        .unwrap();
 7380
 7381    // Record which buffer changes have been sent to the language server
 7382    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7383    cx.lsp
 7384        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7385            let buffer_changes = buffer_changes.clone();
 7386            move |params, _| {
 7387                buffer_changes.lock().extend(
 7388                    params
 7389                        .content_changes
 7390                        .into_iter()
 7391                        .map(|e| (e.range.unwrap(), e.text)),
 7392                );
 7393            }
 7394        });
 7395
 7396    // Handle formatting requests to the language server.
 7397    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7398        let buffer_changes = buffer_changes.clone();
 7399        move |_, _| {
 7400            // When formatting is requested, trailing whitespace has already been stripped,
 7401            // and the trailing newline has already been added.
 7402            assert_eq!(
 7403                &buffer_changes.lock()[1..],
 7404                &[
 7405                    (
 7406                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7407                        "".into()
 7408                    ),
 7409                    (
 7410                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7411                        "".into()
 7412                    ),
 7413                    (
 7414                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7415                        "\n".into()
 7416                    ),
 7417                ]
 7418            );
 7419
 7420            // Insert blank lines between each line of the buffer.
 7421            async move {
 7422                Ok(Some(vec![
 7423                    lsp::TextEdit {
 7424                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7425                        new_text: "\n".into(),
 7426                    },
 7427                    lsp::TextEdit {
 7428                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7429                        new_text: "\n".into(),
 7430                    },
 7431                ]))
 7432            }
 7433        }
 7434    });
 7435
 7436    // After formatting the buffer, the trailing whitespace is stripped,
 7437    // a newline is appended, and the edits provided by the language server
 7438    // have been applied.
 7439    format.await.unwrap();
 7440    cx.assert_editor_state(
 7441        &[
 7442            "one",   //
 7443            "",      //
 7444            "twoˇ",  //
 7445            "",      //
 7446            "three", //
 7447            "four",  //
 7448            "",      //
 7449        ]
 7450        .join("\n"),
 7451    );
 7452
 7453    // Undoing the formatting undoes the trailing whitespace removal, the
 7454    // trailing newline, and the LSP edits.
 7455    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7456    cx.assert_editor_state(
 7457        &[
 7458            "one ",   //
 7459            "twoˇ",   //
 7460            "three ", //
 7461            "four",   //
 7462        ]
 7463        .join("\n"),
 7464    );
 7465}
 7466
 7467#[gpui::test]
 7468async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7469    cx: &mut gpui::TestAppContext,
 7470) {
 7471    init_test(cx, |_| {});
 7472
 7473    cx.update(|cx| {
 7474        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7475            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7476                settings.auto_signature_help = Some(true);
 7477            });
 7478        });
 7479    });
 7480
 7481    let mut cx = EditorLspTestContext::new_rust(
 7482        lsp::ServerCapabilities {
 7483            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7484                ..Default::default()
 7485            }),
 7486            ..Default::default()
 7487        },
 7488        cx,
 7489    )
 7490    .await;
 7491
 7492    let language = Language::new(
 7493        LanguageConfig {
 7494            name: "Rust".into(),
 7495            brackets: BracketPairConfig {
 7496                pairs: vec![
 7497                    BracketPair {
 7498                        start: "{".to_string(),
 7499                        end: "}".to_string(),
 7500                        close: true,
 7501                        surround: true,
 7502                        newline: true,
 7503                    },
 7504                    BracketPair {
 7505                        start: "(".to_string(),
 7506                        end: ")".to_string(),
 7507                        close: true,
 7508                        surround: true,
 7509                        newline: true,
 7510                    },
 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: false,
 7522                        surround: false,
 7523                        newline: true,
 7524                    },
 7525                    BracketPair {
 7526                        start: "\"".to_string(),
 7527                        end: "\"".to_string(),
 7528                        close: true,
 7529                        surround: true,
 7530                        newline: false,
 7531                    },
 7532                    BracketPair {
 7533                        start: "<".to_string(),
 7534                        end: ">".to_string(),
 7535                        close: false,
 7536                        surround: true,
 7537                        newline: true,
 7538                    },
 7539                ],
 7540                ..Default::default()
 7541            },
 7542            autoclose_before: "})]".to_string(),
 7543            ..Default::default()
 7544        },
 7545        Some(tree_sitter_rust::LANGUAGE.into()),
 7546    );
 7547    let language = Arc::new(language);
 7548
 7549    cx.language_registry().add(language.clone());
 7550    cx.update_buffer(|buffer, cx| {
 7551        buffer.set_language(Some(language), cx);
 7552    });
 7553
 7554    cx.set_state(
 7555        &r#"
 7556            fn main() {
 7557                sampleˇ
 7558            }
 7559        "#
 7560        .unindent(),
 7561    );
 7562
 7563    cx.update_editor(|view, cx| {
 7564        view.handle_input("(", cx);
 7565    });
 7566    cx.assert_editor_state(
 7567        &"
 7568            fn main() {
 7569                sample(ˇ)
 7570            }
 7571        "
 7572        .unindent(),
 7573    );
 7574
 7575    let mocked_response = lsp::SignatureHelp {
 7576        signatures: vec![lsp::SignatureInformation {
 7577            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7578            documentation: None,
 7579            parameters: Some(vec![
 7580                lsp::ParameterInformation {
 7581                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7582                    documentation: None,
 7583                },
 7584                lsp::ParameterInformation {
 7585                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7586                    documentation: None,
 7587                },
 7588            ]),
 7589            active_parameter: None,
 7590        }],
 7591        active_signature: Some(0),
 7592        active_parameter: Some(0),
 7593    };
 7594    handle_signature_help_request(&mut cx, mocked_response).await;
 7595
 7596    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7597        .await;
 7598
 7599    cx.editor(|editor, _| {
 7600        let signature_help_state = editor.signature_help_state.popover().cloned();
 7601        assert!(signature_help_state.is_some());
 7602        let ParsedMarkdown {
 7603            text, highlights, ..
 7604        } = signature_help_state.unwrap().parsed_content;
 7605        assert_eq!(text, "param1: u8, param2: u8");
 7606        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7607    });
 7608}
 7609
 7610#[gpui::test]
 7611async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7612    init_test(cx, |_| {});
 7613
 7614    cx.update(|cx| {
 7615        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7616            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7617                settings.auto_signature_help = Some(false);
 7618                settings.show_signature_help_after_edits = Some(false);
 7619            });
 7620        });
 7621    });
 7622
 7623    let mut cx = EditorLspTestContext::new_rust(
 7624        lsp::ServerCapabilities {
 7625            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7626                ..Default::default()
 7627            }),
 7628            ..Default::default()
 7629        },
 7630        cx,
 7631    )
 7632    .await;
 7633
 7634    let language = Language::new(
 7635        LanguageConfig {
 7636            name: "Rust".into(),
 7637            brackets: BracketPairConfig {
 7638                pairs: vec![
 7639                    BracketPair {
 7640                        start: "{".to_string(),
 7641                        end: "}".to_string(),
 7642                        close: true,
 7643                        surround: true,
 7644                        newline: true,
 7645                    },
 7646                    BracketPair {
 7647                        start: "(".to_string(),
 7648                        end: ")".to_string(),
 7649                        close: true,
 7650                        surround: true,
 7651                        newline: true,
 7652                    },
 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: false,
 7664                        surround: false,
 7665                        newline: true,
 7666                    },
 7667                    BracketPair {
 7668                        start: "\"".to_string(),
 7669                        end: "\"".to_string(),
 7670                        close: true,
 7671                        surround: true,
 7672                        newline: false,
 7673                    },
 7674                    BracketPair {
 7675                        start: "<".to_string(),
 7676                        end: ">".to_string(),
 7677                        close: false,
 7678                        surround: true,
 7679                        newline: true,
 7680                    },
 7681                ],
 7682                ..Default::default()
 7683            },
 7684            autoclose_before: "})]".to_string(),
 7685            ..Default::default()
 7686        },
 7687        Some(tree_sitter_rust::LANGUAGE.into()),
 7688    );
 7689    let language = Arc::new(language);
 7690
 7691    cx.language_registry().add(language.clone());
 7692    cx.update_buffer(|buffer, cx| {
 7693        buffer.set_language(Some(language), cx);
 7694    });
 7695
 7696    // Ensure that signature_help is not called when no signature help is enabled.
 7697    cx.set_state(
 7698        &r#"
 7699            fn main() {
 7700                sampleˇ
 7701            }
 7702        "#
 7703        .unindent(),
 7704    );
 7705    cx.update_editor(|view, cx| {
 7706        view.handle_input("(", cx);
 7707    });
 7708    cx.assert_editor_state(
 7709        &"
 7710            fn main() {
 7711                sample(ˇ)
 7712            }
 7713        "
 7714        .unindent(),
 7715    );
 7716    cx.editor(|editor, _| {
 7717        assert!(editor.signature_help_state.task().is_none());
 7718    });
 7719
 7720    let mocked_response = lsp::SignatureHelp {
 7721        signatures: vec![lsp::SignatureInformation {
 7722            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7723            documentation: None,
 7724            parameters: Some(vec![
 7725                lsp::ParameterInformation {
 7726                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7727                    documentation: None,
 7728                },
 7729                lsp::ParameterInformation {
 7730                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7731                    documentation: None,
 7732                },
 7733            ]),
 7734            active_parameter: None,
 7735        }],
 7736        active_signature: Some(0),
 7737        active_parameter: Some(0),
 7738    };
 7739
 7740    // Ensure that signature_help is called when enabled afte edits
 7741    cx.update(|cx| {
 7742        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7743            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7744                settings.auto_signature_help = Some(false);
 7745                settings.show_signature_help_after_edits = Some(true);
 7746            });
 7747        });
 7748    });
 7749    cx.set_state(
 7750        &r#"
 7751            fn main() {
 7752                sampleˇ
 7753            }
 7754        "#
 7755        .unindent(),
 7756    );
 7757    cx.update_editor(|view, cx| {
 7758        view.handle_input("(", cx);
 7759    });
 7760    cx.assert_editor_state(
 7761        &"
 7762            fn main() {
 7763                sample(ˇ)
 7764            }
 7765        "
 7766        .unindent(),
 7767    );
 7768    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7769    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7770        .await;
 7771    cx.update_editor(|editor, _| {
 7772        let signature_help_state = editor.signature_help_state.popover().cloned();
 7773        assert!(signature_help_state.is_some());
 7774        let ParsedMarkdown {
 7775            text, highlights, ..
 7776        } = signature_help_state.unwrap().parsed_content;
 7777        assert_eq!(text, "param1: u8, param2: u8");
 7778        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7779        editor.signature_help_state = SignatureHelpState::default();
 7780    });
 7781
 7782    // Ensure that signature_help is called when auto signature help override is enabled
 7783    cx.update(|cx| {
 7784        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7785            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7786                settings.auto_signature_help = Some(true);
 7787                settings.show_signature_help_after_edits = Some(false);
 7788            });
 7789        });
 7790    });
 7791    cx.set_state(
 7792        &r#"
 7793            fn main() {
 7794                sampleˇ
 7795            }
 7796        "#
 7797        .unindent(),
 7798    );
 7799    cx.update_editor(|view, cx| {
 7800        view.handle_input("(", cx);
 7801    });
 7802    cx.assert_editor_state(
 7803        &"
 7804            fn main() {
 7805                sample(ˇ)
 7806            }
 7807        "
 7808        .unindent(),
 7809    );
 7810    handle_signature_help_request(&mut cx, mocked_response).await;
 7811    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7812        .await;
 7813    cx.editor(|editor, _| {
 7814        let signature_help_state = editor.signature_help_state.popover().cloned();
 7815        assert!(signature_help_state.is_some());
 7816        let ParsedMarkdown {
 7817            text, highlights, ..
 7818        } = signature_help_state.unwrap().parsed_content;
 7819        assert_eq!(text, "param1: u8, param2: u8");
 7820        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7821    });
 7822}
 7823
 7824#[gpui::test]
 7825async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7826    init_test(cx, |_| {});
 7827    cx.update(|cx| {
 7828        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7829            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7830                settings.auto_signature_help = Some(true);
 7831            });
 7832        });
 7833    });
 7834
 7835    let mut cx = EditorLspTestContext::new_rust(
 7836        lsp::ServerCapabilities {
 7837            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7838                ..Default::default()
 7839            }),
 7840            ..Default::default()
 7841        },
 7842        cx,
 7843    )
 7844    .await;
 7845
 7846    // A test that directly calls `show_signature_help`
 7847    cx.update_editor(|editor, cx| {
 7848        editor.show_signature_help(&ShowSignatureHelp, cx);
 7849    });
 7850
 7851    let mocked_response = lsp::SignatureHelp {
 7852        signatures: vec![lsp::SignatureInformation {
 7853            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7854            documentation: None,
 7855            parameters: Some(vec![
 7856                lsp::ParameterInformation {
 7857                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7858                    documentation: None,
 7859                },
 7860                lsp::ParameterInformation {
 7861                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7862                    documentation: None,
 7863                },
 7864            ]),
 7865            active_parameter: None,
 7866        }],
 7867        active_signature: Some(0),
 7868        active_parameter: Some(0),
 7869    };
 7870    handle_signature_help_request(&mut cx, mocked_response).await;
 7871
 7872    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7873        .await;
 7874
 7875    cx.editor(|editor, _| {
 7876        let signature_help_state = editor.signature_help_state.popover().cloned();
 7877        assert!(signature_help_state.is_some());
 7878        let ParsedMarkdown {
 7879            text, highlights, ..
 7880        } = signature_help_state.unwrap().parsed_content;
 7881        assert_eq!(text, "param1: u8, param2: u8");
 7882        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7883    });
 7884
 7885    // When exiting outside from inside the brackets, `signature_help` is closed.
 7886    cx.set_state(indoc! {"
 7887        fn main() {
 7888            sample(ˇ);
 7889        }
 7890
 7891        fn sample(param1: u8, param2: u8) {}
 7892    "});
 7893
 7894    cx.update_editor(|editor, cx| {
 7895        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7896    });
 7897
 7898    let mocked_response = lsp::SignatureHelp {
 7899        signatures: Vec::new(),
 7900        active_signature: None,
 7901        active_parameter: None,
 7902    };
 7903    handle_signature_help_request(&mut cx, mocked_response).await;
 7904
 7905    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7906        .await;
 7907
 7908    cx.editor(|editor, _| {
 7909        assert!(!editor.signature_help_state.is_shown());
 7910    });
 7911
 7912    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7913    cx.set_state(indoc! {"
 7914        fn main() {
 7915            sample(ˇ);
 7916        }
 7917
 7918        fn sample(param1: u8, param2: u8) {}
 7919    "});
 7920
 7921    let mocked_response = lsp::SignatureHelp {
 7922        signatures: vec![lsp::SignatureInformation {
 7923            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7924            documentation: None,
 7925            parameters: Some(vec![
 7926                lsp::ParameterInformation {
 7927                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7928                    documentation: None,
 7929                },
 7930                lsp::ParameterInformation {
 7931                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7932                    documentation: None,
 7933                },
 7934            ]),
 7935            active_parameter: None,
 7936        }],
 7937        active_signature: Some(0),
 7938        active_parameter: Some(0),
 7939    };
 7940    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7941    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7942        .await;
 7943    cx.editor(|editor, _| {
 7944        assert!(editor.signature_help_state.is_shown());
 7945    });
 7946
 7947    // Restore the popover with more parameter input
 7948    cx.set_state(indoc! {"
 7949        fn main() {
 7950            sample(param1, param2ˇ);
 7951        }
 7952
 7953        fn sample(param1: u8, param2: u8) {}
 7954    "});
 7955
 7956    let mocked_response = lsp::SignatureHelp {
 7957        signatures: vec![lsp::SignatureInformation {
 7958            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7959            documentation: None,
 7960            parameters: Some(vec![
 7961                lsp::ParameterInformation {
 7962                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7963                    documentation: None,
 7964                },
 7965                lsp::ParameterInformation {
 7966                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7967                    documentation: None,
 7968                },
 7969            ]),
 7970            active_parameter: None,
 7971        }],
 7972        active_signature: Some(0),
 7973        active_parameter: Some(1),
 7974    };
 7975    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7976    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7977        .await;
 7978
 7979    // When selecting a range, the popover is gone.
 7980    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7981    cx.update_editor(|editor, cx| {
 7982        editor.change_selections(None, cx, |s| {
 7983            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7984        })
 7985    });
 7986    cx.assert_editor_state(indoc! {"
 7987        fn main() {
 7988            sample(param1, «ˇparam2»);
 7989        }
 7990
 7991        fn sample(param1: u8, param2: u8) {}
 7992    "});
 7993    cx.editor(|editor, _| {
 7994        assert!(!editor.signature_help_state.is_shown());
 7995    });
 7996
 7997    // When unselecting again, the popover is back if within the brackets.
 7998    cx.update_editor(|editor, cx| {
 7999        editor.change_selections(None, cx, |s| {
 8000            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8001        })
 8002    });
 8003    cx.assert_editor_state(indoc! {"
 8004        fn main() {
 8005            sample(param1, ˇparam2);
 8006        }
 8007
 8008        fn sample(param1: u8, param2: u8) {}
 8009    "});
 8010    handle_signature_help_request(&mut cx, mocked_response).await;
 8011    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8012        .await;
 8013    cx.editor(|editor, _| {
 8014        assert!(editor.signature_help_state.is_shown());
 8015    });
 8016
 8017    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8018    cx.update_editor(|editor, cx| {
 8019        editor.change_selections(None, cx, |s| {
 8020            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8021            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8022        })
 8023    });
 8024    cx.assert_editor_state(indoc! {"
 8025        fn main() {
 8026            sample(param1, ˇparam2);
 8027        }
 8028
 8029        fn sample(param1: u8, param2: u8) {}
 8030    "});
 8031
 8032    let mocked_response = lsp::SignatureHelp {
 8033        signatures: vec![lsp::SignatureInformation {
 8034            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8035            documentation: None,
 8036            parameters: Some(vec![
 8037                lsp::ParameterInformation {
 8038                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8039                    documentation: None,
 8040                },
 8041                lsp::ParameterInformation {
 8042                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8043                    documentation: None,
 8044                },
 8045            ]),
 8046            active_parameter: None,
 8047        }],
 8048        active_signature: Some(0),
 8049        active_parameter: Some(1),
 8050    };
 8051    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8052    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8053        .await;
 8054    cx.update_editor(|editor, cx| {
 8055        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8056    });
 8057    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8058        .await;
 8059    cx.update_editor(|editor, cx| {
 8060        editor.change_selections(None, cx, |s| {
 8061            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8062        })
 8063    });
 8064    cx.assert_editor_state(indoc! {"
 8065        fn main() {
 8066            sample(param1, «ˇparam2»);
 8067        }
 8068
 8069        fn sample(param1: u8, param2: u8) {}
 8070    "});
 8071    cx.update_editor(|editor, cx| {
 8072        editor.change_selections(None, cx, |s| {
 8073            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8074        })
 8075    });
 8076    cx.assert_editor_state(indoc! {"
 8077        fn main() {
 8078            sample(param1, ˇparam2);
 8079        }
 8080
 8081        fn sample(param1: u8, param2: u8) {}
 8082    "});
 8083    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8084        .await;
 8085}
 8086
 8087#[gpui::test]
 8088async fn test_completion(cx: &mut gpui::TestAppContext) {
 8089    init_test(cx, |_| {});
 8090
 8091    let mut cx = EditorLspTestContext::new_rust(
 8092        lsp::ServerCapabilities {
 8093            completion_provider: Some(lsp::CompletionOptions {
 8094                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8095                resolve_provider: Some(true),
 8096                ..Default::default()
 8097            }),
 8098            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8099            ..Default::default()
 8100        },
 8101        cx,
 8102    )
 8103    .await;
 8104    let counter = Arc::new(AtomicUsize::new(0));
 8105
 8106    cx.set_state(indoc! {"
 8107        oneˇ
 8108        two
 8109        three
 8110    "});
 8111    cx.simulate_keystroke(".");
 8112    handle_completion_request(
 8113        &mut cx,
 8114        indoc! {"
 8115            one.|<>
 8116            two
 8117            three
 8118        "},
 8119        vec!["first_completion", "second_completion"],
 8120        counter.clone(),
 8121    )
 8122    .await;
 8123    cx.condition(|editor, _| editor.context_menu_visible())
 8124        .await;
 8125    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8126
 8127    let _handler = handle_signature_help_request(
 8128        &mut cx,
 8129        lsp::SignatureHelp {
 8130            signatures: vec![lsp::SignatureInformation {
 8131                label: "test signature".to_string(),
 8132                documentation: None,
 8133                parameters: Some(vec![lsp::ParameterInformation {
 8134                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8135                    documentation: None,
 8136                }]),
 8137                active_parameter: None,
 8138            }],
 8139            active_signature: None,
 8140            active_parameter: None,
 8141        },
 8142    );
 8143    cx.update_editor(|editor, cx| {
 8144        assert!(
 8145            !editor.signature_help_state.is_shown(),
 8146            "No signature help was called for"
 8147        );
 8148        editor.show_signature_help(&ShowSignatureHelp, cx);
 8149    });
 8150    cx.run_until_parked();
 8151    cx.update_editor(|editor, _| {
 8152        assert!(
 8153            !editor.signature_help_state.is_shown(),
 8154            "No signature help should be shown when completions menu is open"
 8155        );
 8156    });
 8157
 8158    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8159        editor.context_menu_next(&Default::default(), cx);
 8160        editor
 8161            .confirm_completion(&ConfirmCompletion::default(), cx)
 8162            .unwrap()
 8163    });
 8164    cx.assert_editor_state(indoc! {"
 8165        one.second_completionˇ
 8166        two
 8167        three
 8168    "});
 8169
 8170    handle_resolve_completion_request(
 8171        &mut cx,
 8172        Some(vec![
 8173            (
 8174                //This overlaps with the primary completion edit which is
 8175                //misbehavior from the LSP spec, test that we filter it out
 8176                indoc! {"
 8177                    one.second_ˇcompletion
 8178                    two
 8179                    threeˇ
 8180                "},
 8181                "overlapping additional edit",
 8182            ),
 8183            (
 8184                indoc! {"
 8185                    one.second_completion
 8186                    two
 8187                    threeˇ
 8188                "},
 8189                "\nadditional edit",
 8190            ),
 8191        ]),
 8192    )
 8193    .await;
 8194    apply_additional_edits.await.unwrap();
 8195    cx.assert_editor_state(indoc! {"
 8196        one.second_completionˇ
 8197        two
 8198        three
 8199        additional edit
 8200    "});
 8201
 8202    cx.set_state(indoc! {"
 8203        one.second_completion
 8204        twoˇ
 8205        threeˇ
 8206        additional edit
 8207    "});
 8208    cx.simulate_keystroke(" ");
 8209    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8210    cx.simulate_keystroke("s");
 8211    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8212
 8213    cx.assert_editor_state(indoc! {"
 8214        one.second_completion
 8215        two sˇ
 8216        three sˇ
 8217        additional edit
 8218    "});
 8219    handle_completion_request(
 8220        &mut cx,
 8221        indoc! {"
 8222            one.second_completion
 8223            two s
 8224            three <s|>
 8225            additional edit
 8226        "},
 8227        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8228        counter.clone(),
 8229    )
 8230    .await;
 8231    cx.condition(|editor, _| editor.context_menu_visible())
 8232        .await;
 8233    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8234
 8235    cx.simulate_keystroke("i");
 8236
 8237    handle_completion_request(
 8238        &mut cx,
 8239        indoc! {"
 8240            one.second_completion
 8241            two si
 8242            three <si|>
 8243            additional edit
 8244        "},
 8245        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8246        counter.clone(),
 8247    )
 8248    .await;
 8249    cx.condition(|editor, _| editor.context_menu_visible())
 8250        .await;
 8251    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8252
 8253    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8254        editor
 8255            .confirm_completion(&ConfirmCompletion::default(), cx)
 8256            .unwrap()
 8257    });
 8258    cx.assert_editor_state(indoc! {"
 8259        one.second_completion
 8260        two sixth_completionˇ
 8261        three sixth_completionˇ
 8262        additional edit
 8263    "});
 8264
 8265    handle_resolve_completion_request(&mut cx, None).await;
 8266    apply_additional_edits.await.unwrap();
 8267
 8268    cx.update(|cx| {
 8269        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8270            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8271                settings.show_completions_on_input = Some(false);
 8272            });
 8273        })
 8274    });
 8275    cx.set_state("editorˇ");
 8276    cx.simulate_keystroke(".");
 8277    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8278    cx.simulate_keystroke("c");
 8279    cx.simulate_keystroke("l");
 8280    cx.simulate_keystroke("o");
 8281    cx.assert_editor_state("editor.cloˇ");
 8282    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8283    cx.update_editor(|editor, cx| {
 8284        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8285    });
 8286    handle_completion_request(
 8287        &mut cx,
 8288        "editor.<clo|>",
 8289        vec!["close", "clobber"],
 8290        counter.clone(),
 8291    )
 8292    .await;
 8293    cx.condition(|editor, _| editor.context_menu_visible())
 8294        .await;
 8295    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8296
 8297    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8298        editor
 8299            .confirm_completion(&ConfirmCompletion::default(), cx)
 8300            .unwrap()
 8301    });
 8302    cx.assert_editor_state("editor.closeˇ");
 8303    handle_resolve_completion_request(&mut cx, None).await;
 8304    apply_additional_edits.await.unwrap();
 8305}
 8306
 8307#[gpui::test]
 8308async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8309    init_test(cx, |_| {});
 8310    let mut cx = EditorLspTestContext::new_rust(
 8311        lsp::ServerCapabilities {
 8312            completion_provider: Some(lsp::CompletionOptions {
 8313                trigger_characters: Some(vec![".".to_string()]),
 8314                ..Default::default()
 8315            }),
 8316            ..Default::default()
 8317        },
 8318        cx,
 8319    )
 8320    .await;
 8321    cx.lsp
 8322        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8323            Ok(Some(lsp::CompletionResponse::Array(vec![
 8324                lsp::CompletionItem {
 8325                    label: "first".into(),
 8326                    ..Default::default()
 8327                },
 8328                lsp::CompletionItem {
 8329                    label: "last".into(),
 8330                    ..Default::default()
 8331                },
 8332            ])))
 8333        });
 8334    cx.set_state("variableˇ");
 8335    cx.simulate_keystroke(".");
 8336    cx.executor().run_until_parked();
 8337
 8338    cx.update_editor(|editor, _| {
 8339        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8340            assert_eq!(
 8341                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8342                &["first", "last"]
 8343            );
 8344        } else {
 8345            panic!("expected completion menu to be open");
 8346        }
 8347    });
 8348
 8349    cx.update_editor(|editor, cx| {
 8350        editor.move_page_down(&MovePageDown::default(), cx);
 8351        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8352            assert!(
 8353                menu.selected_item == 1,
 8354                "expected PageDown to select the last item from the context menu"
 8355            );
 8356        } else {
 8357            panic!("expected completion menu to stay open after PageDown");
 8358        }
 8359    });
 8360
 8361    cx.update_editor(|editor, cx| {
 8362        editor.move_page_up(&MovePageUp::default(), cx);
 8363        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8364            assert!(
 8365                menu.selected_item == 0,
 8366                "expected PageUp to select the first item from the context menu"
 8367            );
 8368        } else {
 8369            panic!("expected completion menu to stay open after PageUp");
 8370        }
 8371    });
 8372}
 8373
 8374#[gpui::test]
 8375async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8376    init_test(cx, |_| {});
 8377    let mut cx = EditorLspTestContext::new_rust(
 8378        lsp::ServerCapabilities {
 8379            completion_provider: Some(lsp::CompletionOptions {
 8380                trigger_characters: Some(vec![".".to_string()]),
 8381                ..Default::default()
 8382            }),
 8383            ..Default::default()
 8384        },
 8385        cx,
 8386    )
 8387    .await;
 8388    cx.lsp
 8389        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8390            Ok(Some(lsp::CompletionResponse::Array(vec![
 8391                lsp::CompletionItem {
 8392                    label: "Range".into(),
 8393                    sort_text: Some("a".into()),
 8394                    ..Default::default()
 8395                },
 8396                lsp::CompletionItem {
 8397                    label: "r".into(),
 8398                    sort_text: Some("b".into()),
 8399                    ..Default::default()
 8400                },
 8401                lsp::CompletionItem {
 8402                    label: "ret".into(),
 8403                    sort_text: Some("c".into()),
 8404                    ..Default::default()
 8405                },
 8406                lsp::CompletionItem {
 8407                    label: "return".into(),
 8408                    sort_text: Some("d".into()),
 8409                    ..Default::default()
 8410                },
 8411                lsp::CompletionItem {
 8412                    label: "slice".into(),
 8413                    sort_text: Some("d".into()),
 8414                    ..Default::default()
 8415                },
 8416            ])))
 8417        });
 8418    cx.set_state("");
 8419    cx.executor().run_until_parked();
 8420    cx.update_editor(|editor, cx| {
 8421        editor.show_completions(
 8422            &ShowCompletions {
 8423                trigger: Some("r".into()),
 8424            },
 8425            cx,
 8426        );
 8427    });
 8428    cx.executor().run_until_parked();
 8429
 8430    cx.update_editor(|editor, _| {
 8431        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8432            assert_eq!(
 8433                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8434                &["r", "ret", "Range", "return"]
 8435            );
 8436        } else {
 8437            panic!("expected completion menu to be open");
 8438        }
 8439    });
 8440}
 8441
 8442#[gpui::test]
 8443async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8444    init_test(cx, |_| {});
 8445
 8446    let mut cx = EditorLspTestContext::new_rust(
 8447        lsp::ServerCapabilities {
 8448            completion_provider: Some(lsp::CompletionOptions {
 8449                trigger_characters: Some(vec![".".to_string()]),
 8450                resolve_provider: Some(true),
 8451                ..Default::default()
 8452            }),
 8453            ..Default::default()
 8454        },
 8455        cx,
 8456    )
 8457    .await;
 8458
 8459    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8460    cx.simulate_keystroke(".");
 8461    let completion_item = lsp::CompletionItem {
 8462        label: "Some".into(),
 8463        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8464        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8465        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8466            kind: lsp::MarkupKind::Markdown,
 8467            value: "```rust\nSome(2)\n```".to_string(),
 8468        })),
 8469        deprecated: Some(false),
 8470        sort_text: Some("Some".to_string()),
 8471        filter_text: Some("Some".to_string()),
 8472        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8473        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8474            range: lsp::Range {
 8475                start: lsp::Position {
 8476                    line: 0,
 8477                    character: 22,
 8478                },
 8479                end: lsp::Position {
 8480                    line: 0,
 8481                    character: 22,
 8482                },
 8483            },
 8484            new_text: "Some(2)".to_string(),
 8485        })),
 8486        additional_text_edits: Some(vec![lsp::TextEdit {
 8487            range: lsp::Range {
 8488                start: lsp::Position {
 8489                    line: 0,
 8490                    character: 20,
 8491                },
 8492                end: lsp::Position {
 8493                    line: 0,
 8494                    character: 22,
 8495                },
 8496            },
 8497            new_text: "".to_string(),
 8498        }]),
 8499        ..Default::default()
 8500    };
 8501
 8502    let closure_completion_item = completion_item.clone();
 8503    let counter = Arc::new(AtomicUsize::new(0));
 8504    let counter_clone = counter.clone();
 8505    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8506        let task_completion_item = closure_completion_item.clone();
 8507        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8508        async move {
 8509            Ok(Some(lsp::CompletionResponse::Array(vec![
 8510                task_completion_item,
 8511            ])))
 8512        }
 8513    });
 8514
 8515    cx.condition(|editor, _| editor.context_menu_visible())
 8516        .await;
 8517    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8518    assert!(request.next().await.is_some());
 8519    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8520
 8521    cx.simulate_keystroke("S");
 8522    cx.simulate_keystroke("o");
 8523    cx.simulate_keystroke("m");
 8524    cx.condition(|editor, _| editor.context_menu_visible())
 8525        .await;
 8526    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8527    assert!(request.next().await.is_some());
 8528    assert!(request.next().await.is_some());
 8529    assert!(request.next().await.is_some());
 8530    request.close();
 8531    assert!(request.next().await.is_none());
 8532    assert_eq!(
 8533        counter.load(atomic::Ordering::Acquire),
 8534        4,
 8535        "With the completions menu open, only one LSP request should happen per input"
 8536    );
 8537}
 8538
 8539#[gpui::test]
 8540async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8541    init_test(cx, |_| {});
 8542    let mut cx = EditorTestContext::new(cx).await;
 8543    let language = Arc::new(Language::new(
 8544        LanguageConfig {
 8545            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8546            ..Default::default()
 8547        },
 8548        Some(tree_sitter_rust::LANGUAGE.into()),
 8549    ));
 8550    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8551
 8552    // If multiple selections intersect a line, the line is only toggled once.
 8553    cx.set_state(indoc! {"
 8554        fn a() {
 8555            «//b();
 8556            ˇ»// «c();
 8557            //ˇ»  d();
 8558        }
 8559    "});
 8560
 8561    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8562
 8563    cx.assert_editor_state(indoc! {"
 8564        fn a() {
 8565            «b();
 8566            c();
 8567            ˇ» d();
 8568        }
 8569    "});
 8570
 8571    // The comment prefix is inserted at the same column for every line in a
 8572    // selection.
 8573    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8574
 8575    cx.assert_editor_state(indoc! {"
 8576        fn a() {
 8577            // «b();
 8578            // c();
 8579            ˇ»//  d();
 8580        }
 8581    "});
 8582
 8583    // If a selection ends at the beginning of a line, that line is not toggled.
 8584    cx.set_selections_state(indoc! {"
 8585        fn a() {
 8586            // b();
 8587            «// c();
 8588        ˇ»    //  d();
 8589        }
 8590    "});
 8591
 8592    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8593
 8594    cx.assert_editor_state(indoc! {"
 8595        fn a() {
 8596            // b();
 8597            «c();
 8598        ˇ»    //  d();
 8599        }
 8600    "});
 8601
 8602    // If a selection span a single line and is empty, the line is toggled.
 8603    cx.set_state(indoc! {"
 8604        fn a() {
 8605            a();
 8606            b();
 8607        ˇ
 8608        }
 8609    "});
 8610
 8611    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8612
 8613    cx.assert_editor_state(indoc! {"
 8614        fn a() {
 8615            a();
 8616            b();
 8617        //•ˇ
 8618        }
 8619    "});
 8620
 8621    // If a selection span multiple lines, empty lines are not toggled.
 8622    cx.set_state(indoc! {"
 8623        fn a() {
 8624            «a();
 8625
 8626            c();ˇ»
 8627        }
 8628    "});
 8629
 8630    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8631
 8632    cx.assert_editor_state(indoc! {"
 8633        fn a() {
 8634            // «a();
 8635
 8636            // c();ˇ»
 8637        }
 8638    "});
 8639
 8640    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8641    cx.set_state(indoc! {"
 8642        fn a() {
 8643            «// a();
 8644            /// b();
 8645            //! c();ˇ»
 8646        }
 8647    "});
 8648
 8649    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8650
 8651    cx.assert_editor_state(indoc! {"
 8652        fn a() {
 8653            «a();
 8654            b();
 8655            c();ˇ»
 8656        }
 8657    "});
 8658}
 8659
 8660#[gpui::test]
 8661async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8662    init_test(cx, |_| {});
 8663    let mut cx = EditorTestContext::new(cx).await;
 8664    let language = Arc::new(Language::new(
 8665        LanguageConfig {
 8666            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8667            ..Default::default()
 8668        },
 8669        Some(tree_sitter_rust::LANGUAGE.into()),
 8670    ));
 8671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8672
 8673    let toggle_comments = &ToggleComments {
 8674        advance_downwards: false,
 8675        ignore_indent: true,
 8676    };
 8677
 8678    // If multiple selections intersect a line, the line is only toggled once.
 8679    cx.set_state(indoc! {"
 8680        fn a() {
 8681        //    «b();
 8682        //    c();
 8683        //    ˇ» d();
 8684        }
 8685    "});
 8686
 8687    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8688
 8689    cx.assert_editor_state(indoc! {"
 8690        fn a() {
 8691            «b();
 8692            c();
 8693            ˇ» d();
 8694        }
 8695    "});
 8696
 8697    // The comment prefix is inserted at the beginning of each line
 8698    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8699
 8700    cx.assert_editor_state(indoc! {"
 8701        fn a() {
 8702        //    «b();
 8703        //    c();
 8704        //    ˇ» d();
 8705        }
 8706    "});
 8707
 8708    // If a selection ends at the beginning of a line, that line is not toggled.
 8709    cx.set_selections_state(indoc! {"
 8710        fn a() {
 8711        //    b();
 8712        //    «c();
 8713        ˇ»//     d();
 8714        }
 8715    "});
 8716
 8717    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8718
 8719    cx.assert_editor_state(indoc! {"
 8720        fn a() {
 8721        //    b();
 8722            «c();
 8723        ˇ»//     d();
 8724        }
 8725    "});
 8726
 8727    // If a selection span a single line and is empty, the line is toggled.
 8728    cx.set_state(indoc! {"
 8729        fn a() {
 8730            a();
 8731            b();
 8732        ˇ
 8733        }
 8734    "});
 8735
 8736    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8737
 8738    cx.assert_editor_state(indoc! {"
 8739        fn a() {
 8740            a();
 8741            b();
 8742        //ˇ
 8743        }
 8744    "});
 8745
 8746    // If a selection span multiple lines, empty lines are not toggled.
 8747    cx.set_state(indoc! {"
 8748        fn a() {
 8749            «a();
 8750
 8751            c();ˇ»
 8752        }
 8753    "});
 8754
 8755    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8756
 8757    cx.assert_editor_state(indoc! {"
 8758        fn a() {
 8759        //    «a();
 8760
 8761        //    c();ˇ»
 8762        }
 8763    "});
 8764
 8765    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8766    cx.set_state(indoc! {"
 8767        fn a() {
 8768        //    «a();
 8769        ///    b();
 8770        //!    c();ˇ»
 8771        }
 8772    "});
 8773
 8774    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8775
 8776    cx.assert_editor_state(indoc! {"
 8777        fn a() {
 8778            «a();
 8779            b();
 8780            c();ˇ»
 8781        }
 8782    "});
 8783}
 8784
 8785#[gpui::test]
 8786async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8787    init_test(cx, |_| {});
 8788
 8789    let language = Arc::new(Language::new(
 8790        LanguageConfig {
 8791            line_comments: vec!["// ".into()],
 8792            ..Default::default()
 8793        },
 8794        Some(tree_sitter_rust::LANGUAGE.into()),
 8795    ));
 8796
 8797    let mut cx = EditorTestContext::new(cx).await;
 8798
 8799    cx.language_registry().add(language.clone());
 8800    cx.update_buffer(|buffer, cx| {
 8801        buffer.set_language(Some(language), cx);
 8802    });
 8803
 8804    let toggle_comments = &ToggleComments {
 8805        advance_downwards: true,
 8806        ignore_indent: false,
 8807    };
 8808
 8809    // Single cursor on one line -> advance
 8810    // Cursor moves horizontally 3 characters as well on non-blank line
 8811    cx.set_state(indoc!(
 8812        "fn a() {
 8813             ˇdog();
 8814             cat();
 8815        }"
 8816    ));
 8817    cx.update_editor(|editor, cx| {
 8818        editor.toggle_comments(toggle_comments, cx);
 8819    });
 8820    cx.assert_editor_state(indoc!(
 8821        "fn a() {
 8822             // dog();
 8823             catˇ();
 8824        }"
 8825    ));
 8826
 8827    // Single selection on one line -> don't advance
 8828    cx.set_state(indoc!(
 8829        "fn a() {
 8830             «dog()ˇ»;
 8831             cat();
 8832        }"
 8833    ));
 8834    cx.update_editor(|editor, cx| {
 8835        editor.toggle_comments(toggle_comments, cx);
 8836    });
 8837    cx.assert_editor_state(indoc!(
 8838        "fn a() {
 8839             // «dog()ˇ»;
 8840             cat();
 8841        }"
 8842    ));
 8843
 8844    // Multiple cursors on one line -> advance
 8845    cx.set_state(indoc!(
 8846        "fn a() {
 8847             ˇdˇog();
 8848             cat();
 8849        }"
 8850    ));
 8851    cx.update_editor(|editor, cx| {
 8852        editor.toggle_comments(toggle_comments, cx);
 8853    });
 8854    cx.assert_editor_state(indoc!(
 8855        "fn a() {
 8856             // dog();
 8857             catˇ(ˇ);
 8858        }"
 8859    ));
 8860
 8861    // Multiple cursors on one line, with selection -> don't advance
 8862    cx.set_state(indoc!(
 8863        "fn a() {
 8864             ˇdˇog«()ˇ»;
 8865             cat();
 8866        }"
 8867    ));
 8868    cx.update_editor(|editor, cx| {
 8869        editor.toggle_comments(toggle_comments, cx);
 8870    });
 8871    cx.assert_editor_state(indoc!(
 8872        "fn a() {
 8873             // ˇdˇog«()ˇ»;
 8874             cat();
 8875        }"
 8876    ));
 8877
 8878    // Single cursor on one line -> advance
 8879    // Cursor moves to column 0 on blank line
 8880    cx.set_state(indoc!(
 8881        "fn a() {
 8882             ˇdog();
 8883
 8884             cat();
 8885        }"
 8886    ));
 8887    cx.update_editor(|editor, cx| {
 8888        editor.toggle_comments(toggle_comments, cx);
 8889    });
 8890    cx.assert_editor_state(indoc!(
 8891        "fn a() {
 8892             // dog();
 8893        ˇ
 8894             cat();
 8895        }"
 8896    ));
 8897
 8898    // Single cursor on one line -> advance
 8899    // Cursor starts and ends at column 0
 8900    cx.set_state(indoc!(
 8901        "fn a() {
 8902         ˇ    dog();
 8903             cat();
 8904        }"
 8905    ));
 8906    cx.update_editor(|editor, cx| {
 8907        editor.toggle_comments(toggle_comments, cx);
 8908    });
 8909    cx.assert_editor_state(indoc!(
 8910        "fn a() {
 8911             // dog();
 8912         ˇ    cat();
 8913        }"
 8914    ));
 8915}
 8916
 8917#[gpui::test]
 8918async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8919    init_test(cx, |_| {});
 8920
 8921    let mut cx = EditorTestContext::new(cx).await;
 8922
 8923    let html_language = Arc::new(
 8924        Language::new(
 8925            LanguageConfig {
 8926                name: "HTML".into(),
 8927                block_comment: Some(("<!-- ".into(), " -->".into())),
 8928                ..Default::default()
 8929            },
 8930            Some(tree_sitter_html::language()),
 8931        )
 8932        .with_injection_query(
 8933            r#"
 8934            (script_element
 8935                (raw_text) @content
 8936                (#set! "language" "javascript"))
 8937            "#,
 8938        )
 8939        .unwrap(),
 8940    );
 8941
 8942    let javascript_language = Arc::new(Language::new(
 8943        LanguageConfig {
 8944            name: "JavaScript".into(),
 8945            line_comments: vec!["// ".into()],
 8946            ..Default::default()
 8947        },
 8948        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8949    ));
 8950
 8951    cx.language_registry().add(html_language.clone());
 8952    cx.language_registry().add(javascript_language.clone());
 8953    cx.update_buffer(|buffer, cx| {
 8954        buffer.set_language(Some(html_language), cx);
 8955    });
 8956
 8957    // Toggle comments for empty selections
 8958    cx.set_state(
 8959        &r#"
 8960            <p>A</p>ˇ
 8961            <p>B</p>ˇ
 8962            <p>C</p>ˇ
 8963        "#
 8964        .unindent(),
 8965    );
 8966    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8967    cx.assert_editor_state(
 8968        &r#"
 8969            <!-- <p>A</p>ˇ -->
 8970            <!-- <p>B</p>ˇ -->
 8971            <!-- <p>C</p>ˇ -->
 8972        "#
 8973        .unindent(),
 8974    );
 8975    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8976    cx.assert_editor_state(
 8977        &r#"
 8978            <p>A</p>ˇ
 8979            <p>B</p>ˇ
 8980            <p>C</p>ˇ
 8981        "#
 8982        .unindent(),
 8983    );
 8984
 8985    // Toggle comments for mixture of empty and non-empty selections, where
 8986    // multiple selections occupy a given line.
 8987    cx.set_state(
 8988        &r#"
 8989            <p>A«</p>
 8990            <p>ˇ»B</p>ˇ
 8991            <p>C«</p>
 8992            <p>ˇ»D</p>ˇ
 8993        "#
 8994        .unindent(),
 8995    );
 8996
 8997    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8998    cx.assert_editor_state(
 8999        &r#"
 9000            <!-- <p>A«</p>
 9001            <p>ˇ»B</p>ˇ -->
 9002            <!-- <p>C«</p>
 9003            <p>ˇ»D</p>ˇ -->
 9004        "#
 9005        .unindent(),
 9006    );
 9007    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9008    cx.assert_editor_state(
 9009        &r#"
 9010            <p>A«</p>
 9011            <p>ˇ»B</p>ˇ
 9012            <p>C«</p>
 9013            <p>ˇ»D</p>ˇ
 9014        "#
 9015        .unindent(),
 9016    );
 9017
 9018    // Toggle comments when different languages are active for different
 9019    // selections.
 9020    cx.set_state(
 9021        &r#"
 9022            ˇ<script>
 9023                ˇvar x = new Y();
 9024            ˇ</script>
 9025        "#
 9026        .unindent(),
 9027    );
 9028    cx.executor().run_until_parked();
 9029    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9030    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9031    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9032    cx.assert_editor_state(
 9033        &r#"
 9034            <!-- ˇ<script> -->
 9035                // ˇvar x = new Y();
 9036            // ˇ</script>
 9037        "#
 9038        .unindent(),
 9039    );
 9040}
 9041
 9042#[gpui::test]
 9043fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9044    init_test(cx, |_| {});
 9045
 9046    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9047    let multibuffer = cx.new_model(|cx| {
 9048        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9049        multibuffer.push_excerpts(
 9050            buffer.clone(),
 9051            [
 9052                ExcerptRange {
 9053                    context: Point::new(0, 0)..Point::new(0, 4),
 9054                    primary: None,
 9055                },
 9056                ExcerptRange {
 9057                    context: Point::new(1, 0)..Point::new(1, 4),
 9058                    primary: None,
 9059                },
 9060            ],
 9061            cx,
 9062        );
 9063        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9064        multibuffer
 9065    });
 9066
 9067    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9068    view.update(cx, |view, cx| {
 9069        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9070        view.change_selections(None, cx, |s| {
 9071            s.select_ranges([
 9072                Point::new(0, 0)..Point::new(0, 0),
 9073                Point::new(1, 0)..Point::new(1, 0),
 9074            ])
 9075        });
 9076
 9077        view.handle_input("X", cx);
 9078        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9079        assert_eq!(
 9080            view.selections.ranges(cx),
 9081            [
 9082                Point::new(0, 1)..Point::new(0, 1),
 9083                Point::new(1, 1)..Point::new(1, 1),
 9084            ]
 9085        );
 9086
 9087        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9088        view.change_selections(None, cx, |s| {
 9089            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9090        });
 9091        view.backspace(&Default::default(), cx);
 9092        assert_eq!(view.text(cx), "Xa\nbbb");
 9093        assert_eq!(
 9094            view.selections.ranges(cx),
 9095            [Point::new(1, 0)..Point::new(1, 0)]
 9096        );
 9097
 9098        view.change_selections(None, cx, |s| {
 9099            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9100        });
 9101        view.backspace(&Default::default(), cx);
 9102        assert_eq!(view.text(cx), "X\nbb");
 9103        assert_eq!(
 9104            view.selections.ranges(cx),
 9105            [Point::new(0, 1)..Point::new(0, 1)]
 9106        );
 9107    });
 9108}
 9109
 9110#[gpui::test]
 9111fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9112    init_test(cx, |_| {});
 9113
 9114    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9115    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9116        indoc! {"
 9117            [aaaa
 9118            (bbbb]
 9119            cccc)",
 9120        },
 9121        markers.clone(),
 9122    );
 9123    let excerpt_ranges = markers.into_iter().map(|marker| {
 9124        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9125        ExcerptRange {
 9126            context,
 9127            primary: None,
 9128        }
 9129    });
 9130    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9131    let multibuffer = cx.new_model(|cx| {
 9132        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9133        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9134        multibuffer
 9135    });
 9136
 9137    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9138    view.update(cx, |view, cx| {
 9139        let (expected_text, selection_ranges) = marked_text_ranges(
 9140            indoc! {"
 9141                aaaa
 9142                bˇbbb
 9143                bˇbbˇb
 9144                cccc"
 9145            },
 9146            true,
 9147        );
 9148        assert_eq!(view.text(cx), expected_text);
 9149        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9150
 9151        view.handle_input("X", cx);
 9152
 9153        let (expected_text, expected_selections) = marked_text_ranges(
 9154            indoc! {"
 9155                aaaa
 9156                bXˇbbXb
 9157                bXˇbbXˇb
 9158                cccc"
 9159            },
 9160            false,
 9161        );
 9162        assert_eq!(view.text(cx), expected_text);
 9163        assert_eq!(view.selections.ranges(cx), expected_selections);
 9164
 9165        view.newline(&Newline, cx);
 9166        let (expected_text, expected_selections) = marked_text_ranges(
 9167            indoc! {"
 9168                aaaa
 9169                bX
 9170                ˇbbX
 9171                b
 9172                bX
 9173                ˇbbX
 9174                ˇb
 9175                cccc"
 9176            },
 9177            false,
 9178        );
 9179        assert_eq!(view.text(cx), expected_text);
 9180        assert_eq!(view.selections.ranges(cx), expected_selections);
 9181    });
 9182}
 9183
 9184#[gpui::test]
 9185fn test_refresh_selections(cx: &mut TestAppContext) {
 9186    init_test(cx, |_| {});
 9187
 9188    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9189    let mut excerpt1_id = None;
 9190    let multibuffer = cx.new_model(|cx| {
 9191        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9192        excerpt1_id = multibuffer
 9193            .push_excerpts(
 9194                buffer.clone(),
 9195                [
 9196                    ExcerptRange {
 9197                        context: Point::new(0, 0)..Point::new(1, 4),
 9198                        primary: None,
 9199                    },
 9200                    ExcerptRange {
 9201                        context: Point::new(1, 0)..Point::new(2, 4),
 9202                        primary: None,
 9203                    },
 9204                ],
 9205                cx,
 9206            )
 9207            .into_iter()
 9208            .next();
 9209        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9210        multibuffer
 9211    });
 9212
 9213    let editor = cx.add_window(|cx| {
 9214        let mut editor = build_editor(multibuffer.clone(), cx);
 9215        let snapshot = editor.snapshot(cx);
 9216        editor.change_selections(None, cx, |s| {
 9217            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9218        });
 9219        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9220        assert_eq!(
 9221            editor.selections.ranges(cx),
 9222            [
 9223                Point::new(1, 3)..Point::new(1, 3),
 9224                Point::new(2, 1)..Point::new(2, 1),
 9225            ]
 9226        );
 9227        editor
 9228    });
 9229
 9230    // Refreshing selections is a no-op when excerpts haven't changed.
 9231    _ = editor.update(cx, |editor, cx| {
 9232        editor.change_selections(None, cx, |s| s.refresh());
 9233        assert_eq!(
 9234            editor.selections.ranges(cx),
 9235            [
 9236                Point::new(1, 3)..Point::new(1, 3),
 9237                Point::new(2, 1)..Point::new(2, 1),
 9238            ]
 9239        );
 9240    });
 9241
 9242    multibuffer.update(cx, |multibuffer, cx| {
 9243        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9244    });
 9245    _ = editor.update(cx, |editor, cx| {
 9246        // Removing an excerpt causes the first selection to become degenerate.
 9247        assert_eq!(
 9248            editor.selections.ranges(cx),
 9249            [
 9250                Point::new(0, 0)..Point::new(0, 0),
 9251                Point::new(0, 1)..Point::new(0, 1)
 9252            ]
 9253        );
 9254
 9255        // Refreshing selections will relocate the first selection to the original buffer
 9256        // location.
 9257        editor.change_selections(None, cx, |s| s.refresh());
 9258        assert_eq!(
 9259            editor.selections.ranges(cx),
 9260            [
 9261                Point::new(0, 1)..Point::new(0, 1),
 9262                Point::new(0, 3)..Point::new(0, 3)
 9263            ]
 9264        );
 9265        assert!(editor.selections.pending_anchor().is_some());
 9266    });
 9267}
 9268
 9269#[gpui::test]
 9270fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9271    init_test(cx, |_| {});
 9272
 9273    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9274    let mut excerpt1_id = None;
 9275    let multibuffer = cx.new_model(|cx| {
 9276        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9277        excerpt1_id = multibuffer
 9278            .push_excerpts(
 9279                buffer.clone(),
 9280                [
 9281                    ExcerptRange {
 9282                        context: Point::new(0, 0)..Point::new(1, 4),
 9283                        primary: None,
 9284                    },
 9285                    ExcerptRange {
 9286                        context: Point::new(1, 0)..Point::new(2, 4),
 9287                        primary: None,
 9288                    },
 9289                ],
 9290                cx,
 9291            )
 9292            .into_iter()
 9293            .next();
 9294        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9295        multibuffer
 9296    });
 9297
 9298    let editor = cx.add_window(|cx| {
 9299        let mut editor = build_editor(multibuffer.clone(), cx);
 9300        let snapshot = editor.snapshot(cx);
 9301        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9302        assert_eq!(
 9303            editor.selections.ranges(cx),
 9304            [Point::new(1, 3)..Point::new(1, 3)]
 9305        );
 9306        editor
 9307    });
 9308
 9309    multibuffer.update(cx, |multibuffer, cx| {
 9310        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9311    });
 9312    _ = editor.update(cx, |editor, cx| {
 9313        assert_eq!(
 9314            editor.selections.ranges(cx),
 9315            [Point::new(0, 0)..Point::new(0, 0)]
 9316        );
 9317
 9318        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9319        editor.change_selections(None, cx, |s| s.refresh());
 9320        assert_eq!(
 9321            editor.selections.ranges(cx),
 9322            [Point::new(0, 3)..Point::new(0, 3)]
 9323        );
 9324        assert!(editor.selections.pending_anchor().is_some());
 9325    });
 9326}
 9327
 9328#[gpui::test]
 9329async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9330    init_test(cx, |_| {});
 9331
 9332    let language = Arc::new(
 9333        Language::new(
 9334            LanguageConfig {
 9335                brackets: BracketPairConfig {
 9336                    pairs: vec![
 9337                        BracketPair {
 9338                            start: "{".to_string(),
 9339                            end: "}".to_string(),
 9340                            close: true,
 9341                            surround: true,
 9342                            newline: true,
 9343                        },
 9344                        BracketPair {
 9345                            start: "/* ".to_string(),
 9346                            end: " */".to_string(),
 9347                            close: true,
 9348                            surround: true,
 9349                            newline: true,
 9350                        },
 9351                    ],
 9352                    ..Default::default()
 9353                },
 9354                ..Default::default()
 9355            },
 9356            Some(tree_sitter_rust::LANGUAGE.into()),
 9357        )
 9358        .with_indents_query("")
 9359        .unwrap(),
 9360    );
 9361
 9362    let text = concat!(
 9363        "{   }\n",     //
 9364        "  x\n",       //
 9365        "  /*   */\n", //
 9366        "x\n",         //
 9367        "{{} }\n",     //
 9368    );
 9369
 9370    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9371    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9372    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9373    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9374        .await;
 9375
 9376    view.update(cx, |view, cx| {
 9377        view.change_selections(None, cx, |s| {
 9378            s.select_display_ranges([
 9379                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9380                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9381                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9382            ])
 9383        });
 9384        view.newline(&Newline, cx);
 9385
 9386        assert_eq!(
 9387            view.buffer().read(cx).read(cx).text(),
 9388            concat!(
 9389                "{ \n",    // Suppress rustfmt
 9390                "\n",      //
 9391                "}\n",     //
 9392                "  x\n",   //
 9393                "  /* \n", //
 9394                "  \n",    //
 9395                "  */\n",  //
 9396                "x\n",     //
 9397                "{{} \n",  //
 9398                "}\n",     //
 9399            )
 9400        );
 9401    });
 9402}
 9403
 9404#[gpui::test]
 9405fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9406    init_test(cx, |_| {});
 9407
 9408    let editor = cx.add_window(|cx| {
 9409        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9410        build_editor(buffer.clone(), cx)
 9411    });
 9412
 9413    _ = editor.update(cx, |editor, cx| {
 9414        struct Type1;
 9415        struct Type2;
 9416
 9417        let buffer = editor.buffer.read(cx).snapshot(cx);
 9418
 9419        let anchor_range =
 9420            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9421
 9422        editor.highlight_background::<Type1>(
 9423            &[
 9424                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9425                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9426                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9427                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9428            ],
 9429            |_| Hsla::red(),
 9430            cx,
 9431        );
 9432        editor.highlight_background::<Type2>(
 9433            &[
 9434                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9435                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9436                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9437                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9438            ],
 9439            |_| Hsla::green(),
 9440            cx,
 9441        );
 9442
 9443        let snapshot = editor.snapshot(cx);
 9444        let mut highlighted_ranges = editor.background_highlights_in_range(
 9445            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9446            &snapshot,
 9447            cx.theme().colors(),
 9448        );
 9449        // Enforce a consistent ordering based on color without relying on the ordering of the
 9450        // highlight's `TypeId` which is non-executor.
 9451        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9452        assert_eq!(
 9453            highlighted_ranges,
 9454            &[
 9455                (
 9456                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9457                    Hsla::red(),
 9458                ),
 9459                (
 9460                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9461                    Hsla::red(),
 9462                ),
 9463                (
 9464                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9465                    Hsla::green(),
 9466                ),
 9467                (
 9468                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9469                    Hsla::green(),
 9470                ),
 9471            ]
 9472        );
 9473        assert_eq!(
 9474            editor.background_highlights_in_range(
 9475                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9476                &snapshot,
 9477                cx.theme().colors(),
 9478            ),
 9479            &[(
 9480                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9481                Hsla::red(),
 9482            )]
 9483        );
 9484    });
 9485}
 9486
 9487#[gpui::test]
 9488async fn test_following(cx: &mut gpui::TestAppContext) {
 9489    init_test(cx, |_| {});
 9490
 9491    let fs = FakeFs::new(cx.executor());
 9492    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9493
 9494    let buffer = project.update(cx, |project, cx| {
 9495        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9496        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9497    });
 9498    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9499    let follower = cx.update(|cx| {
 9500        cx.open_window(
 9501            WindowOptions {
 9502                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9503                    gpui::Point::new(px(0.), px(0.)),
 9504                    gpui::Point::new(px(10.), px(80.)),
 9505                ))),
 9506                ..Default::default()
 9507            },
 9508            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9509        )
 9510        .unwrap()
 9511    });
 9512
 9513    let is_still_following = Rc::new(RefCell::new(true));
 9514    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9515    let pending_update = Rc::new(RefCell::new(None));
 9516    _ = follower.update(cx, {
 9517        let update = pending_update.clone();
 9518        let is_still_following = is_still_following.clone();
 9519        let follower_edit_event_count = follower_edit_event_count.clone();
 9520        |_, cx| {
 9521            cx.subscribe(
 9522                &leader.root_view(cx).unwrap(),
 9523                move |_, leader, event, cx| {
 9524                    leader
 9525                        .read(cx)
 9526                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9527                },
 9528            )
 9529            .detach();
 9530
 9531            cx.subscribe(
 9532                &follower.root_view(cx).unwrap(),
 9533                move |_, _, event: &EditorEvent, _cx| {
 9534                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9535                        *is_still_following.borrow_mut() = false;
 9536                    }
 9537
 9538                    if let EditorEvent::BufferEdited = event {
 9539                        *follower_edit_event_count.borrow_mut() += 1;
 9540                    }
 9541                },
 9542            )
 9543            .detach();
 9544        }
 9545    });
 9546
 9547    // Update the selections only
 9548    _ = leader.update(cx, |leader, cx| {
 9549        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9550    });
 9551    follower
 9552        .update(cx, |follower, cx| {
 9553            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9554        })
 9555        .unwrap()
 9556        .await
 9557        .unwrap();
 9558    _ = follower.update(cx, |follower, cx| {
 9559        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9560    });
 9561    assert!(*is_still_following.borrow());
 9562    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9563
 9564    // Update the scroll position only
 9565    _ = leader.update(cx, |leader, cx| {
 9566        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9567    });
 9568    follower
 9569        .update(cx, |follower, cx| {
 9570            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9571        })
 9572        .unwrap()
 9573        .await
 9574        .unwrap();
 9575    assert_eq!(
 9576        follower
 9577            .update(cx, |follower, cx| follower.scroll_position(cx))
 9578            .unwrap(),
 9579        gpui::Point::new(1.5, 3.5)
 9580    );
 9581    assert!(*is_still_following.borrow());
 9582    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9583
 9584    // Update the selections and scroll position. The follower's scroll position is updated
 9585    // via autoscroll, not via the leader's exact scroll position.
 9586    _ = leader.update(cx, |leader, cx| {
 9587        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9588        leader.request_autoscroll(Autoscroll::newest(), cx);
 9589        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9590    });
 9591    follower
 9592        .update(cx, |follower, cx| {
 9593            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9594        })
 9595        .unwrap()
 9596        .await
 9597        .unwrap();
 9598    _ = follower.update(cx, |follower, cx| {
 9599        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9600        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9601    });
 9602    assert!(*is_still_following.borrow());
 9603
 9604    // Creating a pending selection that precedes another selection
 9605    _ = leader.update(cx, |leader, cx| {
 9606        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9607        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9608    });
 9609    follower
 9610        .update(cx, |follower, cx| {
 9611            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9612        })
 9613        .unwrap()
 9614        .await
 9615        .unwrap();
 9616    _ = follower.update(cx, |follower, cx| {
 9617        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9618    });
 9619    assert!(*is_still_following.borrow());
 9620
 9621    // Extend the pending selection so that it surrounds another selection
 9622    _ = leader.update(cx, |leader, cx| {
 9623        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9624    });
 9625    follower
 9626        .update(cx, |follower, cx| {
 9627            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9628        })
 9629        .unwrap()
 9630        .await
 9631        .unwrap();
 9632    _ = follower.update(cx, |follower, cx| {
 9633        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9634    });
 9635
 9636    // Scrolling locally breaks the follow
 9637    _ = follower.update(cx, |follower, cx| {
 9638        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9639        follower.set_scroll_anchor(
 9640            ScrollAnchor {
 9641                anchor: top_anchor,
 9642                offset: gpui::Point::new(0.0, 0.5),
 9643            },
 9644            cx,
 9645        );
 9646    });
 9647    assert!(!(*is_still_following.borrow()));
 9648}
 9649
 9650#[gpui::test]
 9651async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9652    init_test(cx, |_| {});
 9653
 9654    let fs = FakeFs::new(cx.executor());
 9655    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9656    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9657    let pane = workspace
 9658        .update(cx, |workspace, _| workspace.active_pane().clone())
 9659        .unwrap();
 9660
 9661    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9662
 9663    let leader = pane.update(cx, |_, cx| {
 9664        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9665        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9666    });
 9667
 9668    // Start following the editor when it has no excerpts.
 9669    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9670    let follower_1 = cx
 9671        .update_window(*workspace.deref(), |_, cx| {
 9672            Editor::from_state_proto(
 9673                workspace.root_view(cx).unwrap(),
 9674                ViewId {
 9675                    creator: Default::default(),
 9676                    id: 0,
 9677                },
 9678                &mut state_message,
 9679                cx,
 9680            )
 9681        })
 9682        .unwrap()
 9683        .unwrap()
 9684        .await
 9685        .unwrap();
 9686
 9687    let update_message = Rc::new(RefCell::new(None));
 9688    follower_1.update(cx, {
 9689        let update = update_message.clone();
 9690        |_, cx| {
 9691            cx.subscribe(&leader, move |_, leader, event, cx| {
 9692                leader
 9693                    .read(cx)
 9694                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9695            })
 9696            .detach();
 9697        }
 9698    });
 9699
 9700    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9701        (
 9702            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9703            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9704        )
 9705    });
 9706
 9707    // Insert some excerpts.
 9708    leader.update(cx, |leader, cx| {
 9709        leader.buffer.update(cx, |multibuffer, cx| {
 9710            let excerpt_ids = multibuffer.push_excerpts(
 9711                buffer_1.clone(),
 9712                [
 9713                    ExcerptRange {
 9714                        context: 1..6,
 9715                        primary: None,
 9716                    },
 9717                    ExcerptRange {
 9718                        context: 12..15,
 9719                        primary: None,
 9720                    },
 9721                    ExcerptRange {
 9722                        context: 0..3,
 9723                        primary: None,
 9724                    },
 9725                ],
 9726                cx,
 9727            );
 9728            multibuffer.insert_excerpts_after(
 9729                excerpt_ids[0],
 9730                buffer_2.clone(),
 9731                [
 9732                    ExcerptRange {
 9733                        context: 8..12,
 9734                        primary: None,
 9735                    },
 9736                    ExcerptRange {
 9737                        context: 0..6,
 9738                        primary: None,
 9739                    },
 9740                ],
 9741                cx,
 9742            );
 9743        });
 9744    });
 9745
 9746    // Apply the update of adding the excerpts.
 9747    follower_1
 9748        .update(cx, |follower, cx| {
 9749            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9750        })
 9751        .await
 9752        .unwrap();
 9753    assert_eq!(
 9754        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9755        leader.update(cx, |editor, cx| editor.text(cx))
 9756    );
 9757    update_message.borrow_mut().take();
 9758
 9759    // Start following separately after it already has excerpts.
 9760    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9761    let follower_2 = cx
 9762        .update_window(*workspace.deref(), |_, cx| {
 9763            Editor::from_state_proto(
 9764                workspace.root_view(cx).unwrap().clone(),
 9765                ViewId {
 9766                    creator: Default::default(),
 9767                    id: 0,
 9768                },
 9769                &mut state_message,
 9770                cx,
 9771            )
 9772        })
 9773        .unwrap()
 9774        .unwrap()
 9775        .await
 9776        .unwrap();
 9777    assert_eq!(
 9778        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9779        leader.update(cx, |editor, cx| editor.text(cx))
 9780    );
 9781
 9782    // Remove some excerpts.
 9783    leader.update(cx, |leader, cx| {
 9784        leader.buffer.update(cx, |multibuffer, cx| {
 9785            let excerpt_ids = multibuffer.excerpt_ids();
 9786            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9787            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9788        });
 9789    });
 9790
 9791    // Apply the update of removing the excerpts.
 9792    follower_1
 9793        .update(cx, |follower, cx| {
 9794            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9795        })
 9796        .await
 9797        .unwrap();
 9798    follower_2
 9799        .update(cx, |follower, cx| {
 9800            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9801        })
 9802        .await
 9803        .unwrap();
 9804    update_message.borrow_mut().take();
 9805    assert_eq!(
 9806        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9807        leader.update(cx, |editor, cx| editor.text(cx))
 9808    );
 9809}
 9810
 9811#[gpui::test]
 9812async fn go_to_prev_overlapping_diagnostic(
 9813    executor: BackgroundExecutor,
 9814    cx: &mut gpui::TestAppContext,
 9815) {
 9816    init_test(cx, |_| {});
 9817
 9818    let mut cx = EditorTestContext::new(cx).await;
 9819    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9820
 9821    cx.set_state(indoc! {"
 9822        ˇfn func(abc def: i32) -> u32 {
 9823        }
 9824    "});
 9825
 9826    cx.update(|cx| {
 9827        project.update(cx, |project, cx| {
 9828            project
 9829                .update_diagnostics(
 9830                    LanguageServerId(0),
 9831                    lsp::PublishDiagnosticsParams {
 9832                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9833                        version: None,
 9834                        diagnostics: vec![
 9835                            lsp::Diagnostic {
 9836                                range: lsp::Range::new(
 9837                                    lsp::Position::new(0, 11),
 9838                                    lsp::Position::new(0, 12),
 9839                                ),
 9840                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9841                                ..Default::default()
 9842                            },
 9843                            lsp::Diagnostic {
 9844                                range: lsp::Range::new(
 9845                                    lsp::Position::new(0, 12),
 9846                                    lsp::Position::new(0, 15),
 9847                                ),
 9848                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9849                                ..Default::default()
 9850                            },
 9851                            lsp::Diagnostic {
 9852                                range: lsp::Range::new(
 9853                                    lsp::Position::new(0, 25),
 9854                                    lsp::Position::new(0, 28),
 9855                                ),
 9856                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9857                                ..Default::default()
 9858                            },
 9859                        ],
 9860                    },
 9861                    &[],
 9862                    cx,
 9863                )
 9864                .unwrap()
 9865        });
 9866    });
 9867
 9868    executor.run_until_parked();
 9869
 9870    cx.update_editor(|editor, cx| {
 9871        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9872    });
 9873
 9874    cx.assert_editor_state(indoc! {"
 9875        fn func(abc def: i32) -> ˇu32 {
 9876        }
 9877    "});
 9878
 9879    cx.update_editor(|editor, cx| {
 9880        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9881    });
 9882
 9883    cx.assert_editor_state(indoc! {"
 9884        fn func(abc ˇdef: i32) -> u32 {
 9885        }
 9886    "});
 9887
 9888    cx.update_editor(|editor, cx| {
 9889        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9890    });
 9891
 9892    cx.assert_editor_state(indoc! {"
 9893        fn func(abcˇ def: i32) -> u32 {
 9894        }
 9895    "});
 9896
 9897    cx.update_editor(|editor, cx| {
 9898        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9899    });
 9900
 9901    cx.assert_editor_state(indoc! {"
 9902        fn func(abc def: i32) -> ˇu32 {
 9903        }
 9904    "});
 9905}
 9906
 9907#[gpui::test]
 9908async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9909    init_test(cx, |_| {});
 9910
 9911    let mut cx = EditorTestContext::new(cx).await;
 9912
 9913    cx.set_state(indoc! {"
 9914        fn func(abˇc def: i32) -> u32 {
 9915        }
 9916    "});
 9917    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9918
 9919    cx.update(|cx| {
 9920        project.update(cx, |project, cx| {
 9921            project.update_diagnostics(
 9922                LanguageServerId(0),
 9923                lsp::PublishDiagnosticsParams {
 9924                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9925                    version: None,
 9926                    diagnostics: vec![lsp::Diagnostic {
 9927                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9928                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9929                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9930                        ..Default::default()
 9931                    }],
 9932                },
 9933                &[],
 9934                cx,
 9935            )
 9936        })
 9937    }).unwrap();
 9938    cx.run_until_parked();
 9939    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9940    cx.run_until_parked();
 9941    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9942}
 9943
 9944#[gpui::test]
 9945async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9946    init_test(cx, |_| {});
 9947
 9948    let mut cx = EditorTestContext::new(cx).await;
 9949
 9950    let diff_base = r#"
 9951        use some::mod;
 9952
 9953        const A: u32 = 42;
 9954
 9955        fn main() {
 9956            println!("hello");
 9957
 9958            println!("world");
 9959        }
 9960        "#
 9961    .unindent();
 9962
 9963    // Edits are modified, removed, modified, added
 9964    cx.set_state(
 9965        &r#"
 9966        use some::modified;
 9967
 9968        ˇ
 9969        fn main() {
 9970            println!("hello there");
 9971
 9972            println!("around the");
 9973            println!("world");
 9974        }
 9975        "#
 9976        .unindent(),
 9977    );
 9978
 9979    cx.set_diff_base(Some(&diff_base));
 9980    executor.run_until_parked();
 9981
 9982    cx.update_editor(|editor, cx| {
 9983        //Wrap around the bottom of the buffer
 9984        for _ in 0..3 {
 9985            editor.go_to_next_hunk(&GoToHunk, cx);
 9986        }
 9987    });
 9988
 9989    cx.assert_editor_state(
 9990        &r#"
 9991        ˇuse some::modified;
 9992
 9993
 9994        fn main() {
 9995            println!("hello there");
 9996
 9997            println!("around the");
 9998            println!("world");
 9999        }
10000        "#
10001        .unindent(),
10002    );
10003
10004    cx.update_editor(|editor, cx| {
10005        //Wrap around the top of the buffer
10006        for _ in 0..2 {
10007            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10008        }
10009    });
10010
10011    cx.assert_editor_state(
10012        &r#"
10013        use some::modified;
10014
10015
10016        fn main() {
10017        ˇ    println!("hello there");
10018
10019            println!("around the");
10020            println!("world");
10021        }
10022        "#
10023        .unindent(),
10024    );
10025
10026    cx.update_editor(|editor, cx| {
10027        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10028    });
10029
10030    cx.assert_editor_state(
10031        &r#"
10032        use some::modified;
10033
10034        ˇ
10035        fn main() {
10036            println!("hello there");
10037
10038            println!("around the");
10039            println!("world");
10040        }
10041        "#
10042        .unindent(),
10043    );
10044
10045    cx.update_editor(|editor, cx| {
10046        for _ in 0..3 {
10047            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10048        }
10049    });
10050
10051    cx.assert_editor_state(
10052        &r#"
10053        use some::modified;
10054
10055
10056        fn main() {
10057        ˇ    println!("hello there");
10058
10059            println!("around the");
10060            println!("world");
10061        }
10062        "#
10063        .unindent(),
10064    );
10065
10066    cx.update_editor(|editor, cx| {
10067        editor.fold(&Fold, cx);
10068
10069        //Make sure that the fold only gets one hunk
10070        for _ in 0..4 {
10071            editor.go_to_next_hunk(&GoToHunk, cx);
10072        }
10073    });
10074
10075    cx.assert_editor_state(
10076        &r#"
10077        ˇuse some::modified;
10078
10079
10080        fn main() {
10081            println!("hello there");
10082
10083            println!("around the");
10084            println!("world");
10085        }
10086        "#
10087        .unindent(),
10088    );
10089}
10090
10091#[test]
10092fn test_split_words() {
10093    fn split(text: &str) -> Vec<&str> {
10094        split_words(text).collect()
10095    }
10096
10097    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10098    assert_eq!(split("hello_world"), &["hello_", "world"]);
10099    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10100    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10101    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10102    assert_eq!(split("helloworld"), &["helloworld"]);
10103
10104    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10105}
10106
10107#[gpui::test]
10108async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10109    init_test(cx, |_| {});
10110
10111    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10112    let mut assert = |before, after| {
10113        let _state_context = cx.set_state(before);
10114        cx.update_editor(|editor, cx| {
10115            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10116        });
10117        cx.assert_editor_state(after);
10118    };
10119
10120    // Outside bracket jumps to outside of matching bracket
10121    assert("console.logˇ(var);", "console.log(var)ˇ;");
10122    assert("console.log(var)ˇ;", "console.logˇ(var);");
10123
10124    // Inside bracket jumps to inside of matching bracket
10125    assert("console.log(ˇvar);", "console.log(varˇ);");
10126    assert("console.log(varˇ);", "console.log(ˇvar);");
10127
10128    // When outside a bracket and inside, favor jumping to the inside bracket
10129    assert(
10130        "console.log('foo', [1, 2, 3]ˇ);",
10131        "console.log(ˇ'foo', [1, 2, 3]);",
10132    );
10133    assert(
10134        "console.log(ˇ'foo', [1, 2, 3]);",
10135        "console.log('foo', [1, 2, 3]ˇ);",
10136    );
10137
10138    // Bias forward if two options are equally likely
10139    assert(
10140        "let result = curried_fun()ˇ();",
10141        "let result = curried_fun()()ˇ;",
10142    );
10143
10144    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10145    assert(
10146        indoc! {"
10147            function test() {
10148                console.log('test')ˇ
10149            }"},
10150        indoc! {"
10151            function test() {
10152                console.logˇ('test')
10153            }"},
10154    );
10155}
10156
10157#[gpui::test]
10158async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10159    init_test(cx, |_| {});
10160
10161    let fs = FakeFs::new(cx.executor());
10162    fs.insert_tree(
10163        "/a",
10164        json!({
10165            "main.rs": "fn main() { let a = 5; }",
10166            "other.rs": "// Test file",
10167        }),
10168    )
10169    .await;
10170    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10171
10172    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10173    language_registry.add(Arc::new(Language::new(
10174        LanguageConfig {
10175            name: "Rust".into(),
10176            matcher: LanguageMatcher {
10177                path_suffixes: vec!["rs".to_string()],
10178                ..Default::default()
10179            },
10180            brackets: BracketPairConfig {
10181                pairs: vec![BracketPair {
10182                    start: "{".to_string(),
10183                    end: "}".to_string(),
10184                    close: true,
10185                    surround: true,
10186                    newline: true,
10187                }],
10188                disabled_scopes_by_bracket_ix: Vec::new(),
10189            },
10190            ..Default::default()
10191        },
10192        Some(tree_sitter_rust::LANGUAGE.into()),
10193    )));
10194    let mut fake_servers = language_registry.register_fake_lsp(
10195        "Rust",
10196        FakeLspAdapter {
10197            capabilities: lsp::ServerCapabilities {
10198                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10199                    first_trigger_character: "{".to_string(),
10200                    more_trigger_character: None,
10201                }),
10202                ..Default::default()
10203            },
10204            ..Default::default()
10205        },
10206    );
10207
10208    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10209
10210    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10211
10212    let worktree_id = workspace
10213        .update(cx, |workspace, cx| {
10214            workspace.project().update(cx, |project, cx| {
10215                project.worktrees(cx).next().unwrap().read(cx).id()
10216            })
10217        })
10218        .unwrap();
10219
10220    let buffer = project
10221        .update(cx, |project, cx| {
10222            project.open_local_buffer("/a/main.rs", cx)
10223        })
10224        .await
10225        .unwrap();
10226    cx.executor().run_until_parked();
10227    cx.executor().start_waiting();
10228    let fake_server = fake_servers.next().await.unwrap();
10229    let editor_handle = workspace
10230        .update(cx, |workspace, cx| {
10231            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10232        })
10233        .unwrap()
10234        .await
10235        .unwrap()
10236        .downcast::<Editor>()
10237        .unwrap();
10238
10239    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10240        assert_eq!(
10241            params.text_document_position.text_document.uri,
10242            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10243        );
10244        assert_eq!(
10245            params.text_document_position.position,
10246            lsp::Position::new(0, 21),
10247        );
10248
10249        Ok(Some(vec![lsp::TextEdit {
10250            new_text: "]".to_string(),
10251            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10252        }]))
10253    });
10254
10255    editor_handle.update(cx, |editor, cx| {
10256        editor.focus(cx);
10257        editor.change_selections(None, cx, |s| {
10258            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10259        });
10260        editor.handle_input("{", cx);
10261    });
10262
10263    cx.executor().run_until_parked();
10264
10265    buffer.update(cx, |buffer, _| {
10266        assert_eq!(
10267            buffer.text(),
10268            "fn main() { let a = {5}; }",
10269            "No extra braces from on type formatting should appear in the buffer"
10270        )
10271    });
10272}
10273
10274#[gpui::test]
10275async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10276    init_test(cx, |_| {});
10277
10278    let fs = FakeFs::new(cx.executor());
10279    fs.insert_tree(
10280        "/a",
10281        json!({
10282            "main.rs": "fn main() { let a = 5; }",
10283            "other.rs": "// Test file",
10284        }),
10285    )
10286    .await;
10287
10288    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10289
10290    let server_restarts = Arc::new(AtomicUsize::new(0));
10291    let closure_restarts = Arc::clone(&server_restarts);
10292    let language_server_name = "test language server";
10293    let language_name: LanguageName = "Rust".into();
10294
10295    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10296    language_registry.add(Arc::new(Language::new(
10297        LanguageConfig {
10298            name: language_name.clone(),
10299            matcher: LanguageMatcher {
10300                path_suffixes: vec!["rs".to_string()],
10301                ..Default::default()
10302            },
10303            ..Default::default()
10304        },
10305        Some(tree_sitter_rust::LANGUAGE.into()),
10306    )));
10307    let mut fake_servers = language_registry.register_fake_lsp(
10308        "Rust",
10309        FakeLspAdapter {
10310            name: language_server_name,
10311            initialization_options: Some(json!({
10312                "testOptionValue": true
10313            })),
10314            initializer: Some(Box::new(move |fake_server| {
10315                let task_restarts = Arc::clone(&closure_restarts);
10316                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10317                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10318                    futures::future::ready(Ok(()))
10319                });
10320            })),
10321            ..Default::default()
10322        },
10323    );
10324
10325    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10326    let _buffer = project
10327        .update(cx, |project, cx| {
10328            project.open_local_buffer("/a/main.rs", cx)
10329        })
10330        .await
10331        .unwrap();
10332    let _fake_server = fake_servers.next().await.unwrap();
10333    update_test_language_settings(cx, |language_settings| {
10334        language_settings.languages.insert(
10335            language_name.clone(),
10336            LanguageSettingsContent {
10337                tab_size: NonZeroU32::new(8),
10338                ..Default::default()
10339            },
10340        );
10341    });
10342    cx.executor().run_until_parked();
10343    assert_eq!(
10344        server_restarts.load(atomic::Ordering::Acquire),
10345        0,
10346        "Should not restart LSP server on an unrelated change"
10347    );
10348
10349    update_test_project_settings(cx, |project_settings| {
10350        project_settings.lsp.insert(
10351            "Some other server name".into(),
10352            LspSettings {
10353                binary: None,
10354                settings: None,
10355                initialization_options: Some(json!({
10356                    "some other init value": false
10357                })),
10358            },
10359        );
10360    });
10361    cx.executor().run_until_parked();
10362    assert_eq!(
10363        server_restarts.load(atomic::Ordering::Acquire),
10364        0,
10365        "Should not restart LSP server on an unrelated LSP settings change"
10366    );
10367
10368    update_test_project_settings(cx, |project_settings| {
10369        project_settings.lsp.insert(
10370            language_server_name.into(),
10371            LspSettings {
10372                binary: None,
10373                settings: None,
10374                initialization_options: Some(json!({
10375                    "anotherInitValue": false
10376                })),
10377            },
10378        );
10379    });
10380    cx.executor().run_until_parked();
10381    assert_eq!(
10382        server_restarts.load(atomic::Ordering::Acquire),
10383        1,
10384        "Should restart LSP server on a related LSP settings change"
10385    );
10386
10387    update_test_project_settings(cx, |project_settings| {
10388        project_settings.lsp.insert(
10389            language_server_name.into(),
10390            LspSettings {
10391                binary: None,
10392                settings: None,
10393                initialization_options: Some(json!({
10394                    "anotherInitValue": false
10395                })),
10396            },
10397        );
10398    });
10399    cx.executor().run_until_parked();
10400    assert_eq!(
10401        server_restarts.load(atomic::Ordering::Acquire),
10402        1,
10403        "Should not restart LSP server on a related LSP settings change that is the same"
10404    );
10405
10406    update_test_project_settings(cx, |project_settings| {
10407        project_settings.lsp.insert(
10408            language_server_name.into(),
10409            LspSettings {
10410                binary: None,
10411                settings: None,
10412                initialization_options: None,
10413            },
10414        );
10415    });
10416    cx.executor().run_until_parked();
10417    assert_eq!(
10418        server_restarts.load(atomic::Ordering::Acquire),
10419        2,
10420        "Should restart LSP server on another related LSP settings change"
10421    );
10422}
10423
10424#[gpui::test]
10425async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10426    init_test(cx, |_| {});
10427
10428    let mut cx = EditorLspTestContext::new_rust(
10429        lsp::ServerCapabilities {
10430            completion_provider: Some(lsp::CompletionOptions {
10431                trigger_characters: Some(vec![".".to_string()]),
10432                resolve_provider: Some(true),
10433                ..Default::default()
10434            }),
10435            ..Default::default()
10436        },
10437        cx,
10438    )
10439    .await;
10440
10441    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10442    cx.simulate_keystroke(".");
10443    let completion_item = lsp::CompletionItem {
10444        label: "some".into(),
10445        kind: Some(lsp::CompletionItemKind::SNIPPET),
10446        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10447        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10448            kind: lsp::MarkupKind::Markdown,
10449            value: "```rust\nSome(2)\n```".to_string(),
10450        })),
10451        deprecated: Some(false),
10452        sort_text: Some("fffffff2".to_string()),
10453        filter_text: Some("some".to_string()),
10454        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10455        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10456            range: lsp::Range {
10457                start: lsp::Position {
10458                    line: 0,
10459                    character: 22,
10460                },
10461                end: lsp::Position {
10462                    line: 0,
10463                    character: 22,
10464                },
10465            },
10466            new_text: "Some(2)".to_string(),
10467        })),
10468        additional_text_edits: Some(vec![lsp::TextEdit {
10469            range: lsp::Range {
10470                start: lsp::Position {
10471                    line: 0,
10472                    character: 20,
10473                },
10474                end: lsp::Position {
10475                    line: 0,
10476                    character: 22,
10477                },
10478            },
10479            new_text: "".to_string(),
10480        }]),
10481        ..Default::default()
10482    };
10483
10484    let closure_completion_item = completion_item.clone();
10485    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10486        let task_completion_item = closure_completion_item.clone();
10487        async move {
10488            Ok(Some(lsp::CompletionResponse::Array(vec![
10489                task_completion_item,
10490            ])))
10491        }
10492    });
10493
10494    request.next().await;
10495
10496    cx.condition(|editor, _| editor.context_menu_visible())
10497        .await;
10498    let apply_additional_edits = cx.update_editor(|editor, cx| {
10499        editor
10500            .confirm_completion(&ConfirmCompletion::default(), cx)
10501            .unwrap()
10502    });
10503    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10504
10505    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10506        let task_completion_item = completion_item.clone();
10507        async move { Ok(task_completion_item) }
10508    })
10509    .next()
10510    .await
10511    .unwrap();
10512    apply_additional_edits.await.unwrap();
10513    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10514}
10515
10516#[gpui::test]
10517async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10518    init_test(cx, |_| {});
10519
10520    let mut cx = EditorLspTestContext::new(
10521        Language::new(
10522            LanguageConfig {
10523                matcher: LanguageMatcher {
10524                    path_suffixes: vec!["jsx".into()],
10525                    ..Default::default()
10526                },
10527                overrides: [(
10528                    "element".into(),
10529                    LanguageConfigOverride {
10530                        word_characters: Override::Set(['-'].into_iter().collect()),
10531                        ..Default::default()
10532                    },
10533                )]
10534                .into_iter()
10535                .collect(),
10536                ..Default::default()
10537            },
10538            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10539        )
10540        .with_override_query("(jsx_self_closing_element) @element")
10541        .unwrap(),
10542        lsp::ServerCapabilities {
10543            completion_provider: Some(lsp::CompletionOptions {
10544                trigger_characters: Some(vec![":".to_string()]),
10545                ..Default::default()
10546            }),
10547            ..Default::default()
10548        },
10549        cx,
10550    )
10551    .await;
10552
10553    cx.lsp
10554        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10555            Ok(Some(lsp::CompletionResponse::Array(vec![
10556                lsp::CompletionItem {
10557                    label: "bg-blue".into(),
10558                    ..Default::default()
10559                },
10560                lsp::CompletionItem {
10561                    label: "bg-red".into(),
10562                    ..Default::default()
10563                },
10564                lsp::CompletionItem {
10565                    label: "bg-yellow".into(),
10566                    ..Default::default()
10567                },
10568            ])))
10569        });
10570
10571    cx.set_state(r#"<p class="bgˇ" />"#);
10572
10573    // Trigger completion when typing a dash, because the dash is an extra
10574    // word character in the 'element' scope, which contains the cursor.
10575    cx.simulate_keystroke("-");
10576    cx.executor().run_until_parked();
10577    cx.update_editor(|editor, _| {
10578        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10579            assert_eq!(
10580                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10581                &["bg-red", "bg-blue", "bg-yellow"]
10582            );
10583        } else {
10584            panic!("expected completion menu to be open");
10585        }
10586    });
10587
10588    cx.simulate_keystroke("l");
10589    cx.executor().run_until_parked();
10590    cx.update_editor(|editor, _| {
10591        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10592            assert_eq!(
10593                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10594                &["bg-blue", "bg-yellow"]
10595            );
10596        } else {
10597            panic!("expected completion menu to be open");
10598        }
10599    });
10600
10601    // When filtering completions, consider the character after the '-' to
10602    // be the start of a subword.
10603    cx.set_state(r#"<p class="yelˇ" />"#);
10604    cx.simulate_keystroke("l");
10605    cx.executor().run_until_parked();
10606    cx.update_editor(|editor, _| {
10607        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10608            assert_eq!(
10609                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10610                &["bg-yellow"]
10611            );
10612        } else {
10613            panic!("expected completion menu to be open");
10614        }
10615    });
10616}
10617
10618#[gpui::test]
10619async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10620    init_test(cx, |settings| {
10621        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10622            FormatterList(vec![Formatter::Prettier].into()),
10623        ))
10624    });
10625
10626    let fs = FakeFs::new(cx.executor());
10627    fs.insert_file("/file.ts", Default::default()).await;
10628
10629    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10630    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10631
10632    language_registry.add(Arc::new(Language::new(
10633        LanguageConfig {
10634            name: "TypeScript".into(),
10635            matcher: LanguageMatcher {
10636                path_suffixes: vec!["ts".to_string()],
10637                ..Default::default()
10638            },
10639            ..Default::default()
10640        },
10641        Some(tree_sitter_rust::LANGUAGE.into()),
10642    )));
10643    update_test_language_settings(cx, |settings| {
10644        settings.defaults.prettier = Some(PrettierSettings {
10645            allowed: true,
10646            ..PrettierSettings::default()
10647        });
10648    });
10649
10650    let test_plugin = "test_plugin";
10651    let _ = language_registry.register_fake_lsp(
10652        "TypeScript",
10653        FakeLspAdapter {
10654            prettier_plugins: vec![test_plugin],
10655            ..Default::default()
10656        },
10657    );
10658
10659    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10660    let buffer = project
10661        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10662        .await
10663        .unwrap();
10664
10665    let buffer_text = "one\ntwo\nthree\n";
10666    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10667    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10668    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10669
10670    editor
10671        .update(cx, |editor, cx| {
10672            editor.perform_format(
10673                project.clone(),
10674                FormatTrigger::Manual,
10675                FormatTarget::Buffer,
10676                cx,
10677            )
10678        })
10679        .unwrap()
10680        .await;
10681    assert_eq!(
10682        editor.update(cx, |editor, cx| editor.text(cx)),
10683        buffer_text.to_string() + prettier_format_suffix,
10684        "Test prettier formatting was not applied to the original buffer text",
10685    );
10686
10687    update_test_language_settings(cx, |settings| {
10688        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10689    });
10690    let format = editor.update(cx, |editor, cx| {
10691        editor.perform_format(
10692            project.clone(),
10693            FormatTrigger::Manual,
10694            FormatTarget::Buffer,
10695            cx,
10696        )
10697    });
10698    format.await.unwrap();
10699    assert_eq!(
10700        editor.update(cx, |editor, cx| editor.text(cx)),
10701        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10702        "Autoformatting (via test prettier) was not applied to the original buffer text",
10703    );
10704}
10705
10706#[gpui::test]
10707async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10708    init_test(cx, |_| {});
10709    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10710    let base_text = indoc! {r#"struct Row;
10711struct Row1;
10712struct Row2;
10713
10714struct Row4;
10715struct Row5;
10716struct Row6;
10717
10718struct Row8;
10719struct Row9;
10720struct Row10;"#};
10721
10722    // When addition hunks are not adjacent to carets, no hunk revert is performed
10723    assert_hunk_revert(
10724        indoc! {r#"struct Row;
10725                   struct Row1;
10726                   struct Row1.1;
10727                   struct Row1.2;
10728                   struct Row2;ˇ
10729
10730                   struct Row4;
10731                   struct Row5;
10732                   struct Row6;
10733
10734                   struct Row8;
10735                   ˇstruct Row9;
10736                   struct Row9.1;
10737                   struct Row9.2;
10738                   struct Row9.3;
10739                   struct Row10;"#},
10740        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10741        indoc! {r#"struct Row;
10742                   struct Row1;
10743                   struct Row1.1;
10744                   struct Row1.2;
10745                   struct Row2;ˇ
10746
10747                   struct Row4;
10748                   struct Row5;
10749                   struct Row6;
10750
10751                   struct Row8;
10752                   ˇstruct Row9;
10753                   struct Row9.1;
10754                   struct Row9.2;
10755                   struct Row9.3;
10756                   struct Row10;"#},
10757        base_text,
10758        &mut cx,
10759    );
10760    // Same for selections
10761    assert_hunk_revert(
10762        indoc! {r#"struct Row;
10763                   struct Row1;
10764                   struct Row2;
10765                   struct Row2.1;
10766                   struct Row2.2;
10767                   «ˇ
10768                   struct Row4;
10769                   struct» Row5;
10770                   «struct Row6;
10771                   ˇ»
10772                   struct Row9.1;
10773                   struct Row9.2;
10774                   struct Row9.3;
10775                   struct Row8;
10776                   struct Row9;
10777                   struct Row10;"#},
10778        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10779        indoc! {r#"struct Row;
10780                   struct Row1;
10781                   struct Row2;
10782                   struct Row2.1;
10783                   struct Row2.2;
10784                   «ˇ
10785                   struct Row4;
10786                   struct» Row5;
10787                   «struct Row6;
10788                   ˇ»
10789                   struct Row9.1;
10790                   struct Row9.2;
10791                   struct Row9.3;
10792                   struct Row8;
10793                   struct Row9;
10794                   struct Row10;"#},
10795        base_text,
10796        &mut cx,
10797    );
10798
10799    // When carets and selections intersect the addition hunks, those are reverted.
10800    // Adjacent carets got merged.
10801    assert_hunk_revert(
10802        indoc! {r#"struct Row;
10803                   ˇ// something on the top
10804                   struct Row1;
10805                   struct Row2;
10806                   struct Roˇw3.1;
10807                   struct Row2.2;
10808                   struct Row2.3;ˇ
10809
10810                   struct Row4;
10811                   struct ˇRow5.1;
10812                   struct Row5.2;
10813                   struct «Rowˇ»5.3;
10814                   struct Row5;
10815                   struct Row6;
10816                   ˇ
10817                   struct Row9.1;
10818                   struct «Rowˇ»9.2;
10819                   struct «ˇRow»9.3;
10820                   struct Row8;
10821                   struct Row9;
10822                   «ˇ// something on bottom»
10823                   struct Row10;"#},
10824        vec![
10825            DiffHunkStatus::Added,
10826            DiffHunkStatus::Added,
10827            DiffHunkStatus::Added,
10828            DiffHunkStatus::Added,
10829            DiffHunkStatus::Added,
10830        ],
10831        indoc! {r#"struct Row;
10832                   ˇstruct Row1;
10833                   struct Row2;
10834                   ˇ
10835                   struct Row4;
10836                   ˇstruct Row5;
10837                   struct Row6;
10838                   ˇ
10839                   ˇstruct Row8;
10840                   struct Row9;
10841                   ˇstruct Row10;"#},
10842        base_text,
10843        &mut cx,
10844    );
10845}
10846
10847#[gpui::test]
10848async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10849    init_test(cx, |_| {});
10850    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10851    let base_text = indoc! {r#"struct Row;
10852struct Row1;
10853struct Row2;
10854
10855struct Row4;
10856struct Row5;
10857struct Row6;
10858
10859struct Row8;
10860struct Row9;
10861struct Row10;"#};
10862
10863    // Modification hunks behave the same as the addition ones.
10864    assert_hunk_revert(
10865        indoc! {r#"struct Row;
10866                   struct Row1;
10867                   struct Row33;
10868                   ˇ
10869                   struct Row4;
10870                   struct Row5;
10871                   struct Row6;
10872                   ˇ
10873                   struct Row99;
10874                   struct Row9;
10875                   struct Row10;"#},
10876        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10877        indoc! {r#"struct Row;
10878                   struct Row1;
10879                   struct Row33;
10880                   ˇ
10881                   struct Row4;
10882                   struct Row5;
10883                   struct Row6;
10884                   ˇ
10885                   struct Row99;
10886                   struct Row9;
10887                   struct Row10;"#},
10888        base_text,
10889        &mut cx,
10890    );
10891    assert_hunk_revert(
10892        indoc! {r#"struct Row;
10893                   struct Row1;
10894                   struct Row33;
10895                   «ˇ
10896                   struct Row4;
10897                   struct» Row5;
10898                   «struct Row6;
10899                   ˇ»
10900                   struct Row99;
10901                   struct Row9;
10902                   struct Row10;"#},
10903        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10904        indoc! {r#"struct Row;
10905                   struct Row1;
10906                   struct Row33;
10907                   «ˇ
10908                   struct Row4;
10909                   struct» Row5;
10910                   «struct Row6;
10911                   ˇ»
10912                   struct Row99;
10913                   struct Row9;
10914                   struct Row10;"#},
10915        base_text,
10916        &mut cx,
10917    );
10918
10919    assert_hunk_revert(
10920        indoc! {r#"ˇstruct Row1.1;
10921                   struct Row1;
10922                   «ˇstr»uct Row22;
10923
10924                   struct ˇRow44;
10925                   struct Row5;
10926                   struct «Rˇ»ow66;ˇ
10927
10928                   «struˇ»ct Row88;
10929                   struct Row9;
10930                   struct Row1011;ˇ"#},
10931        vec![
10932            DiffHunkStatus::Modified,
10933            DiffHunkStatus::Modified,
10934            DiffHunkStatus::Modified,
10935            DiffHunkStatus::Modified,
10936            DiffHunkStatus::Modified,
10937            DiffHunkStatus::Modified,
10938        ],
10939        indoc! {r#"struct Row;
10940                   ˇstruct Row1;
10941                   struct Row2;
10942                   ˇ
10943                   struct Row4;
10944                   ˇstruct Row5;
10945                   struct Row6;
10946                   ˇ
10947                   struct Row8;
10948                   ˇstruct Row9;
10949                   struct Row10;ˇ"#},
10950        base_text,
10951        &mut cx,
10952    );
10953}
10954
10955#[gpui::test]
10956async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10957    init_test(cx, |_| {});
10958    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10959    let base_text = indoc! {r#"struct Row;
10960struct Row1;
10961struct Row2;
10962
10963struct Row4;
10964struct Row5;
10965struct Row6;
10966
10967struct Row8;
10968struct Row9;
10969struct Row10;"#};
10970
10971    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10972    assert_hunk_revert(
10973        indoc! {r#"struct Row;
10974                   struct Row2;
10975
10976                   ˇstruct Row4;
10977                   struct Row5;
10978                   struct Row6;
10979                   ˇ
10980                   struct Row8;
10981                   struct Row10;"#},
10982        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10983        indoc! {r#"struct Row;
10984                   struct Row2;
10985
10986                   ˇstruct Row4;
10987                   struct Row5;
10988                   struct Row6;
10989                   ˇ
10990                   struct Row8;
10991                   struct Row10;"#},
10992        base_text,
10993        &mut cx,
10994    );
10995    assert_hunk_revert(
10996        indoc! {r#"struct Row;
10997                   struct Row2;
10998
10999                   «ˇstruct Row4;
11000                   struct» Row5;
11001                   «struct Row6;
11002                   ˇ»
11003                   struct Row8;
11004                   struct Row10;"#},
11005        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11006        indoc! {r#"struct Row;
11007                   struct Row2;
11008
11009                   «ˇstruct Row4;
11010                   struct» Row5;
11011                   «struct Row6;
11012                   ˇ»
11013                   struct Row8;
11014                   struct Row10;"#},
11015        base_text,
11016        &mut cx,
11017    );
11018
11019    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11020    assert_hunk_revert(
11021        indoc! {r#"struct Row;
11022                   ˇstruct Row2;
11023
11024                   struct Row4;
11025                   struct Row5;
11026                   struct Row6;
11027
11028                   struct Row8;ˇ
11029                   struct Row10;"#},
11030        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11031        indoc! {r#"struct Row;
11032                   struct Row1;
11033                   ˇstruct Row2;
11034
11035                   struct Row4;
11036                   struct Row5;
11037                   struct Row6;
11038
11039                   struct Row8;ˇ
11040                   struct Row9;
11041                   struct Row10;"#},
11042        base_text,
11043        &mut cx,
11044    );
11045    assert_hunk_revert(
11046        indoc! {r#"struct Row;
11047                   struct Row2«ˇ;
11048                   struct Row4;
11049                   struct» Row5;
11050                   «struct Row6;
11051
11052                   struct Row8;ˇ»
11053                   struct Row10;"#},
11054        vec![
11055            DiffHunkStatus::Removed,
11056            DiffHunkStatus::Removed,
11057            DiffHunkStatus::Removed,
11058        ],
11059        indoc! {r#"struct Row;
11060                   struct Row1;
11061                   struct Row2«ˇ;
11062
11063                   struct Row4;
11064                   struct» Row5;
11065                   «struct Row6;
11066
11067                   struct Row8;ˇ»
11068                   struct Row9;
11069                   struct Row10;"#},
11070        base_text,
11071        &mut cx,
11072    );
11073}
11074
11075#[gpui::test]
11076async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11077    init_test(cx, |_| {});
11078
11079    let cols = 4;
11080    let rows = 10;
11081    let sample_text_1 = sample_text(rows, cols, 'a');
11082    assert_eq!(
11083        sample_text_1,
11084        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11085    );
11086    let sample_text_2 = sample_text(rows, cols, 'l');
11087    assert_eq!(
11088        sample_text_2,
11089        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11090    );
11091    let sample_text_3 = sample_text(rows, cols, 'v');
11092    assert_eq!(
11093        sample_text_3,
11094        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11095    );
11096
11097    fn diff_every_buffer_row(
11098        buffer: &Model<Buffer>,
11099        sample_text: String,
11100        cols: usize,
11101        cx: &mut gpui::TestAppContext,
11102    ) {
11103        // revert first character in each row, creating one large diff hunk per buffer
11104        let is_first_char = |offset: usize| offset % cols == 0;
11105        buffer.update(cx, |buffer, cx| {
11106            buffer.set_text(
11107                sample_text
11108                    .chars()
11109                    .enumerate()
11110                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11111                    .collect::<String>(),
11112                cx,
11113            );
11114            buffer.set_diff_base(Some(sample_text), cx);
11115        });
11116        cx.executor().run_until_parked();
11117    }
11118
11119    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11120    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11121
11122    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11123    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11124
11125    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11126    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11127
11128    let multibuffer = cx.new_model(|cx| {
11129        let mut multibuffer = MultiBuffer::new(ReadWrite);
11130        multibuffer.push_excerpts(
11131            buffer_1.clone(),
11132            [
11133                ExcerptRange {
11134                    context: Point::new(0, 0)..Point::new(3, 0),
11135                    primary: None,
11136                },
11137                ExcerptRange {
11138                    context: Point::new(5, 0)..Point::new(7, 0),
11139                    primary: None,
11140                },
11141                ExcerptRange {
11142                    context: Point::new(9, 0)..Point::new(10, 4),
11143                    primary: None,
11144                },
11145            ],
11146            cx,
11147        );
11148        multibuffer.push_excerpts(
11149            buffer_2.clone(),
11150            [
11151                ExcerptRange {
11152                    context: Point::new(0, 0)..Point::new(3, 0),
11153                    primary: None,
11154                },
11155                ExcerptRange {
11156                    context: Point::new(5, 0)..Point::new(7, 0),
11157                    primary: None,
11158                },
11159                ExcerptRange {
11160                    context: Point::new(9, 0)..Point::new(10, 4),
11161                    primary: None,
11162                },
11163            ],
11164            cx,
11165        );
11166        multibuffer.push_excerpts(
11167            buffer_3.clone(),
11168            [
11169                ExcerptRange {
11170                    context: Point::new(0, 0)..Point::new(3, 0),
11171                    primary: None,
11172                },
11173                ExcerptRange {
11174                    context: Point::new(5, 0)..Point::new(7, 0),
11175                    primary: None,
11176                },
11177                ExcerptRange {
11178                    context: Point::new(9, 0)..Point::new(10, 4),
11179                    primary: None,
11180                },
11181            ],
11182            cx,
11183        );
11184        multibuffer
11185    });
11186
11187    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11188    editor.update(cx, |editor, cx| {
11189        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");
11190        editor.select_all(&SelectAll, cx);
11191        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11192    });
11193    cx.executor().run_until_parked();
11194    // When all ranges are selected, all buffer hunks are reverted.
11195    editor.update(cx, |editor, cx| {
11196        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");
11197    });
11198    buffer_1.update(cx, |buffer, _| {
11199        assert_eq!(buffer.text(), sample_text_1);
11200    });
11201    buffer_2.update(cx, |buffer, _| {
11202        assert_eq!(buffer.text(), sample_text_2);
11203    });
11204    buffer_3.update(cx, |buffer, _| {
11205        assert_eq!(buffer.text(), sample_text_3);
11206    });
11207
11208    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11209    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11210    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11211    editor.update(cx, |editor, cx| {
11212        editor.change_selections(None, cx, |s| {
11213            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11214        });
11215        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11216    });
11217    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11218    // but not affect buffer_2 and its related excerpts.
11219    editor.update(cx, |editor, cx| {
11220        assert_eq!(
11221            editor.text(cx),
11222            "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"
11223        );
11224    });
11225    buffer_1.update(cx, |buffer, _| {
11226        assert_eq!(buffer.text(), sample_text_1);
11227    });
11228    buffer_2.update(cx, |buffer, _| {
11229        assert_eq!(
11230            buffer.text(),
11231            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11232        );
11233    });
11234    buffer_3.update(cx, |buffer, _| {
11235        assert_eq!(
11236            buffer.text(),
11237            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11238        );
11239    });
11240}
11241
11242#[gpui::test]
11243async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11244    init_test(cx, |_| {});
11245
11246    let cols = 4;
11247    let rows = 10;
11248    let sample_text_1 = sample_text(rows, cols, 'a');
11249    assert_eq!(
11250        sample_text_1,
11251        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11252    );
11253    let sample_text_2 = sample_text(rows, cols, 'l');
11254    assert_eq!(
11255        sample_text_2,
11256        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11257    );
11258    let sample_text_3 = sample_text(rows, cols, 'v');
11259    assert_eq!(
11260        sample_text_3,
11261        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11262    );
11263
11264    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11265    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11266    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11267
11268    let multi_buffer = cx.new_model(|cx| {
11269        let mut multibuffer = MultiBuffer::new(ReadWrite);
11270        multibuffer.push_excerpts(
11271            buffer_1.clone(),
11272            [
11273                ExcerptRange {
11274                    context: Point::new(0, 0)..Point::new(3, 0),
11275                    primary: None,
11276                },
11277                ExcerptRange {
11278                    context: Point::new(5, 0)..Point::new(7, 0),
11279                    primary: None,
11280                },
11281                ExcerptRange {
11282                    context: Point::new(9, 0)..Point::new(10, 4),
11283                    primary: None,
11284                },
11285            ],
11286            cx,
11287        );
11288        multibuffer.push_excerpts(
11289            buffer_2.clone(),
11290            [
11291                ExcerptRange {
11292                    context: Point::new(0, 0)..Point::new(3, 0),
11293                    primary: None,
11294                },
11295                ExcerptRange {
11296                    context: Point::new(5, 0)..Point::new(7, 0),
11297                    primary: None,
11298                },
11299                ExcerptRange {
11300                    context: Point::new(9, 0)..Point::new(10, 4),
11301                    primary: None,
11302                },
11303            ],
11304            cx,
11305        );
11306        multibuffer.push_excerpts(
11307            buffer_3.clone(),
11308            [
11309                ExcerptRange {
11310                    context: Point::new(0, 0)..Point::new(3, 0),
11311                    primary: None,
11312                },
11313                ExcerptRange {
11314                    context: Point::new(5, 0)..Point::new(7, 0),
11315                    primary: None,
11316                },
11317                ExcerptRange {
11318                    context: Point::new(9, 0)..Point::new(10, 4),
11319                    primary: None,
11320                },
11321            ],
11322            cx,
11323        );
11324        multibuffer
11325    });
11326
11327    let fs = FakeFs::new(cx.executor());
11328    fs.insert_tree(
11329        "/a",
11330        json!({
11331            "main.rs": sample_text_1,
11332            "other.rs": sample_text_2,
11333            "lib.rs": sample_text_3,
11334        }),
11335    )
11336    .await;
11337    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11338    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11339    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11340    let multi_buffer_editor = cx.new_view(|cx| {
11341        Editor::new(
11342            EditorMode::Full,
11343            multi_buffer,
11344            Some(project.clone()),
11345            true,
11346            cx,
11347        )
11348    });
11349    let multibuffer_item_id = workspace
11350        .update(cx, |workspace, cx| {
11351            assert!(
11352                workspace.active_item(cx).is_none(),
11353                "active item should be None before the first item is added"
11354            );
11355            workspace.add_item_to_active_pane(
11356                Box::new(multi_buffer_editor.clone()),
11357                None,
11358                true,
11359                cx,
11360            );
11361            let active_item = workspace
11362                .active_item(cx)
11363                .expect("should have an active item after adding the multi buffer");
11364            assert!(
11365                !active_item.is_singleton(cx),
11366                "A multi buffer was expected to active after adding"
11367            );
11368            active_item.item_id()
11369        })
11370        .unwrap();
11371    cx.executor().run_until_parked();
11372
11373    multi_buffer_editor.update(cx, |editor, cx| {
11374        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11375        editor.open_excerpts(&OpenExcerpts, cx);
11376    });
11377    cx.executor().run_until_parked();
11378    let first_item_id = workspace
11379        .update(cx, |workspace, cx| {
11380            let active_item = workspace
11381                .active_item(cx)
11382                .expect("should have an active item after navigating into the 1st buffer");
11383            let first_item_id = active_item.item_id();
11384            assert_ne!(
11385                first_item_id, multibuffer_item_id,
11386                "Should navigate into the 1st buffer and activate it"
11387            );
11388            assert!(
11389                active_item.is_singleton(cx),
11390                "New active item should be a singleton buffer"
11391            );
11392            assert_eq!(
11393                active_item
11394                    .act_as::<Editor>(cx)
11395                    .expect("should have navigated into an editor for the 1st buffer")
11396                    .read(cx)
11397                    .text(cx),
11398                sample_text_1
11399            );
11400
11401            workspace
11402                .go_back(workspace.active_pane().downgrade(), cx)
11403                .detach_and_log_err(cx);
11404
11405            first_item_id
11406        })
11407        .unwrap();
11408    cx.executor().run_until_parked();
11409    workspace
11410        .update(cx, |workspace, cx| {
11411            let active_item = workspace
11412                .active_item(cx)
11413                .expect("should have an active item after navigating back");
11414            assert_eq!(
11415                active_item.item_id(),
11416                multibuffer_item_id,
11417                "Should navigate back to the multi buffer"
11418            );
11419            assert!(!active_item.is_singleton(cx));
11420        })
11421        .unwrap();
11422
11423    multi_buffer_editor.update(cx, |editor, cx| {
11424        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11425            s.select_ranges(Some(39..40))
11426        });
11427        editor.open_excerpts(&OpenExcerpts, cx);
11428    });
11429    cx.executor().run_until_parked();
11430    let second_item_id = workspace
11431        .update(cx, |workspace, cx| {
11432            let active_item = workspace
11433                .active_item(cx)
11434                .expect("should have an active item after navigating into the 2nd buffer");
11435            let second_item_id = active_item.item_id();
11436            assert_ne!(
11437                second_item_id, multibuffer_item_id,
11438                "Should navigate away from the multibuffer"
11439            );
11440            assert_ne!(
11441                second_item_id, first_item_id,
11442                "Should navigate into the 2nd buffer and activate it"
11443            );
11444            assert!(
11445                active_item.is_singleton(cx),
11446                "New active item should be a singleton buffer"
11447            );
11448            assert_eq!(
11449                active_item
11450                    .act_as::<Editor>(cx)
11451                    .expect("should have navigated into an editor")
11452                    .read(cx)
11453                    .text(cx),
11454                sample_text_2
11455            );
11456
11457            workspace
11458                .go_back(workspace.active_pane().downgrade(), cx)
11459                .detach_and_log_err(cx);
11460
11461            second_item_id
11462        })
11463        .unwrap();
11464    cx.executor().run_until_parked();
11465    workspace
11466        .update(cx, |workspace, cx| {
11467            let active_item = workspace
11468                .active_item(cx)
11469                .expect("should have an active item after navigating back from the 2nd buffer");
11470            assert_eq!(
11471                active_item.item_id(),
11472                multibuffer_item_id,
11473                "Should navigate back from the 2nd buffer to the multi buffer"
11474            );
11475            assert!(!active_item.is_singleton(cx));
11476        })
11477        .unwrap();
11478
11479    multi_buffer_editor.update(cx, |editor, cx| {
11480        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11481            s.select_ranges(Some(60..70))
11482        });
11483        editor.open_excerpts(&OpenExcerpts, cx);
11484    });
11485    cx.executor().run_until_parked();
11486    workspace
11487        .update(cx, |workspace, cx| {
11488            let active_item = workspace
11489                .active_item(cx)
11490                .expect("should have an active item after navigating into the 3rd buffer");
11491            let third_item_id = active_item.item_id();
11492            assert_ne!(
11493                third_item_id, multibuffer_item_id,
11494                "Should navigate into the 3rd buffer and activate it"
11495            );
11496            assert_ne!(third_item_id, first_item_id);
11497            assert_ne!(third_item_id, second_item_id);
11498            assert!(
11499                active_item.is_singleton(cx),
11500                "New active item should be a singleton buffer"
11501            );
11502            assert_eq!(
11503                active_item
11504                    .act_as::<Editor>(cx)
11505                    .expect("should have navigated into an editor")
11506                    .read(cx)
11507                    .text(cx),
11508                sample_text_3
11509            );
11510
11511            workspace
11512                .go_back(workspace.active_pane().downgrade(), cx)
11513                .detach_and_log_err(cx);
11514        })
11515        .unwrap();
11516    cx.executor().run_until_parked();
11517    workspace
11518        .update(cx, |workspace, cx| {
11519            let active_item = workspace
11520                .active_item(cx)
11521                .expect("should have an active item after navigating back from the 3rd buffer");
11522            assert_eq!(
11523                active_item.item_id(),
11524                multibuffer_item_id,
11525                "Should navigate back from the 3rd buffer to the multi buffer"
11526            );
11527            assert!(!active_item.is_singleton(cx));
11528        })
11529        .unwrap();
11530}
11531
11532#[gpui::test]
11533async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11534    init_test(cx, |_| {});
11535
11536    let mut cx = EditorTestContext::new(cx).await;
11537
11538    let diff_base = r#"
11539        use some::mod;
11540
11541        const A: u32 = 42;
11542
11543        fn main() {
11544            println!("hello");
11545
11546            println!("world");
11547        }
11548        "#
11549    .unindent();
11550
11551    cx.set_state(
11552        &r#"
11553        use some::modified;
11554
11555        ˇ
11556        fn main() {
11557            println!("hello there");
11558
11559            println!("around the");
11560            println!("world");
11561        }
11562        "#
11563        .unindent(),
11564    );
11565
11566    cx.set_diff_base(Some(&diff_base));
11567    executor.run_until_parked();
11568
11569    cx.update_editor(|editor, cx| {
11570        editor.go_to_next_hunk(&GoToHunk, cx);
11571        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11572    });
11573    executor.run_until_parked();
11574    cx.assert_diff_hunks(
11575        r#"
11576          use some::modified;
11577
11578
11579          fn main() {
11580        -     println!("hello");
11581        +     println!("hello there");
11582
11583              println!("around the");
11584              println!("world");
11585          }
11586        "#
11587        .unindent(),
11588    );
11589
11590    cx.update_editor(|editor, cx| {
11591        for _ in 0..3 {
11592            editor.go_to_next_hunk(&GoToHunk, cx);
11593            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11594        }
11595    });
11596    executor.run_until_parked();
11597    cx.assert_editor_state(
11598        &r#"
11599        use some::modified;
11600
11601        ˇ
11602        fn main() {
11603            println!("hello there");
11604
11605            println!("around the");
11606            println!("world");
11607        }
11608        "#
11609        .unindent(),
11610    );
11611
11612    cx.assert_diff_hunks(
11613        r#"
11614        - use some::mod;
11615        + use some::modified;
11616
11617        - const A: u32 = 42;
11618
11619          fn main() {
11620        -     println!("hello");
11621        +     println!("hello there");
11622
11623        +     println!("around the");
11624              println!("world");
11625          }
11626        "#
11627        .unindent(),
11628    );
11629
11630    cx.update_editor(|editor, cx| {
11631        editor.cancel(&Cancel, cx);
11632    });
11633
11634    cx.assert_diff_hunks(
11635        r#"
11636          use some::modified;
11637
11638
11639          fn main() {
11640              println!("hello there");
11641
11642              println!("around the");
11643              println!("world");
11644          }
11645        "#
11646        .unindent(),
11647    );
11648}
11649
11650#[gpui::test]
11651async fn test_diff_base_change_with_expanded_diff_hunks(
11652    executor: BackgroundExecutor,
11653    cx: &mut gpui::TestAppContext,
11654) {
11655    init_test(cx, |_| {});
11656
11657    let mut cx = EditorTestContext::new(cx).await;
11658
11659    let diff_base = r#"
11660        use some::mod1;
11661        use some::mod2;
11662
11663        const A: u32 = 42;
11664        const B: u32 = 42;
11665        const C: u32 = 42;
11666
11667        fn main() {
11668            println!("hello");
11669
11670            println!("world");
11671        }
11672        "#
11673    .unindent();
11674
11675    cx.set_state(
11676        &r#"
11677        use some::mod2;
11678
11679        const A: u32 = 42;
11680        const C: u32 = 42;
11681
11682        fn main(ˇ) {
11683            //println!("hello");
11684
11685            println!("world");
11686            //
11687            //
11688        }
11689        "#
11690        .unindent(),
11691    );
11692
11693    cx.set_diff_base(Some(&diff_base));
11694    executor.run_until_parked();
11695
11696    cx.update_editor(|editor, cx| {
11697        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11698    });
11699    executor.run_until_parked();
11700    cx.assert_diff_hunks(
11701        r#"
11702        - use some::mod1;
11703          use some::mod2;
11704
11705          const A: u32 = 42;
11706        - const B: u32 = 42;
11707          const C: u32 = 42;
11708
11709          fn main() {
11710        -     println!("hello");
11711        +     //println!("hello");
11712
11713              println!("world");
11714        +     //
11715        +     //
11716          }
11717        "#
11718        .unindent(),
11719    );
11720
11721    cx.set_diff_base(Some("new diff base!"));
11722    executor.run_until_parked();
11723    cx.assert_diff_hunks(
11724        r#"
11725          use some::mod2;
11726
11727          const A: u32 = 42;
11728          const C: u32 = 42;
11729
11730          fn main() {
11731              //println!("hello");
11732
11733              println!("world");
11734              //
11735              //
11736          }
11737        "#
11738        .unindent(),
11739    );
11740
11741    cx.update_editor(|editor, cx| {
11742        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11743    });
11744    executor.run_until_parked();
11745    cx.assert_diff_hunks(
11746        r#"
11747        - new diff base!
11748        + use some::mod2;
11749        +
11750        + const A: u32 = 42;
11751        + const C: u32 = 42;
11752        +
11753        + fn main() {
11754        +     //println!("hello");
11755        +
11756        +     println!("world");
11757        +     //
11758        +     //
11759        + }
11760        "#
11761        .unindent(),
11762    );
11763}
11764
11765#[gpui::test]
11766async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11767    init_test(cx, |_| {});
11768
11769    let mut cx = EditorTestContext::new(cx).await;
11770
11771    let diff_base = r#"
11772        use some::mod1;
11773        use some::mod2;
11774
11775        const A: u32 = 42;
11776        const B: u32 = 42;
11777        const C: u32 = 42;
11778
11779        fn main() {
11780            println!("hello");
11781
11782            println!("world");
11783        }
11784
11785        fn another() {
11786            println!("another");
11787        }
11788
11789        fn another2() {
11790            println!("another2");
11791        }
11792        "#
11793    .unindent();
11794
11795    cx.set_state(
11796        &r#"
11797        «use some::mod2;
11798
11799        const A: u32 = 42;
11800        const C: u32 = 42;
11801
11802        fn main() {
11803            //println!("hello");
11804
11805            println!("world");
11806            //
11807            //ˇ»
11808        }
11809
11810        fn another() {
11811            println!("another");
11812            println!("another");
11813        }
11814
11815            println!("another2");
11816        }
11817        "#
11818        .unindent(),
11819    );
11820
11821    cx.set_diff_base(Some(&diff_base));
11822    executor.run_until_parked();
11823
11824    cx.update_editor(|editor, cx| {
11825        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11826    });
11827    executor.run_until_parked();
11828
11829    cx.assert_diff_hunks(
11830        r#"
11831        - use some::mod1;
11832          use some::mod2;
11833
11834          const A: u32 = 42;
11835        - const B: u32 = 42;
11836          const C: u32 = 42;
11837
11838          fn main() {
11839        -     println!("hello");
11840        +     //println!("hello");
11841
11842              println!("world");
11843        +     //
11844        +     //
11845          }
11846
11847          fn another() {
11848              println!("another");
11849        +     println!("another");
11850          }
11851
11852        - fn another2() {
11853              println!("another2");
11854          }
11855        "#
11856        .unindent(),
11857    );
11858
11859    // Fold across some of the diff hunks. They should no longer appear expanded.
11860    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11861    cx.executor().run_until_parked();
11862
11863    // Hunks are not shown if their position is within a fold
11864    cx.assert_diff_hunks(
11865        r#"
11866          use some::mod2;
11867
11868          const A: u32 = 42;
11869          const C: u32 = 42;
11870
11871          fn main() {
11872              //println!("hello");
11873
11874              println!("world");
11875              //
11876              //
11877          }
11878
11879          fn another() {
11880              println!("another");
11881        +     println!("another");
11882          }
11883
11884        - fn another2() {
11885              println!("another2");
11886          }
11887        "#
11888        .unindent(),
11889    );
11890
11891    cx.update_editor(|editor, cx| {
11892        editor.select_all(&SelectAll, cx);
11893        editor.unfold_lines(&UnfoldLines, cx);
11894    });
11895    cx.executor().run_until_parked();
11896
11897    // The deletions reappear when unfolding.
11898    cx.assert_diff_hunks(
11899        r#"
11900        - use some::mod1;
11901          use some::mod2;
11902
11903          const A: u32 = 42;
11904        - const B: u32 = 42;
11905          const C: u32 = 42;
11906
11907          fn main() {
11908        -     println!("hello");
11909        +     //println!("hello");
11910
11911              println!("world");
11912        +     //
11913        +     //
11914          }
11915
11916          fn another() {
11917              println!("another");
11918        +     println!("another");
11919          }
11920
11921        - fn another2() {
11922              println!("another2");
11923          }
11924        "#
11925        .unindent(),
11926    );
11927}
11928
11929#[gpui::test]
11930async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11931    init_test(cx, |_| {});
11932
11933    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11934    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11935    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11936    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11937    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11938    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11939
11940    let buffer_1 = cx.new_model(|cx| {
11941        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11942        buffer.set_diff_base(Some(file_1_old.into()), cx);
11943        buffer
11944    });
11945    let buffer_2 = cx.new_model(|cx| {
11946        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11947        buffer.set_diff_base(Some(file_2_old.into()), cx);
11948        buffer
11949    });
11950    let buffer_3 = cx.new_model(|cx| {
11951        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11952        buffer.set_diff_base(Some(file_3_old.into()), cx);
11953        buffer
11954    });
11955
11956    let multi_buffer = cx.new_model(|cx| {
11957        let mut multibuffer = MultiBuffer::new(ReadWrite);
11958        multibuffer.push_excerpts(
11959            buffer_1.clone(),
11960            [
11961                ExcerptRange {
11962                    context: Point::new(0, 0)..Point::new(3, 0),
11963                    primary: None,
11964                },
11965                ExcerptRange {
11966                    context: Point::new(5, 0)..Point::new(7, 0),
11967                    primary: None,
11968                },
11969                ExcerptRange {
11970                    context: Point::new(9, 0)..Point::new(10, 3),
11971                    primary: None,
11972                },
11973            ],
11974            cx,
11975        );
11976        multibuffer.push_excerpts(
11977            buffer_2.clone(),
11978            [
11979                ExcerptRange {
11980                    context: Point::new(0, 0)..Point::new(3, 0),
11981                    primary: None,
11982                },
11983                ExcerptRange {
11984                    context: Point::new(5, 0)..Point::new(7, 0),
11985                    primary: None,
11986                },
11987                ExcerptRange {
11988                    context: Point::new(9, 0)..Point::new(10, 3),
11989                    primary: None,
11990                },
11991            ],
11992            cx,
11993        );
11994        multibuffer.push_excerpts(
11995            buffer_3.clone(),
11996            [
11997                ExcerptRange {
11998                    context: Point::new(0, 0)..Point::new(3, 0),
11999                    primary: None,
12000                },
12001                ExcerptRange {
12002                    context: Point::new(5, 0)..Point::new(7, 0),
12003                    primary: None,
12004                },
12005                ExcerptRange {
12006                    context: Point::new(9, 0)..Point::new(10, 3),
12007                    primary: None,
12008                },
12009            ],
12010            cx,
12011        );
12012        multibuffer
12013    });
12014
12015    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12016    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12017    cx.run_until_parked();
12018
12019    cx.assert_editor_state(
12020        &"
12021            ˇaaa
12022            ccc
12023            ddd
12024
12025            ggg
12026            hhh
12027
12028
12029            lll
12030            mmm
12031            NNN
12032
12033            qqq
12034            rrr
12035
12036            uuu
12037            111
12038            222
12039            333
12040
12041            666
12042            777
12043
12044            000
12045            !!!"
12046        .unindent(),
12047    );
12048
12049    cx.update_editor(|editor, cx| {
12050        editor.select_all(&SelectAll, cx);
12051        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12052    });
12053    cx.executor().run_until_parked();
12054
12055    cx.assert_diff_hunks(
12056        "
12057            aaa
12058          - bbb
12059            ccc
12060            ddd
12061
12062            ggg
12063            hhh
12064
12065
12066            lll
12067            mmm
12068          - nnn
12069          + NNN
12070
12071            qqq
12072            rrr
12073
12074            uuu
12075            111
12076            222
12077            333
12078
12079          + 666
12080            777
12081
12082            000
12083            !!!"
12084        .unindent(),
12085    );
12086}
12087
12088#[gpui::test]
12089async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12090    init_test(cx, |_| {});
12091
12092    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12093    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12094
12095    let buffer = cx.new_model(|cx| {
12096        let mut buffer = Buffer::local(text.to_string(), cx);
12097        buffer.set_diff_base(Some(base.into()), cx);
12098        buffer
12099    });
12100
12101    let multi_buffer = cx.new_model(|cx| {
12102        let mut multibuffer = MultiBuffer::new(ReadWrite);
12103        multibuffer.push_excerpts(
12104            buffer.clone(),
12105            [
12106                ExcerptRange {
12107                    context: Point::new(0, 0)..Point::new(2, 0),
12108                    primary: None,
12109                },
12110                ExcerptRange {
12111                    context: Point::new(5, 0)..Point::new(7, 0),
12112                    primary: None,
12113                },
12114            ],
12115            cx,
12116        );
12117        multibuffer
12118    });
12119
12120    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12121    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12122    cx.run_until_parked();
12123
12124    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12125    cx.executor().run_until_parked();
12126
12127    cx.assert_diff_hunks(
12128        "
12129            aaa
12130          - bbb
12131          + BBB
12132
12133          - ddd
12134          - eee
12135          + EEE
12136            fff
12137        "
12138        .unindent(),
12139    );
12140}
12141
12142#[gpui::test]
12143async fn test_edits_around_expanded_insertion_hunks(
12144    executor: BackgroundExecutor,
12145    cx: &mut gpui::TestAppContext,
12146) {
12147    init_test(cx, |_| {});
12148
12149    let mut cx = EditorTestContext::new(cx).await;
12150
12151    let diff_base = r#"
12152        use some::mod1;
12153        use some::mod2;
12154
12155        const A: u32 = 42;
12156
12157        fn main() {
12158            println!("hello");
12159
12160            println!("world");
12161        }
12162        "#
12163    .unindent();
12164    executor.run_until_parked();
12165    cx.set_state(
12166        &r#"
12167        use some::mod1;
12168        use some::mod2;
12169
12170        const A: u32 = 42;
12171        const B: u32 = 42;
12172        const C: u32 = 42;
12173        ˇ
12174
12175        fn main() {
12176            println!("hello");
12177
12178            println!("world");
12179        }
12180        "#
12181        .unindent(),
12182    );
12183
12184    cx.set_diff_base(Some(&diff_base));
12185    executor.run_until_parked();
12186
12187    cx.update_editor(|editor, cx| {
12188        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12189    });
12190    executor.run_until_parked();
12191
12192    cx.assert_diff_hunks(
12193        r#"
12194        use some::mod1;
12195        use some::mod2;
12196
12197        const A: u32 = 42;
12198      + const B: u32 = 42;
12199      + const C: u32 = 42;
12200      +
12201
12202        fn main() {
12203            println!("hello");
12204
12205            println!("world");
12206        }
12207        "#
12208        .unindent(),
12209    );
12210
12211    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12212    executor.run_until_parked();
12213
12214    cx.assert_diff_hunks(
12215        r#"
12216        use some::mod1;
12217        use some::mod2;
12218
12219        const A: u32 = 42;
12220      + const B: u32 = 42;
12221      + const C: u32 = 42;
12222      + const D: u32 = 42;
12223      +
12224
12225        fn main() {
12226            println!("hello");
12227
12228            println!("world");
12229        }
12230        "#
12231        .unindent(),
12232    );
12233
12234    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12235    executor.run_until_parked();
12236
12237    cx.assert_diff_hunks(
12238        r#"
12239        use some::mod1;
12240        use some::mod2;
12241
12242        const A: u32 = 42;
12243      + const B: u32 = 42;
12244      + const C: u32 = 42;
12245      + const D: u32 = 42;
12246      + const E: u32 = 42;
12247      +
12248
12249        fn main() {
12250            println!("hello");
12251
12252            println!("world");
12253        }
12254        "#
12255        .unindent(),
12256    );
12257
12258    cx.update_editor(|editor, cx| {
12259        editor.delete_line(&DeleteLine, cx);
12260    });
12261    executor.run_until_parked();
12262
12263    cx.assert_diff_hunks(
12264        r#"
12265        use some::mod1;
12266        use some::mod2;
12267
12268        const A: u32 = 42;
12269      + const B: u32 = 42;
12270      + const C: u32 = 42;
12271      + const D: u32 = 42;
12272      + const E: u32 = 42;
12273
12274        fn main() {
12275            println!("hello");
12276
12277            println!("world");
12278        }
12279        "#
12280        .unindent(),
12281    );
12282
12283    cx.update_editor(|editor, cx| {
12284        editor.move_up(&MoveUp, cx);
12285        editor.delete_line(&DeleteLine, cx);
12286        editor.move_up(&MoveUp, cx);
12287        editor.delete_line(&DeleteLine, cx);
12288        editor.move_up(&MoveUp, cx);
12289        editor.delete_line(&DeleteLine, cx);
12290    });
12291    executor.run_until_parked();
12292    cx.assert_editor_state(
12293        &r#"
12294        use some::mod1;
12295        use some::mod2;
12296
12297        const A: u32 = 42;
12298        const B: u32 = 42;
12299        ˇ
12300        fn main() {
12301            println!("hello");
12302
12303            println!("world");
12304        }
12305        "#
12306        .unindent(),
12307    );
12308
12309    cx.assert_diff_hunks(
12310        r#"
12311        use some::mod1;
12312        use some::mod2;
12313
12314        const A: u32 = 42;
12315      + const B: u32 = 42;
12316
12317        fn main() {
12318            println!("hello");
12319
12320            println!("world");
12321        }
12322        "#
12323        .unindent(),
12324    );
12325
12326    cx.update_editor(|editor, cx| {
12327        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12328        editor.delete_line(&DeleteLine, cx);
12329    });
12330    executor.run_until_parked();
12331    cx.assert_diff_hunks(
12332        r#"
12333        use some::mod1;
12334      - use some::mod2;
12335      -
12336      - const A: u32 = 42;
12337
12338        fn main() {
12339            println!("hello");
12340
12341            println!("world");
12342        }
12343        "#
12344        .unindent(),
12345    );
12346}
12347
12348#[gpui::test]
12349async fn test_edits_around_expanded_deletion_hunks(
12350    executor: BackgroundExecutor,
12351    cx: &mut gpui::TestAppContext,
12352) {
12353    init_test(cx, |_| {});
12354
12355    let mut cx = EditorTestContext::new(cx).await;
12356
12357    let diff_base = r#"
12358        use some::mod1;
12359        use some::mod2;
12360
12361        const A: u32 = 42;
12362        const B: u32 = 42;
12363        const C: u32 = 42;
12364
12365
12366        fn main() {
12367            println!("hello");
12368
12369            println!("world");
12370        }
12371    "#
12372    .unindent();
12373    executor.run_until_parked();
12374    cx.set_state(
12375        &r#"
12376        use some::mod1;
12377        use some::mod2;
12378
12379        ˇconst B: u32 = 42;
12380        const C: u32 = 42;
12381
12382
12383        fn main() {
12384            println!("hello");
12385
12386            println!("world");
12387        }
12388        "#
12389        .unindent(),
12390    );
12391
12392    cx.set_diff_base(Some(&diff_base));
12393    executor.run_until_parked();
12394
12395    cx.update_editor(|editor, cx| {
12396        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12397    });
12398    executor.run_until_parked();
12399
12400    cx.assert_diff_hunks(
12401        r#"
12402        use some::mod1;
12403        use some::mod2;
12404
12405      - const A: u32 = 42;
12406        const B: u32 = 42;
12407        const C: u32 = 42;
12408
12409
12410        fn main() {
12411            println!("hello");
12412
12413            println!("world");
12414        }
12415        "#
12416        .unindent(),
12417    );
12418
12419    cx.update_editor(|editor, cx| {
12420        editor.delete_line(&DeleteLine, cx);
12421    });
12422    executor.run_until_parked();
12423    cx.assert_editor_state(
12424        &r#"
12425        use some::mod1;
12426        use some::mod2;
12427
12428        ˇconst C: u32 = 42;
12429
12430
12431        fn main() {
12432            println!("hello");
12433
12434            println!("world");
12435        }
12436        "#
12437        .unindent(),
12438    );
12439    cx.assert_diff_hunks(
12440        r#"
12441        use some::mod1;
12442        use some::mod2;
12443
12444      - const A: u32 = 42;
12445      - const B: u32 = 42;
12446        const C: u32 = 42;
12447
12448
12449        fn main() {
12450            println!("hello");
12451
12452            println!("world");
12453        }
12454        "#
12455        .unindent(),
12456    );
12457
12458    cx.update_editor(|editor, cx| {
12459        editor.delete_line(&DeleteLine, cx);
12460    });
12461    executor.run_until_parked();
12462    cx.assert_editor_state(
12463        &r#"
12464        use some::mod1;
12465        use some::mod2;
12466
12467        ˇ
12468
12469        fn main() {
12470            println!("hello");
12471
12472            println!("world");
12473        }
12474        "#
12475        .unindent(),
12476    );
12477    cx.assert_diff_hunks(
12478        r#"
12479        use some::mod1;
12480        use some::mod2;
12481
12482      - const A: u32 = 42;
12483      - const B: u32 = 42;
12484      - const C: u32 = 42;
12485
12486
12487        fn main() {
12488            println!("hello");
12489
12490            println!("world");
12491        }
12492        "#
12493        .unindent(),
12494    );
12495
12496    cx.update_editor(|editor, cx| {
12497        editor.handle_input("replacement", cx);
12498    });
12499    executor.run_until_parked();
12500    cx.assert_editor_state(
12501        &r#"
12502        use some::mod1;
12503        use some::mod2;
12504
12505        replacementˇ
12506
12507        fn main() {
12508            println!("hello");
12509
12510            println!("world");
12511        }
12512        "#
12513        .unindent(),
12514    );
12515    cx.assert_diff_hunks(
12516        r#"
12517        use some::mod1;
12518        use some::mod2;
12519
12520      - const A: u32 = 42;
12521      - const B: u32 = 42;
12522      - const C: u32 = 42;
12523      -
12524      + replacement
12525
12526        fn main() {
12527            println!("hello");
12528
12529            println!("world");
12530        }
12531        "#
12532        .unindent(),
12533    );
12534}
12535
12536#[gpui::test]
12537async fn test_edit_after_expanded_modification_hunk(
12538    executor: BackgroundExecutor,
12539    cx: &mut gpui::TestAppContext,
12540) {
12541    init_test(cx, |_| {});
12542
12543    let mut cx = EditorTestContext::new(cx).await;
12544
12545    let diff_base = r#"
12546        use some::mod1;
12547        use some::mod2;
12548
12549        const A: u32 = 42;
12550        const B: u32 = 42;
12551        const C: u32 = 42;
12552        const D: u32 = 42;
12553
12554
12555        fn main() {
12556            println!("hello");
12557
12558            println!("world");
12559        }"#
12560    .unindent();
12561
12562    cx.set_state(
12563        &r#"
12564        use some::mod1;
12565        use some::mod2;
12566
12567        const A: u32 = 42;
12568        const B: u32 = 42;
12569        const C: u32 = 43ˇ
12570        const D: u32 = 42;
12571
12572
12573        fn main() {
12574            println!("hello");
12575
12576            println!("world");
12577        }"#
12578        .unindent(),
12579    );
12580
12581    cx.set_diff_base(Some(&diff_base));
12582    executor.run_until_parked();
12583    cx.update_editor(|editor, cx| {
12584        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12585    });
12586    executor.run_until_parked();
12587
12588    cx.assert_diff_hunks(
12589        r#"
12590        use some::mod1;
12591        use some::mod2;
12592
12593        const A: u32 = 42;
12594        const B: u32 = 42;
12595      - const C: u32 = 42;
12596      + const C: u32 = 43
12597        const D: u32 = 42;
12598
12599
12600        fn main() {
12601            println!("hello");
12602
12603            println!("world");
12604        }"#
12605        .unindent(),
12606    );
12607
12608    cx.update_editor(|editor, cx| {
12609        editor.handle_input("\nnew_line\n", cx);
12610    });
12611    executor.run_until_parked();
12612
12613    cx.assert_diff_hunks(
12614        r#"
12615        use some::mod1;
12616        use some::mod2;
12617
12618        const A: u32 = 42;
12619        const B: u32 = 42;
12620      - const C: u32 = 42;
12621      + const C: u32 = 43
12622      + new_line
12623      +
12624        const D: u32 = 42;
12625
12626
12627        fn main() {
12628            println!("hello");
12629
12630            println!("world");
12631        }"#
12632        .unindent(),
12633    );
12634}
12635
12636async fn setup_indent_guides_editor(
12637    text: &str,
12638    cx: &mut gpui::TestAppContext,
12639) -> (BufferId, EditorTestContext) {
12640    init_test(cx, |_| {});
12641
12642    let mut cx = EditorTestContext::new(cx).await;
12643
12644    let buffer_id = cx.update_editor(|editor, cx| {
12645        editor.set_text(text, cx);
12646        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12647
12648        buffer_ids[0]
12649    });
12650
12651    (buffer_id, cx)
12652}
12653
12654fn assert_indent_guides(
12655    range: Range<u32>,
12656    expected: Vec<IndentGuide>,
12657    active_indices: Option<Vec<usize>>,
12658    cx: &mut EditorTestContext,
12659) {
12660    let indent_guides = cx.update_editor(|editor, cx| {
12661        let snapshot = editor.snapshot(cx).display_snapshot;
12662        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12663            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12664            true,
12665            &snapshot,
12666            cx,
12667        );
12668
12669        indent_guides.sort_by(|a, b| {
12670            a.depth.cmp(&b.depth).then(
12671                a.start_row
12672                    .cmp(&b.start_row)
12673                    .then(a.end_row.cmp(&b.end_row)),
12674            )
12675        });
12676        indent_guides
12677    });
12678
12679    if let Some(expected) = active_indices {
12680        let active_indices = cx.update_editor(|editor, cx| {
12681            let snapshot = editor.snapshot(cx).display_snapshot;
12682            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12683        });
12684
12685        assert_eq!(
12686            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12687            expected,
12688            "Active indent guide indices do not match"
12689        );
12690    }
12691
12692    let expected: Vec<_> = expected
12693        .into_iter()
12694        .map(|guide| MultiBufferIndentGuide {
12695            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12696            buffer: guide,
12697        })
12698        .collect();
12699
12700    assert_eq!(indent_guides, expected, "Indent guides do not match");
12701}
12702
12703fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12704    IndentGuide {
12705        buffer_id,
12706        start_row,
12707        end_row,
12708        depth,
12709        tab_size: 4,
12710        settings: IndentGuideSettings {
12711            enabled: true,
12712            line_width: 1,
12713            active_line_width: 1,
12714            ..Default::default()
12715        },
12716    }
12717}
12718
12719#[gpui::test]
12720async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12721    let (buffer_id, mut cx) = setup_indent_guides_editor(
12722        &"
12723    fn main() {
12724        let a = 1;
12725    }"
12726        .unindent(),
12727        cx,
12728    )
12729    .await;
12730
12731    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12732}
12733
12734#[gpui::test]
12735async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12736    let (buffer_id, mut cx) = setup_indent_guides_editor(
12737        &"
12738    fn main() {
12739        let a = 1;
12740        let b = 2;
12741    }"
12742        .unindent(),
12743        cx,
12744    )
12745    .await;
12746
12747    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12748}
12749
12750#[gpui::test]
12751async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12752    let (buffer_id, mut cx) = setup_indent_guides_editor(
12753        &"
12754    fn main() {
12755        let a = 1;
12756        if a == 3 {
12757            let b = 2;
12758        } else {
12759            let c = 3;
12760        }
12761    }"
12762        .unindent(),
12763        cx,
12764    )
12765    .await;
12766
12767    assert_indent_guides(
12768        0..8,
12769        vec![
12770            indent_guide(buffer_id, 1, 6, 0),
12771            indent_guide(buffer_id, 3, 3, 1),
12772            indent_guide(buffer_id, 5, 5, 1),
12773        ],
12774        None,
12775        &mut cx,
12776    );
12777}
12778
12779#[gpui::test]
12780async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12781    let (buffer_id, mut cx) = setup_indent_guides_editor(
12782        &"
12783    fn main() {
12784        let a = 1;
12785            let b = 2;
12786        let c = 3;
12787    }"
12788        .unindent(),
12789        cx,
12790    )
12791    .await;
12792
12793    assert_indent_guides(
12794        0..5,
12795        vec![
12796            indent_guide(buffer_id, 1, 3, 0),
12797            indent_guide(buffer_id, 2, 2, 1),
12798        ],
12799        None,
12800        &mut cx,
12801    );
12802}
12803
12804#[gpui::test]
12805async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12806    let (buffer_id, mut cx) = setup_indent_guides_editor(
12807        &"
12808        fn main() {
12809            let a = 1;
12810
12811            let c = 3;
12812        }"
12813        .unindent(),
12814        cx,
12815    )
12816    .await;
12817
12818    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12819}
12820
12821#[gpui::test]
12822async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12823    let (buffer_id, mut cx) = setup_indent_guides_editor(
12824        &"
12825        fn main() {
12826            let a = 1;
12827
12828            let c = 3;
12829
12830            if a == 3 {
12831                let b = 2;
12832            } else {
12833                let c = 3;
12834            }
12835        }"
12836        .unindent(),
12837        cx,
12838    )
12839    .await;
12840
12841    assert_indent_guides(
12842        0..11,
12843        vec![
12844            indent_guide(buffer_id, 1, 9, 0),
12845            indent_guide(buffer_id, 6, 6, 1),
12846            indent_guide(buffer_id, 8, 8, 1),
12847        ],
12848        None,
12849        &mut cx,
12850    );
12851}
12852
12853#[gpui::test]
12854async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12855    let (buffer_id, mut cx) = setup_indent_guides_editor(
12856        &"
12857        fn main() {
12858            let a = 1;
12859
12860            let c = 3;
12861
12862            if a == 3 {
12863                let b = 2;
12864            } else {
12865                let c = 3;
12866            }
12867        }"
12868        .unindent(),
12869        cx,
12870    )
12871    .await;
12872
12873    assert_indent_guides(
12874        1..11,
12875        vec![
12876            indent_guide(buffer_id, 1, 9, 0),
12877            indent_guide(buffer_id, 6, 6, 1),
12878            indent_guide(buffer_id, 8, 8, 1),
12879        ],
12880        None,
12881        &mut cx,
12882    );
12883}
12884
12885#[gpui::test]
12886async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12887    let (buffer_id, mut cx) = setup_indent_guides_editor(
12888        &"
12889        fn main() {
12890            let a = 1;
12891
12892            let c = 3;
12893
12894            if a == 3 {
12895                let b = 2;
12896            } else {
12897                let c = 3;
12898            }
12899        }"
12900        .unindent(),
12901        cx,
12902    )
12903    .await;
12904
12905    assert_indent_guides(
12906        1..10,
12907        vec![
12908            indent_guide(buffer_id, 1, 9, 0),
12909            indent_guide(buffer_id, 6, 6, 1),
12910            indent_guide(buffer_id, 8, 8, 1),
12911        ],
12912        None,
12913        &mut cx,
12914    );
12915}
12916
12917#[gpui::test]
12918async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12919    let (buffer_id, mut cx) = setup_indent_guides_editor(
12920        &"
12921        block1
12922            block2
12923                block3
12924                    block4
12925            block2
12926        block1
12927        block1"
12928            .unindent(),
12929        cx,
12930    )
12931    .await;
12932
12933    assert_indent_guides(
12934        1..10,
12935        vec![
12936            indent_guide(buffer_id, 1, 4, 0),
12937            indent_guide(buffer_id, 2, 3, 1),
12938            indent_guide(buffer_id, 3, 3, 2),
12939        ],
12940        None,
12941        &mut cx,
12942    );
12943}
12944
12945#[gpui::test]
12946async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12947    let (buffer_id, mut cx) = setup_indent_guides_editor(
12948        &"
12949        block1
12950            block2
12951                block3
12952
12953        block1
12954        block1"
12955            .unindent(),
12956        cx,
12957    )
12958    .await;
12959
12960    assert_indent_guides(
12961        0..6,
12962        vec![
12963            indent_guide(buffer_id, 1, 2, 0),
12964            indent_guide(buffer_id, 2, 2, 1),
12965        ],
12966        None,
12967        &mut cx,
12968    );
12969}
12970
12971#[gpui::test]
12972async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12973    let (buffer_id, mut cx) = setup_indent_guides_editor(
12974        &"
12975        block1
12976
12977
12978
12979            block2
12980        "
12981        .unindent(),
12982        cx,
12983    )
12984    .await;
12985
12986    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12987}
12988
12989#[gpui::test]
12990async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12991    let (buffer_id, mut cx) = setup_indent_guides_editor(
12992        &"
12993        def a:
12994        \tb = 3
12995        \tif True:
12996        \t\tc = 4
12997        \t\td = 5
12998        \tprint(b)
12999        "
13000        .unindent(),
13001        cx,
13002    )
13003    .await;
13004
13005    assert_indent_guides(
13006        0..6,
13007        vec![
13008            indent_guide(buffer_id, 1, 6, 0),
13009            indent_guide(buffer_id, 3, 4, 1),
13010        ],
13011        None,
13012        &mut cx,
13013    );
13014}
13015
13016#[gpui::test]
13017async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13018    let (buffer_id, mut cx) = setup_indent_guides_editor(
13019        &"
13020    fn main() {
13021        let a = 1;
13022    }"
13023        .unindent(),
13024        cx,
13025    )
13026    .await;
13027
13028    cx.update_editor(|editor, cx| {
13029        editor.change_selections(None, cx, |s| {
13030            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13031        });
13032    });
13033
13034    assert_indent_guides(
13035        0..3,
13036        vec![indent_guide(buffer_id, 1, 1, 0)],
13037        Some(vec![0]),
13038        &mut cx,
13039    );
13040}
13041
13042#[gpui::test]
13043async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13044    let (buffer_id, mut cx) = setup_indent_guides_editor(
13045        &"
13046    fn main() {
13047        if 1 == 2 {
13048            let a = 1;
13049        }
13050    }"
13051        .unindent(),
13052        cx,
13053    )
13054    .await;
13055
13056    cx.update_editor(|editor, cx| {
13057        editor.change_selections(None, cx, |s| {
13058            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13059        });
13060    });
13061
13062    assert_indent_guides(
13063        0..4,
13064        vec![
13065            indent_guide(buffer_id, 1, 3, 0),
13066            indent_guide(buffer_id, 2, 2, 1),
13067        ],
13068        Some(vec![1]),
13069        &mut cx,
13070    );
13071
13072    cx.update_editor(|editor, cx| {
13073        editor.change_selections(None, cx, |s| {
13074            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13075        });
13076    });
13077
13078    assert_indent_guides(
13079        0..4,
13080        vec![
13081            indent_guide(buffer_id, 1, 3, 0),
13082            indent_guide(buffer_id, 2, 2, 1),
13083        ],
13084        Some(vec![1]),
13085        &mut cx,
13086    );
13087
13088    cx.update_editor(|editor, cx| {
13089        editor.change_selections(None, cx, |s| {
13090            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13091        });
13092    });
13093
13094    assert_indent_guides(
13095        0..4,
13096        vec![
13097            indent_guide(buffer_id, 1, 3, 0),
13098            indent_guide(buffer_id, 2, 2, 1),
13099        ],
13100        Some(vec![0]),
13101        &mut cx,
13102    );
13103}
13104
13105#[gpui::test]
13106async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13107    let (buffer_id, mut cx) = setup_indent_guides_editor(
13108        &"
13109    fn main() {
13110        let a = 1;
13111
13112        let b = 2;
13113    }"
13114        .unindent(),
13115        cx,
13116    )
13117    .await;
13118
13119    cx.update_editor(|editor, cx| {
13120        editor.change_selections(None, cx, |s| {
13121            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13122        });
13123    });
13124
13125    assert_indent_guides(
13126        0..5,
13127        vec![indent_guide(buffer_id, 1, 3, 0)],
13128        Some(vec![0]),
13129        &mut cx,
13130    );
13131}
13132
13133#[gpui::test]
13134async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13135    let (buffer_id, mut cx) = setup_indent_guides_editor(
13136        &"
13137    def m:
13138        a = 1
13139        pass"
13140            .unindent(),
13141        cx,
13142    )
13143    .await;
13144
13145    cx.update_editor(|editor, cx| {
13146        editor.change_selections(None, cx, |s| {
13147            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13148        });
13149    });
13150
13151    assert_indent_guides(
13152        0..3,
13153        vec![indent_guide(buffer_id, 1, 2, 0)],
13154        Some(vec![0]),
13155        &mut cx,
13156    );
13157}
13158
13159#[gpui::test]
13160fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13161    init_test(cx, |_| {});
13162
13163    let editor = cx.add_window(|cx| {
13164        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13165        build_editor(buffer, cx)
13166    });
13167
13168    let render_args = Arc::new(Mutex::new(None));
13169    let snapshot = editor
13170        .update(cx, |editor, cx| {
13171            let snapshot = editor.buffer().read(cx).snapshot(cx);
13172            let range =
13173                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13174
13175            struct RenderArgs {
13176                row: MultiBufferRow,
13177                folded: bool,
13178                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13179            }
13180
13181            let crease = Crease::inline(
13182                range,
13183                FoldPlaceholder::test(),
13184                {
13185                    let toggle_callback = render_args.clone();
13186                    move |row, folded, callback, _cx| {
13187                        *toggle_callback.lock() = Some(RenderArgs {
13188                            row,
13189                            folded,
13190                            callback,
13191                        });
13192                        div()
13193                    }
13194                },
13195                |_row, _folded, _cx| div(),
13196            );
13197
13198            editor.insert_creases(Some(crease), cx);
13199            let snapshot = editor.snapshot(cx);
13200            let _div =
13201                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13202            snapshot
13203        })
13204        .unwrap();
13205
13206    let render_args = render_args.lock().take().unwrap();
13207    assert_eq!(render_args.row, MultiBufferRow(1));
13208    assert!(!render_args.folded);
13209    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13210
13211    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13212        .unwrap();
13213    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13214    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13215
13216    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13217        .unwrap();
13218    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13219    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13220}
13221
13222#[gpui::test]
13223async fn test_input_text(cx: &mut gpui::TestAppContext) {
13224    init_test(cx, |_| {});
13225    let mut cx = EditorTestContext::new(cx).await;
13226
13227    cx.set_state(
13228        &r#"ˇone
13229        two
13230
13231        three
13232        fourˇ
13233        five
13234
13235        siˇx"#
13236            .unindent(),
13237    );
13238
13239    cx.dispatch_action(HandleInput(String::new()));
13240    cx.assert_editor_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("AAAA".to_string()));
13253    cx.assert_editor_state(
13254        &r#"AAAAˇone
13255        two
13256
13257        three
13258        fourAAAAˇ
13259        five
13260
13261        siAAAAˇx"#
13262            .unindent(),
13263    );
13264}
13265
13266#[gpui::test]
13267async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13268    init_test(cx, |_| {});
13269
13270    let mut cx = EditorTestContext::new(cx).await;
13271    cx.set_state(
13272        r#"let foo = 1;
13273let foo = 2;
13274let foo = 3;
13275let fooˇ = 4;
13276let foo = 5;
13277let foo = 6;
13278let foo = 7;
13279let foo = 8;
13280let foo = 9;
13281let foo = 10;
13282let foo = 11;
13283let foo = 12;
13284let foo = 13;
13285let foo = 14;
13286let foo = 15;"#,
13287    );
13288
13289    cx.update_editor(|e, cx| {
13290        assert_eq!(
13291            e.next_scroll_position,
13292            NextScrollCursorCenterTopBottom::Center,
13293            "Default next scroll direction is center",
13294        );
13295
13296        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13297        assert_eq!(
13298            e.next_scroll_position,
13299            NextScrollCursorCenterTopBottom::Top,
13300            "After center, next scroll direction should be top",
13301        );
13302
13303        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13304        assert_eq!(
13305            e.next_scroll_position,
13306            NextScrollCursorCenterTopBottom::Bottom,
13307            "After top, next scroll direction should be bottom",
13308        );
13309
13310        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13311        assert_eq!(
13312            e.next_scroll_position,
13313            NextScrollCursorCenterTopBottom::Center,
13314            "After bottom, scrolling should start over",
13315        );
13316
13317        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13318        assert_eq!(
13319            e.next_scroll_position,
13320            NextScrollCursorCenterTopBottom::Top,
13321            "Scrolling continues if retriggered fast enough"
13322        );
13323    });
13324
13325    cx.executor()
13326        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13327    cx.executor().run_until_parked();
13328    cx.update_editor(|e, _| {
13329        assert_eq!(
13330            e.next_scroll_position,
13331            NextScrollCursorCenterTopBottom::Center,
13332            "If scrolling is not triggered fast enough, it should reset"
13333        );
13334    });
13335}
13336
13337#[gpui::test]
13338async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13339    init_test(cx, |_| {});
13340    let mut cx = EditorLspTestContext::new_rust(
13341        lsp::ServerCapabilities {
13342            definition_provider: Some(lsp::OneOf::Left(true)),
13343            references_provider: Some(lsp::OneOf::Left(true)),
13344            ..lsp::ServerCapabilities::default()
13345        },
13346        cx,
13347    )
13348    .await;
13349
13350    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13351        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13352            move |params, _| async move {
13353                if empty_go_to_definition {
13354                    Ok(None)
13355                } else {
13356                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13357                        uri: params.text_document_position_params.text_document.uri,
13358                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13359                    })))
13360                }
13361            },
13362        );
13363        let references =
13364            cx.lsp
13365                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13366                    Ok(Some(vec![lsp::Location {
13367                        uri: params.text_document_position.text_document.uri,
13368                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13369                    }]))
13370                });
13371        (go_to_definition, references)
13372    };
13373
13374    cx.set_state(
13375        &r#"fn one() {
13376            let mut a = ˇtwo();
13377        }
13378
13379        fn two() {}"#
13380            .unindent(),
13381    );
13382    set_up_lsp_handlers(false, &mut cx);
13383    let navigated = cx
13384        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13385        .await
13386        .expect("Failed to navigate to definition");
13387    assert_eq!(
13388        navigated,
13389        Navigated::Yes,
13390        "Should have navigated to definition from the GetDefinition response"
13391    );
13392    cx.assert_editor_state(
13393        &r#"fn one() {
13394            let mut a = two();
13395        }
13396
13397        fn «twoˇ»() {}"#
13398            .unindent(),
13399    );
13400
13401    let editors = cx.update_workspace(|workspace, cx| {
13402        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13403    });
13404    cx.update_editor(|_, test_editor_cx| {
13405        assert_eq!(
13406            editors.len(),
13407            1,
13408            "Initially, only one, test, editor should be open in the workspace"
13409        );
13410        assert_eq!(
13411            test_editor_cx.view(),
13412            editors.last().expect("Asserted len is 1")
13413        );
13414    });
13415
13416    set_up_lsp_handlers(true, &mut cx);
13417    let navigated = cx
13418        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13419        .await
13420        .expect("Failed to navigate to lookup references");
13421    assert_eq!(
13422        navigated,
13423        Navigated::Yes,
13424        "Should have navigated to references as a fallback after empty GoToDefinition response"
13425    );
13426    // We should not change the selections in the existing file,
13427    // if opening another milti buffer with the references
13428    cx.assert_editor_state(
13429        &r#"fn one() {
13430            let mut a = two();
13431        }
13432
13433        fn «twoˇ»() {}"#
13434            .unindent(),
13435    );
13436    let editors = cx.update_workspace(|workspace, cx| {
13437        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13438    });
13439    cx.update_editor(|_, test_editor_cx| {
13440        assert_eq!(
13441            editors.len(),
13442            2,
13443            "After falling back to references search, we open a new editor with the results"
13444        );
13445        let references_fallback_text = editors
13446            .into_iter()
13447            .find(|new_editor| new_editor != test_editor_cx.view())
13448            .expect("Should have one non-test editor now")
13449            .read(test_editor_cx)
13450            .text(test_editor_cx);
13451        assert_eq!(
13452            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13453            "Should use the range from the references response and not the GoToDefinition one"
13454        );
13455    });
13456}
13457
13458#[gpui::test]
13459async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13460    init_test(cx, |_| {});
13461
13462    let language = Arc::new(Language::new(
13463        LanguageConfig::default(),
13464        Some(tree_sitter_rust::LANGUAGE.into()),
13465    ));
13466
13467    let text = r#"
13468        #[cfg(test)]
13469        mod tests() {
13470            #[test]
13471            fn runnable_1() {
13472                let a = 1;
13473            }
13474
13475            #[test]
13476            fn runnable_2() {
13477                let a = 1;
13478                let b = 2;
13479            }
13480        }
13481    "#
13482    .unindent();
13483
13484    let fs = FakeFs::new(cx.executor());
13485    fs.insert_file("/file.rs", Default::default()).await;
13486
13487    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13488    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13489    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13490    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13491    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13492
13493    let editor = cx.new_view(|cx| {
13494        Editor::new(
13495            EditorMode::Full,
13496            multi_buffer,
13497            Some(project.clone()),
13498            true,
13499            cx,
13500        )
13501    });
13502
13503    editor.update(cx, |editor, cx| {
13504        editor.tasks.insert(
13505            (buffer.read(cx).remote_id(), 3),
13506            RunnableTasks {
13507                templates: vec![],
13508                offset: MultiBufferOffset(43),
13509                column: 0,
13510                extra_variables: HashMap::default(),
13511                context_range: BufferOffset(43)..BufferOffset(85),
13512            },
13513        );
13514        editor.tasks.insert(
13515            (buffer.read(cx).remote_id(), 8),
13516            RunnableTasks {
13517                templates: vec![],
13518                offset: MultiBufferOffset(86),
13519                column: 0,
13520                extra_variables: HashMap::default(),
13521                context_range: BufferOffset(86)..BufferOffset(191),
13522            },
13523        );
13524
13525        // Test finding task when cursor is inside function body
13526        editor.change_selections(None, cx, |s| {
13527            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13528        });
13529        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13530        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13531
13532        // Test finding task when cursor is on function name
13533        editor.change_selections(None, cx, |s| {
13534            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13535        });
13536        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13537        assert_eq!(row, 8, "Should find task when cursor is on function name");
13538    });
13539}
13540
13541fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13542    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13543    point..point
13544}
13545
13546fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13547    let (text, ranges) = marked_text_ranges(marked_text, true);
13548    assert_eq!(view.text(cx), text);
13549    assert_eq!(
13550        view.selections.ranges(cx),
13551        ranges,
13552        "Assert selections are {}",
13553        marked_text
13554    );
13555}
13556
13557pub fn handle_signature_help_request(
13558    cx: &mut EditorLspTestContext,
13559    mocked_response: lsp::SignatureHelp,
13560) -> impl Future<Output = ()> {
13561    let mut request =
13562        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13563            let mocked_response = mocked_response.clone();
13564            async move { Ok(Some(mocked_response)) }
13565        });
13566
13567    async move {
13568        request.next().await;
13569    }
13570}
13571
13572/// Handle completion request passing a marked string specifying where the completion
13573/// should be triggered from using '|' character, what range should be replaced, and what completions
13574/// should be returned using '<' and '>' to delimit the range
13575pub fn handle_completion_request(
13576    cx: &mut EditorLspTestContext,
13577    marked_string: &str,
13578    completions: Vec<&'static str>,
13579    counter: Arc<AtomicUsize>,
13580) -> impl Future<Output = ()> {
13581    let complete_from_marker: TextRangeMarker = '|'.into();
13582    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13583    let (_, mut marked_ranges) = marked_text_ranges_by(
13584        marked_string,
13585        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13586    );
13587
13588    let complete_from_position =
13589        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13590    let replace_range =
13591        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13592
13593    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13594        let completions = completions.clone();
13595        counter.fetch_add(1, atomic::Ordering::Release);
13596        async move {
13597            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13598            assert_eq!(
13599                params.text_document_position.position,
13600                complete_from_position
13601            );
13602            Ok(Some(lsp::CompletionResponse::Array(
13603                completions
13604                    .iter()
13605                    .map(|completion_text| lsp::CompletionItem {
13606                        label: completion_text.to_string(),
13607                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13608                            range: replace_range,
13609                            new_text: completion_text.to_string(),
13610                        })),
13611                        ..Default::default()
13612                    })
13613                    .collect(),
13614            )))
13615        }
13616    });
13617
13618    async move {
13619        request.next().await;
13620    }
13621}
13622
13623fn handle_resolve_completion_request(
13624    cx: &mut EditorLspTestContext,
13625    edits: Option<Vec<(&'static str, &'static str)>>,
13626) -> impl Future<Output = ()> {
13627    let edits = edits.map(|edits| {
13628        edits
13629            .iter()
13630            .map(|(marked_string, new_text)| {
13631                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13632                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13633                lsp::TextEdit::new(replace_range, new_text.to_string())
13634            })
13635            .collect::<Vec<_>>()
13636    });
13637
13638    let mut request =
13639        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13640            let edits = edits.clone();
13641            async move {
13642                Ok(lsp::CompletionItem {
13643                    additional_text_edits: edits,
13644                    ..Default::default()
13645                })
13646            }
13647        });
13648
13649    async move {
13650        request.next().await;
13651    }
13652}
13653
13654pub(crate) fn update_test_language_settings(
13655    cx: &mut TestAppContext,
13656    f: impl Fn(&mut AllLanguageSettingsContent),
13657) {
13658    cx.update(|cx| {
13659        SettingsStore::update_global(cx, |store, cx| {
13660            store.update_user_settings::<AllLanguageSettings>(cx, f);
13661        });
13662    });
13663}
13664
13665pub(crate) fn update_test_project_settings(
13666    cx: &mut TestAppContext,
13667    f: impl Fn(&mut ProjectSettings),
13668) {
13669    cx.update(|cx| {
13670        SettingsStore::update_global(cx, |store, cx| {
13671            store.update_user_settings::<ProjectSettings>(cx, f);
13672        });
13673    });
13674}
13675
13676pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13677    cx.update(|cx| {
13678        assets::Assets.load_test_fonts(cx);
13679        let store = SettingsStore::test(cx);
13680        cx.set_global(store);
13681        theme::init(theme::LoadThemes::JustBase, cx);
13682        release_channel::init(SemanticVersion::default(), cx);
13683        client::init_settings(cx);
13684        language::init(cx);
13685        Project::init_settings(cx);
13686        workspace::init_settings(cx);
13687        crate::init(cx);
13688    });
13689
13690    update_test_language_settings(cx, f);
13691}
13692
13693pub(crate) fn rust_lang() -> Arc<Language> {
13694    Arc::new(Language::new(
13695        LanguageConfig {
13696            name: "Rust".into(),
13697            matcher: LanguageMatcher {
13698                path_suffixes: vec!["rs".to_string()],
13699                ..Default::default()
13700            },
13701            ..Default::default()
13702        },
13703        Some(tree_sitter_rust::LANGUAGE.into()),
13704    ))
13705}
13706
13707#[track_caller]
13708fn assert_hunk_revert(
13709    not_reverted_text_with_selections: &str,
13710    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13711    expected_reverted_text_with_selections: &str,
13712    base_text: &str,
13713    cx: &mut EditorLspTestContext,
13714) {
13715    cx.set_state(not_reverted_text_with_selections);
13716    cx.update_editor(|editor, cx| {
13717        editor
13718            .buffer()
13719            .read(cx)
13720            .as_singleton()
13721            .unwrap()
13722            .update(cx, |buffer, cx| {
13723                buffer.set_diff_base(Some(base_text.into()), cx);
13724            });
13725    });
13726    cx.executor().run_until_parked();
13727
13728    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13729        let snapshot = editor.buffer().read(cx).snapshot(cx);
13730        let reverted_hunk_statuses = snapshot
13731            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13732            .map(|hunk| hunk_status(&hunk))
13733            .collect::<Vec<_>>();
13734
13735        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13736        reverted_hunk_statuses
13737    });
13738    cx.executor().run_until_parked();
13739    cx.assert_editor_state(expected_reverted_text_with_selections);
13740    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13741}