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
 1402        // moving above start of document should move selection to start of document,
 1403        // but the next move down should still be at the original goal_x
 1404        view.move_up(&MoveUp, cx);
 1405        assert_eq!(
 1406            view.selections.display_ranges(cx),
 1407            &[empty_range(0, "".len())]
 1408        );
 1409
 1410        view.move_down(&MoveDown, cx);
 1411        assert_eq!(
 1412            view.selections.display_ranges(cx),
 1413            &[empty_range(1, "abcd".len())]
 1414        );
 1415
 1416        view.move_down(&MoveDown, cx);
 1417        assert_eq!(
 1418            view.selections.display_ranges(cx),
 1419            &[empty_range(2, "αβγ".len())]
 1420        );
 1421
 1422        view.move_down(&MoveDown, cx);
 1423        assert_eq!(
 1424            view.selections.display_ranges(cx),
 1425            &[empty_range(3, "abcd".len())]
 1426        );
 1427
 1428        view.move_down(&MoveDown, cx);
 1429        assert_eq!(
 1430            view.selections.display_ranges(cx),
 1431            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1432        );
 1433
 1434        // moving past end of document should not change goal_x
 1435        view.move_down(&MoveDown, cx);
 1436        assert_eq!(
 1437            view.selections.display_ranges(cx),
 1438            &[empty_range(5, "".len())]
 1439        );
 1440
 1441        view.move_down(&MoveDown, cx);
 1442        assert_eq!(
 1443            view.selections.display_ranges(cx),
 1444            &[empty_range(5, "".len())]
 1445        );
 1446
 1447        view.move_up(&MoveUp, cx);
 1448        assert_eq!(
 1449            view.selections.display_ranges(cx),
 1450            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1451        );
 1452
 1453        view.move_up(&MoveUp, cx);
 1454        assert_eq!(
 1455            view.selections.display_ranges(cx),
 1456            &[empty_range(3, "abcd".len())]
 1457        );
 1458
 1459        view.move_up(&MoveUp, cx);
 1460        assert_eq!(
 1461            view.selections.display_ranges(cx),
 1462            &[empty_range(2, "αβγ".len())]
 1463        );
 1464    });
 1465}
 1466
 1467#[gpui::test]
 1468fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1469    init_test(cx, |_| {});
 1470    let move_to_beg = MoveToBeginningOfLine {
 1471        stop_at_soft_wraps: true,
 1472    };
 1473
 1474    let move_to_end = MoveToEndOfLine {
 1475        stop_at_soft_wraps: true,
 1476    };
 1477
 1478    let view = cx.add_window(|cx| {
 1479        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1480        build_editor(buffer, cx)
 1481    });
 1482    _ = view.update(cx, |view, cx| {
 1483        view.change_selections(None, cx, |s| {
 1484            s.select_display_ranges([
 1485                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1486                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1487            ]);
 1488        });
 1489    });
 1490
 1491    _ = view.update(cx, |view, cx| {
 1492        view.move_to_beginning_of_line(&move_to_beg, cx);
 1493        assert_eq!(
 1494            view.selections.display_ranges(cx),
 1495            &[
 1496                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1497                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1498            ]
 1499        );
 1500    });
 1501
 1502    _ = view.update(cx, |view, cx| {
 1503        view.move_to_beginning_of_line(&move_to_beg, cx);
 1504        assert_eq!(
 1505            view.selections.display_ranges(cx),
 1506            &[
 1507                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1508                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1509            ]
 1510        );
 1511    });
 1512
 1513    _ = view.update(cx, |view, cx| {
 1514        view.move_to_beginning_of_line(&move_to_beg, cx);
 1515        assert_eq!(
 1516            view.selections.display_ranges(cx),
 1517            &[
 1518                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1519                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1520            ]
 1521        );
 1522    });
 1523
 1524    _ = view.update(cx, |view, cx| {
 1525        view.move_to_end_of_line(&move_to_end, cx);
 1526        assert_eq!(
 1527            view.selections.display_ranges(cx),
 1528            &[
 1529                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1530                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1531            ]
 1532        );
 1533    });
 1534
 1535    // Moving to the end of line again is a no-op.
 1536    _ = view.update(cx, |view, cx| {
 1537        view.move_to_end_of_line(&move_to_end, cx);
 1538        assert_eq!(
 1539            view.selections.display_ranges(cx),
 1540            &[
 1541                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1542                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1543            ]
 1544        );
 1545    });
 1546
 1547    _ = view.update(cx, |view, cx| {
 1548        view.move_left(&MoveLeft, cx);
 1549        view.select_to_beginning_of_line(
 1550            &SelectToBeginningOfLine {
 1551                stop_at_soft_wraps: true,
 1552            },
 1553            cx,
 1554        );
 1555        assert_eq!(
 1556            view.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = view.update(cx, |view, cx| {
 1565        view.select_to_beginning_of_line(
 1566            &SelectToBeginningOfLine {
 1567                stop_at_soft_wraps: true,
 1568            },
 1569            cx,
 1570        );
 1571        assert_eq!(
 1572            view.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1575                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = view.update(cx, |view, cx| {
 1581        view.select_to_beginning_of_line(
 1582            &SelectToBeginningOfLine {
 1583                stop_at_soft_wraps: true,
 1584            },
 1585            cx,
 1586        );
 1587        assert_eq!(
 1588            view.selections.display_ranges(cx),
 1589            &[
 1590                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1591                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1592            ]
 1593        );
 1594    });
 1595
 1596    _ = view.update(cx, |view, cx| {
 1597        view.select_to_end_of_line(
 1598            &SelectToEndOfLine {
 1599                stop_at_soft_wraps: true,
 1600            },
 1601            cx,
 1602        );
 1603        assert_eq!(
 1604            view.selections.display_ranges(cx),
 1605            &[
 1606                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1607                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1608            ]
 1609        );
 1610    });
 1611
 1612    _ = view.update(cx, |view, cx| {
 1613        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1614        assert_eq!(view.display_text(cx), "ab\n  de");
 1615        assert_eq!(
 1616            view.selections.display_ranges(cx),
 1617            &[
 1618                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1619                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1620            ]
 1621        );
 1622    });
 1623
 1624    _ = view.update(cx, |view, cx| {
 1625        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1626        assert_eq!(view.display_text(cx), "\n");
 1627        assert_eq!(
 1628            view.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635}
 1636
 1637#[gpui::test]
 1638fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1639    init_test(cx, |_| {});
 1640    let move_to_beg = MoveToBeginningOfLine {
 1641        stop_at_soft_wraps: false,
 1642    };
 1643
 1644    let move_to_end = MoveToEndOfLine {
 1645        stop_at_soft_wraps: false,
 1646    };
 1647
 1648    let view = cx.add_window(|cx| {
 1649        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1650        build_editor(buffer, cx)
 1651    });
 1652
 1653    _ = view.update(cx, |view, cx| {
 1654        view.set_wrap_width(Some(140.0.into()), cx);
 1655
 1656        // We expect the following lines after wrapping
 1657        // ```
 1658        // thequickbrownfox
 1659        // jumpedoverthelazydo
 1660        // gs
 1661        // ```
 1662        // The final `gs` was soft-wrapped onto a new line.
 1663        assert_eq!(
 1664            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1665            view.display_text(cx),
 1666        );
 1667
 1668        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1669        // Start the cursor at the `k` on the first line
 1670        view.change_selections(None, cx, |s| {
 1671            s.select_display_ranges([
 1672                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1673            ]);
 1674        });
 1675
 1676        // Moving to the beginning of the line should put us at the beginning of the line.
 1677        view.move_to_beginning_of_line(&move_to_beg, cx);
 1678        assert_eq!(
 1679            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1680            view.selections.display_ranges(cx)
 1681        );
 1682
 1683        // Moving to the end of the line should put us at the end of the line.
 1684        view.move_to_end_of_line(&move_to_end, cx);
 1685        assert_eq!(
 1686            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1687            view.selections.display_ranges(cx)
 1688        );
 1689
 1690        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1691        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1692        view.change_selections(None, cx, |s| {
 1693            s.select_display_ranges([
 1694                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1695            ]);
 1696        });
 1697
 1698        // Moving to the beginning of the line should put us at the start of the second line of
 1699        // display text, i.e., the `j`.
 1700        view.move_to_beginning_of_line(&move_to_beg, cx);
 1701        assert_eq!(
 1702            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1703            view.selections.display_ranges(cx)
 1704        );
 1705
 1706        // Moving to the beginning of the line again should be a no-op.
 1707        view.move_to_beginning_of_line(&move_to_beg, cx);
 1708        assert_eq!(
 1709            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1710            view.selections.display_ranges(cx)
 1711        );
 1712
 1713        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1714        // next display line.
 1715        view.move_to_end_of_line(&move_to_end, cx);
 1716        assert_eq!(
 1717            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1718            view.selections.display_ranges(cx)
 1719        );
 1720
 1721        // Moving to the end of the line again should be a no-op.
 1722        view.move_to_end_of_line(&move_to_end, cx);
 1723        assert_eq!(
 1724            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1725            view.selections.display_ranges(cx)
 1726        );
 1727    });
 1728}
 1729
 1730#[gpui::test]
 1731fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1732    init_test(cx, |_| {});
 1733
 1734    let view = cx.add_window(|cx| {
 1735        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1736        build_editor(buffer, cx)
 1737    });
 1738    _ = view.update(cx, |view, cx| {
 1739        view.change_selections(None, cx, |s| {
 1740            s.select_display_ranges([
 1741                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1742                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1743            ])
 1744        });
 1745
 1746        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1747        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1748
 1749        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1750        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1751
 1752        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1753        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1754
 1755        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1756        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1757
 1758        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1759        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1760
 1761        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1762        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1763
 1764        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1765        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1766
 1767        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1768        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1769
 1770        view.move_right(&MoveRight, cx);
 1771        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1772        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1773
 1774        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1775        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1776
 1777        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1778        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1779    });
 1780}
 1781
 1782#[gpui::test]
 1783fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1784    init_test(cx, |_| {});
 1785
 1786    let view = cx.add_window(|cx| {
 1787        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1788        build_editor(buffer, cx)
 1789    });
 1790
 1791    _ = view.update(cx, |view, cx| {
 1792        view.set_wrap_width(Some(140.0.into()), cx);
 1793        assert_eq!(
 1794            view.display_text(cx),
 1795            "use one::{\n    two::three::\n    four::five\n};"
 1796        );
 1797
 1798        view.change_selections(None, cx, |s| {
 1799            s.select_display_ranges([
 1800                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1801            ]);
 1802        });
 1803
 1804        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1805        assert_eq!(
 1806            view.selections.display_ranges(cx),
 1807            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1808        );
 1809
 1810        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1811        assert_eq!(
 1812            view.selections.display_ranges(cx),
 1813            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1814        );
 1815
 1816        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1817        assert_eq!(
 1818            view.selections.display_ranges(cx),
 1819            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1820        );
 1821
 1822        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1823        assert_eq!(
 1824            view.selections.display_ranges(cx),
 1825            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1826        );
 1827
 1828        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1829        assert_eq!(
 1830            view.selections.display_ranges(cx),
 1831            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1832        );
 1833
 1834        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1835        assert_eq!(
 1836            view.selections.display_ranges(cx),
 1837            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1838        );
 1839    });
 1840}
 1841
 1842#[gpui::test]
 1843async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1844    init_test(cx, |_| {});
 1845    let mut cx = EditorTestContext::new(cx).await;
 1846
 1847    let line_height = cx.editor(|editor, cx| {
 1848        editor
 1849            .style()
 1850            .unwrap()
 1851            .text
 1852            .line_height_in_pixels(cx.rem_size())
 1853    });
 1854    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1855
 1856    cx.set_state(
 1857        &r#"ˇone
 1858        two
 1859
 1860        three
 1861        fourˇ
 1862        five
 1863
 1864        six"#
 1865            .unindent(),
 1866    );
 1867
 1868    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1869    cx.assert_editor_state(
 1870        &r#"one
 1871        two
 1872        ˇ
 1873        three
 1874        four
 1875        five
 1876        ˇ
 1877        six"#
 1878            .unindent(),
 1879    );
 1880
 1881    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1882    cx.assert_editor_state(
 1883        &r#"one
 1884        two
 1885
 1886        three
 1887        four
 1888        five
 1889        ˇ
 1890        sixˇ"#
 1891            .unindent(),
 1892    );
 1893
 1894    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1895    cx.assert_editor_state(
 1896        &r#"one
 1897        two
 1898
 1899        three
 1900        four
 1901        five
 1902
 1903        sixˇ"#
 1904            .unindent(),
 1905    );
 1906
 1907    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1908    cx.assert_editor_state(
 1909        &r#"one
 1910        two
 1911
 1912        three
 1913        four
 1914        five
 1915        ˇ
 1916        six"#
 1917            .unindent(),
 1918    );
 1919
 1920    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1921    cx.assert_editor_state(
 1922        &r#"one
 1923        two
 1924        ˇ
 1925        three
 1926        four
 1927        five
 1928
 1929        six"#
 1930            .unindent(),
 1931    );
 1932
 1933    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1934    cx.assert_editor_state(
 1935        &r#"ˇone
 1936        two
 1937
 1938        three
 1939        four
 1940        five
 1941
 1942        six"#
 1943            .unindent(),
 1944    );
 1945}
 1946
 1947#[gpui::test]
 1948async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1949    init_test(cx, |_| {});
 1950    let mut cx = EditorTestContext::new(cx).await;
 1951    let line_height = cx.editor(|editor, cx| {
 1952        editor
 1953            .style()
 1954            .unwrap()
 1955            .text
 1956            .line_height_in_pixels(cx.rem_size())
 1957    });
 1958    let window = cx.window;
 1959    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1960
 1961    cx.set_state(
 1962        r#"ˇone
 1963        two
 1964        three
 1965        four
 1966        five
 1967        six
 1968        seven
 1969        eight
 1970        nine
 1971        ten
 1972        "#,
 1973    );
 1974
 1975    cx.update_editor(|editor, cx| {
 1976        assert_eq!(
 1977            editor.snapshot(cx).scroll_position(),
 1978            gpui::Point::new(0., 0.)
 1979        );
 1980        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1981        assert_eq!(
 1982            editor.snapshot(cx).scroll_position(),
 1983            gpui::Point::new(0., 3.)
 1984        );
 1985        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1986        assert_eq!(
 1987            editor.snapshot(cx).scroll_position(),
 1988            gpui::Point::new(0., 6.)
 1989        );
 1990        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1991        assert_eq!(
 1992            editor.snapshot(cx).scroll_position(),
 1993            gpui::Point::new(0., 3.)
 1994        );
 1995
 1996        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1997        assert_eq!(
 1998            editor.snapshot(cx).scroll_position(),
 1999            gpui::Point::new(0., 1.)
 2000        );
 2001        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2002        assert_eq!(
 2003            editor.snapshot(cx).scroll_position(),
 2004            gpui::Point::new(0., 3.)
 2005        );
 2006    });
 2007}
 2008
 2009#[gpui::test]
 2010async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2011    init_test(cx, |_| {});
 2012    let mut cx = EditorTestContext::new(cx).await;
 2013
 2014    let line_height = cx.update_editor(|editor, cx| {
 2015        editor.set_vertical_scroll_margin(2, cx);
 2016        editor
 2017            .style()
 2018            .unwrap()
 2019            .text
 2020            .line_height_in_pixels(cx.rem_size())
 2021    });
 2022    let window = cx.window;
 2023    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2024
 2025    cx.set_state(
 2026        r#"ˇone
 2027            two
 2028            three
 2029            four
 2030            five
 2031            six
 2032            seven
 2033            eight
 2034            nine
 2035            ten
 2036        "#,
 2037    );
 2038    cx.update_editor(|editor, cx| {
 2039        assert_eq!(
 2040            editor.snapshot(cx).scroll_position(),
 2041            gpui::Point::new(0., 0.0)
 2042        );
 2043    });
 2044
 2045    // Add a cursor below the visible area. Since both cursors cannot fit
 2046    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2047    // allows the vertical scroll margin below that cursor.
 2048    cx.update_editor(|editor, cx| {
 2049        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2050            selections.select_ranges([
 2051                Point::new(0, 0)..Point::new(0, 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., 3.0)
 2060        );
 2061    });
 2062
 2063    // Move down. The editor cursor scrolls down to track the newest cursor.
 2064    cx.update_editor(|editor, cx| {
 2065        editor.move_down(&Default::default(), cx);
 2066    });
 2067    cx.update_editor(|editor, cx| {
 2068        assert_eq!(
 2069            editor.snapshot(cx).scroll_position(),
 2070            gpui::Point::new(0., 4.0)
 2071        );
 2072    });
 2073
 2074    // Add a cursor above the visible area. Since both cursors fit on screen,
 2075    // the editor scrolls to show both.
 2076    cx.update_editor(|editor, cx| {
 2077        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2078            selections.select_ranges([
 2079                Point::new(1, 0)..Point::new(1, 0),
 2080                Point::new(6, 0)..Point::new(6, 0),
 2081            ]);
 2082        })
 2083    });
 2084    cx.update_editor(|editor, cx| {
 2085        assert_eq!(
 2086            editor.snapshot(cx).scroll_position(),
 2087            gpui::Point::new(0., 1.0)
 2088        );
 2089    });
 2090}
 2091
 2092#[gpui::test]
 2093async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2094    init_test(cx, |_| {});
 2095    let mut cx = EditorTestContext::new(cx).await;
 2096
 2097    let line_height = cx.editor(|editor, cx| {
 2098        editor
 2099            .style()
 2100            .unwrap()
 2101            .text
 2102            .line_height_in_pixels(cx.rem_size())
 2103    });
 2104    let window = cx.window;
 2105    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2106    cx.set_state(
 2107        &r#"
 2108        ˇone
 2109        two
 2110        threeˇ
 2111        four
 2112        five
 2113        six
 2114        seven
 2115        eight
 2116        nine
 2117        ten
 2118        "#
 2119        .unindent(),
 2120    );
 2121
 2122    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2123    cx.assert_editor_state(
 2124        &r#"
 2125        one
 2126        two
 2127        three
 2128        ˇfour
 2129        five
 2130        sixˇ
 2131        seven
 2132        eight
 2133        nine
 2134        ten
 2135        "#
 2136        .unindent(),
 2137    );
 2138
 2139    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2140    cx.assert_editor_state(
 2141        &r#"
 2142        one
 2143        two
 2144        three
 2145        four
 2146        five
 2147        six
 2148        ˇseven
 2149        eight
 2150        nineˇ
 2151        ten
 2152        "#
 2153        .unindent(),
 2154    );
 2155
 2156    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2157    cx.assert_editor_state(
 2158        &r#"
 2159        one
 2160        two
 2161        three
 2162        ˇfour
 2163        five
 2164        sixˇ
 2165        seven
 2166        eight
 2167        nine
 2168        ten
 2169        "#
 2170        .unindent(),
 2171    );
 2172
 2173    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2174    cx.assert_editor_state(
 2175        &r#"
 2176        ˇone
 2177        two
 2178        threeˇ
 2179        four
 2180        five
 2181        six
 2182        seven
 2183        eight
 2184        nine
 2185        ten
 2186        "#
 2187        .unindent(),
 2188    );
 2189
 2190    // Test select collapsing
 2191    cx.update_editor(|editor, cx| {
 2192        editor.move_page_down(&MovePageDown::default(), cx);
 2193        editor.move_page_down(&MovePageDown::default(), cx);
 2194        editor.move_page_down(&MovePageDown::default(), cx);
 2195    });
 2196    cx.assert_editor_state(
 2197        &r#"
 2198        one
 2199        two
 2200        three
 2201        four
 2202        five
 2203        six
 2204        seven
 2205        eight
 2206        nine
 2207        ˇten
 2208        ˇ"#
 2209        .unindent(),
 2210    );
 2211}
 2212
 2213#[gpui::test]
 2214async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2215    init_test(cx, |_| {});
 2216    let mut cx = EditorTestContext::new(cx).await;
 2217    cx.set_state("one «two threeˇ» four");
 2218    cx.update_editor(|editor, cx| {
 2219        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2220        assert_eq!(editor.text(cx), " four");
 2221    });
 2222}
 2223
 2224#[gpui::test]
 2225fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2226    init_test(cx, |_| {});
 2227
 2228    let view = cx.add_window(|cx| {
 2229        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2230        build_editor(buffer.clone(), cx)
 2231    });
 2232
 2233    _ = view.update(cx, |view, cx| {
 2234        view.change_selections(None, cx, |s| {
 2235            s.select_display_ranges([
 2236                // an empty selection - the preceding word fragment is deleted
 2237                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2238                // characters selected - they are deleted
 2239                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2240            ])
 2241        });
 2242        view.delete_to_previous_word_start(
 2243            &DeleteToPreviousWordStart {
 2244                ignore_newlines: false,
 2245            },
 2246            cx,
 2247        );
 2248        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2249    });
 2250
 2251    _ = view.update(cx, |view, cx| {
 2252        view.change_selections(None, cx, |s| {
 2253            s.select_display_ranges([
 2254                // an empty selection - the following word fragment is deleted
 2255                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2256                // characters selected - they are deleted
 2257                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2258            ])
 2259        });
 2260        view.delete_to_next_word_end(
 2261            &DeleteToNextWordEnd {
 2262                ignore_newlines: false,
 2263            },
 2264            cx,
 2265        );
 2266        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2267    });
 2268}
 2269
 2270#[gpui::test]
 2271fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2272    init_test(cx, |_| {});
 2273
 2274    let view = cx.add_window(|cx| {
 2275        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2276        build_editor(buffer.clone(), cx)
 2277    });
 2278    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2279        ignore_newlines: false,
 2280    };
 2281    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2282        ignore_newlines: true,
 2283    };
 2284
 2285    _ = view.update(cx, |view, cx| {
 2286        view.change_selections(None, cx, |s| {
 2287            s.select_display_ranges([
 2288                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2289            ])
 2290        });
 2291        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2292        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2293        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2294        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2295        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2296        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2297        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2298        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2299        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2300        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2301        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2302        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2303    });
 2304}
 2305
 2306#[gpui::test]
 2307fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2308    init_test(cx, |_| {});
 2309
 2310    let view = cx.add_window(|cx| {
 2311        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2312        build_editor(buffer.clone(), cx)
 2313    });
 2314    let del_to_next_word_end = DeleteToNextWordEnd {
 2315        ignore_newlines: false,
 2316    };
 2317    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2318        ignore_newlines: true,
 2319    };
 2320
 2321    _ = view.update(cx, |view, cx| {
 2322        view.change_selections(None, cx, |s| {
 2323            s.select_display_ranges([
 2324                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2325            ])
 2326        });
 2327        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2328        assert_eq!(
 2329            view.buffer.read(cx).read(cx).text(),
 2330            "one\n   two\nthree\n   four"
 2331        );
 2332        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2333        assert_eq!(
 2334            view.buffer.read(cx).read(cx).text(),
 2335            "\n   two\nthree\n   four"
 2336        );
 2337        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2338        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2339        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2340        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2341        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2342        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2343        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2344        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2345    });
 2346}
 2347
 2348#[gpui::test]
 2349fn test_newline(cx: &mut TestAppContext) {
 2350    init_test(cx, |_| {});
 2351
 2352    let view = cx.add_window(|cx| {
 2353        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2354        build_editor(buffer.clone(), cx)
 2355    });
 2356
 2357    _ = view.update(cx, |view, cx| {
 2358        view.change_selections(None, cx, |s| {
 2359            s.select_display_ranges([
 2360                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2361                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2362                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2363            ])
 2364        });
 2365
 2366        view.newline(&Newline, cx);
 2367        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2368    });
 2369}
 2370
 2371#[gpui::test]
 2372fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2373    init_test(cx, |_| {});
 2374
 2375    let editor = cx.add_window(|cx| {
 2376        let buffer = MultiBuffer::build_simple(
 2377            "
 2378                a
 2379                b(
 2380                    X
 2381                )
 2382                c(
 2383                    X
 2384                )
 2385            "
 2386            .unindent()
 2387            .as_str(),
 2388            cx,
 2389        );
 2390        let mut editor = build_editor(buffer.clone(), cx);
 2391        editor.change_selections(None, cx, |s| {
 2392            s.select_ranges([
 2393                Point::new(2, 4)..Point::new(2, 5),
 2394                Point::new(5, 4)..Point::new(5, 5),
 2395            ])
 2396        });
 2397        editor
 2398    });
 2399
 2400    _ = editor.update(cx, |editor, cx| {
 2401        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2402        editor.buffer.update(cx, |buffer, cx| {
 2403            buffer.edit(
 2404                [
 2405                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2406                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2407                ],
 2408                None,
 2409                cx,
 2410            );
 2411            assert_eq!(
 2412                buffer.read(cx).text(),
 2413                "
 2414                    a
 2415                    b()
 2416                    c()
 2417                "
 2418                .unindent()
 2419            );
 2420        });
 2421        assert_eq!(
 2422            editor.selections.ranges(cx),
 2423            &[
 2424                Point::new(1, 2)..Point::new(1, 2),
 2425                Point::new(2, 2)..Point::new(2, 2),
 2426            ],
 2427        );
 2428
 2429        editor.newline(&Newline, cx);
 2430        assert_eq!(
 2431            editor.text(cx),
 2432            "
 2433                a
 2434                b(
 2435                )
 2436                c(
 2437                )
 2438            "
 2439            .unindent()
 2440        );
 2441
 2442        // The selections are moved after the inserted newlines
 2443        assert_eq!(
 2444            editor.selections.ranges(cx),
 2445            &[
 2446                Point::new(2, 0)..Point::new(2, 0),
 2447                Point::new(4, 0)..Point::new(4, 0),
 2448            ],
 2449        );
 2450    });
 2451}
 2452
 2453#[gpui::test]
 2454async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2455    init_test(cx, |settings| {
 2456        settings.defaults.tab_size = NonZeroU32::new(4)
 2457    });
 2458
 2459    let language = Arc::new(
 2460        Language::new(
 2461            LanguageConfig::default(),
 2462            Some(tree_sitter_rust::LANGUAGE.into()),
 2463        )
 2464        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2465        .unwrap(),
 2466    );
 2467
 2468    let mut cx = EditorTestContext::new(cx).await;
 2469    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2470    cx.set_state(indoc! {"
 2471        const a: ˇA = (
 2472 2473                «const_functionˇ»(ˇ),
 2474                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2475 2476        ˇ);ˇ
 2477    "});
 2478
 2479    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2480    cx.assert_editor_state(indoc! {"
 2481        ˇ
 2482        const a: A = (
 2483            ˇ
 2484            (
 2485                ˇ
 2486                ˇ
 2487                const_function(),
 2488                ˇ
 2489                ˇ
 2490                ˇ
 2491                ˇ
 2492                something_else,
 2493                ˇ
 2494            )
 2495            ˇ
 2496            ˇ
 2497        );
 2498    "});
 2499}
 2500
 2501#[gpui::test]
 2502async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2503    init_test(cx, |settings| {
 2504        settings.defaults.tab_size = NonZeroU32::new(4)
 2505    });
 2506
 2507    let language = Arc::new(
 2508        Language::new(
 2509            LanguageConfig::default(),
 2510            Some(tree_sitter_rust::LANGUAGE.into()),
 2511        )
 2512        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2513        .unwrap(),
 2514    );
 2515
 2516    let mut cx = EditorTestContext::new(cx).await;
 2517    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2518    cx.set_state(indoc! {"
 2519        const a: ˇA = (
 2520 2521                «const_functionˇ»(ˇ),
 2522                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2523 2524        ˇ);ˇ
 2525    "});
 2526
 2527    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2528    cx.assert_editor_state(indoc! {"
 2529        const a: A = (
 2530            ˇ
 2531            (
 2532                ˇ
 2533                const_function(),
 2534                ˇ
 2535                ˇ
 2536                something_else,
 2537                ˇ
 2538                ˇ
 2539                ˇ
 2540                ˇ
 2541            )
 2542            ˇ
 2543        );
 2544        ˇ
 2545        ˇ
 2546    "});
 2547}
 2548
 2549#[gpui::test]
 2550async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2551    init_test(cx, |settings| {
 2552        settings.defaults.tab_size = NonZeroU32::new(4)
 2553    });
 2554
 2555    let language = Arc::new(Language::new(
 2556        LanguageConfig {
 2557            line_comments: vec!["//".into()],
 2558            ..LanguageConfig::default()
 2559        },
 2560        None,
 2561    ));
 2562    {
 2563        let mut cx = EditorTestContext::new(cx).await;
 2564        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2565        cx.set_state(indoc! {"
 2566        // Fooˇ
 2567    "});
 2568
 2569        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2570        cx.assert_editor_state(indoc! {"
 2571        // Foo
 2572        //ˇ
 2573    "});
 2574        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2575        cx.set_state(indoc! {"
 2576        ˇ// Foo
 2577    "});
 2578        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2579        cx.assert_editor_state(indoc! {"
 2580
 2581        ˇ// Foo
 2582    "});
 2583    }
 2584    // Ensure that comment continuations can be disabled.
 2585    update_test_language_settings(cx, |settings| {
 2586        settings.defaults.extend_comment_on_newline = Some(false);
 2587    });
 2588    let mut cx = EditorTestContext::new(cx).await;
 2589    cx.set_state(indoc! {"
 2590        // Fooˇ
 2591    "});
 2592    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2593    cx.assert_editor_state(indoc! {"
 2594        // Foo
 2595        ˇ
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2601    init_test(cx, |_| {});
 2602
 2603    let editor = cx.add_window(|cx| {
 2604        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2605        let mut editor = build_editor(buffer.clone(), cx);
 2606        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2607        editor
 2608    });
 2609
 2610    _ = editor.update(cx, |editor, cx| {
 2611        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2612        editor.buffer.update(cx, |buffer, cx| {
 2613            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2614            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2615        });
 2616        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2617
 2618        editor.insert("Z", cx);
 2619        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2620
 2621        // The selections are moved after the inserted characters
 2622        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2623    });
 2624}
 2625
 2626#[gpui::test]
 2627async fn test_tab(cx: &mut gpui::TestAppContext) {
 2628    init_test(cx, |settings| {
 2629        settings.defaults.tab_size = NonZeroU32::new(3)
 2630    });
 2631
 2632    let mut cx = EditorTestContext::new(cx).await;
 2633    cx.set_state(indoc! {"
 2634        ˇabˇc
 2635        ˇ🏀ˇ🏀ˇefg
 2636 2637    "});
 2638    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2639    cx.assert_editor_state(indoc! {"
 2640           ˇab ˇc
 2641           ˇ🏀  ˇ🏀  ˇefg
 2642        d  ˇ
 2643    "});
 2644
 2645    cx.set_state(indoc! {"
 2646        a
 2647        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2648    "});
 2649    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2650    cx.assert_editor_state(indoc! {"
 2651        a
 2652           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2653    "});
 2654}
 2655
 2656#[gpui::test]
 2657async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2658    init_test(cx, |_| {});
 2659
 2660    let mut cx = EditorTestContext::new(cx).await;
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670
 2671    // cursors that are already at the suggested indent level insert
 2672    // a soft tab. cursors that are to the left of the suggested indent
 2673    // auto-indent their line.
 2674    cx.set_state(indoc! {"
 2675        ˇ
 2676        const a: B = (
 2677            c(
 2678                d(
 2679        ˇ
 2680                )
 2681        ˇ
 2682        ˇ    )
 2683        );
 2684    "});
 2685    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2686    cx.assert_editor_state(indoc! {"
 2687            ˇ
 2688        const a: B = (
 2689            c(
 2690                d(
 2691                    ˇ
 2692                )
 2693                ˇ
 2694            ˇ)
 2695        );
 2696    "});
 2697
 2698    // handle auto-indent when there are multiple cursors on the same line
 2699    cx.set_state(indoc! {"
 2700        const a: B = (
 2701            c(
 2702        ˇ    ˇ
 2703        ˇ    )
 2704        );
 2705    "});
 2706    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2707    cx.assert_editor_state(indoc! {"
 2708        const a: B = (
 2709            c(
 2710                ˇ
 2711            ˇ)
 2712        );
 2713    "});
 2714}
 2715
 2716#[gpui::test]
 2717async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2718    init_test(cx, |settings| {
 2719        settings.defaults.tab_size = NonZeroU32::new(4)
 2720    });
 2721
 2722    let language = Arc::new(
 2723        Language::new(
 2724            LanguageConfig::default(),
 2725            Some(tree_sitter_rust::LANGUAGE.into()),
 2726        )
 2727        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2728        .unwrap(),
 2729    );
 2730
 2731    let mut cx = EditorTestContext::new(cx).await;
 2732    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2733    cx.set_state(indoc! {"
 2734        fn a() {
 2735            if b {
 2736        \t ˇc
 2737            }
 2738        }
 2739    "});
 2740
 2741    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2742    cx.assert_editor_state(indoc! {"
 2743        fn a() {
 2744            if b {
 2745                ˇc
 2746            }
 2747        }
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4);
 2755    });
 2756
 2757    let mut cx = EditorTestContext::new(cx).await;
 2758
 2759    cx.set_state(indoc! {"
 2760          «oneˇ» «twoˇ»
 2761        three
 2762         four
 2763    "});
 2764    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2765    cx.assert_editor_state(indoc! {"
 2766            «oneˇ» «twoˇ»
 2767        three
 2768         four
 2769    "});
 2770
 2771    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2772    cx.assert_editor_state(indoc! {"
 2773        «oneˇ» «twoˇ»
 2774        three
 2775         four
 2776    "});
 2777
 2778    // select across line ending
 2779    cx.set_state(indoc! {"
 2780        one two
 2781        t«hree
 2782        ˇ» four
 2783    "});
 2784    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2785    cx.assert_editor_state(indoc! {"
 2786        one two
 2787            t«hree
 2788        ˇ» four
 2789    "});
 2790
 2791    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2792    cx.assert_editor_state(indoc! {"
 2793        one two
 2794        t«hree
 2795        ˇ» four
 2796    "});
 2797
 2798    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2799    cx.set_state(indoc! {"
 2800        one two
 2801        ˇthree
 2802            four
 2803    "});
 2804    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2805    cx.assert_editor_state(indoc! {"
 2806        one two
 2807            ˇthree
 2808            four
 2809    "});
 2810
 2811    cx.set_state(indoc! {"
 2812        one two
 2813        ˇ    three
 2814            four
 2815    "});
 2816    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2817    cx.assert_editor_state(indoc! {"
 2818        one two
 2819        ˇthree
 2820            four
 2821    "});
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.hard_tabs = Some(true);
 2828    });
 2829
 2830    let mut cx = EditorTestContext::new(cx).await;
 2831
 2832    // select two ranges on one line
 2833    cx.set_state(indoc! {"
 2834        «oneˇ» «twoˇ»
 2835        three
 2836        four
 2837    "});
 2838    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2839    cx.assert_editor_state(indoc! {"
 2840        \t«oneˇ» «twoˇ»
 2841        three
 2842        four
 2843    "});
 2844    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2845    cx.assert_editor_state(indoc! {"
 2846        \t\t«oneˇ» «twoˇ»
 2847        three
 2848        four
 2849    "});
 2850    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2851    cx.assert_editor_state(indoc! {"
 2852        \t«oneˇ» «twoˇ»
 2853        three
 2854        four
 2855    "});
 2856    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2857    cx.assert_editor_state(indoc! {"
 2858        «oneˇ» «twoˇ»
 2859        three
 2860        four
 2861    "});
 2862
 2863    // select across a line ending
 2864    cx.set_state(indoc! {"
 2865        one two
 2866        t«hree
 2867        ˇ»four
 2868    "});
 2869    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2870    cx.assert_editor_state(indoc! {"
 2871        one two
 2872        \tt«hree
 2873        ˇ»four
 2874    "});
 2875    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2876    cx.assert_editor_state(indoc! {"
 2877        one two
 2878        \t\tt«hree
 2879        ˇ»four
 2880    "});
 2881    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2882    cx.assert_editor_state(indoc! {"
 2883        one two
 2884        \tt«hree
 2885        ˇ»four
 2886    "});
 2887    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889        one two
 2890        t«hree
 2891        ˇ»four
 2892    "});
 2893
 2894    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2895    cx.set_state(indoc! {"
 2896        one two
 2897        ˇthree
 2898        four
 2899    "});
 2900    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2901    cx.assert_editor_state(indoc! {"
 2902        one two
 2903        ˇthree
 2904        four
 2905    "});
 2906    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2907    cx.assert_editor_state(indoc! {"
 2908        one two
 2909        \tˇthree
 2910        four
 2911    "});
 2912    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2913    cx.assert_editor_state(indoc! {"
 2914        one two
 2915        ˇthree
 2916        four
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.languages.extend([
 2924            (
 2925                "TOML".into(),
 2926                LanguageSettingsContent {
 2927                    tab_size: NonZeroU32::new(2),
 2928                    ..Default::default()
 2929                },
 2930            ),
 2931            (
 2932                "Rust".into(),
 2933                LanguageSettingsContent {
 2934                    tab_size: NonZeroU32::new(4),
 2935                    ..Default::default()
 2936                },
 2937            ),
 2938        ]);
 2939    });
 2940
 2941    let toml_language = Arc::new(Language::new(
 2942        LanguageConfig {
 2943            name: "TOML".into(),
 2944            ..Default::default()
 2945        },
 2946        None,
 2947    ));
 2948    let rust_language = Arc::new(Language::new(
 2949        LanguageConfig {
 2950            name: "Rust".into(),
 2951            ..Default::default()
 2952        },
 2953        None,
 2954    ));
 2955
 2956    let toml_buffer =
 2957        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2958    let rust_buffer = cx.new_model(|cx| {
 2959        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2960    });
 2961    let multibuffer = cx.new_model(|cx| {
 2962        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2963        multibuffer.push_excerpts(
 2964            toml_buffer.clone(),
 2965            [ExcerptRange {
 2966                context: Point::new(0, 0)..Point::new(2, 0),
 2967                primary: None,
 2968            }],
 2969            cx,
 2970        );
 2971        multibuffer.push_excerpts(
 2972            rust_buffer.clone(),
 2973            [ExcerptRange {
 2974                context: Point::new(0, 0)..Point::new(1, 0),
 2975                primary: None,
 2976            }],
 2977            cx,
 2978        );
 2979        multibuffer
 2980    });
 2981
 2982    cx.add_window(|cx| {
 2983        let mut editor = build_editor(multibuffer, cx);
 2984
 2985        assert_eq!(
 2986            editor.text(cx),
 2987            indoc! {"
 2988                a = 1
 2989                b = 2
 2990
 2991                const c: usize = 3;
 2992            "}
 2993        );
 2994
 2995        select_ranges(
 2996            &mut editor,
 2997            indoc! {"
 2998                «aˇ» = 1
 2999                b = 2
 3000
 3001                «const c:ˇ» usize = 3;
 3002            "},
 3003            cx,
 3004        );
 3005
 3006        editor.tab(&Tab, cx);
 3007        assert_text_with_selections(
 3008            &mut editor,
 3009            indoc! {"
 3010                  «aˇ» = 1
 3011                b = 2
 3012
 3013                    «const c:ˇ» usize = 3;
 3014            "},
 3015            cx,
 3016        );
 3017        editor.tab_prev(&TabPrev, cx);
 3018        assert_text_with_selections(
 3019            &mut editor,
 3020            indoc! {"
 3021                «aˇ» = 1
 3022                b = 2
 3023
 3024                «const c:ˇ» usize = 3;
 3025            "},
 3026            cx,
 3027        );
 3028
 3029        editor
 3030    });
 3031}
 3032
 3033#[gpui::test]
 3034async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3035    init_test(cx, |_| {});
 3036
 3037    let mut cx = EditorTestContext::new(cx).await;
 3038
 3039    // Basic backspace
 3040    cx.set_state(indoc! {"
 3041        onˇe two three
 3042        fou«rˇ» five six
 3043        seven «ˇeight nine
 3044        »ten
 3045    "});
 3046    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3047    cx.assert_editor_state(indoc! {"
 3048        oˇe two three
 3049        fouˇ five six
 3050        seven ˇten
 3051    "});
 3052
 3053    // Test backspace inside and around indents
 3054    cx.set_state(indoc! {"
 3055        zero
 3056            ˇone
 3057                ˇtwo
 3058            ˇ ˇ ˇ  three
 3059        ˇ  ˇ  four
 3060    "});
 3061    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3062    cx.assert_editor_state(indoc! {"
 3063        zero
 3064        ˇone
 3065            ˇtwo
 3066        ˇ  threeˇ  four
 3067    "});
 3068
 3069    // Test backspace with line_mode set to true
 3070    cx.update_editor(|e, _| e.selections.line_mode = true);
 3071    cx.set_state(indoc! {"
 3072        The ˇquick ˇbrown
 3073        fox jumps over
 3074        the lazy dog
 3075        ˇThe qu«ick bˇ»rown"});
 3076    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3077    cx.assert_editor_state(indoc! {"
 3078        ˇfox jumps over
 3079        the lazy dogˇ"});
 3080}
 3081
 3082#[gpui::test]
 3083async fn test_delete(cx: &mut gpui::TestAppContext) {
 3084    init_test(cx, |_| {});
 3085
 3086    let mut cx = EditorTestContext::new(cx).await;
 3087    cx.set_state(indoc! {"
 3088        onˇe two three
 3089        fou«rˇ» five six
 3090        seven «ˇeight nine
 3091        »ten
 3092    "});
 3093    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3094    cx.assert_editor_state(indoc! {"
 3095        onˇ two three
 3096        fouˇ five six
 3097        seven ˇten
 3098    "});
 3099
 3100    // Test backspace with line_mode set to true
 3101    cx.update_editor(|e, _| e.selections.line_mode = true);
 3102    cx.set_state(indoc! {"
 3103        The ˇquick ˇbrown
 3104        fox «ˇjum»ps over
 3105        the lazy dog
 3106        ˇThe qu«ick bˇ»rown"});
 3107    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3108    cx.assert_editor_state("ˇthe lazy dogˇ");
 3109}
 3110
 3111#[gpui::test]
 3112fn test_delete_line(cx: &mut TestAppContext) {
 3113    init_test(cx, |_| {});
 3114
 3115    let view = cx.add_window(|cx| {
 3116        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3117        build_editor(buffer, cx)
 3118    });
 3119    _ = view.update(cx, |view, cx| {
 3120        view.change_selections(None, cx, |s| {
 3121            s.select_display_ranges([
 3122                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3123                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3124                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3125            ])
 3126        });
 3127        view.delete_line(&DeleteLine, cx);
 3128        assert_eq!(view.display_text(cx), "ghi");
 3129        assert_eq!(
 3130            view.selections.display_ranges(cx),
 3131            vec![
 3132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3133                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3134            ]
 3135        );
 3136    });
 3137
 3138    let view = cx.add_window(|cx| {
 3139        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3140        build_editor(buffer, cx)
 3141    });
 3142    _ = view.update(cx, |view, cx| {
 3143        view.change_selections(None, cx, |s| {
 3144            s.select_display_ranges([
 3145                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3146            ])
 3147        });
 3148        view.delete_line(&DeleteLine, cx);
 3149        assert_eq!(view.display_text(cx), "ghi\n");
 3150        assert_eq!(
 3151            view.selections.display_ranges(cx),
 3152            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3153        );
 3154    });
 3155}
 3156
 3157#[gpui::test]
 3158fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3159    init_test(cx, |_| {});
 3160
 3161    cx.add_window(|cx| {
 3162        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3163        let mut editor = build_editor(buffer.clone(), cx);
 3164        let buffer = buffer.read(cx).as_singleton().unwrap();
 3165
 3166        assert_eq!(
 3167            editor.selections.ranges::<Point>(cx),
 3168            &[Point::new(0, 0)..Point::new(0, 0)]
 3169        );
 3170
 3171        // When on single line, replace newline at end by space
 3172        editor.join_lines(&JoinLines, cx);
 3173        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3174        assert_eq!(
 3175            editor.selections.ranges::<Point>(cx),
 3176            &[Point::new(0, 3)..Point::new(0, 3)]
 3177        );
 3178
 3179        // When multiple lines are selected, remove newlines that are spanned by the selection
 3180        editor.change_selections(None, cx, |s| {
 3181            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3182        });
 3183        editor.join_lines(&JoinLines, cx);
 3184        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3185        assert_eq!(
 3186            editor.selections.ranges::<Point>(cx),
 3187            &[Point::new(0, 11)..Point::new(0, 11)]
 3188        );
 3189
 3190        // Undo should be transactional
 3191        editor.undo(&Undo, cx);
 3192        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3193        assert_eq!(
 3194            editor.selections.ranges::<Point>(cx),
 3195            &[Point::new(0, 5)..Point::new(2, 2)]
 3196        );
 3197
 3198        // When joining an empty line don't insert a space
 3199        editor.change_selections(None, cx, |s| {
 3200            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3201        });
 3202        editor.join_lines(&JoinLines, cx);
 3203        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3204        assert_eq!(
 3205            editor.selections.ranges::<Point>(cx),
 3206            [Point::new(2, 3)..Point::new(2, 3)]
 3207        );
 3208
 3209        // We can remove trailing newlines
 3210        editor.join_lines(&JoinLines, cx);
 3211        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3212        assert_eq!(
 3213            editor.selections.ranges::<Point>(cx),
 3214            [Point::new(2, 3)..Point::new(2, 3)]
 3215        );
 3216
 3217        // We don't blow up on the last line
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3220        assert_eq!(
 3221            editor.selections.ranges::<Point>(cx),
 3222            [Point::new(2, 3)..Point::new(2, 3)]
 3223        );
 3224
 3225        // reset to test indentation
 3226        editor.buffer.update(cx, |buffer, cx| {
 3227            buffer.edit(
 3228                [
 3229                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3230                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3231                ],
 3232                None,
 3233                cx,
 3234            )
 3235        });
 3236
 3237        // We remove any leading spaces
 3238        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3239        editor.change_selections(None, cx, |s| {
 3240            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3241        });
 3242        editor.join_lines(&JoinLines, cx);
 3243        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3244
 3245        // We don't insert a space for a line containing only spaces
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3248
 3249        // We ignore any leading tabs
 3250        editor.join_lines(&JoinLines, cx);
 3251        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3252
 3253        editor
 3254    });
 3255}
 3256
 3257#[gpui::test]
 3258fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3259    init_test(cx, |_| {});
 3260
 3261    cx.add_window(|cx| {
 3262        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3263        let mut editor = build_editor(buffer.clone(), cx);
 3264        let buffer = buffer.read(cx).as_singleton().unwrap();
 3265
 3266        editor.change_selections(None, cx, |s| {
 3267            s.select_ranges([
 3268                Point::new(0, 2)..Point::new(1, 1),
 3269                Point::new(1, 2)..Point::new(1, 2),
 3270                Point::new(3, 1)..Point::new(3, 2),
 3271            ])
 3272        });
 3273
 3274        editor.join_lines(&JoinLines, cx);
 3275        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3276
 3277        assert_eq!(
 3278            editor.selections.ranges::<Point>(cx),
 3279            [
 3280                Point::new(0, 7)..Point::new(0, 7),
 3281                Point::new(1, 3)..Point::new(1, 3)
 3282            ]
 3283        );
 3284        editor
 3285    });
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_join_lines_with_git_diff_base(
 3290    executor: BackgroundExecutor,
 3291    cx: &mut gpui::TestAppContext,
 3292) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296
 3297    let diff_base = r#"
 3298        Line 0
 3299        Line 1
 3300        Line 2
 3301        Line 3
 3302        "#
 3303    .unindent();
 3304
 3305    cx.set_state(
 3306        &r#"
 3307        ˇLine 0
 3308        Line 1
 3309        Line 2
 3310        Line 3
 3311        "#
 3312        .unindent(),
 3313    );
 3314
 3315    cx.set_diff_base(Some(&diff_base));
 3316    executor.run_until_parked();
 3317
 3318    // Join lines
 3319    cx.update_editor(|editor, cx| {
 3320        editor.join_lines(&JoinLines, cx);
 3321    });
 3322    executor.run_until_parked();
 3323
 3324    cx.assert_editor_state(
 3325        &r#"
 3326        Line 0ˇ Line 1
 3327        Line 2
 3328        Line 3
 3329        "#
 3330        .unindent(),
 3331    );
 3332    // Join again
 3333    cx.update_editor(|editor, cx| {
 3334        editor.join_lines(&JoinLines, cx);
 3335    });
 3336    executor.run_until_parked();
 3337
 3338    cx.assert_editor_state(
 3339        &r#"
 3340        Line 0 Line 1ˇ Line 2
 3341        Line 3
 3342        "#
 3343        .unindent(),
 3344    );
 3345}
 3346
 3347#[gpui::test]
 3348async fn test_custom_newlines_cause_no_false_positive_diffs(
 3349    executor: BackgroundExecutor,
 3350    cx: &mut gpui::TestAppContext,
 3351) {
 3352    init_test(cx, |_| {});
 3353    let mut cx = EditorTestContext::new(cx).await;
 3354    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3355    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3356    executor.run_until_parked();
 3357
 3358    cx.update_editor(|editor, cx| {
 3359        assert_eq!(
 3360            editor
 3361                .buffer()
 3362                .read(cx)
 3363                .snapshot(cx)
 3364                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3365                .collect::<Vec<_>>(),
 3366            Vec::new(),
 3367            "Should not have any diffs for files with custom newlines"
 3368        );
 3369    });
 3370}
 3371
 3372#[gpui::test]
 3373async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3374    init_test(cx, |_| {});
 3375
 3376    let mut cx = EditorTestContext::new(cx).await;
 3377
 3378    // Test sort_lines_case_insensitive()
 3379    cx.set_state(indoc! {"
 3380        «z
 3381        y
 3382        x
 3383        Z
 3384        Y
 3385        Xˇ»
 3386    "});
 3387    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3388    cx.assert_editor_state(indoc! {"
 3389        «x
 3390        X
 3391        y
 3392        Y
 3393        z
 3394        Zˇ»
 3395    "});
 3396
 3397    // Test reverse_lines()
 3398    cx.set_state(indoc! {"
 3399        «5
 3400        4
 3401        3
 3402        2
 3403        1ˇ»
 3404    "});
 3405    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3406    cx.assert_editor_state(indoc! {"
 3407        «1
 3408        2
 3409        3
 3410        4
 3411        5ˇ»
 3412    "});
 3413
 3414    // Skip testing shuffle_line()
 3415
 3416    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3417    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3418
 3419    // Don't manipulate when cursor is on single line, but expand the selection
 3420    cx.set_state(indoc! {"
 3421        ddˇdd
 3422        ccc
 3423        bb
 3424        a
 3425    "});
 3426    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3427    cx.assert_editor_state(indoc! {"
 3428        «ddddˇ»
 3429        ccc
 3430        bb
 3431        a
 3432    "});
 3433
 3434    // Basic manipulate case
 3435    // Start selection moves to column 0
 3436    // End of selection shrinks to fit shorter line
 3437    cx.set_state(indoc! {"
 3438        dd«d
 3439        ccc
 3440        bb
 3441        aaaaaˇ»
 3442    "});
 3443    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3444    cx.assert_editor_state(indoc! {"
 3445        «aaaaa
 3446        bb
 3447        ccc
 3448        dddˇ»
 3449    "});
 3450
 3451    // Manipulate case with newlines
 3452    cx.set_state(indoc! {"
 3453        dd«d
 3454        ccc
 3455
 3456        bb
 3457        aaaaa
 3458
 3459        ˇ»
 3460    "});
 3461    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3462    cx.assert_editor_state(indoc! {"
 3463        «
 3464
 3465        aaaaa
 3466        bb
 3467        ccc
 3468        dddˇ»
 3469
 3470    "});
 3471
 3472    // Adding new line
 3473    cx.set_state(indoc! {"
 3474        aa«a
 3475        bbˇ»b
 3476    "});
 3477    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3478    cx.assert_editor_state(indoc! {"
 3479        «aaa
 3480        bbb
 3481        added_lineˇ»
 3482    "});
 3483
 3484    // Removing line
 3485    cx.set_state(indoc! {"
 3486        aa«a
 3487        bbbˇ»
 3488    "});
 3489    cx.update_editor(|e, cx| {
 3490        e.manipulate_lines(cx, |lines| {
 3491            lines.pop();
 3492        })
 3493    });
 3494    cx.assert_editor_state(indoc! {"
 3495        «aaaˇ»
 3496    "});
 3497
 3498    // Removing all lines
 3499    cx.set_state(indoc! {"
 3500        aa«a
 3501        bbbˇ»
 3502    "});
 3503    cx.update_editor(|e, cx| {
 3504        e.manipulate_lines(cx, |lines| {
 3505            lines.drain(..);
 3506        })
 3507    });
 3508    cx.assert_editor_state(indoc! {"
 3509        ˇ
 3510    "});
 3511}
 3512
 3513#[gpui::test]
 3514async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3515    init_test(cx, |_| {});
 3516
 3517    let mut cx = EditorTestContext::new(cx).await;
 3518
 3519    // Consider continuous selection as single selection
 3520    cx.set_state(indoc! {"
 3521        Aaa«aa
 3522        cˇ»c«c
 3523        bb
 3524        aaaˇ»aa
 3525    "});
 3526    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3527    cx.assert_editor_state(indoc! {"
 3528        «Aaaaa
 3529        ccc
 3530        bb
 3531        aaaaaˇ»
 3532    "});
 3533
 3534    cx.set_state(indoc! {"
 3535        Aaa«aa
 3536        cˇ»c«c
 3537        bb
 3538        aaaˇ»aa
 3539    "});
 3540    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3541    cx.assert_editor_state(indoc! {"
 3542        «Aaaaa
 3543        ccc
 3544        bbˇ»
 3545    "});
 3546
 3547    // Consider non continuous selection as distinct dedup operations
 3548    cx.set_state(indoc! {"
 3549        «aaaaa
 3550        bb
 3551        aaaaa
 3552        aaaaaˇ»
 3553
 3554        aaa«aaˇ»
 3555    "});
 3556    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3557    cx.assert_editor_state(indoc! {"
 3558        «aaaaa
 3559        bbˇ»
 3560
 3561        «aaaaaˇ»
 3562    "});
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    cx.set_state(indoc! {"
 3572        «Aaa
 3573        aAa
 3574        Aaaˇ»
 3575    "});
 3576    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3577    cx.assert_editor_state(indoc! {"
 3578        «Aaa
 3579        aAaˇ»
 3580    "});
 3581
 3582    cx.set_state(indoc! {"
 3583        «Aaa
 3584        aAa
 3585        aaAˇ»
 3586    "});
 3587    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3588    cx.assert_editor_state(indoc! {"
 3589        «Aaaˇ»
 3590    "});
 3591}
 3592
 3593#[gpui::test]
 3594async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3595    init_test(cx, |_| {});
 3596
 3597    let mut cx = EditorTestContext::new(cx).await;
 3598
 3599    // Manipulate with multiple selections on a single line
 3600    cx.set_state(indoc! {"
 3601        dd«dd
 3602        cˇ»c«c
 3603        bb
 3604        aaaˇ»aa
 3605    "});
 3606    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3607    cx.assert_editor_state(indoc! {"
 3608        «aaaaa
 3609        bb
 3610        ccc
 3611        ddddˇ»
 3612    "});
 3613
 3614    // Manipulate with multiple disjoin selections
 3615    cx.set_state(indoc! {"
 3616 3617        4
 3618        3
 3619        2
 3620        1ˇ»
 3621
 3622        dd«dd
 3623        ccc
 3624        bb
 3625        aaaˇ»aa
 3626    "});
 3627    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3628    cx.assert_editor_state(indoc! {"
 3629        «1
 3630        2
 3631        3
 3632        4
 3633        5ˇ»
 3634
 3635        «aaaaa
 3636        bb
 3637        ccc
 3638        ddddˇ»
 3639    "});
 3640
 3641    // Adding lines on each selection
 3642    cx.set_state(indoc! {"
 3643 3644        1ˇ»
 3645
 3646        bb«bb
 3647        aaaˇ»aa
 3648    "});
 3649    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3650    cx.assert_editor_state(indoc! {"
 3651        «2
 3652        1
 3653        added lineˇ»
 3654
 3655        «bbbb
 3656        aaaaa
 3657        added lineˇ»
 3658    "});
 3659
 3660    // Removing lines on each selection
 3661    cx.set_state(indoc! {"
 3662 3663        1ˇ»
 3664
 3665        bb«bb
 3666        aaaˇ»aa
 3667    "});
 3668    cx.update_editor(|e, cx| {
 3669        e.manipulate_lines(cx, |lines| {
 3670            lines.pop();
 3671        })
 3672    });
 3673    cx.assert_editor_state(indoc! {"
 3674        «2ˇ»
 3675
 3676        «bbbbˇ»
 3677    "});
 3678}
 3679
 3680#[gpui::test]
 3681async fn test_manipulate_text(cx: &mut TestAppContext) {
 3682    init_test(cx, |_| {});
 3683
 3684    let mut cx = EditorTestContext::new(cx).await;
 3685
 3686    // Test convert_to_upper_case()
 3687    cx.set_state(indoc! {"
 3688        «hello worldˇ»
 3689    "});
 3690    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3691    cx.assert_editor_state(indoc! {"
 3692        «HELLO WORLDˇ»
 3693    "});
 3694
 3695    // Test convert_to_lower_case()
 3696    cx.set_state(indoc! {"
 3697        «HELLO WORLDˇ»
 3698    "});
 3699    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3700    cx.assert_editor_state(indoc! {"
 3701        «hello worldˇ»
 3702    "});
 3703
 3704    // Test multiple line, single selection case
 3705    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3706    cx.set_state(indoc! {"
 3707        «The quick brown
 3708        fox jumps over
 3709        the lazy dogˇ»
 3710    "});
 3711    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3712    cx.assert_editor_state(indoc! {"
 3713        «The Quick Brown
 3714        Fox Jumps Over
 3715        The Lazy Dogˇ»
 3716    "});
 3717
 3718    // Test multiple line, single selection case
 3719    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3720    cx.set_state(indoc! {"
 3721        «The quick brown
 3722        fox jumps over
 3723        the lazy dogˇ»
 3724    "});
 3725    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3726    cx.assert_editor_state(indoc! {"
 3727        «TheQuickBrown
 3728        FoxJumpsOver
 3729        TheLazyDogˇ»
 3730    "});
 3731
 3732    // From here on out, test more complex cases of manipulate_text()
 3733
 3734    // Test no selection case - should affect words cursors are in
 3735    // Cursor at beginning, middle, and end of word
 3736    cx.set_state(indoc! {"
 3737        ˇhello big beauˇtiful worldˇ
 3738    "});
 3739    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3740    cx.assert_editor_state(indoc! {"
 3741        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3742    "});
 3743
 3744    // Test multiple selections on a single line and across multiple lines
 3745    cx.set_state(indoc! {"
 3746        «Theˇ» quick «brown
 3747        foxˇ» jumps «overˇ»
 3748        the «lazyˇ» dog
 3749    "});
 3750    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3751    cx.assert_editor_state(indoc! {"
 3752        «THEˇ» quick «BROWN
 3753        FOXˇ» jumps «OVERˇ»
 3754        the «LAZYˇ» dog
 3755    "});
 3756
 3757    // Test case where text length grows
 3758    cx.set_state(indoc! {"
 3759        «tschüߡ»
 3760    "});
 3761    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3762    cx.assert_editor_state(indoc! {"
 3763        «TSCHÜSSˇ»
 3764    "});
 3765
 3766    // Test to make sure we don't crash when text shrinks
 3767    cx.set_state(indoc! {"
 3768        aaa_bbbˇ
 3769    "});
 3770    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3771    cx.assert_editor_state(indoc! {"
 3772        «aaaBbbˇ»
 3773    "});
 3774
 3775    // Test to make sure we all aware of the fact that each word can grow and shrink
 3776    // Final selections should be aware of this fact
 3777    cx.set_state(indoc! {"
 3778        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3779    "});
 3780    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3781    cx.assert_editor_state(indoc! {"
 3782        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3783    "});
 3784
 3785    cx.set_state(indoc! {"
 3786        «hElLo, WoRld!ˇ»
 3787    "});
 3788    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3789    cx.assert_editor_state(indoc! {"
 3790        «HeLlO, wOrLD!ˇ»
 3791    "});
 3792}
 3793
 3794#[gpui::test]
 3795fn test_duplicate_line(cx: &mut TestAppContext) {
 3796    init_test(cx, |_| {});
 3797
 3798    let view = cx.add_window(|cx| {
 3799        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3800        build_editor(buffer, cx)
 3801    });
 3802    _ = view.update(cx, |view, cx| {
 3803        view.change_selections(None, cx, |s| {
 3804            s.select_display_ranges([
 3805                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3806                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3807                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3808                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3809            ])
 3810        });
 3811        view.duplicate_line_down(&DuplicateLineDown, cx);
 3812        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3818                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3819                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3820            ]
 3821        );
 3822    });
 3823
 3824    let view = cx.add_window(|cx| {
 3825        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3826        build_editor(buffer, cx)
 3827    });
 3828    _ = view.update(cx, |view, cx| {
 3829        view.change_selections(None, cx, |s| {
 3830            s.select_display_ranges([
 3831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3833            ])
 3834        });
 3835        view.duplicate_line_down(&DuplicateLineDown, cx);
 3836        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3837        assert_eq!(
 3838            view.selections.display_ranges(cx),
 3839            vec![
 3840                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3841                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3842            ]
 3843        );
 3844    });
 3845
 3846    // With `move_upwards` the selections stay in place, except for
 3847    // the lines inserted above them
 3848    let view = cx.add_window(|cx| {
 3849        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3850        build_editor(buffer, cx)
 3851    });
 3852    _ = view.update(cx, |view, cx| {
 3853        view.change_selections(None, cx, |s| {
 3854            s.select_display_ranges([
 3855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3856                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3857                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3858                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3859            ])
 3860        });
 3861        view.duplicate_line_up(&DuplicateLineUp, cx);
 3862        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3863        assert_eq!(
 3864            view.selections.display_ranges(cx),
 3865            vec![
 3866                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3868                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3869                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3870            ]
 3871        );
 3872    });
 3873
 3874    let view = cx.add_window(|cx| {
 3875        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3876        build_editor(buffer, cx)
 3877    });
 3878    _ = view.update(cx, |view, cx| {
 3879        view.change_selections(None, cx, |s| {
 3880            s.select_display_ranges([
 3881                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3882                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3883            ])
 3884        });
 3885        view.duplicate_line_up(&DuplicateLineUp, cx);
 3886        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3887        assert_eq!(
 3888            view.selections.display_ranges(cx),
 3889            vec![
 3890                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3891                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3892            ]
 3893        );
 3894    });
 3895}
 3896
 3897#[gpui::test]
 3898fn test_move_line_up_down(cx: &mut TestAppContext) {
 3899    init_test(cx, |_| {});
 3900
 3901    let view = cx.add_window(|cx| {
 3902        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3903        build_editor(buffer, cx)
 3904    });
 3905    _ = view.update(cx, |view, cx| {
 3906        view.fold_creases(
 3907            vec![
 3908                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3909                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3910                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3911            ],
 3912            true,
 3913            cx,
 3914        );
 3915        view.change_selections(None, cx, |s| {
 3916            s.select_display_ranges([
 3917                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3918                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3919                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3920                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3921            ])
 3922        });
 3923        assert_eq!(
 3924            view.display_text(cx),
 3925            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3926        );
 3927
 3928        view.move_line_up(&MoveLineUp, cx);
 3929        assert_eq!(
 3930            view.display_text(cx),
 3931            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3932        );
 3933        assert_eq!(
 3934            view.selections.display_ranges(cx),
 3935            vec![
 3936                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3937                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3938                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3939                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3940            ]
 3941        );
 3942    });
 3943
 3944    _ = view.update(cx, |view, cx| {
 3945        view.move_line_down(&MoveLineDown, cx);
 3946        assert_eq!(
 3947            view.display_text(cx),
 3948            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3949        );
 3950        assert_eq!(
 3951            view.selections.display_ranges(cx),
 3952            vec![
 3953                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3955                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3956                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3957            ]
 3958        );
 3959    });
 3960
 3961    _ = view.update(cx, |view, cx| {
 3962        view.move_line_down(&MoveLineDown, cx);
 3963        assert_eq!(
 3964            view.display_text(cx),
 3965            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3966        );
 3967        assert_eq!(
 3968            view.selections.display_ranges(cx),
 3969            vec![
 3970                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3971                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3972                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3973                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3974            ]
 3975        );
 3976    });
 3977
 3978    _ = view.update(cx, |view, cx| {
 3979        view.move_line_up(&MoveLineUp, cx);
 3980        assert_eq!(
 3981            view.display_text(cx),
 3982            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3983        );
 3984        assert_eq!(
 3985            view.selections.display_ranges(cx),
 3986            vec![
 3987                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3988                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3989                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3990                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3991            ]
 3992        );
 3993    });
 3994}
 3995
 3996#[gpui::test]
 3997fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3998    init_test(cx, |_| {});
 3999
 4000    let editor = cx.add_window(|cx| {
 4001        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4002        build_editor(buffer, cx)
 4003    });
 4004    _ = editor.update(cx, |editor, cx| {
 4005        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4006        editor.insert_blocks(
 4007            [BlockProperties {
 4008                style: BlockStyle::Fixed,
 4009                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4010                height: 1,
 4011                render: Arc::new(|_| div().into_any()),
 4012                priority: 0,
 4013            }],
 4014            Some(Autoscroll::fit()),
 4015            cx,
 4016        );
 4017        editor.change_selections(None, cx, |s| {
 4018            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4019        });
 4020        editor.move_line_down(&MoveLineDown, cx);
 4021    });
 4022}
 4023
 4024#[gpui::test]
 4025async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4026    init_test(cx, |_| {});
 4027
 4028    let mut cx = EditorTestContext::new(cx).await;
 4029    cx.set_state(
 4030        &"
 4031            ˇzero
 4032            one
 4033            two
 4034            three
 4035            four
 4036            five
 4037        "
 4038        .unindent(),
 4039    );
 4040
 4041    // Create a four-line block that replaces three lines of text.
 4042    cx.update_editor(|editor, cx| {
 4043        let snapshot = editor.snapshot(cx);
 4044        let snapshot = &snapshot.buffer_snapshot;
 4045        let placement = BlockPlacement::Replace(
 4046            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4047        );
 4048        editor.insert_blocks(
 4049            [BlockProperties {
 4050                placement,
 4051                height: 4,
 4052                style: BlockStyle::Sticky,
 4053                render: Arc::new(|_| gpui::div().into_any_element()),
 4054                priority: 0,
 4055            }],
 4056            None,
 4057            cx,
 4058        );
 4059    });
 4060
 4061    // Move down so that the cursor touches the block.
 4062    cx.update_editor(|editor, cx| {
 4063        editor.move_down(&Default::default(), cx);
 4064    });
 4065    cx.assert_editor_state(
 4066        &"
 4067            zero
 4068            «one
 4069            two
 4070            threeˇ»
 4071            four
 4072            five
 4073        "
 4074        .unindent(),
 4075    );
 4076
 4077    // Move down past the block.
 4078    cx.update_editor(|editor, cx| {
 4079        editor.move_down(&Default::default(), cx);
 4080    });
 4081    cx.assert_editor_state(
 4082        &"
 4083            zero
 4084            one
 4085            two
 4086            three
 4087            ˇfour
 4088            five
 4089        "
 4090        .unindent(),
 4091    );
 4092}
 4093
 4094#[gpui::test]
 4095fn test_transpose(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    _ = cx.add_window(|cx| {
 4099        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4100        editor.set_style(EditorStyle::default(), cx);
 4101        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "bac");
 4104        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4105
 4106        editor.transpose(&Default::default(), cx);
 4107        assert_eq!(editor.text(cx), "bca");
 4108        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4109
 4110        editor.transpose(&Default::default(), cx);
 4111        assert_eq!(editor.text(cx), "bac");
 4112        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4113
 4114        editor
 4115    });
 4116
 4117    _ = cx.add_window(|cx| {
 4118        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4119        editor.set_style(EditorStyle::default(), cx);
 4120        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "acb\nde");
 4123        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4124
 4125        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4126        editor.transpose(&Default::default(), cx);
 4127        assert_eq!(editor.text(cx), "acbd\ne");
 4128        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4129
 4130        editor.transpose(&Default::default(), cx);
 4131        assert_eq!(editor.text(cx), "acbde\n");
 4132        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4133
 4134        editor.transpose(&Default::default(), cx);
 4135        assert_eq!(editor.text(cx), "acbd\ne");
 4136        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4137
 4138        editor
 4139    });
 4140
 4141    _ = cx.add_window(|cx| {
 4142        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4143        editor.set_style(EditorStyle::default(), cx);
 4144        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4145        editor.transpose(&Default::default(), cx);
 4146        assert_eq!(editor.text(cx), "bacd\ne");
 4147        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4148
 4149        editor.transpose(&Default::default(), cx);
 4150        assert_eq!(editor.text(cx), "bcade\n");
 4151        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4152
 4153        editor.transpose(&Default::default(), cx);
 4154        assert_eq!(editor.text(cx), "bcda\ne");
 4155        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4156
 4157        editor.transpose(&Default::default(), cx);
 4158        assert_eq!(editor.text(cx), "bcade\n");
 4159        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4160
 4161        editor.transpose(&Default::default(), cx);
 4162        assert_eq!(editor.text(cx), "bcaed\n");
 4163        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4164
 4165        editor
 4166    });
 4167
 4168    _ = cx.add_window(|cx| {
 4169        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4170        editor.set_style(EditorStyle::default(), cx);
 4171        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4172        editor.transpose(&Default::default(), cx);
 4173        assert_eq!(editor.text(cx), "🏀🍐✋");
 4174        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4175
 4176        editor.transpose(&Default::default(), cx);
 4177        assert_eq!(editor.text(cx), "🏀✋🍐");
 4178        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4179
 4180        editor.transpose(&Default::default(), cx);
 4181        assert_eq!(editor.text(cx), "🏀🍐✋");
 4182        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4183
 4184        editor
 4185    });
 4186}
 4187
 4188#[gpui::test]
 4189async fn test_rewrap(cx: &mut TestAppContext) {
 4190    init_test(cx, |_| {});
 4191
 4192    let mut cx = EditorTestContext::new(cx).await;
 4193
 4194    let language_with_c_comments = Arc::new(Language::new(
 4195        LanguageConfig {
 4196            line_comments: vec!["// ".into()],
 4197            ..LanguageConfig::default()
 4198        },
 4199        None,
 4200    ));
 4201    let language_with_pound_comments = Arc::new(Language::new(
 4202        LanguageConfig {
 4203            line_comments: vec!["# ".into()],
 4204            ..LanguageConfig::default()
 4205        },
 4206        None,
 4207    ));
 4208    let markdown_language = Arc::new(Language::new(
 4209        LanguageConfig {
 4210            name: "Markdown".into(),
 4211            ..LanguageConfig::default()
 4212        },
 4213        None,
 4214    ));
 4215    let language_with_doc_comments = Arc::new(Language::new(
 4216        LanguageConfig {
 4217            line_comments: vec!["// ".into(), "/// ".into()],
 4218            ..LanguageConfig::default()
 4219        },
 4220        Some(tree_sitter_rust::LANGUAGE.into()),
 4221    ));
 4222
 4223    let plaintext_language = Arc::new(Language::new(
 4224        LanguageConfig {
 4225            name: "Plain Text".into(),
 4226            ..LanguageConfig::default()
 4227        },
 4228        None,
 4229    ));
 4230
 4231    assert_rewrap(
 4232        indoc! {"
 4233            // ˇ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.
 4234        "},
 4235        indoc! {"
 4236            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4237            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4238            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4239            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4240            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4241            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4242            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4243            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4244            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4245            // porttitor id. Aliquam id accumsan eros.
 4246        "},
 4247        language_with_c_comments.clone(),
 4248        &mut cx,
 4249    );
 4250
 4251    // Test that rewrapping works inside of a selection
 4252    assert_rewrap(
 4253        indoc! {"
 4254            «// 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.ˇ»
 4255        "},
 4256        indoc! {"
 4257            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4258            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4259            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4260            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4261            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4262            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4263            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4264            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4265            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4266            // porttitor id. Aliquam id accumsan eros.ˇ»
 4267        "},
 4268        language_with_c_comments.clone(),
 4269        &mut cx,
 4270    );
 4271
 4272    // Test that cursors that expand to the same region are collapsed.
 4273    assert_rewrap(
 4274        indoc! {"
 4275            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4276            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4277            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4278            // ˇ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.
 4279        "},
 4280        indoc! {"
 4281            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4282            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4283            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4284            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4285            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4286            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4287            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4288            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4289            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4290            // porttitor id. Aliquam id accumsan eros.
 4291        "},
 4292        language_with_c_comments.clone(),
 4293        &mut cx,
 4294    );
 4295
 4296    // Test that non-contiguous selections are treated separately.
 4297    assert_rewrap(
 4298        indoc! {"
 4299            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4300            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4301            //
 4302            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4303            // ˇ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.
 4304        "},
 4305        indoc! {"
 4306            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4307            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4308            // auctor, eu lacinia sapien scelerisque.
 4309            //
 4310            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4311            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4312            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4313            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4314            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4315            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4316            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4317        "},
 4318        language_with_c_comments.clone(),
 4319        &mut cx,
 4320    );
 4321
 4322    // Test that different comment prefixes are supported.
 4323    assert_rewrap(
 4324        indoc! {"
 4325            # ˇ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.
 4326        "},
 4327        indoc! {"
 4328            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4329            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4330            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4331            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4332            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4333            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4334            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4335            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4336            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4337            # accumsan eros.
 4338        "},
 4339        language_with_pound_comments.clone(),
 4340        &mut cx,
 4341    );
 4342
 4343    // Test that rewrapping is ignored outside of comments in most languages.
 4344    assert_rewrap(
 4345        indoc! {"
 4346            /// Adds two numbers.
 4347            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4348            fn add(a: u32, b: u32) -> u32 {
 4349                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4350            }
 4351        "},
 4352        indoc! {"
 4353            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4354            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4355            fn add(a: u32, b: u32) -> u32 {
 4356                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ˇ
 4357            }
 4358        "},
 4359        language_with_doc_comments.clone(),
 4360        &mut cx,
 4361    );
 4362
 4363    // Test that rewrapping works in Markdown and Plain Text languages.
 4364    assert_rewrap(
 4365        indoc! {"
 4366            # Hello
 4367
 4368            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.
 4369        "},
 4370        indoc! {"
 4371            # Hello
 4372
 4373            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4374            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4375            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4376            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4377            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4378            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4379            Integer sit amet scelerisque nisi.
 4380        "},
 4381        markdown_language,
 4382        &mut cx,
 4383    );
 4384
 4385    assert_rewrap(
 4386        indoc! {"
 4387            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.
 4388        "},
 4389        indoc! {"
 4390            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4391            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4392            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4393            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4394            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4395            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4396            Integer sit amet scelerisque nisi.
 4397        "},
 4398        plaintext_language,
 4399        &mut cx,
 4400    );
 4401
 4402    // Test rewrapping unaligned comments in a selection.
 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        indoc! {"
 4416            fn foo() {
 4417                if true {
 4418            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4419                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4420                    // egestas tellus id dignissim.ˇ»
 4421                    do_something();
 4422                } else {
 4423                    //
 4424                }
 4425            }
 4426        "},
 4427        language_with_doc_comments.clone(),
 4428        &mut cx,
 4429    );
 4430
 4431    assert_rewrap(
 4432        indoc! {"
 4433            fn foo() {
 4434                if true {
 4435            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4436            // Praesent semper egestas tellus id dignissim.»
 4437                    do_something();
 4438                } else {
 4439                    //
 4440                }
 4441
 4442            }
 4443        "},
 4444        indoc! {"
 4445            fn foo() {
 4446                if true {
 4447            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4448                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4449                    // egestas tellus id dignissim.»
 4450                    do_something();
 4451                } else {
 4452                    //
 4453                }
 4454
 4455            }
 4456        "},
 4457        language_with_doc_comments.clone(),
 4458        &mut cx,
 4459    );
 4460
 4461    #[track_caller]
 4462    fn assert_rewrap(
 4463        unwrapped_text: &str,
 4464        wrapped_text: &str,
 4465        language: Arc<Language>,
 4466        cx: &mut EditorTestContext,
 4467    ) {
 4468        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4469        cx.set_state(unwrapped_text);
 4470        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4471        cx.assert_editor_state(wrapped_text);
 4472    }
 4473}
 4474
 4475#[gpui::test]
 4476async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4477    init_test(cx, |_| {});
 4478
 4479    let mut cx = EditorTestContext::new(cx).await;
 4480
 4481    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4482    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4483    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4484
 4485    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4486    cx.set_state("two ˇfour ˇsix ˇ");
 4487    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4488    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4489
 4490    // Paste again but with only two cursors. Since the number of cursors doesn't
 4491    // match the number of slices in the clipboard, the entire clipboard text
 4492    // is pasted at each cursor.
 4493    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4494    cx.update_editor(|e, cx| {
 4495        e.handle_input("( ", cx);
 4496        e.paste(&Paste, cx);
 4497        e.handle_input(") ", cx);
 4498    });
 4499    cx.assert_editor_state(
 4500        &([
 4501            "( one✅ ",
 4502            "three ",
 4503            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4504            "three ",
 4505            "five ) ˇ",
 4506        ]
 4507        .join("\n")),
 4508    );
 4509
 4510    // Cut with three selections, one of which is full-line.
 4511    cx.set_state(indoc! {"
 4512        1«2ˇ»3
 4513        4ˇ567
 4514        «8ˇ»9"});
 4515    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4516    cx.assert_editor_state(indoc! {"
 4517        1ˇ3
 4518        ˇ9"});
 4519
 4520    // Paste with three selections, noticing how the copied selection that was full-line
 4521    // gets inserted before the second cursor.
 4522    cx.set_state(indoc! {"
 4523        1ˇ3
 4524 4525        «oˇ»ne"});
 4526    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4527    cx.assert_editor_state(indoc! {"
 4528        12ˇ3
 4529        4567
 4530 4531        8ˇne"});
 4532
 4533    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4534    cx.set_state(indoc! {"
 4535        The quick brown
 4536        fox juˇmps over
 4537        the lazy dog"});
 4538    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4539    assert_eq!(
 4540        cx.read_from_clipboard()
 4541            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4542        Some("fox jumps over\n".to_string())
 4543    );
 4544
 4545    // Paste with three selections, noticing how the copied full-line selection is inserted
 4546    // before the empty selections but replaces the selection that is non-empty.
 4547    cx.set_state(indoc! {"
 4548        Tˇhe quick brown
 4549        «foˇ»x jumps over
 4550        tˇhe lazy dog"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        fox jumps over
 4554        Tˇhe quick brown
 4555        fox jumps over
 4556        ˇx jumps over
 4557        fox jumps over
 4558        tˇhe lazy dog"});
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new(cx).await;
 4566    let language = Arc::new(Language::new(
 4567        LanguageConfig::default(),
 4568        Some(tree_sitter_rust::LANGUAGE.into()),
 4569    ));
 4570    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4571
 4572    // Cut an indented block, without the leading whitespace.
 4573    cx.set_state(indoc! {"
 4574        const a: B = (
 4575            c(),
 4576            «d(
 4577                e,
 4578                f
 4579            )ˇ»
 4580        );
 4581    "});
 4582    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4583    cx.assert_editor_state(indoc! {"
 4584        const a: B = (
 4585            c(),
 4586            ˇ
 4587        );
 4588    "});
 4589
 4590    // Paste it at the same position.
 4591    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4592    cx.assert_editor_state(indoc! {"
 4593        const a: B = (
 4594            c(),
 4595            d(
 4596                e,
 4597                f
 4598 4599        );
 4600    "});
 4601
 4602    // Paste it at a line with a lower indent level.
 4603    cx.set_state(indoc! {"
 4604        ˇ
 4605        const a: B = (
 4606            c(),
 4607        );
 4608    "});
 4609    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4610    cx.assert_editor_state(indoc! {"
 4611        d(
 4612            e,
 4613            f
 4614 4615        const a: B = (
 4616            c(),
 4617        );
 4618    "});
 4619
 4620    // Cut an indented block, with the leading whitespace.
 4621    cx.set_state(indoc! {"
 4622        const a: B = (
 4623            c(),
 4624        «    d(
 4625                e,
 4626                f
 4627            )
 4628        ˇ»);
 4629    "});
 4630    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4631    cx.assert_editor_state(indoc! {"
 4632        const a: B = (
 4633            c(),
 4634        ˇ);
 4635    "});
 4636
 4637    // Paste it at the same position.
 4638    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4639    cx.assert_editor_state(indoc! {"
 4640        const a: B = (
 4641            c(),
 4642            d(
 4643                e,
 4644                f
 4645            )
 4646        ˇ);
 4647    "});
 4648
 4649    // Paste it at a line with a higher indent level.
 4650    cx.set_state(indoc! {"
 4651        const a: B = (
 4652            c(),
 4653            d(
 4654                e,
 4655 4656            )
 4657        );
 4658    "});
 4659    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4660    cx.assert_editor_state(indoc! {"
 4661        const a: B = (
 4662            c(),
 4663            d(
 4664                e,
 4665                f    d(
 4666                    e,
 4667                    f
 4668                )
 4669        ˇ
 4670            )
 4671        );
 4672    "});
 4673}
 4674
 4675#[gpui::test]
 4676fn test_select_all(cx: &mut TestAppContext) {
 4677    init_test(cx, |_| {});
 4678
 4679    let view = cx.add_window(|cx| {
 4680        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4681        build_editor(buffer, cx)
 4682    });
 4683    _ = view.update(cx, |view, cx| {
 4684        view.select_all(&SelectAll, cx);
 4685        assert_eq!(
 4686            view.selections.display_ranges(cx),
 4687            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4688        );
 4689    });
 4690}
 4691
 4692#[gpui::test]
 4693fn test_select_line(cx: &mut TestAppContext) {
 4694    init_test(cx, |_| {});
 4695
 4696    let view = cx.add_window(|cx| {
 4697        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4698        build_editor(buffer, cx)
 4699    });
 4700    _ = view.update(cx, |view, cx| {
 4701        view.change_selections(None, cx, |s| {
 4702            s.select_display_ranges([
 4703                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4704                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4705                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4706                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4707            ])
 4708        });
 4709        view.select_line(&SelectLine, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            vec![
 4713                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4715            ]
 4716        );
 4717    });
 4718
 4719    _ = view.update(cx, |view, cx| {
 4720        view.select_line(&SelectLine, cx);
 4721        assert_eq!(
 4722            view.selections.display_ranges(cx),
 4723            vec![
 4724                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4725                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4726            ]
 4727        );
 4728    });
 4729
 4730    _ = view.update(cx, |view, cx| {
 4731        view.select_line(&SelectLine, cx);
 4732        assert_eq!(
 4733            view.selections.display_ranges(cx),
 4734            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4735        );
 4736    });
 4737}
 4738
 4739#[gpui::test]
 4740fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4741    init_test(cx, |_| {});
 4742
 4743    let view = cx.add_window(|cx| {
 4744        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4745        build_editor(buffer, cx)
 4746    });
 4747    _ = view.update(cx, |view, cx| {
 4748        view.fold_creases(
 4749            vec![
 4750                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4751                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4752                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4753            ],
 4754            true,
 4755            cx,
 4756        );
 4757        view.change_selections(None, cx, |s| {
 4758            s.select_display_ranges([
 4759                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4760                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4761                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4762                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4763            ])
 4764        });
 4765        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4766    });
 4767
 4768    _ = view.update(cx, |view, cx| {
 4769        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4770        assert_eq!(
 4771            view.display_text(cx),
 4772            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4773        );
 4774        assert_eq!(
 4775            view.selections.display_ranges(cx),
 4776            [
 4777                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4778                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4779                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4780                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4781            ]
 4782        );
 4783    });
 4784
 4785    _ = view.update(cx, |view, cx| {
 4786        view.change_selections(None, cx, |s| {
 4787            s.select_display_ranges([
 4788                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4789            ])
 4790        });
 4791        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4792        assert_eq!(
 4793            view.display_text(cx),
 4794            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4795        );
 4796        assert_eq!(
 4797            view.selections.display_ranges(cx),
 4798            [
 4799                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4800                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4801                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4802                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4803                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4804                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4805                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4806                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4807            ]
 4808        );
 4809    });
 4810}
 4811
 4812#[gpui::test]
 4813async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4814    init_test(cx, |_| {});
 4815
 4816    let mut cx = EditorTestContext::new(cx).await;
 4817
 4818    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4819    cx.set_state(indoc!(
 4820        r#"abc
 4821           defˇghi
 4822
 4823           jk
 4824           nlmo
 4825           "#
 4826    ));
 4827
 4828    cx.update_editor(|editor, cx| {
 4829        editor.add_selection_above(&Default::default(), cx);
 4830    });
 4831
 4832    cx.assert_editor_state(indoc!(
 4833        r#"abcˇ
 4834           defˇghi
 4835
 4836           jk
 4837           nlmo
 4838           "#
 4839    ));
 4840
 4841    cx.update_editor(|editor, cx| {
 4842        editor.add_selection_above(&Default::default(), cx);
 4843    });
 4844
 4845    cx.assert_editor_state(indoc!(
 4846        r#"abcˇ
 4847            defˇghi
 4848
 4849            jk
 4850            nlmo
 4851            "#
 4852    ));
 4853
 4854    cx.update_editor(|view, cx| {
 4855        view.add_selection_below(&Default::default(), cx);
 4856    });
 4857
 4858    cx.assert_editor_state(indoc!(
 4859        r#"abc
 4860           defˇghi
 4861
 4862           jk
 4863           nlmo
 4864           "#
 4865    ));
 4866
 4867    cx.update_editor(|view, cx| {
 4868        view.undo_selection(&Default::default(), cx);
 4869    });
 4870
 4871    cx.assert_editor_state(indoc!(
 4872        r#"abcˇ
 4873           defˇghi
 4874
 4875           jk
 4876           nlmo
 4877           "#
 4878    ));
 4879
 4880    cx.update_editor(|view, cx| {
 4881        view.redo_selection(&Default::default(), cx);
 4882    });
 4883
 4884    cx.assert_editor_state(indoc!(
 4885        r#"abc
 4886           defˇghi
 4887
 4888           jk
 4889           nlmo
 4890           "#
 4891    ));
 4892
 4893    cx.update_editor(|view, cx| {
 4894        view.add_selection_below(&Default::default(), cx);
 4895    });
 4896
 4897    cx.assert_editor_state(indoc!(
 4898        r#"abc
 4899           defˇghi
 4900
 4901           jk
 4902           nlmˇo
 4903           "#
 4904    ));
 4905
 4906    cx.update_editor(|view, cx| {
 4907        view.add_selection_below(&Default::default(), cx);
 4908    });
 4909
 4910    cx.assert_editor_state(indoc!(
 4911        r#"abc
 4912           defˇghi
 4913
 4914           jk
 4915           nlmˇo
 4916           "#
 4917    ));
 4918
 4919    // change selections
 4920    cx.set_state(indoc!(
 4921        r#"abc
 4922           def«ˇg»hi
 4923
 4924           jk
 4925           nlmo
 4926           "#
 4927    ));
 4928
 4929    cx.update_editor(|view, cx| {
 4930        view.add_selection_below(&Default::default(), cx);
 4931    });
 4932
 4933    cx.assert_editor_state(indoc!(
 4934        r#"abc
 4935           def«ˇg»hi
 4936
 4937           jk
 4938           nlm«ˇo»
 4939           "#
 4940    ));
 4941
 4942    cx.update_editor(|view, cx| {
 4943        view.add_selection_below(&Default::default(), cx);
 4944    });
 4945
 4946    cx.assert_editor_state(indoc!(
 4947        r#"abc
 4948           def«ˇg»hi
 4949
 4950           jk
 4951           nlm«ˇo»
 4952           "#
 4953    ));
 4954
 4955    cx.update_editor(|view, cx| {
 4956        view.add_selection_above(&Default::default(), cx);
 4957    });
 4958
 4959    cx.assert_editor_state(indoc!(
 4960        r#"abc
 4961           def«ˇg»hi
 4962
 4963           jk
 4964           nlmo
 4965           "#
 4966    ));
 4967
 4968    cx.update_editor(|view, cx| {
 4969        view.add_selection_above(&Default::default(), cx);
 4970    });
 4971
 4972    cx.assert_editor_state(indoc!(
 4973        r#"abc
 4974           def«ˇg»hi
 4975
 4976           jk
 4977           nlmo
 4978           "#
 4979    ));
 4980
 4981    // Change selections again
 4982    cx.set_state(indoc!(
 4983        r#"a«bc
 4984           defgˇ»hi
 4985
 4986           jk
 4987           nlmo
 4988           "#
 4989    ));
 4990
 4991    cx.update_editor(|view, cx| {
 4992        view.add_selection_below(&Default::default(), cx);
 4993    });
 4994
 4995    cx.assert_editor_state(indoc!(
 4996        r#"a«bcˇ»
 4997           d«efgˇ»hi
 4998
 4999           j«kˇ»
 5000           nlmo
 5001           "#
 5002    ));
 5003
 5004    cx.update_editor(|view, cx| {
 5005        view.add_selection_below(&Default::default(), cx);
 5006    });
 5007    cx.assert_editor_state(indoc!(
 5008        r#"a«bcˇ»
 5009           d«efgˇ»hi
 5010
 5011           j«kˇ»
 5012           n«lmoˇ»
 5013           "#
 5014    ));
 5015    cx.update_editor(|view, cx| {
 5016        view.add_selection_above(&Default::default(), cx);
 5017    });
 5018
 5019    cx.assert_editor_state(indoc!(
 5020        r#"a«bcˇ»
 5021           d«efgˇ»hi
 5022
 5023           j«kˇ»
 5024           nlmo
 5025           "#
 5026    ));
 5027
 5028    // Change selections again
 5029    cx.set_state(indoc!(
 5030        r#"abc
 5031           d«ˇefghi
 5032
 5033           jk
 5034           nlm»o
 5035           "#
 5036    ));
 5037
 5038    cx.update_editor(|view, cx| {
 5039        view.add_selection_above(&Default::default(), cx);
 5040    });
 5041
 5042    cx.assert_editor_state(indoc!(
 5043        r#"a«ˇbc»
 5044           d«ˇef»ghi
 5045
 5046           j«ˇk»
 5047           n«ˇlm»o
 5048           "#
 5049    ));
 5050
 5051    cx.update_editor(|view, cx| {
 5052        view.add_selection_below(&Default::default(), cx);
 5053    });
 5054
 5055    cx.assert_editor_state(indoc!(
 5056        r#"abc
 5057           d«ˇef»ghi
 5058
 5059           j«ˇk»
 5060           n«ˇlm»o
 5061           "#
 5062    ));
 5063}
 5064
 5065#[gpui::test]
 5066async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5067    init_test(cx, |_| {});
 5068
 5069    let mut cx = EditorTestContext::new(cx).await;
 5070    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5071
 5072    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5073        .unwrap();
 5074    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5075
 5076    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5077        .unwrap();
 5078    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5079
 5080    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5081    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5082
 5083    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5084    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5085
 5086    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5087        .unwrap();
 5088    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5089
 5090    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5091        .unwrap();
 5092    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5093}
 5094
 5095#[gpui::test]
 5096async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5097    init_test(cx, |_| {});
 5098
 5099    let mut cx = EditorTestContext::new(cx).await;
 5100    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5101
 5102    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5103        .unwrap();
 5104    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5105}
 5106
 5107#[gpui::test]
 5108async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5109    init_test(cx, |_| {});
 5110
 5111    let mut cx = EditorTestContext::new(cx).await;
 5112    cx.set_state(
 5113        r#"let foo = 2;
 5114lˇet foo = 2;
 5115let fooˇ = 2;
 5116let foo = 2;
 5117let foo = ˇ2;"#,
 5118    );
 5119
 5120    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5121        .unwrap();
 5122    cx.assert_editor_state(
 5123        r#"let foo = 2;
 5124«letˇ» foo = 2;
 5125let «fooˇ» = 2;
 5126let foo = 2;
 5127let foo = «2ˇ»;"#,
 5128    );
 5129
 5130    // noop for multiple selections with different contents
 5131    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5132        .unwrap();
 5133    cx.assert_editor_state(
 5134        r#"let foo = 2;
 5135«letˇ» foo = 2;
 5136let «fooˇ» = 2;
 5137let foo = 2;
 5138let foo = «2ˇ»;"#,
 5139    );
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |_| {});
 5145
 5146    let mut cx =
 5147        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5148
 5149    cx.assert_editor_state(indoc! {"
 5150        ˇbbb
 5151        ccc
 5152
 5153        bbb
 5154        ccc
 5155        "});
 5156    cx.dispatch_action(SelectPrevious::default());
 5157    cx.assert_editor_state(indoc! {"
 5158                «bbbˇ»
 5159                ccc
 5160
 5161                bbb
 5162                ccc
 5163                "});
 5164    cx.dispatch_action(SelectPrevious::default());
 5165    cx.assert_editor_state(indoc! {"
 5166                «bbbˇ»
 5167                ccc
 5168
 5169                «bbbˇ»
 5170                ccc
 5171                "});
 5172}
 5173
 5174#[gpui::test]
 5175async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5176    init_test(cx, |_| {});
 5177
 5178    let mut cx = EditorTestContext::new(cx).await;
 5179    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5180
 5181    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5182        .unwrap();
 5183    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5184
 5185    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5186        .unwrap();
 5187    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5188
 5189    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5190    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5191
 5192    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5193    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5194
 5195    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5196        .unwrap();
 5197    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5198
 5199    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5200        .unwrap();
 5201    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5202
 5203    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5204        .unwrap();
 5205    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5206}
 5207
 5208#[gpui::test]
 5209async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5210    init_test(cx, |_| {});
 5211
 5212    let mut cx = EditorTestContext::new(cx).await;
 5213    cx.set_state(
 5214        r#"let foo = 2;
 5215lˇet foo = 2;
 5216let fooˇ = 2;
 5217let foo = 2;
 5218let foo = ˇ2;"#,
 5219    );
 5220
 5221    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5222        .unwrap();
 5223    cx.assert_editor_state(
 5224        r#"let foo = 2;
 5225«letˇ» foo = 2;
 5226let «fooˇ» = 2;
 5227let foo = 2;
 5228let foo = «2ˇ»;"#,
 5229    );
 5230
 5231    // noop for multiple selections with different contents
 5232    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5233        .unwrap();
 5234    cx.assert_editor_state(
 5235        r#"let foo = 2;
 5236«letˇ» foo = 2;
 5237let «fooˇ» = 2;
 5238let foo = 2;
 5239let foo = «2ˇ»;"#,
 5240    );
 5241}
 5242
 5243#[gpui::test]
 5244async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5245    init_test(cx, |_| {});
 5246
 5247    let mut cx = EditorTestContext::new(cx).await;
 5248    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5249
 5250    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5251        .unwrap();
 5252    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5253
 5254    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5255        .unwrap();
 5256    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5257
 5258    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5259    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5260
 5261    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5262    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5263
 5264    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5265        .unwrap();
 5266    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5267
 5268    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5269        .unwrap();
 5270    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5271}
 5272
 5273#[gpui::test]
 5274async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5275    init_test(cx, |_| {});
 5276
 5277    let language = Arc::new(Language::new(
 5278        LanguageConfig::default(),
 5279        Some(tree_sitter_rust::LANGUAGE.into()),
 5280    ));
 5281
 5282    let text = r#"
 5283        use mod1::mod2::{mod3, mod4};
 5284
 5285        fn fn_1(param1: bool, param2: &str) {
 5286            let var1 = "text";
 5287        }
 5288    "#
 5289    .unindent();
 5290
 5291    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5293    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5294
 5295    editor
 5296        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5297        .await;
 5298
 5299    editor.update(cx, |view, cx| {
 5300        view.change_selections(None, cx, |s| {
 5301            s.select_display_ranges([
 5302                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5303                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5304                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5305            ]);
 5306        });
 5307        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5308    });
 5309    editor.update(cx, |editor, cx| {
 5310        assert_text_with_selections(
 5311            editor,
 5312            indoc! {r#"
 5313                use mod1::mod2::{mod3, «mod4ˇ»};
 5314
 5315                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5316                    let var1 = "«textˇ»";
 5317                }
 5318            "#},
 5319            cx,
 5320        );
 5321    });
 5322
 5323    editor.update(cx, |view, cx| {
 5324        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5325    });
 5326    editor.update(cx, |editor, cx| {
 5327        assert_text_with_selections(
 5328            editor,
 5329            indoc! {r#"
 5330                use mod1::mod2::«{mod3, mod4}ˇ»;
 5331
 5332                «ˇfn fn_1(param1: bool, param2: &str) {
 5333                    let var1 = "text";
 5334 5335            "#},
 5336            cx,
 5337        );
 5338    });
 5339
 5340    editor.update(cx, |view, cx| {
 5341        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5342    });
 5343    assert_eq!(
 5344        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5345        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5346    );
 5347
 5348    // Trying to expand the selected syntax node one more time has no effect.
 5349    editor.update(cx, |view, cx| {
 5350        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5351    });
 5352    assert_eq!(
 5353        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5354        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5355    );
 5356
 5357    editor.update(cx, |view, cx| {
 5358        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5359    });
 5360    editor.update(cx, |editor, cx| {
 5361        assert_text_with_selections(
 5362            editor,
 5363            indoc! {r#"
 5364                use mod1::mod2::«{mod3, mod4}ˇ»;
 5365
 5366                «ˇfn fn_1(param1: bool, param2: &str) {
 5367                    let var1 = "text";
 5368 5369            "#},
 5370            cx,
 5371        );
 5372    });
 5373
 5374    editor.update(cx, |view, cx| {
 5375        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5376    });
 5377    editor.update(cx, |editor, cx| {
 5378        assert_text_with_selections(
 5379            editor,
 5380            indoc! {r#"
 5381                use mod1::mod2::{mod3, «mod4ˇ»};
 5382
 5383                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5384                    let var1 = "«textˇ»";
 5385                }
 5386            "#},
 5387            cx,
 5388        );
 5389    });
 5390
 5391    editor.update(cx, |view, cx| {
 5392        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5393    });
 5394    editor.update(cx, |editor, cx| {
 5395        assert_text_with_selections(
 5396            editor,
 5397            indoc! {r#"
 5398                use mod1::mod2::{mod3, mo«ˇ»d4};
 5399
 5400                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5401                    let var1 = "te«ˇ»xt";
 5402                }
 5403            "#},
 5404            cx,
 5405        );
 5406    });
 5407
 5408    // Trying to shrink the selected syntax node one more time has no effect.
 5409    editor.update(cx, |view, cx| {
 5410        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5411    });
 5412    editor.update(cx, |editor, cx| {
 5413        assert_text_with_selections(
 5414            editor,
 5415            indoc! {r#"
 5416                use mod1::mod2::{mod3, mo«ˇ»d4};
 5417
 5418                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5419                    let var1 = "te«ˇ»xt";
 5420                }
 5421            "#},
 5422            cx,
 5423        );
 5424    });
 5425
 5426    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5427    // a fold.
 5428    editor.update(cx, |view, cx| {
 5429        view.fold_creases(
 5430            vec![
 5431                Crease::simple(
 5432                    Point::new(0, 21)..Point::new(0, 24),
 5433                    FoldPlaceholder::test(),
 5434                ),
 5435                Crease::simple(
 5436                    Point::new(3, 20)..Point::new(3, 22),
 5437                    FoldPlaceholder::test(),
 5438                ),
 5439            ],
 5440            true,
 5441            cx,
 5442        );
 5443        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5444    });
 5445    editor.update(cx, |editor, cx| {
 5446        assert_text_with_selections(
 5447            editor,
 5448            indoc! {r#"
 5449                use mod1::mod2::«{mod3, mod4}ˇ»;
 5450
 5451                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5452                    «let var1 = "text";ˇ»
 5453                }
 5454            "#},
 5455            cx,
 5456        );
 5457    });
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let language = Arc::new(
 5465        Language::new(
 5466            LanguageConfig {
 5467                brackets: BracketPairConfig {
 5468                    pairs: vec![
 5469                        BracketPair {
 5470                            start: "{".to_string(),
 5471                            end: "}".to_string(),
 5472                            close: false,
 5473                            surround: false,
 5474                            newline: true,
 5475                        },
 5476                        BracketPair {
 5477                            start: "(".to_string(),
 5478                            end: ")".to_string(),
 5479                            close: false,
 5480                            surround: false,
 5481                            newline: true,
 5482                        },
 5483                    ],
 5484                    ..Default::default()
 5485                },
 5486                ..Default::default()
 5487            },
 5488            Some(tree_sitter_rust::LANGUAGE.into()),
 5489        )
 5490        .with_indents_query(
 5491            r#"
 5492                (_ "(" ")" @end) @indent
 5493                (_ "{" "}" @end) @indent
 5494            "#,
 5495        )
 5496        .unwrap(),
 5497    );
 5498
 5499    let text = "fn a() {}";
 5500
 5501    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5502    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5503    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5504    editor
 5505        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5506        .await;
 5507
 5508    editor.update(cx, |editor, cx| {
 5509        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5510        editor.newline(&Newline, cx);
 5511        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5512        assert_eq!(
 5513            editor.selections.ranges(cx),
 5514            &[
 5515                Point::new(1, 4)..Point::new(1, 4),
 5516                Point::new(3, 4)..Point::new(3, 4),
 5517                Point::new(5, 0)..Point::new(5, 0)
 5518            ]
 5519        );
 5520    });
 5521}
 5522
 5523#[gpui::test]
 5524async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5525    init_test(cx, |_| {});
 5526
 5527    let mut cx = EditorTestContext::new(cx).await;
 5528
 5529    let language = Arc::new(Language::new(
 5530        LanguageConfig {
 5531            brackets: BracketPairConfig {
 5532                pairs: vec![
 5533                    BracketPair {
 5534                        start: "{".to_string(),
 5535                        end: "}".to_string(),
 5536                        close: true,
 5537                        surround: true,
 5538                        newline: true,
 5539                    },
 5540                    BracketPair {
 5541                        start: "(".to_string(),
 5542                        end: ")".to_string(),
 5543                        close: true,
 5544                        surround: true,
 5545                        newline: true,
 5546                    },
 5547                    BracketPair {
 5548                        start: "/*".to_string(),
 5549                        end: " */".to_string(),
 5550                        close: true,
 5551                        surround: true,
 5552                        newline: true,
 5553                    },
 5554                    BracketPair {
 5555                        start: "[".to_string(),
 5556                        end: "]".to_string(),
 5557                        close: false,
 5558                        surround: false,
 5559                        newline: true,
 5560                    },
 5561                    BracketPair {
 5562                        start: "\"".to_string(),
 5563                        end: "\"".to_string(),
 5564                        close: true,
 5565                        surround: true,
 5566                        newline: false,
 5567                    },
 5568                    BracketPair {
 5569                        start: "<".to_string(),
 5570                        end: ">".to_string(),
 5571                        close: false,
 5572                        surround: true,
 5573                        newline: true,
 5574                    },
 5575                ],
 5576                ..Default::default()
 5577            },
 5578            autoclose_before: "})]".to_string(),
 5579            ..Default::default()
 5580        },
 5581        Some(tree_sitter_rust::LANGUAGE.into()),
 5582    ));
 5583
 5584    cx.language_registry().add(language.clone());
 5585    cx.update_buffer(|buffer, cx| {
 5586        buffer.set_language(Some(language), cx);
 5587    });
 5588
 5589    cx.set_state(
 5590        &r#"
 5591            🏀ˇ
 5592            εˇ
 5593            ❤️ˇ
 5594        "#
 5595        .unindent(),
 5596    );
 5597
 5598    // autoclose multiple nested brackets at multiple cursors
 5599    cx.update_editor(|view, cx| {
 5600        view.handle_input("{", cx);
 5601        view.handle_input("{", cx);
 5602        view.handle_input("{", cx);
 5603    });
 5604    cx.assert_editor_state(
 5605        &"
 5606            🏀{{{ˇ}}}
 5607            ε{{{ˇ}}}
 5608            ❤️{{{ˇ}}}
 5609        "
 5610        .unindent(),
 5611    );
 5612
 5613    // insert a different closing bracket
 5614    cx.update_editor(|view, cx| {
 5615        view.handle_input(")", cx);
 5616    });
 5617    cx.assert_editor_state(
 5618        &"
 5619            🏀{{{)ˇ}}}
 5620            ε{{{)ˇ}}}
 5621            ❤️{{{)ˇ}}}
 5622        "
 5623        .unindent(),
 5624    );
 5625
 5626    // skip over the auto-closed brackets when typing a closing bracket
 5627    cx.update_editor(|view, cx| {
 5628        view.move_right(&MoveRight, cx);
 5629        view.handle_input("}", cx);
 5630        view.handle_input("}", cx);
 5631        view.handle_input("}", cx);
 5632    });
 5633    cx.assert_editor_state(
 5634        &"
 5635            🏀{{{)}}}}ˇ
 5636            ε{{{)}}}}ˇ
 5637            ❤️{{{)}}}}ˇ
 5638        "
 5639        .unindent(),
 5640    );
 5641
 5642    // autoclose multi-character pairs
 5643    cx.set_state(
 5644        &"
 5645            ˇ
 5646            ˇ
 5647        "
 5648        .unindent(),
 5649    );
 5650    cx.update_editor(|view, cx| {
 5651        view.handle_input("/", cx);
 5652        view.handle_input("*", cx);
 5653    });
 5654    cx.assert_editor_state(
 5655        &"
 5656            /*ˇ */
 5657            /*ˇ */
 5658        "
 5659        .unindent(),
 5660    );
 5661
 5662    // one cursor autocloses a multi-character pair, one cursor
 5663    // does not autoclose.
 5664    cx.set_state(
 5665        &"
 5666 5667            ˇ
 5668        "
 5669        .unindent(),
 5670    );
 5671    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5672    cx.assert_editor_state(
 5673        &"
 5674            /*ˇ */
 5675 5676        "
 5677        .unindent(),
 5678    );
 5679
 5680    // Don't autoclose if the next character isn't whitespace and isn't
 5681    // listed in the language's "autoclose_before" section.
 5682    cx.set_state("ˇa b");
 5683    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5684    cx.assert_editor_state("{ˇa b");
 5685
 5686    // Don't autoclose if `close` is false for the bracket pair
 5687    cx.set_state("ˇ");
 5688    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5689    cx.assert_editor_state("");
 5690
 5691    // Surround with brackets if text is selected
 5692    cx.set_state("«aˇ» b");
 5693    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5694    cx.assert_editor_state("{«aˇ»} b");
 5695
 5696    // Autclose pair where the start and end characters are the same
 5697    cx.set_state("");
 5698    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5699    cx.assert_editor_state("a\"ˇ\"");
 5700    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5701    cx.assert_editor_state("a\"\"ˇ");
 5702
 5703    // Don't autoclose pair if autoclose is disabled
 5704    cx.set_state("ˇ");
 5705    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5706    cx.assert_editor_state("");
 5707
 5708    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5709    cx.set_state("«aˇ» b");
 5710    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5711    cx.assert_editor_state("<«aˇ»> b");
 5712}
 5713
 5714#[gpui::test]
 5715async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5716    init_test(cx, |settings| {
 5717        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5718    });
 5719
 5720    let mut cx = EditorTestContext::new(cx).await;
 5721
 5722    let language = Arc::new(Language::new(
 5723        LanguageConfig {
 5724            brackets: BracketPairConfig {
 5725                pairs: vec![
 5726                    BracketPair {
 5727                        start: "{".to_string(),
 5728                        end: "}".to_string(),
 5729                        close: true,
 5730                        surround: true,
 5731                        newline: true,
 5732                    },
 5733                    BracketPair {
 5734                        start: "(".to_string(),
 5735                        end: ")".to_string(),
 5736                        close: true,
 5737                        surround: true,
 5738                        newline: true,
 5739                    },
 5740                    BracketPair {
 5741                        start: "[".to_string(),
 5742                        end: "]".to_string(),
 5743                        close: false,
 5744                        surround: false,
 5745                        newline: true,
 5746                    },
 5747                ],
 5748                ..Default::default()
 5749            },
 5750            autoclose_before: "})]".to_string(),
 5751            ..Default::default()
 5752        },
 5753        Some(tree_sitter_rust::LANGUAGE.into()),
 5754    ));
 5755
 5756    cx.language_registry().add(language.clone());
 5757    cx.update_buffer(|buffer, cx| {
 5758        buffer.set_language(Some(language), cx);
 5759    });
 5760
 5761    cx.set_state(
 5762        &"
 5763            ˇ
 5764            ˇ
 5765            ˇ
 5766        "
 5767        .unindent(),
 5768    );
 5769
 5770    // ensure only matching closing brackets are skipped over
 5771    cx.update_editor(|view, cx| {
 5772        view.handle_input("}", cx);
 5773        view.move_left(&MoveLeft, cx);
 5774        view.handle_input(")", cx);
 5775        view.move_left(&MoveLeft, cx);
 5776    });
 5777    cx.assert_editor_state(
 5778        &"
 5779            ˇ)}
 5780            ˇ)}
 5781            ˇ)}
 5782        "
 5783        .unindent(),
 5784    );
 5785
 5786    // skip-over closing brackets at multiple cursors
 5787    cx.update_editor(|view, cx| {
 5788        view.handle_input(")", cx);
 5789        view.handle_input("}", cx);
 5790    });
 5791    cx.assert_editor_state(
 5792        &"
 5793            )}ˇ
 5794            )}ˇ
 5795            )}ˇ
 5796        "
 5797        .unindent(),
 5798    );
 5799
 5800    // ignore non-close brackets
 5801    cx.update_editor(|view, cx| {
 5802        view.handle_input("]", cx);
 5803        view.move_left(&MoveLeft, cx);
 5804        view.handle_input("]", cx);
 5805    });
 5806    cx.assert_editor_state(
 5807        &"
 5808            )}]ˇ]
 5809            )}]ˇ]
 5810            )}]ˇ]
 5811        "
 5812        .unindent(),
 5813    );
 5814}
 5815
 5816#[gpui::test]
 5817async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5818    init_test(cx, |_| {});
 5819
 5820    let mut cx = EditorTestContext::new(cx).await;
 5821
 5822    let html_language = Arc::new(
 5823        Language::new(
 5824            LanguageConfig {
 5825                name: "HTML".into(),
 5826                brackets: BracketPairConfig {
 5827                    pairs: vec![
 5828                        BracketPair {
 5829                            start: "<".into(),
 5830                            end: ">".into(),
 5831                            close: true,
 5832                            ..Default::default()
 5833                        },
 5834                        BracketPair {
 5835                            start: "{".into(),
 5836                            end: "}".into(),
 5837                            close: true,
 5838                            ..Default::default()
 5839                        },
 5840                        BracketPair {
 5841                            start: "(".into(),
 5842                            end: ")".into(),
 5843                            close: true,
 5844                            ..Default::default()
 5845                        },
 5846                    ],
 5847                    ..Default::default()
 5848                },
 5849                autoclose_before: "})]>".into(),
 5850                ..Default::default()
 5851            },
 5852            Some(tree_sitter_html::language()),
 5853        )
 5854        .with_injection_query(
 5855            r#"
 5856            (script_element
 5857                (raw_text) @content
 5858                (#set! "language" "javascript"))
 5859            "#,
 5860        )
 5861        .unwrap(),
 5862    );
 5863
 5864    let javascript_language = Arc::new(Language::new(
 5865        LanguageConfig {
 5866            name: "JavaScript".into(),
 5867            brackets: BracketPairConfig {
 5868                pairs: vec![
 5869                    BracketPair {
 5870                        start: "/*".into(),
 5871                        end: " */".into(),
 5872                        close: true,
 5873                        ..Default::default()
 5874                    },
 5875                    BracketPair {
 5876                        start: "{".into(),
 5877                        end: "}".into(),
 5878                        close: true,
 5879                        ..Default::default()
 5880                    },
 5881                    BracketPair {
 5882                        start: "(".into(),
 5883                        end: ")".into(),
 5884                        close: true,
 5885                        ..Default::default()
 5886                    },
 5887                ],
 5888                ..Default::default()
 5889            },
 5890            autoclose_before: "})]>".into(),
 5891            ..Default::default()
 5892        },
 5893        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5894    ));
 5895
 5896    cx.language_registry().add(html_language.clone());
 5897    cx.language_registry().add(javascript_language.clone());
 5898
 5899    cx.update_buffer(|buffer, cx| {
 5900        buffer.set_language(Some(html_language), cx);
 5901    });
 5902
 5903    cx.set_state(
 5904        &r#"
 5905            <body>ˇ
 5906                <script>
 5907                    var x = 1;ˇ
 5908                </script>
 5909            </body>ˇ
 5910        "#
 5911        .unindent(),
 5912    );
 5913
 5914    // Precondition: different languages are active at different locations.
 5915    cx.update_editor(|editor, cx| {
 5916        let snapshot = editor.snapshot(cx);
 5917        let cursors = editor.selections.ranges::<usize>(cx);
 5918        let languages = cursors
 5919            .iter()
 5920            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5921            .collect::<Vec<_>>();
 5922        assert_eq!(
 5923            languages,
 5924            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5925        );
 5926    });
 5927
 5928    // Angle brackets autoclose in HTML, but not JavaScript.
 5929    cx.update_editor(|editor, cx| {
 5930        editor.handle_input("<", cx);
 5931        editor.handle_input("a", cx);
 5932    });
 5933    cx.assert_editor_state(
 5934        &r#"
 5935            <body><aˇ>
 5936                <script>
 5937                    var x = 1;<aˇ
 5938                </script>
 5939            </body><aˇ>
 5940        "#
 5941        .unindent(),
 5942    );
 5943
 5944    // Curly braces and parens autoclose in both HTML and JavaScript.
 5945    cx.update_editor(|editor, cx| {
 5946        editor.handle_input(" b=", cx);
 5947        editor.handle_input("{", cx);
 5948        editor.handle_input("c", cx);
 5949        editor.handle_input("(", cx);
 5950    });
 5951    cx.assert_editor_state(
 5952        &r#"
 5953            <body><a b={c(ˇ)}>
 5954                <script>
 5955                    var x = 1;<a b={c(ˇ)}
 5956                </script>
 5957            </body><a b={c(ˇ)}>
 5958        "#
 5959        .unindent(),
 5960    );
 5961
 5962    // Brackets that were already autoclosed are skipped.
 5963    cx.update_editor(|editor, cx| {
 5964        editor.handle_input(")", cx);
 5965        editor.handle_input("d", cx);
 5966        editor.handle_input("}", cx);
 5967    });
 5968    cx.assert_editor_state(
 5969        &r#"
 5970            <body><a b={c()d}ˇ>
 5971                <script>
 5972                    var x = 1;<a b={c()d}ˇ
 5973                </script>
 5974            </body><a b={c()d}ˇ>
 5975        "#
 5976        .unindent(),
 5977    );
 5978    cx.update_editor(|editor, cx| {
 5979        editor.handle_input(">", cx);
 5980    });
 5981    cx.assert_editor_state(
 5982        &r#"
 5983            <body><a b={c()d}>ˇ
 5984                <script>
 5985                    var x = 1;<a b={c()d}>ˇ
 5986                </script>
 5987            </body><a b={c()d}>ˇ
 5988        "#
 5989        .unindent(),
 5990    );
 5991
 5992    // Reset
 5993    cx.set_state(
 5994        &r#"
 5995            <body>ˇ
 5996                <script>
 5997                    var x = 1;ˇ
 5998                </script>
 5999            </body>ˇ
 6000        "#
 6001        .unindent(),
 6002    );
 6003
 6004    cx.update_editor(|editor, cx| {
 6005        editor.handle_input("<", cx);
 6006    });
 6007    cx.assert_editor_state(
 6008        &r#"
 6009            <body><ˇ>
 6010                <script>
 6011                    var x = 1;<ˇ
 6012                </script>
 6013            </body><ˇ>
 6014        "#
 6015        .unindent(),
 6016    );
 6017
 6018    // When backspacing, the closing angle brackets are removed.
 6019    cx.update_editor(|editor, cx| {
 6020        editor.backspace(&Backspace, cx);
 6021    });
 6022    cx.assert_editor_state(
 6023        &r#"
 6024            <body>ˇ
 6025                <script>
 6026                    var x = 1;ˇ
 6027                </script>
 6028            </body>ˇ
 6029        "#
 6030        .unindent(),
 6031    );
 6032
 6033    // Block comments autoclose in JavaScript, but not HTML.
 6034    cx.update_editor(|editor, cx| {
 6035        editor.handle_input("/", cx);
 6036        editor.handle_input("*", cx);
 6037    });
 6038    cx.assert_editor_state(
 6039        &r#"
 6040            <body>/*ˇ
 6041                <script>
 6042                    var x = 1;/*ˇ */
 6043                </script>
 6044            </body>/*ˇ
 6045        "#
 6046        .unindent(),
 6047    );
 6048}
 6049
 6050#[gpui::test]
 6051async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6052    init_test(cx, |_| {});
 6053
 6054    let mut cx = EditorTestContext::new(cx).await;
 6055
 6056    let rust_language = Arc::new(
 6057        Language::new(
 6058            LanguageConfig {
 6059                name: "Rust".into(),
 6060                brackets: serde_json::from_value(json!([
 6061                    { "start": "{", "end": "}", "close": true, "newline": true },
 6062                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6063                ]))
 6064                .unwrap(),
 6065                autoclose_before: "})]>".into(),
 6066                ..Default::default()
 6067            },
 6068            Some(tree_sitter_rust::LANGUAGE.into()),
 6069        )
 6070        .with_override_query("(string_literal) @string")
 6071        .unwrap(),
 6072    );
 6073
 6074    cx.language_registry().add(rust_language.clone());
 6075    cx.update_buffer(|buffer, cx| {
 6076        buffer.set_language(Some(rust_language), cx);
 6077    });
 6078
 6079    cx.set_state(
 6080        &r#"
 6081            let x = ˇ
 6082        "#
 6083        .unindent(),
 6084    );
 6085
 6086    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6087    cx.update_editor(|editor, cx| {
 6088        editor.handle_input("\"", cx);
 6089    });
 6090    cx.assert_editor_state(
 6091        &r#"
 6092            let x = "ˇ"
 6093        "#
 6094        .unindent(),
 6095    );
 6096
 6097    // Inserting another quotation mark. The cursor moves across the existing
 6098    // automatically-inserted quotation mark.
 6099    cx.update_editor(|editor, cx| {
 6100        editor.handle_input("\"", cx);
 6101    });
 6102    cx.assert_editor_state(
 6103        &r#"
 6104            let x = ""ˇ
 6105        "#
 6106        .unindent(),
 6107    );
 6108
 6109    // Reset
 6110    cx.set_state(
 6111        &r#"
 6112            let x = ˇ
 6113        "#
 6114        .unindent(),
 6115    );
 6116
 6117    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6118    cx.update_editor(|editor, cx| {
 6119        editor.handle_input("\"", cx);
 6120        editor.handle_input(" ", cx);
 6121        editor.move_left(&Default::default(), cx);
 6122        editor.handle_input("\\", cx);
 6123        editor.handle_input("\"", cx);
 6124    });
 6125    cx.assert_editor_state(
 6126        &r#"
 6127            let x = "\"ˇ "
 6128        "#
 6129        .unindent(),
 6130    );
 6131
 6132    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6133    // mark. Nothing is inserted.
 6134    cx.update_editor(|editor, cx| {
 6135        editor.move_right(&Default::default(), cx);
 6136        editor.handle_input("\"", cx);
 6137    });
 6138    cx.assert_editor_state(
 6139        &r#"
 6140            let x = "\" "ˇ
 6141        "#
 6142        .unindent(),
 6143    );
 6144}
 6145
 6146#[gpui::test]
 6147async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6148    init_test(cx, |_| {});
 6149
 6150    let language = Arc::new(Language::new(
 6151        LanguageConfig {
 6152            brackets: BracketPairConfig {
 6153                pairs: vec![
 6154                    BracketPair {
 6155                        start: "{".to_string(),
 6156                        end: "}".to_string(),
 6157                        close: true,
 6158                        surround: true,
 6159                        newline: true,
 6160                    },
 6161                    BracketPair {
 6162                        start: "/* ".to_string(),
 6163                        end: "*/".to_string(),
 6164                        close: true,
 6165                        surround: true,
 6166                        ..Default::default()
 6167                    },
 6168                ],
 6169                ..Default::default()
 6170            },
 6171            ..Default::default()
 6172        },
 6173        Some(tree_sitter_rust::LANGUAGE.into()),
 6174    ));
 6175
 6176    let text = r#"
 6177        a
 6178        b
 6179        c
 6180    "#
 6181    .unindent();
 6182
 6183    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6184    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6185    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6186    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6187        .await;
 6188
 6189    view.update(cx, |view, cx| {
 6190        view.change_selections(None, cx, |s| {
 6191            s.select_display_ranges([
 6192                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6193                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6194                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6195            ])
 6196        });
 6197
 6198        view.handle_input("{", cx);
 6199        view.handle_input("{", cx);
 6200        view.handle_input("{", cx);
 6201        assert_eq!(
 6202            view.text(cx),
 6203            "
 6204                {{{a}}}
 6205                {{{b}}}
 6206                {{{c}}}
 6207            "
 6208            .unindent()
 6209        );
 6210        assert_eq!(
 6211            view.selections.display_ranges(cx),
 6212            [
 6213                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6214                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6215                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6216            ]
 6217        );
 6218
 6219        view.undo(&Undo, cx);
 6220        view.undo(&Undo, cx);
 6221        view.undo(&Undo, cx);
 6222        assert_eq!(
 6223            view.text(cx),
 6224            "
 6225                a
 6226                b
 6227                c
 6228            "
 6229            .unindent()
 6230        );
 6231        assert_eq!(
 6232            view.selections.display_ranges(cx),
 6233            [
 6234                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6235                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6236                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6237            ]
 6238        );
 6239
 6240        // Ensure inserting the first character of a multi-byte bracket pair
 6241        // doesn't surround the selections with the bracket.
 6242        view.handle_input("/", cx);
 6243        assert_eq!(
 6244            view.text(cx),
 6245            "
 6246                /
 6247                /
 6248                /
 6249            "
 6250            .unindent()
 6251        );
 6252        assert_eq!(
 6253            view.selections.display_ranges(cx),
 6254            [
 6255                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6256                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6257                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6258            ]
 6259        );
 6260
 6261        view.undo(&Undo, cx);
 6262        assert_eq!(
 6263            view.text(cx),
 6264            "
 6265                a
 6266                b
 6267                c
 6268            "
 6269            .unindent()
 6270        );
 6271        assert_eq!(
 6272            view.selections.display_ranges(cx),
 6273            [
 6274                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6275                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6276                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6277            ]
 6278        );
 6279
 6280        // Ensure inserting the last character of a multi-byte bracket pair
 6281        // doesn't surround the selections with the bracket.
 6282        view.handle_input("*", cx);
 6283        assert_eq!(
 6284            view.text(cx),
 6285            "
 6286                *
 6287                *
 6288                *
 6289            "
 6290            .unindent()
 6291        );
 6292        assert_eq!(
 6293            view.selections.display_ranges(cx),
 6294            [
 6295                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6296                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6297                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6298            ]
 6299        );
 6300    });
 6301}
 6302
 6303#[gpui::test]
 6304async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6305    init_test(cx, |_| {});
 6306
 6307    let language = Arc::new(Language::new(
 6308        LanguageConfig {
 6309            brackets: BracketPairConfig {
 6310                pairs: vec![BracketPair {
 6311                    start: "{".to_string(),
 6312                    end: "}".to_string(),
 6313                    close: true,
 6314                    surround: true,
 6315                    newline: true,
 6316                }],
 6317                ..Default::default()
 6318            },
 6319            autoclose_before: "}".to_string(),
 6320            ..Default::default()
 6321        },
 6322        Some(tree_sitter_rust::LANGUAGE.into()),
 6323    ));
 6324
 6325    let text = r#"
 6326        a
 6327        b
 6328        c
 6329    "#
 6330    .unindent();
 6331
 6332    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6333    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6334    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6335    editor
 6336        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6337        .await;
 6338
 6339    editor.update(cx, |editor, cx| {
 6340        editor.change_selections(None, cx, |s| {
 6341            s.select_ranges([
 6342                Point::new(0, 1)..Point::new(0, 1),
 6343                Point::new(1, 1)..Point::new(1, 1),
 6344                Point::new(2, 1)..Point::new(2, 1),
 6345            ])
 6346        });
 6347
 6348        editor.handle_input("{", cx);
 6349        editor.handle_input("{", cx);
 6350        editor.handle_input("_", cx);
 6351        assert_eq!(
 6352            editor.text(cx),
 6353            "
 6354                a{{_}}
 6355                b{{_}}
 6356                c{{_}}
 6357            "
 6358            .unindent()
 6359        );
 6360        assert_eq!(
 6361            editor.selections.ranges::<Point>(cx),
 6362            [
 6363                Point::new(0, 4)..Point::new(0, 4),
 6364                Point::new(1, 4)..Point::new(1, 4),
 6365                Point::new(2, 4)..Point::new(2, 4)
 6366            ]
 6367        );
 6368
 6369        editor.backspace(&Default::default(), cx);
 6370        editor.backspace(&Default::default(), cx);
 6371        assert_eq!(
 6372            editor.text(cx),
 6373            "
 6374                a{}
 6375                b{}
 6376                c{}
 6377            "
 6378            .unindent()
 6379        );
 6380        assert_eq!(
 6381            editor.selections.ranges::<Point>(cx),
 6382            [
 6383                Point::new(0, 2)..Point::new(0, 2),
 6384                Point::new(1, 2)..Point::new(1, 2),
 6385                Point::new(2, 2)..Point::new(2, 2)
 6386            ]
 6387        );
 6388
 6389        editor.delete_to_previous_word_start(&Default::default(), cx);
 6390        assert_eq!(
 6391            editor.text(cx),
 6392            "
 6393                a
 6394                b
 6395                c
 6396            "
 6397            .unindent()
 6398        );
 6399        assert_eq!(
 6400            editor.selections.ranges::<Point>(cx),
 6401            [
 6402                Point::new(0, 1)..Point::new(0, 1),
 6403                Point::new(1, 1)..Point::new(1, 1),
 6404                Point::new(2, 1)..Point::new(2, 1)
 6405            ]
 6406        );
 6407    });
 6408}
 6409
 6410#[gpui::test]
 6411async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6412    init_test(cx, |settings| {
 6413        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6414    });
 6415
 6416    let mut cx = EditorTestContext::new(cx).await;
 6417
 6418    let language = Arc::new(Language::new(
 6419        LanguageConfig {
 6420            brackets: BracketPairConfig {
 6421                pairs: vec![
 6422                    BracketPair {
 6423                        start: "{".to_string(),
 6424                        end: "}".to_string(),
 6425                        close: true,
 6426                        surround: true,
 6427                        newline: true,
 6428                    },
 6429                    BracketPair {
 6430                        start: "(".to_string(),
 6431                        end: ")".to_string(),
 6432                        close: true,
 6433                        surround: true,
 6434                        newline: true,
 6435                    },
 6436                    BracketPair {
 6437                        start: "[".to_string(),
 6438                        end: "]".to_string(),
 6439                        close: false,
 6440                        surround: true,
 6441                        newline: true,
 6442                    },
 6443                ],
 6444                ..Default::default()
 6445            },
 6446            autoclose_before: "})]".to_string(),
 6447            ..Default::default()
 6448        },
 6449        Some(tree_sitter_rust::LANGUAGE.into()),
 6450    ));
 6451
 6452    cx.language_registry().add(language.clone());
 6453    cx.update_buffer(|buffer, cx| {
 6454        buffer.set_language(Some(language), cx);
 6455    });
 6456
 6457    cx.set_state(
 6458        &"
 6459            {(ˇ)}
 6460            [[ˇ]]
 6461            {(ˇ)}
 6462        "
 6463        .unindent(),
 6464    );
 6465
 6466    cx.update_editor(|view, cx| {
 6467        view.backspace(&Default::default(), cx);
 6468        view.backspace(&Default::default(), cx);
 6469    });
 6470
 6471    cx.assert_editor_state(
 6472        &"
 6473            ˇ
 6474            ˇ]]
 6475            ˇ
 6476        "
 6477        .unindent(),
 6478    );
 6479
 6480    cx.update_editor(|view, cx| {
 6481        view.handle_input("{", cx);
 6482        view.handle_input("{", cx);
 6483        view.move_right(&MoveRight, cx);
 6484        view.move_right(&MoveRight, cx);
 6485        view.move_left(&MoveLeft, cx);
 6486        view.move_left(&MoveLeft, cx);
 6487        view.backspace(&Default::default(), cx);
 6488    });
 6489
 6490    cx.assert_editor_state(
 6491        &"
 6492            {ˇ}
 6493            {ˇ}]]
 6494            {ˇ}
 6495        "
 6496        .unindent(),
 6497    );
 6498
 6499    cx.update_editor(|view, cx| {
 6500        view.backspace(&Default::default(), cx);
 6501    });
 6502
 6503    cx.assert_editor_state(
 6504        &"
 6505            ˇ
 6506            ˇ]]
 6507            ˇ
 6508        "
 6509        .unindent(),
 6510    );
 6511}
 6512
 6513#[gpui::test]
 6514async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6515    init_test(cx, |_| {});
 6516
 6517    let language = Arc::new(Language::new(
 6518        LanguageConfig::default(),
 6519        Some(tree_sitter_rust::LANGUAGE.into()),
 6520    ));
 6521
 6522    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6523    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6524    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6525    editor
 6526        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6527        .await;
 6528
 6529    editor.update(cx, |editor, cx| {
 6530        editor.set_auto_replace_emoji_shortcode(true);
 6531
 6532        editor.handle_input("Hello ", cx);
 6533        editor.handle_input(":wave", cx);
 6534        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6535
 6536        editor.handle_input(":", cx);
 6537        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6538
 6539        editor.handle_input(" :smile", cx);
 6540        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6541
 6542        editor.handle_input(":", cx);
 6543        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6544
 6545        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6546        editor.handle_input(":wave", cx);
 6547        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6548
 6549        editor.handle_input(":", cx);
 6550        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6551
 6552        editor.handle_input(":1", cx);
 6553        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6554
 6555        editor.handle_input(":", cx);
 6556        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6557
 6558        // Ensure shortcode does not get replaced when it is part of a word
 6559        editor.handle_input(" Test:wave", cx);
 6560        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6561
 6562        editor.handle_input(":", cx);
 6563        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6564
 6565        editor.set_auto_replace_emoji_shortcode(false);
 6566
 6567        // Ensure shortcode does not get replaced when auto replace is off
 6568        editor.handle_input(" :wave", cx);
 6569        assert_eq!(
 6570            editor.text(cx),
 6571            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6572        );
 6573
 6574        editor.handle_input(":", cx);
 6575        assert_eq!(
 6576            editor.text(cx),
 6577            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6578        );
 6579    });
 6580}
 6581
 6582#[gpui::test]
 6583async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6584    init_test(cx, |_| {});
 6585
 6586    let (text, insertion_ranges) = marked_text_ranges(
 6587        indoc! {"
 6588            ˇ
 6589        "},
 6590        false,
 6591    );
 6592
 6593    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6594    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6595
 6596    _ = editor.update(cx, |editor, cx| {
 6597        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6598
 6599        editor
 6600            .insert_snippet(&insertion_ranges, snippet, cx)
 6601            .unwrap();
 6602
 6603        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6604            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6605            assert_eq!(editor.text(cx), expected_text);
 6606            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6607        }
 6608
 6609        assert(
 6610            editor,
 6611            cx,
 6612            indoc! {"
 6613            type «» =•
 6614            "},
 6615        );
 6616
 6617        assert!(editor.context_menu_visible(), "There should be a matches");
 6618    });
 6619}
 6620
 6621#[gpui::test]
 6622async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6623    init_test(cx, |_| {});
 6624
 6625    let (text, insertion_ranges) = marked_text_ranges(
 6626        indoc! {"
 6627            a.ˇ b
 6628            a.ˇ b
 6629            a.ˇ b
 6630        "},
 6631        false,
 6632    );
 6633
 6634    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6635    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6636
 6637    editor.update(cx, |editor, cx| {
 6638        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6639
 6640        editor
 6641            .insert_snippet(&insertion_ranges, snippet, cx)
 6642            .unwrap();
 6643
 6644        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6645            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6646            assert_eq!(editor.text(cx), expected_text);
 6647            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6648        }
 6649
 6650        assert(
 6651            editor,
 6652            cx,
 6653            indoc! {"
 6654                a.f(«one», two, «three») b
 6655                a.f(«one», two, «three») b
 6656                a.f(«one», two, «three») b
 6657            "},
 6658        );
 6659
 6660        // Can't move earlier than the first tab stop
 6661        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6662        assert(
 6663            editor,
 6664            cx,
 6665            indoc! {"
 6666                a.f(«one», two, «three») b
 6667                a.f(«one», two, «three») b
 6668                a.f(«one», two, «three») b
 6669            "},
 6670        );
 6671
 6672        assert!(editor.move_to_next_snippet_tabstop(cx));
 6673        assert(
 6674            editor,
 6675            cx,
 6676            indoc! {"
 6677                a.f(one, «two», three) b
 6678                a.f(one, «two», three) b
 6679                a.f(one, «two», three) b
 6680            "},
 6681        );
 6682
 6683        editor.move_to_prev_snippet_tabstop(cx);
 6684        assert(
 6685            editor,
 6686            cx,
 6687            indoc! {"
 6688                a.f(«one», two, «three») b
 6689                a.f(«one», two, «three») b
 6690                a.f(«one», two, «three») b
 6691            "},
 6692        );
 6693
 6694        assert!(editor.move_to_next_snippet_tabstop(cx));
 6695        assert(
 6696            editor,
 6697            cx,
 6698            indoc! {"
 6699                a.f(one, «two», three) b
 6700                a.f(one, «two», three) b
 6701                a.f(one, «two», three) b
 6702            "},
 6703        );
 6704        assert!(editor.move_to_next_snippet_tabstop(cx));
 6705        assert(
 6706            editor,
 6707            cx,
 6708            indoc! {"
 6709                a.f(one, two, three)ˇ b
 6710                a.f(one, two, three)ˇ b
 6711                a.f(one, two, three)ˇ b
 6712            "},
 6713        );
 6714
 6715        // As soon as the last tab stop is reached, snippet state is gone
 6716        editor.move_to_prev_snippet_tabstop(cx);
 6717        assert(
 6718            editor,
 6719            cx,
 6720            indoc! {"
 6721                a.f(one, two, three)ˇ b
 6722                a.f(one, two, three)ˇ b
 6723                a.f(one, two, three)ˇ b
 6724            "},
 6725        );
 6726    });
 6727}
 6728
 6729#[gpui::test]
 6730async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6731    init_test(cx, |_| {});
 6732
 6733    let fs = FakeFs::new(cx.executor());
 6734    fs.insert_file("/file.rs", Default::default()).await;
 6735
 6736    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6737
 6738    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6739    language_registry.add(rust_lang());
 6740    let mut fake_servers = language_registry.register_fake_lsp(
 6741        "Rust",
 6742        FakeLspAdapter {
 6743            capabilities: lsp::ServerCapabilities {
 6744                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6745                ..Default::default()
 6746            },
 6747            ..Default::default()
 6748        },
 6749    );
 6750
 6751    let buffer = project
 6752        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6753        .await
 6754        .unwrap();
 6755
 6756    cx.executor().start_waiting();
 6757    let fake_server = fake_servers.next().await.unwrap();
 6758
 6759    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6760    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6761    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6762    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6763
 6764    let save = editor
 6765        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6766        .unwrap();
 6767    fake_server
 6768        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6769            assert_eq!(
 6770                params.text_document.uri,
 6771                lsp::Url::from_file_path("/file.rs").unwrap()
 6772            );
 6773            assert_eq!(params.options.tab_size, 4);
 6774            Ok(Some(vec![lsp::TextEdit::new(
 6775                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6776                ", ".to_string(),
 6777            )]))
 6778        })
 6779        .next()
 6780        .await;
 6781    cx.executor().start_waiting();
 6782    save.await;
 6783
 6784    assert_eq!(
 6785        editor.update(cx, |editor, cx| editor.text(cx)),
 6786        "one, two\nthree\n"
 6787    );
 6788    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6789
 6790    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6791    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6792
 6793    // Ensure we can still save even if formatting hangs.
 6794    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6795        assert_eq!(
 6796            params.text_document.uri,
 6797            lsp::Url::from_file_path("/file.rs").unwrap()
 6798        );
 6799        futures::future::pending::<()>().await;
 6800        unreachable!()
 6801    });
 6802    let save = editor
 6803        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6804        .unwrap();
 6805    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6806    cx.executor().start_waiting();
 6807    save.await;
 6808    assert_eq!(
 6809        editor.update(cx, |editor, cx| editor.text(cx)),
 6810        "one\ntwo\nthree\n"
 6811    );
 6812    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6813
 6814    // For non-dirty buffer, no formatting request should be sent
 6815    let save = editor
 6816        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6817        .unwrap();
 6818    let _pending_format_request = fake_server
 6819        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6820            panic!("Should not be invoked on non-dirty buffer");
 6821        })
 6822        .next();
 6823    cx.executor().start_waiting();
 6824    save.await;
 6825
 6826    // Set rust language override and assert overridden tabsize is sent to language server
 6827    update_test_language_settings(cx, |settings| {
 6828        settings.languages.insert(
 6829            "Rust".into(),
 6830            LanguageSettingsContent {
 6831                tab_size: NonZeroU32::new(8),
 6832                ..Default::default()
 6833            },
 6834        );
 6835    });
 6836
 6837    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6838    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6839    let save = editor
 6840        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6841        .unwrap();
 6842    fake_server
 6843        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6844            assert_eq!(
 6845                params.text_document.uri,
 6846                lsp::Url::from_file_path("/file.rs").unwrap()
 6847            );
 6848            assert_eq!(params.options.tab_size, 8);
 6849            Ok(Some(vec![]))
 6850        })
 6851        .next()
 6852        .await;
 6853    cx.executor().start_waiting();
 6854    save.await;
 6855}
 6856
 6857#[gpui::test]
 6858async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6859    init_test(cx, |_| {});
 6860
 6861    let cols = 4;
 6862    let rows = 10;
 6863    let sample_text_1 = sample_text(rows, cols, 'a');
 6864    assert_eq!(
 6865        sample_text_1,
 6866        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6867    );
 6868    let sample_text_2 = sample_text(rows, cols, 'l');
 6869    assert_eq!(
 6870        sample_text_2,
 6871        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6872    );
 6873    let sample_text_3 = sample_text(rows, cols, 'v');
 6874    assert_eq!(
 6875        sample_text_3,
 6876        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6877    );
 6878
 6879    let fs = FakeFs::new(cx.executor());
 6880    fs.insert_tree(
 6881        "/a",
 6882        json!({
 6883            "main.rs": sample_text_1,
 6884            "other.rs": sample_text_2,
 6885            "lib.rs": sample_text_3,
 6886        }),
 6887    )
 6888    .await;
 6889
 6890    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6891    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6892    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6893
 6894    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6895    language_registry.add(rust_lang());
 6896    let mut fake_servers = language_registry.register_fake_lsp(
 6897        "Rust",
 6898        FakeLspAdapter {
 6899            capabilities: lsp::ServerCapabilities {
 6900                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6901                ..Default::default()
 6902            },
 6903            ..Default::default()
 6904        },
 6905    );
 6906
 6907    let worktree = project.update(cx, |project, cx| {
 6908        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6909        assert_eq!(worktrees.len(), 1);
 6910        worktrees.pop().unwrap()
 6911    });
 6912    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6913
 6914    let buffer_1 = project
 6915        .update(cx, |project, cx| {
 6916            project.open_buffer((worktree_id, "main.rs"), cx)
 6917        })
 6918        .await
 6919        .unwrap();
 6920    let buffer_2 = project
 6921        .update(cx, |project, cx| {
 6922            project.open_buffer((worktree_id, "other.rs"), cx)
 6923        })
 6924        .await
 6925        .unwrap();
 6926    let buffer_3 = project
 6927        .update(cx, |project, cx| {
 6928            project.open_buffer((worktree_id, "lib.rs"), cx)
 6929        })
 6930        .await
 6931        .unwrap();
 6932
 6933    let multi_buffer = cx.new_model(|cx| {
 6934        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6935        multi_buffer.push_excerpts(
 6936            buffer_1.clone(),
 6937            [
 6938                ExcerptRange {
 6939                    context: Point::new(0, 0)..Point::new(3, 0),
 6940                    primary: None,
 6941                },
 6942                ExcerptRange {
 6943                    context: Point::new(5, 0)..Point::new(7, 0),
 6944                    primary: None,
 6945                },
 6946                ExcerptRange {
 6947                    context: Point::new(9, 0)..Point::new(10, 4),
 6948                    primary: None,
 6949                },
 6950            ],
 6951            cx,
 6952        );
 6953        multi_buffer.push_excerpts(
 6954            buffer_2.clone(),
 6955            [
 6956                ExcerptRange {
 6957                    context: Point::new(0, 0)..Point::new(3, 0),
 6958                    primary: None,
 6959                },
 6960                ExcerptRange {
 6961                    context: Point::new(5, 0)..Point::new(7, 0),
 6962                    primary: None,
 6963                },
 6964                ExcerptRange {
 6965                    context: Point::new(9, 0)..Point::new(10, 4),
 6966                    primary: None,
 6967                },
 6968            ],
 6969            cx,
 6970        );
 6971        multi_buffer.push_excerpts(
 6972            buffer_3.clone(),
 6973            [
 6974                ExcerptRange {
 6975                    context: Point::new(0, 0)..Point::new(3, 0),
 6976                    primary: None,
 6977                },
 6978                ExcerptRange {
 6979                    context: Point::new(5, 0)..Point::new(7, 0),
 6980                    primary: None,
 6981                },
 6982                ExcerptRange {
 6983                    context: Point::new(9, 0)..Point::new(10, 4),
 6984                    primary: None,
 6985                },
 6986            ],
 6987            cx,
 6988        );
 6989        multi_buffer
 6990    });
 6991    let multi_buffer_editor = cx.new_view(|cx| {
 6992        Editor::new(
 6993            EditorMode::Full,
 6994            multi_buffer,
 6995            Some(project.clone()),
 6996            true,
 6997            cx,
 6998        )
 6999    });
 7000
 7001    multi_buffer_editor.update(cx, |editor, cx| {
 7002        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7003        editor.insert("|one|two|three|", cx);
 7004    });
 7005    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7006    multi_buffer_editor.update(cx, |editor, cx| {
 7007        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7008            s.select_ranges(Some(60..70))
 7009        });
 7010        editor.insert("|four|five|six|", cx);
 7011    });
 7012    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7013
 7014    // First two buffers should be edited, but not the third one.
 7015    assert_eq!(
 7016        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7017        "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}",
 7018    );
 7019    buffer_1.update(cx, |buffer, _| {
 7020        assert!(buffer.is_dirty());
 7021        assert_eq!(
 7022            buffer.text(),
 7023            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7024        )
 7025    });
 7026    buffer_2.update(cx, |buffer, _| {
 7027        assert!(buffer.is_dirty());
 7028        assert_eq!(
 7029            buffer.text(),
 7030            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7031        )
 7032    });
 7033    buffer_3.update(cx, |buffer, _| {
 7034        assert!(!buffer.is_dirty());
 7035        assert_eq!(buffer.text(), sample_text_3,)
 7036    });
 7037
 7038    cx.executor().start_waiting();
 7039    let save = multi_buffer_editor
 7040        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7041        .unwrap();
 7042
 7043    let fake_server = fake_servers.next().await.unwrap();
 7044    fake_server
 7045        .server
 7046        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7047            Ok(Some(vec![lsp::TextEdit::new(
 7048                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7049                format!("[{} formatted]", params.text_document.uri),
 7050            )]))
 7051        })
 7052        .detach();
 7053    save.await;
 7054
 7055    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7056    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7057    assert_eq!(
 7058        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7059        "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}",
 7060    );
 7061    buffer_1.update(cx, |buffer, _| {
 7062        assert!(!buffer.is_dirty());
 7063        assert_eq!(
 7064            buffer.text(),
 7065            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7066        )
 7067    });
 7068    buffer_2.update(cx, |buffer, _| {
 7069        assert!(!buffer.is_dirty());
 7070        assert_eq!(
 7071            buffer.text(),
 7072            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7073        )
 7074    });
 7075    buffer_3.update(cx, |buffer, _| {
 7076        assert!(!buffer.is_dirty());
 7077        assert_eq!(buffer.text(), sample_text_3,)
 7078    });
 7079}
 7080
 7081#[gpui::test]
 7082async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7083    init_test(cx, |_| {});
 7084
 7085    let fs = FakeFs::new(cx.executor());
 7086    fs.insert_file("/file.rs", Default::default()).await;
 7087
 7088    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7089
 7090    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7091    language_registry.add(rust_lang());
 7092    let mut fake_servers = language_registry.register_fake_lsp(
 7093        "Rust",
 7094        FakeLspAdapter {
 7095            capabilities: lsp::ServerCapabilities {
 7096                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7097                ..Default::default()
 7098            },
 7099            ..Default::default()
 7100        },
 7101    );
 7102
 7103    let buffer = project
 7104        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7105        .await
 7106        .unwrap();
 7107
 7108    cx.executor().start_waiting();
 7109    let fake_server = fake_servers.next().await.unwrap();
 7110
 7111    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7112    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 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    let save = editor
 7117        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7118        .unwrap();
 7119    fake_server
 7120        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7121            assert_eq!(
 7122                params.text_document.uri,
 7123                lsp::Url::from_file_path("/file.rs").unwrap()
 7124            );
 7125            assert_eq!(params.options.tab_size, 4);
 7126            Ok(Some(vec![lsp::TextEdit::new(
 7127                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7128                ", ".to_string(),
 7129            )]))
 7130        })
 7131        .next()
 7132        .await;
 7133    cx.executor().start_waiting();
 7134    save.await;
 7135    assert_eq!(
 7136        editor.update(cx, |editor, cx| editor.text(cx)),
 7137        "one, two\nthree\n"
 7138    );
 7139    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7140
 7141    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7142    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7143
 7144    // Ensure we can still save even if formatting hangs.
 7145    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7146        move |params, _| async move {
 7147            assert_eq!(
 7148                params.text_document.uri,
 7149                lsp::Url::from_file_path("/file.rs").unwrap()
 7150            );
 7151            futures::future::pending::<()>().await;
 7152            unreachable!()
 7153        },
 7154    );
 7155    let save = editor
 7156        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7157        .unwrap();
 7158    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7159    cx.executor().start_waiting();
 7160    save.await;
 7161    assert_eq!(
 7162        editor.update(cx, |editor, cx| editor.text(cx)),
 7163        "one\ntwo\nthree\n"
 7164    );
 7165    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7166
 7167    // For non-dirty buffer, no formatting request should be sent
 7168    let save = editor
 7169        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7170        .unwrap();
 7171    let _pending_format_request = fake_server
 7172        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7173            panic!("Should not be invoked on non-dirty buffer");
 7174        })
 7175        .next();
 7176    cx.executor().start_waiting();
 7177    save.await;
 7178
 7179    // Set Rust language override and assert overridden tabsize is sent to language server
 7180    update_test_language_settings(cx, |settings| {
 7181        settings.languages.insert(
 7182            "Rust".into(),
 7183            LanguageSettingsContent {
 7184                tab_size: NonZeroU32::new(8),
 7185                ..Default::default()
 7186            },
 7187        );
 7188    });
 7189
 7190    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7191    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7192    let save = editor
 7193        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7194        .unwrap();
 7195    fake_server
 7196        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7197            assert_eq!(
 7198                params.text_document.uri,
 7199                lsp::Url::from_file_path("/file.rs").unwrap()
 7200            );
 7201            assert_eq!(params.options.tab_size, 8);
 7202            Ok(Some(vec![]))
 7203        })
 7204        .next()
 7205        .await;
 7206    cx.executor().start_waiting();
 7207    save.await;
 7208}
 7209
 7210#[gpui::test]
 7211async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7212    init_test(cx, |settings| {
 7213        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7214            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7215        ))
 7216    });
 7217
 7218    let fs = FakeFs::new(cx.executor());
 7219    fs.insert_file("/file.rs", Default::default()).await;
 7220
 7221    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7222
 7223    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7224    language_registry.add(Arc::new(Language::new(
 7225        LanguageConfig {
 7226            name: "Rust".into(),
 7227            matcher: LanguageMatcher {
 7228                path_suffixes: vec!["rs".to_string()],
 7229                ..Default::default()
 7230            },
 7231            ..LanguageConfig::default()
 7232        },
 7233        Some(tree_sitter_rust::LANGUAGE.into()),
 7234    )));
 7235    update_test_language_settings(cx, |settings| {
 7236        // Enable Prettier formatting for the same buffer, and ensure
 7237        // LSP is called instead of Prettier.
 7238        settings.defaults.prettier = Some(PrettierSettings {
 7239            allowed: true,
 7240            ..PrettierSettings::default()
 7241        });
 7242    });
 7243    let mut fake_servers = language_registry.register_fake_lsp(
 7244        "Rust",
 7245        FakeLspAdapter {
 7246            capabilities: lsp::ServerCapabilities {
 7247                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7248                ..Default::default()
 7249            },
 7250            ..Default::default()
 7251        },
 7252    );
 7253
 7254    let buffer = project
 7255        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7256        .await
 7257        .unwrap();
 7258
 7259    cx.executor().start_waiting();
 7260    let fake_server = fake_servers.next().await.unwrap();
 7261
 7262    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7263    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7264    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7265
 7266    let format = editor
 7267        .update(cx, |editor, cx| {
 7268            editor.perform_format(
 7269                project.clone(),
 7270                FormatTrigger::Manual,
 7271                FormatTarget::Buffer,
 7272                cx,
 7273            )
 7274        })
 7275        .unwrap();
 7276    fake_server
 7277        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7278            assert_eq!(
 7279                params.text_document.uri,
 7280                lsp::Url::from_file_path("/file.rs").unwrap()
 7281            );
 7282            assert_eq!(params.options.tab_size, 4);
 7283            Ok(Some(vec![lsp::TextEdit::new(
 7284                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7285                ", ".to_string(),
 7286            )]))
 7287        })
 7288        .next()
 7289        .await;
 7290    cx.executor().start_waiting();
 7291    format.await;
 7292    assert_eq!(
 7293        editor.update(cx, |editor, cx| editor.text(cx)),
 7294        "one, two\nthree\n"
 7295    );
 7296
 7297    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7298    // Ensure we don't lock if formatting hangs.
 7299    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7300        assert_eq!(
 7301            params.text_document.uri,
 7302            lsp::Url::from_file_path("/file.rs").unwrap()
 7303        );
 7304        futures::future::pending::<()>().await;
 7305        unreachable!()
 7306    });
 7307    let format = editor
 7308        .update(cx, |editor, cx| {
 7309            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7310        })
 7311        .unwrap();
 7312    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7313    cx.executor().start_waiting();
 7314    format.await;
 7315    assert_eq!(
 7316        editor.update(cx, |editor, cx| editor.text(cx)),
 7317        "one\ntwo\nthree\n"
 7318    );
 7319}
 7320
 7321#[gpui::test]
 7322async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7323    init_test(cx, |_| {});
 7324
 7325    let mut cx = EditorLspTestContext::new_rust(
 7326        lsp::ServerCapabilities {
 7327            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7328            ..Default::default()
 7329        },
 7330        cx,
 7331    )
 7332    .await;
 7333
 7334    cx.set_state(indoc! {"
 7335        one.twoˇ
 7336    "});
 7337
 7338    // The format request takes a long time. When it completes, it inserts
 7339    // a newline and an indent before the `.`
 7340    cx.lsp
 7341        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7342            let executor = cx.background_executor().clone();
 7343            async move {
 7344                executor.timer(Duration::from_millis(100)).await;
 7345                Ok(Some(vec![lsp::TextEdit {
 7346                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7347                    new_text: "\n    ".into(),
 7348                }]))
 7349            }
 7350        });
 7351
 7352    // Submit a format request.
 7353    let format_1 = cx
 7354        .update_editor(|editor, cx| editor.format(&Format, cx))
 7355        .unwrap();
 7356    cx.executor().run_until_parked();
 7357
 7358    // Submit a second format request.
 7359    let format_2 = cx
 7360        .update_editor(|editor, cx| editor.format(&Format, cx))
 7361        .unwrap();
 7362    cx.executor().run_until_parked();
 7363
 7364    // Wait for both format requests to complete
 7365    cx.executor().advance_clock(Duration::from_millis(200));
 7366    cx.executor().start_waiting();
 7367    format_1.await.unwrap();
 7368    cx.executor().start_waiting();
 7369    format_2.await.unwrap();
 7370
 7371    // The formatting edits only happens once.
 7372    cx.assert_editor_state(indoc! {"
 7373        one
 7374            .twoˇ
 7375    "});
 7376}
 7377
 7378#[gpui::test]
 7379async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7380    init_test(cx, |settings| {
 7381        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7382    });
 7383
 7384    let mut cx = EditorLspTestContext::new_rust(
 7385        lsp::ServerCapabilities {
 7386            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7387            ..Default::default()
 7388        },
 7389        cx,
 7390    )
 7391    .await;
 7392
 7393    // Set up a buffer white some trailing whitespace and no trailing newline.
 7394    cx.set_state(
 7395        &[
 7396            "one ",   //
 7397            "twoˇ",   //
 7398            "three ", //
 7399            "four",   //
 7400        ]
 7401        .join("\n"),
 7402    );
 7403
 7404    // Submit a format request.
 7405    let format = cx
 7406        .update_editor(|editor, cx| editor.format(&Format, cx))
 7407        .unwrap();
 7408
 7409    // Record which buffer changes have been sent to the language server
 7410    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7411    cx.lsp
 7412        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7413            let buffer_changes = buffer_changes.clone();
 7414            move |params, _| {
 7415                buffer_changes.lock().extend(
 7416                    params
 7417                        .content_changes
 7418                        .into_iter()
 7419                        .map(|e| (e.range.unwrap(), e.text)),
 7420                );
 7421            }
 7422        });
 7423
 7424    // Handle formatting requests to the language server.
 7425    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7426        let buffer_changes = buffer_changes.clone();
 7427        move |_, _| {
 7428            // When formatting is requested, trailing whitespace has already been stripped,
 7429            // and the trailing newline has already been added.
 7430            assert_eq!(
 7431                &buffer_changes.lock()[1..],
 7432                &[
 7433                    (
 7434                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7435                        "".into()
 7436                    ),
 7437                    (
 7438                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7439                        "".into()
 7440                    ),
 7441                    (
 7442                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7443                        "\n".into()
 7444                    ),
 7445                ]
 7446            );
 7447
 7448            // Insert blank lines between each line of the buffer.
 7449            async move {
 7450                Ok(Some(vec![
 7451                    lsp::TextEdit {
 7452                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7453                        new_text: "\n".into(),
 7454                    },
 7455                    lsp::TextEdit {
 7456                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7457                        new_text: "\n".into(),
 7458                    },
 7459                ]))
 7460            }
 7461        }
 7462    });
 7463
 7464    // After formatting the buffer, the trailing whitespace is stripped,
 7465    // a newline is appended, and the edits provided by the language server
 7466    // have been applied.
 7467    format.await.unwrap();
 7468    cx.assert_editor_state(
 7469        &[
 7470            "one",   //
 7471            "",      //
 7472            "twoˇ",  //
 7473            "",      //
 7474            "three", //
 7475            "four",  //
 7476            "",      //
 7477        ]
 7478        .join("\n"),
 7479    );
 7480
 7481    // Undoing the formatting undoes the trailing whitespace removal, the
 7482    // trailing newline, and the LSP edits.
 7483    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7484    cx.assert_editor_state(
 7485        &[
 7486            "one ",   //
 7487            "twoˇ",   //
 7488            "three ", //
 7489            "four",   //
 7490        ]
 7491        .join("\n"),
 7492    );
 7493}
 7494
 7495#[gpui::test]
 7496async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7497    cx: &mut gpui::TestAppContext,
 7498) {
 7499    init_test(cx, |_| {});
 7500
 7501    cx.update(|cx| {
 7502        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7503            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7504                settings.auto_signature_help = Some(true);
 7505            });
 7506        });
 7507    });
 7508
 7509    let mut cx = EditorLspTestContext::new_rust(
 7510        lsp::ServerCapabilities {
 7511            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7512                ..Default::default()
 7513            }),
 7514            ..Default::default()
 7515        },
 7516        cx,
 7517    )
 7518    .await;
 7519
 7520    let language = Language::new(
 7521        LanguageConfig {
 7522            name: "Rust".into(),
 7523            brackets: BracketPairConfig {
 7524                pairs: vec![
 7525                    BracketPair {
 7526                        start: "{".to_string(),
 7527                        end: "}".to_string(),
 7528                        close: true,
 7529                        surround: true,
 7530                        newline: true,
 7531                    },
 7532                    BracketPair {
 7533                        start: "(".to_string(),
 7534                        end: ")".to_string(),
 7535                        close: true,
 7536                        surround: true,
 7537                        newline: true,
 7538                    },
 7539                    BracketPair {
 7540                        start: "/*".to_string(),
 7541                        end: " */".to_string(),
 7542                        close: true,
 7543                        surround: true,
 7544                        newline: true,
 7545                    },
 7546                    BracketPair {
 7547                        start: "[".to_string(),
 7548                        end: "]".to_string(),
 7549                        close: false,
 7550                        surround: false,
 7551                        newline: true,
 7552                    },
 7553                    BracketPair {
 7554                        start: "\"".to_string(),
 7555                        end: "\"".to_string(),
 7556                        close: true,
 7557                        surround: true,
 7558                        newline: false,
 7559                    },
 7560                    BracketPair {
 7561                        start: "<".to_string(),
 7562                        end: ">".to_string(),
 7563                        close: false,
 7564                        surround: true,
 7565                        newline: true,
 7566                    },
 7567                ],
 7568                ..Default::default()
 7569            },
 7570            autoclose_before: "})]".to_string(),
 7571            ..Default::default()
 7572        },
 7573        Some(tree_sitter_rust::LANGUAGE.into()),
 7574    );
 7575    let language = Arc::new(language);
 7576
 7577    cx.language_registry().add(language.clone());
 7578    cx.update_buffer(|buffer, cx| {
 7579        buffer.set_language(Some(language), cx);
 7580    });
 7581
 7582    cx.set_state(
 7583        &r#"
 7584            fn main() {
 7585                sampleˇ
 7586            }
 7587        "#
 7588        .unindent(),
 7589    );
 7590
 7591    cx.update_editor(|view, cx| {
 7592        view.handle_input("(", cx);
 7593    });
 7594    cx.assert_editor_state(
 7595        &"
 7596            fn main() {
 7597                sample(ˇ)
 7598            }
 7599        "
 7600        .unindent(),
 7601    );
 7602
 7603    let mocked_response = lsp::SignatureHelp {
 7604        signatures: vec![lsp::SignatureInformation {
 7605            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7606            documentation: None,
 7607            parameters: Some(vec![
 7608                lsp::ParameterInformation {
 7609                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7610                    documentation: None,
 7611                },
 7612                lsp::ParameterInformation {
 7613                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7614                    documentation: None,
 7615                },
 7616            ]),
 7617            active_parameter: None,
 7618        }],
 7619        active_signature: Some(0),
 7620        active_parameter: Some(0),
 7621    };
 7622    handle_signature_help_request(&mut cx, mocked_response).await;
 7623
 7624    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7625        .await;
 7626
 7627    cx.editor(|editor, _| {
 7628        let signature_help_state = editor.signature_help_state.popover().cloned();
 7629        assert!(signature_help_state.is_some());
 7630        let ParsedMarkdown {
 7631            text, highlights, ..
 7632        } = signature_help_state.unwrap().parsed_content;
 7633        assert_eq!(text, "param1: u8, param2: u8");
 7634        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7635    });
 7636}
 7637
 7638#[gpui::test]
 7639async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7640    init_test(cx, |_| {});
 7641
 7642    cx.update(|cx| {
 7643        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7644            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7645                settings.auto_signature_help = Some(false);
 7646                settings.show_signature_help_after_edits = Some(false);
 7647            });
 7648        });
 7649    });
 7650
 7651    let mut cx = EditorLspTestContext::new_rust(
 7652        lsp::ServerCapabilities {
 7653            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7654                ..Default::default()
 7655            }),
 7656            ..Default::default()
 7657        },
 7658        cx,
 7659    )
 7660    .await;
 7661
 7662    let language = Language::new(
 7663        LanguageConfig {
 7664            name: "Rust".into(),
 7665            brackets: BracketPairConfig {
 7666                pairs: vec![
 7667                    BracketPair {
 7668                        start: "{".to_string(),
 7669                        end: "}".to_string(),
 7670                        close: true,
 7671                        surround: true,
 7672                        newline: true,
 7673                    },
 7674                    BracketPair {
 7675                        start: "(".to_string(),
 7676                        end: ")".to_string(),
 7677                        close: true,
 7678                        surround: true,
 7679                        newline: true,
 7680                    },
 7681                    BracketPair {
 7682                        start: "/*".to_string(),
 7683                        end: " */".to_string(),
 7684                        close: true,
 7685                        surround: true,
 7686                        newline: true,
 7687                    },
 7688                    BracketPair {
 7689                        start: "[".to_string(),
 7690                        end: "]".to_string(),
 7691                        close: false,
 7692                        surround: false,
 7693                        newline: true,
 7694                    },
 7695                    BracketPair {
 7696                        start: "\"".to_string(),
 7697                        end: "\"".to_string(),
 7698                        close: true,
 7699                        surround: true,
 7700                        newline: false,
 7701                    },
 7702                    BracketPair {
 7703                        start: "<".to_string(),
 7704                        end: ">".to_string(),
 7705                        close: false,
 7706                        surround: true,
 7707                        newline: true,
 7708                    },
 7709                ],
 7710                ..Default::default()
 7711            },
 7712            autoclose_before: "})]".to_string(),
 7713            ..Default::default()
 7714        },
 7715        Some(tree_sitter_rust::LANGUAGE.into()),
 7716    );
 7717    let language = Arc::new(language);
 7718
 7719    cx.language_registry().add(language.clone());
 7720    cx.update_buffer(|buffer, cx| {
 7721        buffer.set_language(Some(language), cx);
 7722    });
 7723
 7724    // Ensure that signature_help is not called when no signature help is enabled.
 7725    cx.set_state(
 7726        &r#"
 7727            fn main() {
 7728                sampleˇ
 7729            }
 7730        "#
 7731        .unindent(),
 7732    );
 7733    cx.update_editor(|view, cx| {
 7734        view.handle_input("(", cx);
 7735    });
 7736    cx.assert_editor_state(
 7737        &"
 7738            fn main() {
 7739                sample(ˇ)
 7740            }
 7741        "
 7742        .unindent(),
 7743    );
 7744    cx.editor(|editor, _| {
 7745        assert!(editor.signature_help_state.task().is_none());
 7746    });
 7747
 7748    let mocked_response = lsp::SignatureHelp {
 7749        signatures: vec![lsp::SignatureInformation {
 7750            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7751            documentation: None,
 7752            parameters: Some(vec![
 7753                lsp::ParameterInformation {
 7754                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7755                    documentation: None,
 7756                },
 7757                lsp::ParameterInformation {
 7758                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7759                    documentation: None,
 7760                },
 7761            ]),
 7762            active_parameter: None,
 7763        }],
 7764        active_signature: Some(0),
 7765        active_parameter: Some(0),
 7766    };
 7767
 7768    // Ensure that signature_help is called when enabled afte edits
 7769    cx.update(|cx| {
 7770        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7771            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7772                settings.auto_signature_help = Some(false);
 7773                settings.show_signature_help_after_edits = Some(true);
 7774            });
 7775        });
 7776    });
 7777    cx.set_state(
 7778        &r#"
 7779            fn main() {
 7780                sampleˇ
 7781            }
 7782        "#
 7783        .unindent(),
 7784    );
 7785    cx.update_editor(|view, cx| {
 7786        view.handle_input("(", cx);
 7787    });
 7788    cx.assert_editor_state(
 7789        &"
 7790            fn main() {
 7791                sample(ˇ)
 7792            }
 7793        "
 7794        .unindent(),
 7795    );
 7796    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7797    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7798        .await;
 7799    cx.update_editor(|editor, _| {
 7800        let signature_help_state = editor.signature_help_state.popover().cloned();
 7801        assert!(signature_help_state.is_some());
 7802        let ParsedMarkdown {
 7803            text, highlights, ..
 7804        } = signature_help_state.unwrap().parsed_content;
 7805        assert_eq!(text, "param1: u8, param2: u8");
 7806        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7807        editor.signature_help_state = SignatureHelpState::default();
 7808    });
 7809
 7810    // Ensure that signature_help is called when auto signature help override is enabled
 7811    cx.update(|cx| {
 7812        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7813            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7814                settings.auto_signature_help = Some(true);
 7815                settings.show_signature_help_after_edits = Some(false);
 7816            });
 7817        });
 7818    });
 7819    cx.set_state(
 7820        &r#"
 7821            fn main() {
 7822                sampleˇ
 7823            }
 7824        "#
 7825        .unindent(),
 7826    );
 7827    cx.update_editor(|view, cx| {
 7828        view.handle_input("(", cx);
 7829    });
 7830    cx.assert_editor_state(
 7831        &"
 7832            fn main() {
 7833                sample(ˇ)
 7834            }
 7835        "
 7836        .unindent(),
 7837    );
 7838    handle_signature_help_request(&mut cx, mocked_response).await;
 7839    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7840        .await;
 7841    cx.editor(|editor, _| {
 7842        let signature_help_state = editor.signature_help_state.popover().cloned();
 7843        assert!(signature_help_state.is_some());
 7844        let ParsedMarkdown {
 7845            text, highlights, ..
 7846        } = signature_help_state.unwrap().parsed_content;
 7847        assert_eq!(text, "param1: u8, param2: u8");
 7848        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7849    });
 7850}
 7851
 7852#[gpui::test]
 7853async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7854    init_test(cx, |_| {});
 7855    cx.update(|cx| {
 7856        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7857            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7858                settings.auto_signature_help = Some(true);
 7859            });
 7860        });
 7861    });
 7862
 7863    let mut cx = EditorLspTestContext::new_rust(
 7864        lsp::ServerCapabilities {
 7865            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7866                ..Default::default()
 7867            }),
 7868            ..Default::default()
 7869        },
 7870        cx,
 7871    )
 7872    .await;
 7873
 7874    // A test that directly calls `show_signature_help`
 7875    cx.update_editor(|editor, cx| {
 7876        editor.show_signature_help(&ShowSignatureHelp, cx);
 7877    });
 7878
 7879    let mocked_response = lsp::SignatureHelp {
 7880        signatures: vec![lsp::SignatureInformation {
 7881            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7882            documentation: None,
 7883            parameters: Some(vec![
 7884                lsp::ParameterInformation {
 7885                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7886                    documentation: None,
 7887                },
 7888                lsp::ParameterInformation {
 7889                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7890                    documentation: None,
 7891                },
 7892            ]),
 7893            active_parameter: None,
 7894        }],
 7895        active_signature: Some(0),
 7896        active_parameter: Some(0),
 7897    };
 7898    handle_signature_help_request(&mut cx, mocked_response).await;
 7899
 7900    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7901        .await;
 7902
 7903    cx.editor(|editor, _| {
 7904        let signature_help_state = editor.signature_help_state.popover().cloned();
 7905        assert!(signature_help_state.is_some());
 7906        let ParsedMarkdown {
 7907            text, highlights, ..
 7908        } = signature_help_state.unwrap().parsed_content;
 7909        assert_eq!(text, "param1: u8, param2: u8");
 7910        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7911    });
 7912
 7913    // When exiting outside from inside the brackets, `signature_help` is closed.
 7914    cx.set_state(indoc! {"
 7915        fn main() {
 7916            sample(ˇ);
 7917        }
 7918
 7919        fn sample(param1: u8, param2: u8) {}
 7920    "});
 7921
 7922    cx.update_editor(|editor, cx| {
 7923        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7924    });
 7925
 7926    let mocked_response = lsp::SignatureHelp {
 7927        signatures: Vec::new(),
 7928        active_signature: None,
 7929        active_parameter: None,
 7930    };
 7931    handle_signature_help_request(&mut cx, mocked_response).await;
 7932
 7933    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7934        .await;
 7935
 7936    cx.editor(|editor, _| {
 7937        assert!(!editor.signature_help_state.is_shown());
 7938    });
 7939
 7940    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7941    cx.set_state(indoc! {"
 7942        fn main() {
 7943            sample(ˇ);
 7944        }
 7945
 7946        fn sample(param1: u8, param2: u8) {}
 7947    "});
 7948
 7949    let mocked_response = lsp::SignatureHelp {
 7950        signatures: vec![lsp::SignatureInformation {
 7951            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7952            documentation: None,
 7953            parameters: Some(vec![
 7954                lsp::ParameterInformation {
 7955                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7956                    documentation: None,
 7957                },
 7958                lsp::ParameterInformation {
 7959                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7960                    documentation: None,
 7961                },
 7962            ]),
 7963            active_parameter: None,
 7964        }],
 7965        active_signature: Some(0),
 7966        active_parameter: Some(0),
 7967    };
 7968    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7969    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7970        .await;
 7971    cx.editor(|editor, _| {
 7972        assert!(editor.signature_help_state.is_shown());
 7973    });
 7974
 7975    // Restore the popover with more parameter input
 7976    cx.set_state(indoc! {"
 7977        fn main() {
 7978            sample(param1, param2ˇ);
 7979        }
 7980
 7981        fn sample(param1: u8, param2: u8) {}
 7982    "});
 7983
 7984    let mocked_response = lsp::SignatureHelp {
 7985        signatures: vec![lsp::SignatureInformation {
 7986            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7987            documentation: None,
 7988            parameters: Some(vec![
 7989                lsp::ParameterInformation {
 7990                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7991                    documentation: None,
 7992                },
 7993                lsp::ParameterInformation {
 7994                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7995                    documentation: None,
 7996                },
 7997            ]),
 7998            active_parameter: None,
 7999        }],
 8000        active_signature: Some(0),
 8001        active_parameter: Some(1),
 8002    };
 8003    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8004    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8005        .await;
 8006
 8007    // When selecting a range, the popover is gone.
 8008    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8009    cx.update_editor(|editor, cx| {
 8010        editor.change_selections(None, cx, |s| {
 8011            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8012        })
 8013    });
 8014    cx.assert_editor_state(indoc! {"
 8015        fn main() {
 8016            sample(param1, «ˇparam2»);
 8017        }
 8018
 8019        fn sample(param1: u8, param2: u8) {}
 8020    "});
 8021    cx.editor(|editor, _| {
 8022        assert!(!editor.signature_help_state.is_shown());
 8023    });
 8024
 8025    // When unselecting again, the popover is back if within the brackets.
 8026    cx.update_editor(|editor, cx| {
 8027        editor.change_selections(None, cx, |s| {
 8028            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8029        })
 8030    });
 8031    cx.assert_editor_state(indoc! {"
 8032        fn main() {
 8033            sample(param1, ˇparam2);
 8034        }
 8035
 8036        fn sample(param1: u8, param2: u8) {}
 8037    "});
 8038    handle_signature_help_request(&mut cx, mocked_response).await;
 8039    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8040        .await;
 8041    cx.editor(|editor, _| {
 8042        assert!(editor.signature_help_state.is_shown());
 8043    });
 8044
 8045    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8046    cx.update_editor(|editor, cx| {
 8047        editor.change_selections(None, cx, |s| {
 8048            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8049            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8050        })
 8051    });
 8052    cx.assert_editor_state(indoc! {"
 8053        fn main() {
 8054            sample(param1, ˇparam2);
 8055        }
 8056
 8057        fn sample(param1: u8, param2: u8) {}
 8058    "});
 8059
 8060    let mocked_response = lsp::SignatureHelp {
 8061        signatures: vec![lsp::SignatureInformation {
 8062            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8063            documentation: None,
 8064            parameters: Some(vec![
 8065                lsp::ParameterInformation {
 8066                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8067                    documentation: None,
 8068                },
 8069                lsp::ParameterInformation {
 8070                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8071                    documentation: None,
 8072                },
 8073            ]),
 8074            active_parameter: None,
 8075        }],
 8076        active_signature: Some(0),
 8077        active_parameter: Some(1),
 8078    };
 8079    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8080    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8081        .await;
 8082    cx.update_editor(|editor, cx| {
 8083        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8084    });
 8085    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8086        .await;
 8087    cx.update_editor(|editor, cx| {
 8088        editor.change_selections(None, cx, |s| {
 8089            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8090        })
 8091    });
 8092    cx.assert_editor_state(indoc! {"
 8093        fn main() {
 8094            sample(param1, «ˇparam2»);
 8095        }
 8096
 8097        fn sample(param1: u8, param2: u8) {}
 8098    "});
 8099    cx.update_editor(|editor, cx| {
 8100        editor.change_selections(None, cx, |s| {
 8101            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8102        })
 8103    });
 8104    cx.assert_editor_state(indoc! {"
 8105        fn main() {
 8106            sample(param1, ˇparam2);
 8107        }
 8108
 8109        fn sample(param1: u8, param2: u8) {}
 8110    "});
 8111    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8112        .await;
 8113}
 8114
 8115#[gpui::test]
 8116async fn test_completion(cx: &mut gpui::TestAppContext) {
 8117    init_test(cx, |_| {});
 8118
 8119    let mut cx = EditorLspTestContext::new_rust(
 8120        lsp::ServerCapabilities {
 8121            completion_provider: Some(lsp::CompletionOptions {
 8122                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8123                resolve_provider: Some(true),
 8124                ..Default::default()
 8125            }),
 8126            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8127            ..Default::default()
 8128        },
 8129        cx,
 8130    )
 8131    .await;
 8132    let counter = Arc::new(AtomicUsize::new(0));
 8133
 8134    cx.set_state(indoc! {"
 8135        oneˇ
 8136        two
 8137        three
 8138    "});
 8139    cx.simulate_keystroke(".");
 8140    handle_completion_request(
 8141        &mut cx,
 8142        indoc! {"
 8143            one.|<>
 8144            two
 8145            three
 8146        "},
 8147        vec!["first_completion", "second_completion"],
 8148        counter.clone(),
 8149    )
 8150    .await;
 8151    cx.condition(|editor, _| editor.context_menu_visible())
 8152        .await;
 8153    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8154
 8155    let _handler = handle_signature_help_request(
 8156        &mut cx,
 8157        lsp::SignatureHelp {
 8158            signatures: vec![lsp::SignatureInformation {
 8159                label: "test signature".to_string(),
 8160                documentation: None,
 8161                parameters: Some(vec![lsp::ParameterInformation {
 8162                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8163                    documentation: None,
 8164                }]),
 8165                active_parameter: None,
 8166            }],
 8167            active_signature: None,
 8168            active_parameter: None,
 8169        },
 8170    );
 8171    cx.update_editor(|editor, cx| {
 8172        assert!(
 8173            !editor.signature_help_state.is_shown(),
 8174            "No signature help was called for"
 8175        );
 8176        editor.show_signature_help(&ShowSignatureHelp, cx);
 8177    });
 8178    cx.run_until_parked();
 8179    cx.update_editor(|editor, _| {
 8180        assert!(
 8181            !editor.signature_help_state.is_shown(),
 8182            "No signature help should be shown when completions menu is open"
 8183        );
 8184    });
 8185
 8186    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8187        editor.context_menu_next(&Default::default(), cx);
 8188        editor
 8189            .confirm_completion(&ConfirmCompletion::default(), cx)
 8190            .unwrap()
 8191    });
 8192    cx.assert_editor_state(indoc! {"
 8193        one.second_completionˇ
 8194        two
 8195        three
 8196    "});
 8197
 8198    handle_resolve_completion_request(
 8199        &mut cx,
 8200        Some(vec![
 8201            (
 8202                //This overlaps with the primary completion edit which is
 8203                //misbehavior from the LSP spec, test that we filter it out
 8204                indoc! {"
 8205                    one.second_ˇcompletion
 8206                    two
 8207                    threeˇ
 8208                "},
 8209                "overlapping additional edit",
 8210            ),
 8211            (
 8212                indoc! {"
 8213                    one.second_completion
 8214                    two
 8215                    threeˇ
 8216                "},
 8217                "\nadditional edit",
 8218            ),
 8219        ]),
 8220    )
 8221    .await;
 8222    apply_additional_edits.await.unwrap();
 8223    cx.assert_editor_state(indoc! {"
 8224        one.second_completionˇ
 8225        two
 8226        three
 8227        additional edit
 8228    "});
 8229
 8230    cx.set_state(indoc! {"
 8231        one.second_completion
 8232        twoˇ
 8233        threeˇ
 8234        additional edit
 8235    "});
 8236    cx.simulate_keystroke(" ");
 8237    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8238    cx.simulate_keystroke("s");
 8239    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8240
 8241    cx.assert_editor_state(indoc! {"
 8242        one.second_completion
 8243        two sˇ
 8244        three sˇ
 8245        additional edit
 8246    "});
 8247    handle_completion_request(
 8248        &mut cx,
 8249        indoc! {"
 8250            one.second_completion
 8251            two s
 8252            three <s|>
 8253            additional edit
 8254        "},
 8255        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8256        counter.clone(),
 8257    )
 8258    .await;
 8259    cx.condition(|editor, _| editor.context_menu_visible())
 8260        .await;
 8261    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8262
 8263    cx.simulate_keystroke("i");
 8264
 8265    handle_completion_request(
 8266        &mut cx,
 8267        indoc! {"
 8268            one.second_completion
 8269            two si
 8270            three <si|>
 8271            additional edit
 8272        "},
 8273        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8274        counter.clone(),
 8275    )
 8276    .await;
 8277    cx.condition(|editor, _| editor.context_menu_visible())
 8278        .await;
 8279    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8280
 8281    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8282        editor
 8283            .confirm_completion(&ConfirmCompletion::default(), cx)
 8284            .unwrap()
 8285    });
 8286    cx.assert_editor_state(indoc! {"
 8287        one.second_completion
 8288        two sixth_completionˇ
 8289        three sixth_completionˇ
 8290        additional edit
 8291    "});
 8292
 8293    handle_resolve_completion_request(&mut cx, None).await;
 8294    apply_additional_edits.await.unwrap();
 8295
 8296    cx.update(|cx| {
 8297        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8298            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8299                settings.show_completions_on_input = Some(false);
 8300            });
 8301        })
 8302    });
 8303    cx.set_state("editorˇ");
 8304    cx.simulate_keystroke(".");
 8305    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8306    cx.simulate_keystroke("c");
 8307    cx.simulate_keystroke("l");
 8308    cx.simulate_keystroke("o");
 8309    cx.assert_editor_state("editor.cloˇ");
 8310    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8311    cx.update_editor(|editor, cx| {
 8312        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8313    });
 8314    handle_completion_request(
 8315        &mut cx,
 8316        "editor.<clo|>",
 8317        vec!["close", "clobber"],
 8318        counter.clone(),
 8319    )
 8320    .await;
 8321    cx.condition(|editor, _| editor.context_menu_visible())
 8322        .await;
 8323    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8324
 8325    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8326        editor
 8327            .confirm_completion(&ConfirmCompletion::default(), cx)
 8328            .unwrap()
 8329    });
 8330    cx.assert_editor_state("editor.closeˇ");
 8331    handle_resolve_completion_request(&mut cx, None).await;
 8332    apply_additional_edits.await.unwrap();
 8333}
 8334
 8335#[gpui::test]
 8336async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8337    init_test(cx, |_| {});
 8338    let mut cx = EditorLspTestContext::new_rust(
 8339        lsp::ServerCapabilities {
 8340            completion_provider: Some(lsp::CompletionOptions {
 8341                trigger_characters: Some(vec![".".to_string()]),
 8342                ..Default::default()
 8343            }),
 8344            ..Default::default()
 8345        },
 8346        cx,
 8347    )
 8348    .await;
 8349    cx.lsp
 8350        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8351            Ok(Some(lsp::CompletionResponse::Array(vec![
 8352                lsp::CompletionItem {
 8353                    label: "first".into(),
 8354                    ..Default::default()
 8355                },
 8356                lsp::CompletionItem {
 8357                    label: "last".into(),
 8358                    ..Default::default()
 8359                },
 8360            ])))
 8361        });
 8362    cx.set_state("variableˇ");
 8363    cx.simulate_keystroke(".");
 8364    cx.executor().run_until_parked();
 8365
 8366    cx.update_editor(|editor, _| {
 8367        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8368            assert_eq!(
 8369                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8370                &["first", "last"]
 8371            );
 8372        } else {
 8373            panic!("expected completion menu to be open");
 8374        }
 8375    });
 8376
 8377    cx.update_editor(|editor, cx| {
 8378        editor.move_page_down(&MovePageDown::default(), cx);
 8379        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8380            assert!(
 8381                menu.selected_item == 1,
 8382                "expected PageDown to select the last item from the context menu"
 8383            );
 8384        } else {
 8385            panic!("expected completion menu to stay open after PageDown");
 8386        }
 8387    });
 8388
 8389    cx.update_editor(|editor, cx| {
 8390        editor.move_page_up(&MovePageUp::default(), cx);
 8391        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8392            assert!(
 8393                menu.selected_item == 0,
 8394                "expected PageUp to select the first item from the context menu"
 8395            );
 8396        } else {
 8397            panic!("expected completion menu to stay open after PageUp");
 8398        }
 8399    });
 8400}
 8401
 8402#[gpui::test]
 8403async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8404    init_test(cx, |_| {});
 8405    let mut cx = EditorLspTestContext::new_rust(
 8406        lsp::ServerCapabilities {
 8407            completion_provider: Some(lsp::CompletionOptions {
 8408                trigger_characters: Some(vec![".".to_string()]),
 8409                ..Default::default()
 8410            }),
 8411            ..Default::default()
 8412        },
 8413        cx,
 8414    )
 8415    .await;
 8416    cx.lsp
 8417        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8418            Ok(Some(lsp::CompletionResponse::Array(vec![
 8419                lsp::CompletionItem {
 8420                    label: "Range".into(),
 8421                    sort_text: Some("a".into()),
 8422                    ..Default::default()
 8423                },
 8424                lsp::CompletionItem {
 8425                    label: "r".into(),
 8426                    sort_text: Some("b".into()),
 8427                    ..Default::default()
 8428                },
 8429                lsp::CompletionItem {
 8430                    label: "ret".into(),
 8431                    sort_text: Some("c".into()),
 8432                    ..Default::default()
 8433                },
 8434                lsp::CompletionItem {
 8435                    label: "return".into(),
 8436                    sort_text: Some("d".into()),
 8437                    ..Default::default()
 8438                },
 8439                lsp::CompletionItem {
 8440                    label: "slice".into(),
 8441                    sort_text: Some("d".into()),
 8442                    ..Default::default()
 8443                },
 8444            ])))
 8445        });
 8446    cx.set_state("");
 8447    cx.executor().run_until_parked();
 8448    cx.update_editor(|editor, cx| {
 8449        editor.show_completions(
 8450            &ShowCompletions {
 8451                trigger: Some("r".into()),
 8452            },
 8453            cx,
 8454        );
 8455    });
 8456    cx.executor().run_until_parked();
 8457
 8458    cx.update_editor(|editor, _| {
 8459        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8460            assert_eq!(
 8461                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8462                &["r", "ret", "Range", "return"]
 8463            );
 8464        } else {
 8465            panic!("expected completion menu to be open");
 8466        }
 8467    });
 8468}
 8469
 8470#[gpui::test]
 8471async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8472    init_test(cx, |_| {});
 8473
 8474    let mut cx = EditorLspTestContext::new_rust(
 8475        lsp::ServerCapabilities {
 8476            completion_provider: Some(lsp::CompletionOptions {
 8477                trigger_characters: Some(vec![".".to_string()]),
 8478                resolve_provider: Some(true),
 8479                ..Default::default()
 8480            }),
 8481            ..Default::default()
 8482        },
 8483        cx,
 8484    )
 8485    .await;
 8486
 8487    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8488    cx.simulate_keystroke(".");
 8489    let completion_item = lsp::CompletionItem {
 8490        label: "Some".into(),
 8491        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8492        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8493        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8494            kind: lsp::MarkupKind::Markdown,
 8495            value: "```rust\nSome(2)\n```".to_string(),
 8496        })),
 8497        deprecated: Some(false),
 8498        sort_text: Some("Some".to_string()),
 8499        filter_text: Some("Some".to_string()),
 8500        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8501        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8502            range: lsp::Range {
 8503                start: lsp::Position {
 8504                    line: 0,
 8505                    character: 22,
 8506                },
 8507                end: lsp::Position {
 8508                    line: 0,
 8509                    character: 22,
 8510                },
 8511            },
 8512            new_text: "Some(2)".to_string(),
 8513        })),
 8514        additional_text_edits: Some(vec![lsp::TextEdit {
 8515            range: lsp::Range {
 8516                start: lsp::Position {
 8517                    line: 0,
 8518                    character: 20,
 8519                },
 8520                end: lsp::Position {
 8521                    line: 0,
 8522                    character: 22,
 8523                },
 8524            },
 8525            new_text: "".to_string(),
 8526        }]),
 8527        ..Default::default()
 8528    };
 8529
 8530    let closure_completion_item = completion_item.clone();
 8531    let counter = Arc::new(AtomicUsize::new(0));
 8532    let counter_clone = counter.clone();
 8533    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8534        let task_completion_item = closure_completion_item.clone();
 8535        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8536        async move {
 8537            Ok(Some(lsp::CompletionResponse::Array(vec![
 8538                task_completion_item,
 8539            ])))
 8540        }
 8541    });
 8542
 8543    cx.condition(|editor, _| editor.context_menu_visible())
 8544        .await;
 8545    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8546    assert!(request.next().await.is_some());
 8547    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8548
 8549    cx.simulate_keystroke("S");
 8550    cx.simulate_keystroke("o");
 8551    cx.simulate_keystroke("m");
 8552    cx.condition(|editor, _| editor.context_menu_visible())
 8553        .await;
 8554    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8555    assert!(request.next().await.is_some());
 8556    assert!(request.next().await.is_some());
 8557    assert!(request.next().await.is_some());
 8558    request.close();
 8559    assert!(request.next().await.is_none());
 8560    assert_eq!(
 8561        counter.load(atomic::Ordering::Acquire),
 8562        4,
 8563        "With the completions menu open, only one LSP request should happen per input"
 8564    );
 8565}
 8566
 8567#[gpui::test]
 8568async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8569    init_test(cx, |_| {});
 8570    let mut cx = EditorTestContext::new(cx).await;
 8571    let language = Arc::new(Language::new(
 8572        LanguageConfig {
 8573            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8574            ..Default::default()
 8575        },
 8576        Some(tree_sitter_rust::LANGUAGE.into()),
 8577    ));
 8578    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8579
 8580    // If multiple selections intersect a line, the line is only toggled once.
 8581    cx.set_state(indoc! {"
 8582        fn a() {
 8583            «//b();
 8584            ˇ»// «c();
 8585            //ˇ»  d();
 8586        }
 8587    "});
 8588
 8589    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8590
 8591    cx.assert_editor_state(indoc! {"
 8592        fn a() {
 8593            «b();
 8594            c();
 8595            ˇ» d();
 8596        }
 8597    "});
 8598
 8599    // The comment prefix is inserted at the same column for every line in a
 8600    // selection.
 8601    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8602
 8603    cx.assert_editor_state(indoc! {"
 8604        fn a() {
 8605            // «b();
 8606            // c();
 8607            ˇ»//  d();
 8608        }
 8609    "});
 8610
 8611    // If a selection ends at the beginning of a line, that line is not toggled.
 8612    cx.set_selections_state(indoc! {"
 8613        fn a() {
 8614            // b();
 8615            «// c();
 8616        ˇ»    //  d();
 8617        }
 8618    "});
 8619
 8620    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8621
 8622    cx.assert_editor_state(indoc! {"
 8623        fn a() {
 8624            // b();
 8625            «c();
 8626        ˇ»    //  d();
 8627        }
 8628    "});
 8629
 8630    // If a selection span a single line and is empty, the line is toggled.
 8631    cx.set_state(indoc! {"
 8632        fn a() {
 8633            a();
 8634            b();
 8635        ˇ
 8636        }
 8637    "});
 8638
 8639    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8640
 8641    cx.assert_editor_state(indoc! {"
 8642        fn a() {
 8643            a();
 8644            b();
 8645        //•ˇ
 8646        }
 8647    "});
 8648
 8649    // If a selection span multiple lines, empty lines are not toggled.
 8650    cx.set_state(indoc! {"
 8651        fn a() {
 8652            «a();
 8653
 8654            c();ˇ»
 8655        }
 8656    "});
 8657
 8658    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8659
 8660    cx.assert_editor_state(indoc! {"
 8661        fn a() {
 8662            // «a();
 8663
 8664            // c();ˇ»
 8665        }
 8666    "});
 8667
 8668    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8669    cx.set_state(indoc! {"
 8670        fn a() {
 8671            «// a();
 8672            /// b();
 8673            //! c();ˇ»
 8674        }
 8675    "});
 8676
 8677    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8678
 8679    cx.assert_editor_state(indoc! {"
 8680        fn a() {
 8681            «a();
 8682            b();
 8683            c();ˇ»
 8684        }
 8685    "});
 8686}
 8687
 8688#[gpui::test]
 8689async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8690    init_test(cx, |_| {});
 8691    let mut cx = EditorTestContext::new(cx).await;
 8692    let language = Arc::new(Language::new(
 8693        LanguageConfig {
 8694            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8695            ..Default::default()
 8696        },
 8697        Some(tree_sitter_rust::LANGUAGE.into()),
 8698    ));
 8699    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8700
 8701    let toggle_comments = &ToggleComments {
 8702        advance_downwards: false,
 8703        ignore_indent: true,
 8704    };
 8705
 8706    // If multiple selections intersect a line, the line is only toggled once.
 8707    cx.set_state(indoc! {"
 8708        fn a() {
 8709        //    «b();
 8710        //    c();
 8711        //    ˇ» d();
 8712        }
 8713    "});
 8714
 8715    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8716
 8717    cx.assert_editor_state(indoc! {"
 8718        fn a() {
 8719            «b();
 8720            c();
 8721            ˇ» d();
 8722        }
 8723    "});
 8724
 8725    // The comment prefix is inserted at the beginning of each line
 8726    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8727
 8728    cx.assert_editor_state(indoc! {"
 8729        fn a() {
 8730        //    «b();
 8731        //    c();
 8732        //    ˇ» d();
 8733        }
 8734    "});
 8735
 8736    // If a selection ends at the beginning of a line, that line is not toggled.
 8737    cx.set_selections_state(indoc! {"
 8738        fn a() {
 8739        //    b();
 8740        //    «c();
 8741        ˇ»//     d();
 8742        }
 8743    "});
 8744
 8745    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8746
 8747    cx.assert_editor_state(indoc! {"
 8748        fn a() {
 8749        //    b();
 8750            «c();
 8751        ˇ»//     d();
 8752        }
 8753    "});
 8754
 8755    // If a selection span a single line and is empty, the line is toggled.
 8756    cx.set_state(indoc! {"
 8757        fn a() {
 8758            a();
 8759            b();
 8760        ˇ
 8761        }
 8762    "});
 8763
 8764    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8765
 8766    cx.assert_editor_state(indoc! {"
 8767        fn a() {
 8768            a();
 8769            b();
 8770        //ˇ
 8771        }
 8772    "});
 8773
 8774    // If a selection span multiple lines, empty lines are not toggled.
 8775    cx.set_state(indoc! {"
 8776        fn a() {
 8777            «a();
 8778
 8779            c();ˇ»
 8780        }
 8781    "});
 8782
 8783    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8784
 8785    cx.assert_editor_state(indoc! {"
 8786        fn a() {
 8787        //    «a();
 8788
 8789        //    c();ˇ»
 8790        }
 8791    "});
 8792
 8793    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8794    cx.set_state(indoc! {"
 8795        fn a() {
 8796        //    «a();
 8797        ///    b();
 8798        //!    c();ˇ»
 8799        }
 8800    "});
 8801
 8802    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8803
 8804    cx.assert_editor_state(indoc! {"
 8805        fn a() {
 8806            «a();
 8807            b();
 8808            c();ˇ»
 8809        }
 8810    "});
 8811}
 8812
 8813#[gpui::test]
 8814async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8815    init_test(cx, |_| {});
 8816
 8817    let language = Arc::new(Language::new(
 8818        LanguageConfig {
 8819            line_comments: vec!["// ".into()],
 8820            ..Default::default()
 8821        },
 8822        Some(tree_sitter_rust::LANGUAGE.into()),
 8823    ));
 8824
 8825    let mut cx = EditorTestContext::new(cx).await;
 8826
 8827    cx.language_registry().add(language.clone());
 8828    cx.update_buffer(|buffer, cx| {
 8829        buffer.set_language(Some(language), cx);
 8830    });
 8831
 8832    let toggle_comments = &ToggleComments {
 8833        advance_downwards: true,
 8834        ignore_indent: false,
 8835    };
 8836
 8837    // Single cursor on one line -> advance
 8838    // Cursor moves horizontally 3 characters as well on non-blank line
 8839    cx.set_state(indoc!(
 8840        "fn a() {
 8841             ˇdog();
 8842             cat();
 8843        }"
 8844    ));
 8845    cx.update_editor(|editor, cx| {
 8846        editor.toggle_comments(toggle_comments, cx);
 8847    });
 8848    cx.assert_editor_state(indoc!(
 8849        "fn a() {
 8850             // dog();
 8851             catˇ();
 8852        }"
 8853    ));
 8854
 8855    // Single selection on one line -> don't advance
 8856    cx.set_state(indoc!(
 8857        "fn a() {
 8858             «dog()ˇ»;
 8859             cat();
 8860        }"
 8861    ));
 8862    cx.update_editor(|editor, cx| {
 8863        editor.toggle_comments(toggle_comments, cx);
 8864    });
 8865    cx.assert_editor_state(indoc!(
 8866        "fn a() {
 8867             // «dog()ˇ»;
 8868             cat();
 8869        }"
 8870    ));
 8871
 8872    // Multiple cursors on one line -> advance
 8873    cx.set_state(indoc!(
 8874        "fn a() {
 8875             ˇdˇog();
 8876             cat();
 8877        }"
 8878    ));
 8879    cx.update_editor(|editor, cx| {
 8880        editor.toggle_comments(toggle_comments, cx);
 8881    });
 8882    cx.assert_editor_state(indoc!(
 8883        "fn a() {
 8884             // dog();
 8885             catˇ(ˇ);
 8886        }"
 8887    ));
 8888
 8889    // Multiple cursors on one line, with selection -> don't advance
 8890    cx.set_state(indoc!(
 8891        "fn a() {
 8892             ˇdˇog«()ˇ»;
 8893             cat();
 8894        }"
 8895    ));
 8896    cx.update_editor(|editor, cx| {
 8897        editor.toggle_comments(toggle_comments, cx);
 8898    });
 8899    cx.assert_editor_state(indoc!(
 8900        "fn a() {
 8901             // ˇdˇog«()ˇ»;
 8902             cat();
 8903        }"
 8904    ));
 8905
 8906    // Single cursor on one line -> advance
 8907    // Cursor moves to column 0 on blank line
 8908    cx.set_state(indoc!(
 8909        "fn a() {
 8910             ˇdog();
 8911
 8912             cat();
 8913        }"
 8914    ));
 8915    cx.update_editor(|editor, cx| {
 8916        editor.toggle_comments(toggle_comments, cx);
 8917    });
 8918    cx.assert_editor_state(indoc!(
 8919        "fn a() {
 8920             // dog();
 8921        ˇ
 8922             cat();
 8923        }"
 8924    ));
 8925
 8926    // Single cursor on one line -> advance
 8927    // Cursor starts and ends at column 0
 8928    cx.set_state(indoc!(
 8929        "fn a() {
 8930         ˇ    dog();
 8931             cat();
 8932        }"
 8933    ));
 8934    cx.update_editor(|editor, cx| {
 8935        editor.toggle_comments(toggle_comments, cx);
 8936    });
 8937    cx.assert_editor_state(indoc!(
 8938        "fn a() {
 8939             // dog();
 8940         ˇ    cat();
 8941        }"
 8942    ));
 8943}
 8944
 8945#[gpui::test]
 8946async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8947    init_test(cx, |_| {});
 8948
 8949    let mut cx = EditorTestContext::new(cx).await;
 8950
 8951    let html_language = Arc::new(
 8952        Language::new(
 8953            LanguageConfig {
 8954                name: "HTML".into(),
 8955                block_comment: Some(("<!-- ".into(), " -->".into())),
 8956                ..Default::default()
 8957            },
 8958            Some(tree_sitter_html::language()),
 8959        )
 8960        .with_injection_query(
 8961            r#"
 8962            (script_element
 8963                (raw_text) @content
 8964                (#set! "language" "javascript"))
 8965            "#,
 8966        )
 8967        .unwrap(),
 8968    );
 8969
 8970    let javascript_language = Arc::new(Language::new(
 8971        LanguageConfig {
 8972            name: "JavaScript".into(),
 8973            line_comments: vec!["// ".into()],
 8974            ..Default::default()
 8975        },
 8976        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8977    ));
 8978
 8979    cx.language_registry().add(html_language.clone());
 8980    cx.language_registry().add(javascript_language.clone());
 8981    cx.update_buffer(|buffer, cx| {
 8982        buffer.set_language(Some(html_language), cx);
 8983    });
 8984
 8985    // Toggle comments for empty selections
 8986    cx.set_state(
 8987        &r#"
 8988            <p>A</p>ˇ
 8989            <p>B</p>ˇ
 8990            <p>C</p>ˇ
 8991        "#
 8992        .unindent(),
 8993    );
 8994    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8995    cx.assert_editor_state(
 8996        &r#"
 8997            <!-- <p>A</p>ˇ -->
 8998            <!-- <p>B</p>ˇ -->
 8999            <!-- <p>C</p>ˇ -->
 9000        "#
 9001        .unindent(),
 9002    );
 9003    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9004    cx.assert_editor_state(
 9005        &r#"
 9006            <p>A</p>ˇ
 9007            <p>B</p>ˇ
 9008            <p>C</p>ˇ
 9009        "#
 9010        .unindent(),
 9011    );
 9012
 9013    // Toggle comments for mixture of empty and non-empty selections, where
 9014    // multiple selections occupy a given line.
 9015    cx.set_state(
 9016        &r#"
 9017            <p>A«</p>
 9018            <p>ˇ»B</p>ˇ
 9019            <p>C«</p>
 9020            <p>ˇ»D</p>ˇ
 9021        "#
 9022        .unindent(),
 9023    );
 9024
 9025    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9026    cx.assert_editor_state(
 9027        &r#"
 9028            <!-- <p>A«</p>
 9029            <p>ˇ»B</p>ˇ -->
 9030            <!-- <p>C«</p>
 9031            <p>ˇ»D</p>ˇ -->
 9032        "#
 9033        .unindent(),
 9034    );
 9035    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9036    cx.assert_editor_state(
 9037        &r#"
 9038            <p>A«</p>
 9039            <p>ˇ»B</p>ˇ
 9040            <p>C«</p>
 9041            <p>ˇ»D</p>ˇ
 9042        "#
 9043        .unindent(),
 9044    );
 9045
 9046    // Toggle comments when different languages are active for different
 9047    // selections.
 9048    cx.set_state(
 9049        &r#"
 9050            ˇ<script>
 9051                ˇvar x = new Y();
 9052            ˇ</script>
 9053        "#
 9054        .unindent(),
 9055    );
 9056    cx.executor().run_until_parked();
 9057    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9058    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9059    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9060    cx.assert_editor_state(
 9061        &r#"
 9062            <!-- ˇ<script> -->
 9063                // ˇvar x = new Y();
 9064            // ˇ</script>
 9065        "#
 9066        .unindent(),
 9067    );
 9068}
 9069
 9070#[gpui::test]
 9071fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9072    init_test(cx, |_| {});
 9073
 9074    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9075    let multibuffer = cx.new_model(|cx| {
 9076        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9077        multibuffer.push_excerpts(
 9078            buffer.clone(),
 9079            [
 9080                ExcerptRange {
 9081                    context: Point::new(0, 0)..Point::new(0, 4),
 9082                    primary: None,
 9083                },
 9084                ExcerptRange {
 9085                    context: Point::new(1, 0)..Point::new(1, 4),
 9086                    primary: None,
 9087                },
 9088            ],
 9089            cx,
 9090        );
 9091        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9092        multibuffer
 9093    });
 9094
 9095    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9096    view.update(cx, |view, cx| {
 9097        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9098        view.change_selections(None, cx, |s| {
 9099            s.select_ranges([
 9100                Point::new(0, 0)..Point::new(0, 0),
 9101                Point::new(1, 0)..Point::new(1, 0),
 9102            ])
 9103        });
 9104
 9105        view.handle_input("X", cx);
 9106        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9107        assert_eq!(
 9108            view.selections.ranges(cx),
 9109            [
 9110                Point::new(0, 1)..Point::new(0, 1),
 9111                Point::new(1, 1)..Point::new(1, 1),
 9112            ]
 9113        );
 9114
 9115        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9116        view.change_selections(None, cx, |s| {
 9117            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9118        });
 9119        view.backspace(&Default::default(), cx);
 9120        assert_eq!(view.text(cx), "Xa\nbbb");
 9121        assert_eq!(
 9122            view.selections.ranges(cx),
 9123            [Point::new(1, 0)..Point::new(1, 0)]
 9124        );
 9125
 9126        view.change_selections(None, cx, |s| {
 9127            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9128        });
 9129        view.backspace(&Default::default(), cx);
 9130        assert_eq!(view.text(cx), "X\nbb");
 9131        assert_eq!(
 9132            view.selections.ranges(cx),
 9133            [Point::new(0, 1)..Point::new(0, 1)]
 9134        );
 9135    });
 9136}
 9137
 9138#[gpui::test]
 9139fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9140    init_test(cx, |_| {});
 9141
 9142    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9143    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9144        indoc! {"
 9145            [aaaa
 9146            (bbbb]
 9147            cccc)",
 9148        },
 9149        markers.clone(),
 9150    );
 9151    let excerpt_ranges = markers.into_iter().map(|marker| {
 9152        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9153        ExcerptRange {
 9154            context,
 9155            primary: None,
 9156        }
 9157    });
 9158    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9159    let multibuffer = cx.new_model(|cx| {
 9160        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9161        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9162        multibuffer
 9163    });
 9164
 9165    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9166    view.update(cx, |view, cx| {
 9167        let (expected_text, selection_ranges) = marked_text_ranges(
 9168            indoc! {"
 9169                aaaa
 9170                bˇbbb
 9171                bˇbbˇb
 9172                cccc"
 9173            },
 9174            true,
 9175        );
 9176        assert_eq!(view.text(cx), expected_text);
 9177        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9178
 9179        view.handle_input("X", cx);
 9180
 9181        let (expected_text, expected_selections) = marked_text_ranges(
 9182            indoc! {"
 9183                aaaa
 9184                bXˇbbXb
 9185                bXˇbbXˇb
 9186                cccc"
 9187            },
 9188            false,
 9189        );
 9190        assert_eq!(view.text(cx), expected_text);
 9191        assert_eq!(view.selections.ranges(cx), expected_selections);
 9192
 9193        view.newline(&Newline, cx);
 9194        let (expected_text, expected_selections) = marked_text_ranges(
 9195            indoc! {"
 9196                aaaa
 9197                bX
 9198                ˇbbX
 9199                b
 9200                bX
 9201                ˇbbX
 9202                ˇb
 9203                cccc"
 9204            },
 9205            false,
 9206        );
 9207        assert_eq!(view.text(cx), expected_text);
 9208        assert_eq!(view.selections.ranges(cx), expected_selections);
 9209    });
 9210}
 9211
 9212#[gpui::test]
 9213fn test_refresh_selections(cx: &mut TestAppContext) {
 9214    init_test(cx, |_| {});
 9215
 9216    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9217    let mut excerpt1_id = None;
 9218    let multibuffer = cx.new_model(|cx| {
 9219        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9220        excerpt1_id = multibuffer
 9221            .push_excerpts(
 9222                buffer.clone(),
 9223                [
 9224                    ExcerptRange {
 9225                        context: Point::new(0, 0)..Point::new(1, 4),
 9226                        primary: None,
 9227                    },
 9228                    ExcerptRange {
 9229                        context: Point::new(1, 0)..Point::new(2, 4),
 9230                        primary: None,
 9231                    },
 9232                ],
 9233                cx,
 9234            )
 9235            .into_iter()
 9236            .next();
 9237        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9238        multibuffer
 9239    });
 9240
 9241    let editor = cx.add_window(|cx| {
 9242        let mut editor = build_editor(multibuffer.clone(), cx);
 9243        let snapshot = editor.snapshot(cx);
 9244        editor.change_selections(None, cx, |s| {
 9245            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9246        });
 9247        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9248        assert_eq!(
 9249            editor.selections.ranges(cx),
 9250            [
 9251                Point::new(1, 3)..Point::new(1, 3),
 9252                Point::new(2, 1)..Point::new(2, 1),
 9253            ]
 9254        );
 9255        editor
 9256    });
 9257
 9258    // Refreshing selections is a no-op when excerpts haven't changed.
 9259    _ = editor.update(cx, |editor, cx| {
 9260        editor.change_selections(None, cx, |s| s.refresh());
 9261        assert_eq!(
 9262            editor.selections.ranges(cx),
 9263            [
 9264                Point::new(1, 3)..Point::new(1, 3),
 9265                Point::new(2, 1)..Point::new(2, 1),
 9266            ]
 9267        );
 9268    });
 9269
 9270    multibuffer.update(cx, |multibuffer, cx| {
 9271        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9272    });
 9273    _ = editor.update(cx, |editor, cx| {
 9274        // Removing an excerpt causes the first selection to become degenerate.
 9275        assert_eq!(
 9276            editor.selections.ranges(cx),
 9277            [
 9278                Point::new(0, 0)..Point::new(0, 0),
 9279                Point::new(0, 1)..Point::new(0, 1)
 9280            ]
 9281        );
 9282
 9283        // Refreshing selections will relocate the first selection to the original buffer
 9284        // location.
 9285        editor.change_selections(None, cx, |s| s.refresh());
 9286        assert_eq!(
 9287            editor.selections.ranges(cx),
 9288            [
 9289                Point::new(0, 1)..Point::new(0, 1),
 9290                Point::new(0, 3)..Point::new(0, 3)
 9291            ]
 9292        );
 9293        assert!(editor.selections.pending_anchor().is_some());
 9294    });
 9295}
 9296
 9297#[gpui::test]
 9298fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9299    init_test(cx, |_| {});
 9300
 9301    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9302    let mut excerpt1_id = None;
 9303    let multibuffer = cx.new_model(|cx| {
 9304        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9305        excerpt1_id = multibuffer
 9306            .push_excerpts(
 9307                buffer.clone(),
 9308                [
 9309                    ExcerptRange {
 9310                        context: Point::new(0, 0)..Point::new(1, 4),
 9311                        primary: None,
 9312                    },
 9313                    ExcerptRange {
 9314                        context: Point::new(1, 0)..Point::new(2, 4),
 9315                        primary: None,
 9316                    },
 9317                ],
 9318                cx,
 9319            )
 9320            .into_iter()
 9321            .next();
 9322        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9323        multibuffer
 9324    });
 9325
 9326    let editor = cx.add_window(|cx| {
 9327        let mut editor = build_editor(multibuffer.clone(), cx);
 9328        let snapshot = editor.snapshot(cx);
 9329        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9330        assert_eq!(
 9331            editor.selections.ranges(cx),
 9332            [Point::new(1, 3)..Point::new(1, 3)]
 9333        );
 9334        editor
 9335    });
 9336
 9337    multibuffer.update(cx, |multibuffer, cx| {
 9338        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9339    });
 9340    _ = editor.update(cx, |editor, cx| {
 9341        assert_eq!(
 9342            editor.selections.ranges(cx),
 9343            [Point::new(0, 0)..Point::new(0, 0)]
 9344        );
 9345
 9346        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9347        editor.change_selections(None, cx, |s| s.refresh());
 9348        assert_eq!(
 9349            editor.selections.ranges(cx),
 9350            [Point::new(0, 3)..Point::new(0, 3)]
 9351        );
 9352        assert!(editor.selections.pending_anchor().is_some());
 9353    });
 9354}
 9355
 9356#[gpui::test]
 9357async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9358    init_test(cx, |_| {});
 9359
 9360    let language = Arc::new(
 9361        Language::new(
 9362            LanguageConfig {
 9363                brackets: BracketPairConfig {
 9364                    pairs: vec![
 9365                        BracketPair {
 9366                            start: "{".to_string(),
 9367                            end: "}".to_string(),
 9368                            close: true,
 9369                            surround: true,
 9370                            newline: true,
 9371                        },
 9372                        BracketPair {
 9373                            start: "/* ".to_string(),
 9374                            end: " */".to_string(),
 9375                            close: true,
 9376                            surround: true,
 9377                            newline: true,
 9378                        },
 9379                    ],
 9380                    ..Default::default()
 9381                },
 9382                ..Default::default()
 9383            },
 9384            Some(tree_sitter_rust::LANGUAGE.into()),
 9385        )
 9386        .with_indents_query("")
 9387        .unwrap(),
 9388    );
 9389
 9390    let text = concat!(
 9391        "{   }\n",     //
 9392        "  x\n",       //
 9393        "  /*   */\n", //
 9394        "x\n",         //
 9395        "{{} }\n",     //
 9396    );
 9397
 9398    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9399    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9400    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9401    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9402        .await;
 9403
 9404    view.update(cx, |view, cx| {
 9405        view.change_selections(None, cx, |s| {
 9406            s.select_display_ranges([
 9407                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9408                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9409                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9410            ])
 9411        });
 9412        view.newline(&Newline, cx);
 9413
 9414        assert_eq!(
 9415            view.buffer().read(cx).read(cx).text(),
 9416            concat!(
 9417                "{ \n",    // Suppress rustfmt
 9418                "\n",      //
 9419                "}\n",     //
 9420                "  x\n",   //
 9421                "  /* \n", //
 9422                "  \n",    //
 9423                "  */\n",  //
 9424                "x\n",     //
 9425                "{{} \n",  //
 9426                "}\n",     //
 9427            )
 9428        );
 9429    });
 9430}
 9431
 9432#[gpui::test]
 9433fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9434    init_test(cx, |_| {});
 9435
 9436    let editor = cx.add_window(|cx| {
 9437        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9438        build_editor(buffer.clone(), cx)
 9439    });
 9440
 9441    _ = editor.update(cx, |editor, cx| {
 9442        struct Type1;
 9443        struct Type2;
 9444
 9445        let buffer = editor.buffer.read(cx).snapshot(cx);
 9446
 9447        let anchor_range =
 9448            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9449
 9450        editor.highlight_background::<Type1>(
 9451            &[
 9452                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9453                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9454                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9455                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9456            ],
 9457            |_| Hsla::red(),
 9458            cx,
 9459        );
 9460        editor.highlight_background::<Type2>(
 9461            &[
 9462                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9463                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9464                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9465                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9466            ],
 9467            |_| Hsla::green(),
 9468            cx,
 9469        );
 9470
 9471        let snapshot = editor.snapshot(cx);
 9472        let mut highlighted_ranges = editor.background_highlights_in_range(
 9473            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9474            &snapshot,
 9475            cx.theme().colors(),
 9476        );
 9477        // Enforce a consistent ordering based on color without relying on the ordering of the
 9478        // highlight's `TypeId` which is non-executor.
 9479        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9480        assert_eq!(
 9481            highlighted_ranges,
 9482            &[
 9483                (
 9484                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9485                    Hsla::red(),
 9486                ),
 9487                (
 9488                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9489                    Hsla::red(),
 9490                ),
 9491                (
 9492                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9493                    Hsla::green(),
 9494                ),
 9495                (
 9496                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9497                    Hsla::green(),
 9498                ),
 9499            ]
 9500        );
 9501        assert_eq!(
 9502            editor.background_highlights_in_range(
 9503                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9504                &snapshot,
 9505                cx.theme().colors(),
 9506            ),
 9507            &[(
 9508                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9509                Hsla::red(),
 9510            )]
 9511        );
 9512    });
 9513}
 9514
 9515#[gpui::test]
 9516async fn test_following(cx: &mut gpui::TestAppContext) {
 9517    init_test(cx, |_| {});
 9518
 9519    let fs = FakeFs::new(cx.executor());
 9520    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9521
 9522    let buffer = project.update(cx, |project, cx| {
 9523        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9524        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9525    });
 9526    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9527    let follower = cx.update(|cx| {
 9528        cx.open_window(
 9529            WindowOptions {
 9530                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9531                    gpui::Point::new(px(0.), px(0.)),
 9532                    gpui::Point::new(px(10.), px(80.)),
 9533                ))),
 9534                ..Default::default()
 9535            },
 9536            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9537        )
 9538        .unwrap()
 9539    });
 9540
 9541    let is_still_following = Rc::new(RefCell::new(true));
 9542    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9543    let pending_update = Rc::new(RefCell::new(None));
 9544    _ = follower.update(cx, {
 9545        let update = pending_update.clone();
 9546        let is_still_following = is_still_following.clone();
 9547        let follower_edit_event_count = follower_edit_event_count.clone();
 9548        |_, cx| {
 9549            cx.subscribe(
 9550                &leader.root_view(cx).unwrap(),
 9551                move |_, leader, event, cx| {
 9552                    leader
 9553                        .read(cx)
 9554                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9555                },
 9556            )
 9557            .detach();
 9558
 9559            cx.subscribe(
 9560                &follower.root_view(cx).unwrap(),
 9561                move |_, _, event: &EditorEvent, _cx| {
 9562                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9563                        *is_still_following.borrow_mut() = false;
 9564                    }
 9565
 9566                    if let EditorEvent::BufferEdited = event {
 9567                        *follower_edit_event_count.borrow_mut() += 1;
 9568                    }
 9569                },
 9570            )
 9571            .detach();
 9572        }
 9573    });
 9574
 9575    // Update the selections only
 9576    _ = leader.update(cx, |leader, cx| {
 9577        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9578    });
 9579    follower
 9580        .update(cx, |follower, cx| {
 9581            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9582        })
 9583        .unwrap()
 9584        .await
 9585        .unwrap();
 9586    _ = follower.update(cx, |follower, cx| {
 9587        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9588    });
 9589    assert!(*is_still_following.borrow());
 9590    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9591
 9592    // Update the scroll position only
 9593    _ = leader.update(cx, |leader, cx| {
 9594        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9595    });
 9596    follower
 9597        .update(cx, |follower, cx| {
 9598            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9599        })
 9600        .unwrap()
 9601        .await
 9602        .unwrap();
 9603    assert_eq!(
 9604        follower
 9605            .update(cx, |follower, cx| follower.scroll_position(cx))
 9606            .unwrap(),
 9607        gpui::Point::new(1.5, 3.5)
 9608    );
 9609    assert!(*is_still_following.borrow());
 9610    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9611
 9612    // Update the selections and scroll position. The follower's scroll position is updated
 9613    // via autoscroll, not via the leader's exact scroll position.
 9614    _ = leader.update(cx, |leader, cx| {
 9615        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9616        leader.request_autoscroll(Autoscroll::newest(), cx);
 9617        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9618    });
 9619    follower
 9620        .update(cx, |follower, cx| {
 9621            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9622        })
 9623        .unwrap()
 9624        .await
 9625        .unwrap();
 9626    _ = follower.update(cx, |follower, cx| {
 9627        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9628        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9629    });
 9630    assert!(*is_still_following.borrow());
 9631
 9632    // Creating a pending selection that precedes another selection
 9633    _ = leader.update(cx, |leader, cx| {
 9634        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9635        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9636    });
 9637    follower
 9638        .update(cx, |follower, cx| {
 9639            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9640        })
 9641        .unwrap()
 9642        .await
 9643        .unwrap();
 9644    _ = follower.update(cx, |follower, cx| {
 9645        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9646    });
 9647    assert!(*is_still_following.borrow());
 9648
 9649    // Extend the pending selection so that it surrounds another selection
 9650    _ = leader.update(cx, |leader, cx| {
 9651        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9652    });
 9653    follower
 9654        .update(cx, |follower, cx| {
 9655            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9656        })
 9657        .unwrap()
 9658        .await
 9659        .unwrap();
 9660    _ = follower.update(cx, |follower, cx| {
 9661        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9662    });
 9663
 9664    // Scrolling locally breaks the follow
 9665    _ = follower.update(cx, |follower, cx| {
 9666        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9667        follower.set_scroll_anchor(
 9668            ScrollAnchor {
 9669                anchor: top_anchor,
 9670                offset: gpui::Point::new(0.0, 0.5),
 9671            },
 9672            cx,
 9673        );
 9674    });
 9675    assert!(!(*is_still_following.borrow()));
 9676}
 9677
 9678#[gpui::test]
 9679async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9680    init_test(cx, |_| {});
 9681
 9682    let fs = FakeFs::new(cx.executor());
 9683    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9684    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9685    let pane = workspace
 9686        .update(cx, |workspace, _| workspace.active_pane().clone())
 9687        .unwrap();
 9688
 9689    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9690
 9691    let leader = pane.update(cx, |_, cx| {
 9692        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9693        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9694    });
 9695
 9696    // Start following the editor when it has no excerpts.
 9697    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9698    let follower_1 = cx
 9699        .update_window(*workspace.deref(), |_, cx| {
 9700            Editor::from_state_proto(
 9701                workspace.root_view(cx).unwrap(),
 9702                ViewId {
 9703                    creator: Default::default(),
 9704                    id: 0,
 9705                },
 9706                &mut state_message,
 9707                cx,
 9708            )
 9709        })
 9710        .unwrap()
 9711        .unwrap()
 9712        .await
 9713        .unwrap();
 9714
 9715    let update_message = Rc::new(RefCell::new(None));
 9716    follower_1.update(cx, {
 9717        let update = update_message.clone();
 9718        |_, cx| {
 9719            cx.subscribe(&leader, move |_, leader, event, cx| {
 9720                leader
 9721                    .read(cx)
 9722                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9723            })
 9724            .detach();
 9725        }
 9726    });
 9727
 9728    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9729        (
 9730            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9731            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9732        )
 9733    });
 9734
 9735    // Insert some excerpts.
 9736    leader.update(cx, |leader, cx| {
 9737        leader.buffer.update(cx, |multibuffer, cx| {
 9738            let excerpt_ids = multibuffer.push_excerpts(
 9739                buffer_1.clone(),
 9740                [
 9741                    ExcerptRange {
 9742                        context: 1..6,
 9743                        primary: None,
 9744                    },
 9745                    ExcerptRange {
 9746                        context: 12..15,
 9747                        primary: None,
 9748                    },
 9749                    ExcerptRange {
 9750                        context: 0..3,
 9751                        primary: None,
 9752                    },
 9753                ],
 9754                cx,
 9755            );
 9756            multibuffer.insert_excerpts_after(
 9757                excerpt_ids[0],
 9758                buffer_2.clone(),
 9759                [
 9760                    ExcerptRange {
 9761                        context: 8..12,
 9762                        primary: None,
 9763                    },
 9764                    ExcerptRange {
 9765                        context: 0..6,
 9766                        primary: None,
 9767                    },
 9768                ],
 9769                cx,
 9770            );
 9771        });
 9772    });
 9773
 9774    // Apply the update of adding the excerpts.
 9775    follower_1
 9776        .update(cx, |follower, cx| {
 9777            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9778        })
 9779        .await
 9780        .unwrap();
 9781    assert_eq!(
 9782        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9783        leader.update(cx, |editor, cx| editor.text(cx))
 9784    );
 9785    update_message.borrow_mut().take();
 9786
 9787    // Start following separately after it already has excerpts.
 9788    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9789    let follower_2 = cx
 9790        .update_window(*workspace.deref(), |_, cx| {
 9791            Editor::from_state_proto(
 9792                workspace.root_view(cx).unwrap().clone(),
 9793                ViewId {
 9794                    creator: Default::default(),
 9795                    id: 0,
 9796                },
 9797                &mut state_message,
 9798                cx,
 9799            )
 9800        })
 9801        .unwrap()
 9802        .unwrap()
 9803        .await
 9804        .unwrap();
 9805    assert_eq!(
 9806        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9807        leader.update(cx, |editor, cx| editor.text(cx))
 9808    );
 9809
 9810    // Remove some excerpts.
 9811    leader.update(cx, |leader, cx| {
 9812        leader.buffer.update(cx, |multibuffer, cx| {
 9813            let excerpt_ids = multibuffer.excerpt_ids();
 9814            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9815            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9816        });
 9817    });
 9818
 9819    // Apply the update of removing the excerpts.
 9820    follower_1
 9821        .update(cx, |follower, cx| {
 9822            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9823        })
 9824        .await
 9825        .unwrap();
 9826    follower_2
 9827        .update(cx, |follower, cx| {
 9828            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9829        })
 9830        .await
 9831        .unwrap();
 9832    update_message.borrow_mut().take();
 9833    assert_eq!(
 9834        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9835        leader.update(cx, |editor, cx| editor.text(cx))
 9836    );
 9837}
 9838
 9839#[gpui::test]
 9840async fn go_to_prev_overlapping_diagnostic(
 9841    executor: BackgroundExecutor,
 9842    cx: &mut gpui::TestAppContext,
 9843) {
 9844    init_test(cx, |_| {});
 9845
 9846    let mut cx = EditorTestContext::new(cx).await;
 9847    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9848
 9849    cx.set_state(indoc! {"
 9850        ˇfn func(abc def: i32) -> u32 {
 9851        }
 9852    "});
 9853
 9854    cx.update(|cx| {
 9855        project.update(cx, |project, cx| {
 9856            project
 9857                .update_diagnostics(
 9858                    LanguageServerId(0),
 9859                    lsp::PublishDiagnosticsParams {
 9860                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9861                        version: None,
 9862                        diagnostics: vec![
 9863                            lsp::Diagnostic {
 9864                                range: lsp::Range::new(
 9865                                    lsp::Position::new(0, 11),
 9866                                    lsp::Position::new(0, 12),
 9867                                ),
 9868                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9869                                ..Default::default()
 9870                            },
 9871                            lsp::Diagnostic {
 9872                                range: lsp::Range::new(
 9873                                    lsp::Position::new(0, 12),
 9874                                    lsp::Position::new(0, 15),
 9875                                ),
 9876                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9877                                ..Default::default()
 9878                            },
 9879                            lsp::Diagnostic {
 9880                                range: lsp::Range::new(
 9881                                    lsp::Position::new(0, 25),
 9882                                    lsp::Position::new(0, 28),
 9883                                ),
 9884                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9885                                ..Default::default()
 9886                            },
 9887                        ],
 9888                    },
 9889                    &[],
 9890                    cx,
 9891                )
 9892                .unwrap()
 9893        });
 9894    });
 9895
 9896    executor.run_until_parked();
 9897
 9898    cx.update_editor(|editor, cx| {
 9899        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9900    });
 9901
 9902    cx.assert_editor_state(indoc! {"
 9903        fn func(abc def: i32) -> ˇu32 {
 9904        }
 9905    "});
 9906
 9907    cx.update_editor(|editor, cx| {
 9908        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9909    });
 9910
 9911    cx.assert_editor_state(indoc! {"
 9912        fn func(abc ˇdef: i32) -> u32 {
 9913        }
 9914    "});
 9915
 9916    cx.update_editor(|editor, cx| {
 9917        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9918    });
 9919
 9920    cx.assert_editor_state(indoc! {"
 9921        fn func(abcˇ def: i32) -> u32 {
 9922        }
 9923    "});
 9924
 9925    cx.update_editor(|editor, cx| {
 9926        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9927    });
 9928
 9929    cx.assert_editor_state(indoc! {"
 9930        fn func(abc def: i32) -> ˇu32 {
 9931        }
 9932    "});
 9933}
 9934
 9935#[gpui::test]
 9936async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9937    init_test(cx, |_| {});
 9938
 9939    let mut cx = EditorTestContext::new(cx).await;
 9940
 9941    cx.set_state(indoc! {"
 9942        fn func(abˇc def: i32) -> u32 {
 9943        }
 9944    "});
 9945    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9946
 9947    cx.update(|cx| {
 9948        project.update(cx, |project, cx| {
 9949            project.update_diagnostics(
 9950                LanguageServerId(0),
 9951                lsp::PublishDiagnosticsParams {
 9952                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9953                    version: None,
 9954                    diagnostics: vec![lsp::Diagnostic {
 9955                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9956                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9957                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9958                        ..Default::default()
 9959                    }],
 9960                },
 9961                &[],
 9962                cx,
 9963            )
 9964        })
 9965    }).unwrap();
 9966    cx.run_until_parked();
 9967    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9968    cx.run_until_parked();
 9969    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9970}
 9971
 9972#[gpui::test]
 9973async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9974    init_test(cx, |_| {});
 9975
 9976    let mut cx = EditorTestContext::new(cx).await;
 9977
 9978    let diff_base = r#"
 9979        use some::mod;
 9980
 9981        const A: u32 = 42;
 9982
 9983        fn main() {
 9984            println!("hello");
 9985
 9986            println!("world");
 9987        }
 9988        "#
 9989    .unindent();
 9990
 9991    // Edits are modified, removed, modified, added
 9992    cx.set_state(
 9993        &r#"
 9994        use some::modified;
 9995
 9996        ˇ
 9997        fn main() {
 9998            println!("hello there");
 9999
10000            println!("around the");
10001            println!("world");
10002        }
10003        "#
10004        .unindent(),
10005    );
10006
10007    cx.set_diff_base(Some(&diff_base));
10008    executor.run_until_parked();
10009
10010    cx.update_editor(|editor, cx| {
10011        //Wrap around the bottom of the buffer
10012        for _ in 0..3 {
10013            editor.go_to_next_hunk(&GoToHunk, cx);
10014        }
10015    });
10016
10017    cx.assert_editor_state(
10018        &r#"
10019        ˇuse some::modified;
10020
10021
10022        fn main() {
10023            println!("hello there");
10024
10025            println!("around the");
10026            println!("world");
10027        }
10028        "#
10029        .unindent(),
10030    );
10031
10032    cx.update_editor(|editor, cx| {
10033        //Wrap around the top of the buffer
10034        for _ in 0..2 {
10035            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10036        }
10037    });
10038
10039    cx.assert_editor_state(
10040        &r#"
10041        use some::modified;
10042
10043
10044        fn main() {
10045        ˇ    println!("hello there");
10046
10047            println!("around the");
10048            println!("world");
10049        }
10050        "#
10051        .unindent(),
10052    );
10053
10054    cx.update_editor(|editor, cx| {
10055        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10056    });
10057
10058    cx.assert_editor_state(
10059        &r#"
10060        use some::modified;
10061
10062        ˇ
10063        fn main() {
10064            println!("hello there");
10065
10066            println!("around the");
10067            println!("world");
10068        }
10069        "#
10070        .unindent(),
10071    );
10072
10073    cx.update_editor(|editor, cx| {
10074        for _ in 0..3 {
10075            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10076        }
10077    });
10078
10079    cx.assert_editor_state(
10080        &r#"
10081        use some::modified;
10082
10083
10084        fn main() {
10085        ˇ    println!("hello there");
10086
10087            println!("around the");
10088            println!("world");
10089        }
10090        "#
10091        .unindent(),
10092    );
10093
10094    cx.update_editor(|editor, cx| {
10095        editor.fold(&Fold, cx);
10096
10097        //Make sure that the fold only gets one hunk
10098        for _ in 0..4 {
10099            editor.go_to_next_hunk(&GoToHunk, cx);
10100        }
10101    });
10102
10103    cx.assert_editor_state(
10104        &r#"
10105        ˇuse some::modified;
10106
10107
10108        fn main() {
10109            println!("hello there");
10110
10111            println!("around the");
10112            println!("world");
10113        }
10114        "#
10115        .unindent(),
10116    );
10117}
10118
10119#[test]
10120fn test_split_words() {
10121    fn split(text: &str) -> Vec<&str> {
10122        split_words(text).collect()
10123    }
10124
10125    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10126    assert_eq!(split("hello_world"), &["hello_", "world"]);
10127    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10128    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10129    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10130    assert_eq!(split("helloworld"), &["helloworld"]);
10131
10132    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10133}
10134
10135#[gpui::test]
10136async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10137    init_test(cx, |_| {});
10138
10139    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10140    let mut assert = |before, after| {
10141        let _state_context = cx.set_state(before);
10142        cx.update_editor(|editor, cx| {
10143            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10144        });
10145        cx.assert_editor_state(after);
10146    };
10147
10148    // Outside bracket jumps to outside of matching bracket
10149    assert("console.logˇ(var);", "console.log(var)ˇ;");
10150    assert("console.log(var)ˇ;", "console.logˇ(var);");
10151
10152    // Inside bracket jumps to inside of matching bracket
10153    assert("console.log(ˇvar);", "console.log(varˇ);");
10154    assert("console.log(varˇ);", "console.log(ˇvar);");
10155
10156    // When outside a bracket and inside, favor jumping to the inside bracket
10157    assert(
10158        "console.log('foo', [1, 2, 3]ˇ);",
10159        "console.log(ˇ'foo', [1, 2, 3]);",
10160    );
10161    assert(
10162        "console.log(ˇ'foo', [1, 2, 3]);",
10163        "console.log('foo', [1, 2, 3]ˇ);",
10164    );
10165
10166    // Bias forward if two options are equally likely
10167    assert(
10168        "let result = curried_fun()ˇ();",
10169        "let result = curried_fun()()ˇ;",
10170    );
10171
10172    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10173    assert(
10174        indoc! {"
10175            function test() {
10176                console.log('test')ˇ
10177            }"},
10178        indoc! {"
10179            function test() {
10180                console.logˇ('test')
10181            }"},
10182    );
10183}
10184
10185#[gpui::test]
10186async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10187    init_test(cx, |_| {});
10188
10189    let fs = FakeFs::new(cx.executor());
10190    fs.insert_tree(
10191        "/a",
10192        json!({
10193            "main.rs": "fn main() { let a = 5; }",
10194            "other.rs": "// Test file",
10195        }),
10196    )
10197    .await;
10198    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10199
10200    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10201    language_registry.add(Arc::new(Language::new(
10202        LanguageConfig {
10203            name: "Rust".into(),
10204            matcher: LanguageMatcher {
10205                path_suffixes: vec!["rs".to_string()],
10206                ..Default::default()
10207            },
10208            brackets: BracketPairConfig {
10209                pairs: vec![BracketPair {
10210                    start: "{".to_string(),
10211                    end: "}".to_string(),
10212                    close: true,
10213                    surround: true,
10214                    newline: true,
10215                }],
10216                disabled_scopes_by_bracket_ix: Vec::new(),
10217            },
10218            ..Default::default()
10219        },
10220        Some(tree_sitter_rust::LANGUAGE.into()),
10221    )));
10222    let mut fake_servers = language_registry.register_fake_lsp(
10223        "Rust",
10224        FakeLspAdapter {
10225            capabilities: lsp::ServerCapabilities {
10226                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10227                    first_trigger_character: "{".to_string(),
10228                    more_trigger_character: None,
10229                }),
10230                ..Default::default()
10231            },
10232            ..Default::default()
10233        },
10234    );
10235
10236    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10237
10238    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10239
10240    let worktree_id = workspace
10241        .update(cx, |workspace, cx| {
10242            workspace.project().update(cx, |project, cx| {
10243                project.worktrees(cx).next().unwrap().read(cx).id()
10244            })
10245        })
10246        .unwrap();
10247
10248    let buffer = project
10249        .update(cx, |project, cx| {
10250            project.open_local_buffer("/a/main.rs", cx)
10251        })
10252        .await
10253        .unwrap();
10254    cx.executor().run_until_parked();
10255    cx.executor().start_waiting();
10256    let fake_server = fake_servers.next().await.unwrap();
10257    let editor_handle = workspace
10258        .update(cx, |workspace, cx| {
10259            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10260        })
10261        .unwrap()
10262        .await
10263        .unwrap()
10264        .downcast::<Editor>()
10265        .unwrap();
10266
10267    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10268        assert_eq!(
10269            params.text_document_position.text_document.uri,
10270            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10271        );
10272        assert_eq!(
10273            params.text_document_position.position,
10274            lsp::Position::new(0, 21),
10275        );
10276
10277        Ok(Some(vec![lsp::TextEdit {
10278            new_text: "]".to_string(),
10279            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10280        }]))
10281    });
10282
10283    editor_handle.update(cx, |editor, cx| {
10284        editor.focus(cx);
10285        editor.change_selections(None, cx, |s| {
10286            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10287        });
10288        editor.handle_input("{", cx);
10289    });
10290
10291    cx.executor().run_until_parked();
10292
10293    buffer.update(cx, |buffer, _| {
10294        assert_eq!(
10295            buffer.text(),
10296            "fn main() { let a = {5}; }",
10297            "No extra braces from on type formatting should appear in the buffer"
10298        )
10299    });
10300}
10301
10302#[gpui::test]
10303async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10304    init_test(cx, |_| {});
10305
10306    let fs = FakeFs::new(cx.executor());
10307    fs.insert_tree(
10308        "/a",
10309        json!({
10310            "main.rs": "fn main() { let a = 5; }",
10311            "other.rs": "// Test file",
10312        }),
10313    )
10314    .await;
10315
10316    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10317
10318    let server_restarts = Arc::new(AtomicUsize::new(0));
10319    let closure_restarts = Arc::clone(&server_restarts);
10320    let language_server_name = "test language server";
10321    let language_name: LanguageName = "Rust".into();
10322
10323    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10324    language_registry.add(Arc::new(Language::new(
10325        LanguageConfig {
10326            name: language_name.clone(),
10327            matcher: LanguageMatcher {
10328                path_suffixes: vec!["rs".to_string()],
10329                ..Default::default()
10330            },
10331            ..Default::default()
10332        },
10333        Some(tree_sitter_rust::LANGUAGE.into()),
10334    )));
10335    let mut fake_servers = language_registry.register_fake_lsp(
10336        "Rust",
10337        FakeLspAdapter {
10338            name: language_server_name,
10339            initialization_options: Some(json!({
10340                "testOptionValue": true
10341            })),
10342            initializer: Some(Box::new(move |fake_server| {
10343                let task_restarts = Arc::clone(&closure_restarts);
10344                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10345                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10346                    futures::future::ready(Ok(()))
10347                });
10348            })),
10349            ..Default::default()
10350        },
10351    );
10352
10353    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10354    let _buffer = project
10355        .update(cx, |project, cx| {
10356            project.open_local_buffer("/a/main.rs", cx)
10357        })
10358        .await
10359        .unwrap();
10360    let _fake_server = fake_servers.next().await.unwrap();
10361    update_test_language_settings(cx, |language_settings| {
10362        language_settings.languages.insert(
10363            language_name.clone(),
10364            LanguageSettingsContent {
10365                tab_size: NonZeroU32::new(8),
10366                ..Default::default()
10367            },
10368        );
10369    });
10370    cx.executor().run_until_parked();
10371    assert_eq!(
10372        server_restarts.load(atomic::Ordering::Acquire),
10373        0,
10374        "Should not restart LSP server on an unrelated change"
10375    );
10376
10377    update_test_project_settings(cx, |project_settings| {
10378        project_settings.lsp.insert(
10379            "Some other server name".into(),
10380            LspSettings {
10381                binary: None,
10382                settings: None,
10383                initialization_options: Some(json!({
10384                    "some other init value": false
10385                })),
10386            },
10387        );
10388    });
10389    cx.executor().run_until_parked();
10390    assert_eq!(
10391        server_restarts.load(atomic::Ordering::Acquire),
10392        0,
10393        "Should not restart LSP server on an unrelated LSP settings change"
10394    );
10395
10396    update_test_project_settings(cx, |project_settings| {
10397        project_settings.lsp.insert(
10398            language_server_name.into(),
10399            LspSettings {
10400                binary: None,
10401                settings: None,
10402                initialization_options: Some(json!({
10403                    "anotherInitValue": false
10404                })),
10405            },
10406        );
10407    });
10408    cx.executor().run_until_parked();
10409    assert_eq!(
10410        server_restarts.load(atomic::Ordering::Acquire),
10411        1,
10412        "Should restart LSP server on a related LSP settings change"
10413    );
10414
10415    update_test_project_settings(cx, |project_settings| {
10416        project_settings.lsp.insert(
10417            language_server_name.into(),
10418            LspSettings {
10419                binary: None,
10420                settings: None,
10421                initialization_options: Some(json!({
10422                    "anotherInitValue": false
10423                })),
10424            },
10425        );
10426    });
10427    cx.executor().run_until_parked();
10428    assert_eq!(
10429        server_restarts.load(atomic::Ordering::Acquire),
10430        1,
10431        "Should not restart LSP server on a related LSP settings change that is the same"
10432    );
10433
10434    update_test_project_settings(cx, |project_settings| {
10435        project_settings.lsp.insert(
10436            language_server_name.into(),
10437            LspSettings {
10438                binary: None,
10439                settings: None,
10440                initialization_options: None,
10441            },
10442        );
10443    });
10444    cx.executor().run_until_parked();
10445    assert_eq!(
10446        server_restarts.load(atomic::Ordering::Acquire),
10447        2,
10448        "Should restart LSP server on another related LSP settings change"
10449    );
10450}
10451
10452#[gpui::test]
10453async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10454    init_test(cx, |_| {});
10455
10456    let mut cx = EditorLspTestContext::new_rust(
10457        lsp::ServerCapabilities {
10458            completion_provider: Some(lsp::CompletionOptions {
10459                trigger_characters: Some(vec![".".to_string()]),
10460                resolve_provider: Some(true),
10461                ..Default::default()
10462            }),
10463            ..Default::default()
10464        },
10465        cx,
10466    )
10467    .await;
10468
10469    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10470    cx.simulate_keystroke(".");
10471    let completion_item = lsp::CompletionItem {
10472        label: "some".into(),
10473        kind: Some(lsp::CompletionItemKind::SNIPPET),
10474        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10475        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10476            kind: lsp::MarkupKind::Markdown,
10477            value: "```rust\nSome(2)\n```".to_string(),
10478        })),
10479        deprecated: Some(false),
10480        sort_text: Some("fffffff2".to_string()),
10481        filter_text: Some("some".to_string()),
10482        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10483        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10484            range: lsp::Range {
10485                start: lsp::Position {
10486                    line: 0,
10487                    character: 22,
10488                },
10489                end: lsp::Position {
10490                    line: 0,
10491                    character: 22,
10492                },
10493            },
10494            new_text: "Some(2)".to_string(),
10495        })),
10496        additional_text_edits: Some(vec![lsp::TextEdit {
10497            range: lsp::Range {
10498                start: lsp::Position {
10499                    line: 0,
10500                    character: 20,
10501                },
10502                end: lsp::Position {
10503                    line: 0,
10504                    character: 22,
10505                },
10506            },
10507            new_text: "".to_string(),
10508        }]),
10509        ..Default::default()
10510    };
10511
10512    let closure_completion_item = completion_item.clone();
10513    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10514        let task_completion_item = closure_completion_item.clone();
10515        async move {
10516            Ok(Some(lsp::CompletionResponse::Array(vec![
10517                task_completion_item,
10518            ])))
10519        }
10520    });
10521
10522    request.next().await;
10523
10524    cx.condition(|editor, _| editor.context_menu_visible())
10525        .await;
10526    let apply_additional_edits = cx.update_editor(|editor, cx| {
10527        editor
10528            .confirm_completion(&ConfirmCompletion::default(), cx)
10529            .unwrap()
10530    });
10531    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10532
10533    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10534        let task_completion_item = completion_item.clone();
10535        async move { Ok(task_completion_item) }
10536    })
10537    .next()
10538    .await
10539    .unwrap();
10540    apply_additional_edits.await.unwrap();
10541    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10542}
10543
10544#[gpui::test]
10545async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10546    init_test(cx, |_| {});
10547
10548    let mut cx = EditorLspTestContext::new(
10549        Language::new(
10550            LanguageConfig {
10551                matcher: LanguageMatcher {
10552                    path_suffixes: vec!["jsx".into()],
10553                    ..Default::default()
10554                },
10555                overrides: [(
10556                    "element".into(),
10557                    LanguageConfigOverride {
10558                        word_characters: Override::Set(['-'].into_iter().collect()),
10559                        ..Default::default()
10560                    },
10561                )]
10562                .into_iter()
10563                .collect(),
10564                ..Default::default()
10565            },
10566            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10567        )
10568        .with_override_query("(jsx_self_closing_element) @element")
10569        .unwrap(),
10570        lsp::ServerCapabilities {
10571            completion_provider: Some(lsp::CompletionOptions {
10572                trigger_characters: Some(vec![":".to_string()]),
10573                ..Default::default()
10574            }),
10575            ..Default::default()
10576        },
10577        cx,
10578    )
10579    .await;
10580
10581    cx.lsp
10582        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10583            Ok(Some(lsp::CompletionResponse::Array(vec![
10584                lsp::CompletionItem {
10585                    label: "bg-blue".into(),
10586                    ..Default::default()
10587                },
10588                lsp::CompletionItem {
10589                    label: "bg-red".into(),
10590                    ..Default::default()
10591                },
10592                lsp::CompletionItem {
10593                    label: "bg-yellow".into(),
10594                    ..Default::default()
10595                },
10596            ])))
10597        });
10598
10599    cx.set_state(r#"<p class="bgˇ" />"#);
10600
10601    // Trigger completion when typing a dash, because the dash is an extra
10602    // word character in the 'element' scope, which contains the cursor.
10603    cx.simulate_keystroke("-");
10604    cx.executor().run_until_parked();
10605    cx.update_editor(|editor, _| {
10606        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10607            assert_eq!(
10608                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10609                &["bg-red", "bg-blue", "bg-yellow"]
10610            );
10611        } else {
10612            panic!("expected completion menu to be open");
10613        }
10614    });
10615
10616    cx.simulate_keystroke("l");
10617    cx.executor().run_until_parked();
10618    cx.update_editor(|editor, _| {
10619        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10620            assert_eq!(
10621                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10622                &["bg-blue", "bg-yellow"]
10623            );
10624        } else {
10625            panic!("expected completion menu to be open");
10626        }
10627    });
10628
10629    // When filtering completions, consider the character after the '-' to
10630    // be the start of a subword.
10631    cx.set_state(r#"<p class="yelˇ" />"#);
10632    cx.simulate_keystroke("l");
10633    cx.executor().run_until_parked();
10634    cx.update_editor(|editor, _| {
10635        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10636            assert_eq!(
10637                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10638                &["bg-yellow"]
10639            );
10640        } else {
10641            panic!("expected completion menu to be open");
10642        }
10643    });
10644}
10645
10646#[gpui::test]
10647async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10648    init_test(cx, |settings| {
10649        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10650            FormatterList(vec![Formatter::Prettier].into()),
10651        ))
10652    });
10653
10654    let fs = FakeFs::new(cx.executor());
10655    fs.insert_file("/file.ts", Default::default()).await;
10656
10657    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10658    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10659
10660    language_registry.add(Arc::new(Language::new(
10661        LanguageConfig {
10662            name: "TypeScript".into(),
10663            matcher: LanguageMatcher {
10664                path_suffixes: vec!["ts".to_string()],
10665                ..Default::default()
10666            },
10667            ..Default::default()
10668        },
10669        Some(tree_sitter_rust::LANGUAGE.into()),
10670    )));
10671    update_test_language_settings(cx, |settings| {
10672        settings.defaults.prettier = Some(PrettierSettings {
10673            allowed: true,
10674            ..PrettierSettings::default()
10675        });
10676    });
10677
10678    let test_plugin = "test_plugin";
10679    let _ = language_registry.register_fake_lsp(
10680        "TypeScript",
10681        FakeLspAdapter {
10682            prettier_plugins: vec![test_plugin],
10683            ..Default::default()
10684        },
10685    );
10686
10687    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10688    let buffer = project
10689        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10690        .await
10691        .unwrap();
10692
10693    let buffer_text = "one\ntwo\nthree\n";
10694    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10695    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10696    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10697
10698    editor
10699        .update(cx, |editor, cx| {
10700            editor.perform_format(
10701                project.clone(),
10702                FormatTrigger::Manual,
10703                FormatTarget::Buffer,
10704                cx,
10705            )
10706        })
10707        .unwrap()
10708        .await;
10709    assert_eq!(
10710        editor.update(cx, |editor, cx| editor.text(cx)),
10711        buffer_text.to_string() + prettier_format_suffix,
10712        "Test prettier formatting was not applied to the original buffer text",
10713    );
10714
10715    update_test_language_settings(cx, |settings| {
10716        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10717    });
10718    let format = editor.update(cx, |editor, cx| {
10719        editor.perform_format(
10720            project.clone(),
10721            FormatTrigger::Manual,
10722            FormatTarget::Buffer,
10723            cx,
10724        )
10725    });
10726    format.await.unwrap();
10727    assert_eq!(
10728        editor.update(cx, |editor, cx| editor.text(cx)),
10729        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10730        "Autoformatting (via test prettier) was not applied to the original buffer text",
10731    );
10732}
10733
10734#[gpui::test]
10735async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10736    init_test(cx, |_| {});
10737    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10738    let base_text = indoc! {r#"struct Row;
10739struct Row1;
10740struct Row2;
10741
10742struct Row4;
10743struct Row5;
10744struct Row6;
10745
10746struct Row8;
10747struct Row9;
10748struct Row10;"#};
10749
10750    // When addition hunks are not adjacent to carets, no hunk revert is performed
10751    assert_hunk_revert(
10752        indoc! {r#"struct Row;
10753                   struct Row1;
10754                   struct Row1.1;
10755                   struct Row1.2;
10756                   struct Row2;ˇ
10757
10758                   struct Row4;
10759                   struct Row5;
10760                   struct Row6;
10761
10762                   struct Row8;
10763                   ˇstruct Row9;
10764                   struct Row9.1;
10765                   struct Row9.2;
10766                   struct Row9.3;
10767                   struct Row10;"#},
10768        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10769        indoc! {r#"struct Row;
10770                   struct Row1;
10771                   struct Row1.1;
10772                   struct Row1.2;
10773                   struct Row2;ˇ
10774
10775                   struct Row4;
10776                   struct Row5;
10777                   struct Row6;
10778
10779                   struct Row8;
10780                   ˇstruct Row9;
10781                   struct Row9.1;
10782                   struct Row9.2;
10783                   struct Row9.3;
10784                   struct Row10;"#},
10785        base_text,
10786        &mut cx,
10787    );
10788    // Same for selections
10789    assert_hunk_revert(
10790        indoc! {r#"struct Row;
10791                   struct Row1;
10792                   struct Row2;
10793                   struct Row2.1;
10794                   struct Row2.2;
10795                   «ˇ
10796                   struct Row4;
10797                   struct» Row5;
10798                   «struct Row6;
10799                   ˇ»
10800                   struct Row9.1;
10801                   struct Row9.2;
10802                   struct Row9.3;
10803                   struct Row8;
10804                   struct Row9;
10805                   struct Row10;"#},
10806        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10807        indoc! {r#"struct Row;
10808                   struct Row1;
10809                   struct Row2;
10810                   struct Row2.1;
10811                   struct Row2.2;
10812                   «ˇ
10813                   struct Row4;
10814                   struct» Row5;
10815                   «struct Row6;
10816                   ˇ»
10817                   struct Row9.1;
10818                   struct Row9.2;
10819                   struct Row9.3;
10820                   struct Row8;
10821                   struct Row9;
10822                   struct Row10;"#},
10823        base_text,
10824        &mut cx,
10825    );
10826
10827    // When carets and selections intersect the addition hunks, those are reverted.
10828    // Adjacent carets got merged.
10829    assert_hunk_revert(
10830        indoc! {r#"struct Row;
10831                   ˇ// something on the top
10832                   struct Row1;
10833                   struct Row2;
10834                   struct Roˇw3.1;
10835                   struct Row2.2;
10836                   struct Row2.3;ˇ
10837
10838                   struct Row4;
10839                   struct ˇRow5.1;
10840                   struct Row5.2;
10841                   struct «Rowˇ»5.3;
10842                   struct Row5;
10843                   struct Row6;
10844                   ˇ
10845                   struct Row9.1;
10846                   struct «Rowˇ»9.2;
10847                   struct «ˇRow»9.3;
10848                   struct Row8;
10849                   struct Row9;
10850                   «ˇ// something on bottom»
10851                   struct Row10;"#},
10852        vec![
10853            DiffHunkStatus::Added,
10854            DiffHunkStatus::Added,
10855            DiffHunkStatus::Added,
10856            DiffHunkStatus::Added,
10857            DiffHunkStatus::Added,
10858        ],
10859        indoc! {r#"struct Row;
10860                   ˇstruct Row1;
10861                   struct Row2;
10862                   ˇ
10863                   struct Row4;
10864                   ˇstruct Row5;
10865                   struct Row6;
10866                   ˇ
10867                   ˇstruct Row8;
10868                   struct Row9;
10869                   ˇstruct Row10;"#},
10870        base_text,
10871        &mut cx,
10872    );
10873}
10874
10875#[gpui::test]
10876async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10877    init_test(cx, |_| {});
10878    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10879    let base_text = indoc! {r#"struct Row;
10880struct Row1;
10881struct Row2;
10882
10883struct Row4;
10884struct Row5;
10885struct Row6;
10886
10887struct Row8;
10888struct Row9;
10889struct Row10;"#};
10890
10891    // Modification hunks behave the same as the addition ones.
10892    assert_hunk_revert(
10893        indoc! {r#"struct Row;
10894                   struct Row1;
10895                   struct Row33;
10896                   ˇ
10897                   struct Row4;
10898                   struct Row5;
10899                   struct Row6;
10900                   ˇ
10901                   struct Row99;
10902                   struct Row9;
10903                   struct Row10;"#},
10904        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10905        indoc! {r#"struct Row;
10906                   struct Row1;
10907                   struct Row33;
10908                   ˇ
10909                   struct Row4;
10910                   struct Row5;
10911                   struct Row6;
10912                   ˇ
10913                   struct Row99;
10914                   struct Row9;
10915                   struct Row10;"#},
10916        base_text,
10917        &mut cx,
10918    );
10919    assert_hunk_revert(
10920        indoc! {r#"struct Row;
10921                   struct Row1;
10922                   struct Row33;
10923                   «ˇ
10924                   struct Row4;
10925                   struct» Row5;
10926                   «struct Row6;
10927                   ˇ»
10928                   struct Row99;
10929                   struct Row9;
10930                   struct Row10;"#},
10931        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10932        indoc! {r#"struct Row;
10933                   struct Row1;
10934                   struct Row33;
10935                   «ˇ
10936                   struct Row4;
10937                   struct» Row5;
10938                   «struct Row6;
10939                   ˇ»
10940                   struct Row99;
10941                   struct Row9;
10942                   struct Row10;"#},
10943        base_text,
10944        &mut cx,
10945    );
10946
10947    assert_hunk_revert(
10948        indoc! {r#"ˇstruct Row1.1;
10949                   struct Row1;
10950                   «ˇstr»uct Row22;
10951
10952                   struct ˇRow44;
10953                   struct Row5;
10954                   struct «Rˇ»ow66;ˇ
10955
10956                   «struˇ»ct Row88;
10957                   struct Row9;
10958                   struct Row1011;ˇ"#},
10959        vec![
10960            DiffHunkStatus::Modified,
10961            DiffHunkStatus::Modified,
10962            DiffHunkStatus::Modified,
10963            DiffHunkStatus::Modified,
10964            DiffHunkStatus::Modified,
10965            DiffHunkStatus::Modified,
10966        ],
10967        indoc! {r#"struct Row;
10968                   ˇstruct Row1;
10969                   struct Row2;
10970                   ˇ
10971                   struct Row4;
10972                   ˇstruct Row5;
10973                   struct Row6;
10974                   ˇ
10975                   struct Row8;
10976                   ˇstruct Row9;
10977                   struct Row10;ˇ"#},
10978        base_text,
10979        &mut cx,
10980    );
10981}
10982
10983#[gpui::test]
10984async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10985    init_test(cx, |_| {});
10986    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10987    let base_text = indoc! {r#"struct Row;
10988struct Row1;
10989struct Row2;
10990
10991struct Row4;
10992struct Row5;
10993struct Row6;
10994
10995struct Row8;
10996struct Row9;
10997struct Row10;"#};
10998
10999    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11000    assert_hunk_revert(
11001        indoc! {r#"struct Row;
11002                   struct Row2;
11003
11004                   ˇstruct Row4;
11005                   struct Row5;
11006                   struct Row6;
11007                   ˇ
11008                   struct Row8;
11009                   struct Row10;"#},
11010        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11011        indoc! {r#"struct Row;
11012                   struct Row2;
11013
11014                   ˇstruct Row4;
11015                   struct Row5;
11016                   struct Row6;
11017                   ˇ
11018                   struct Row8;
11019                   struct Row10;"#},
11020        base_text,
11021        &mut cx,
11022    );
11023    assert_hunk_revert(
11024        indoc! {r#"struct Row;
11025                   struct Row2;
11026
11027                   «ˇstruct Row4;
11028                   struct» Row5;
11029                   «struct Row6;
11030                   ˇ»
11031                   struct Row8;
11032                   struct Row10;"#},
11033        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11034        indoc! {r#"struct Row;
11035                   struct Row2;
11036
11037                   «ˇstruct Row4;
11038                   struct» Row5;
11039                   «struct Row6;
11040                   ˇ»
11041                   struct Row8;
11042                   struct Row10;"#},
11043        base_text,
11044        &mut cx,
11045    );
11046
11047    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11048    assert_hunk_revert(
11049        indoc! {r#"struct Row;
11050                   ˇstruct Row2;
11051
11052                   struct Row4;
11053                   struct Row5;
11054                   struct Row6;
11055
11056                   struct Row8;ˇ
11057                   struct Row10;"#},
11058        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
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    assert_hunk_revert(
11074        indoc! {r#"struct Row;
11075                   struct Row2«ˇ;
11076                   struct Row4;
11077                   struct» Row5;
11078                   «struct Row6;
11079
11080                   struct Row8;ˇ»
11081                   struct Row10;"#},
11082        vec![
11083            DiffHunkStatus::Removed,
11084            DiffHunkStatus::Removed,
11085            DiffHunkStatus::Removed,
11086        ],
11087        indoc! {r#"struct Row;
11088                   struct Row1;
11089                   struct Row2«ˇ;
11090
11091                   struct Row4;
11092                   struct» Row5;
11093                   «struct Row6;
11094
11095                   struct Row8;ˇ»
11096                   struct Row9;
11097                   struct Row10;"#},
11098        base_text,
11099        &mut cx,
11100    );
11101}
11102
11103#[gpui::test]
11104async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11105    init_test(cx, |_| {});
11106
11107    let cols = 4;
11108    let rows = 10;
11109    let sample_text_1 = sample_text(rows, cols, 'a');
11110    assert_eq!(
11111        sample_text_1,
11112        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11113    );
11114    let sample_text_2 = sample_text(rows, cols, 'l');
11115    assert_eq!(
11116        sample_text_2,
11117        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11118    );
11119    let sample_text_3 = sample_text(rows, cols, 'v');
11120    assert_eq!(
11121        sample_text_3,
11122        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11123    );
11124
11125    fn diff_every_buffer_row(
11126        buffer: &Model<Buffer>,
11127        sample_text: String,
11128        cols: usize,
11129        cx: &mut gpui::TestAppContext,
11130    ) {
11131        // revert first character in each row, creating one large diff hunk per buffer
11132        let is_first_char = |offset: usize| offset % cols == 0;
11133        buffer.update(cx, |buffer, cx| {
11134            buffer.set_text(
11135                sample_text
11136                    .chars()
11137                    .enumerate()
11138                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11139                    .collect::<String>(),
11140                cx,
11141            );
11142            buffer.set_diff_base(Some(sample_text), cx);
11143        });
11144        cx.executor().run_until_parked();
11145    }
11146
11147    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11148    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11149
11150    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11151    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11152
11153    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11154    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11155
11156    let multibuffer = cx.new_model(|cx| {
11157        let mut multibuffer = MultiBuffer::new(ReadWrite);
11158        multibuffer.push_excerpts(
11159            buffer_1.clone(),
11160            [
11161                ExcerptRange {
11162                    context: Point::new(0, 0)..Point::new(3, 0),
11163                    primary: None,
11164                },
11165                ExcerptRange {
11166                    context: Point::new(5, 0)..Point::new(7, 0),
11167                    primary: None,
11168                },
11169                ExcerptRange {
11170                    context: Point::new(9, 0)..Point::new(10, 4),
11171                    primary: None,
11172                },
11173            ],
11174            cx,
11175        );
11176        multibuffer.push_excerpts(
11177            buffer_2.clone(),
11178            [
11179                ExcerptRange {
11180                    context: Point::new(0, 0)..Point::new(3, 0),
11181                    primary: None,
11182                },
11183                ExcerptRange {
11184                    context: Point::new(5, 0)..Point::new(7, 0),
11185                    primary: None,
11186                },
11187                ExcerptRange {
11188                    context: Point::new(9, 0)..Point::new(10, 4),
11189                    primary: None,
11190                },
11191            ],
11192            cx,
11193        );
11194        multibuffer.push_excerpts(
11195            buffer_3.clone(),
11196            [
11197                ExcerptRange {
11198                    context: Point::new(0, 0)..Point::new(3, 0),
11199                    primary: None,
11200                },
11201                ExcerptRange {
11202                    context: Point::new(5, 0)..Point::new(7, 0),
11203                    primary: None,
11204                },
11205                ExcerptRange {
11206                    context: Point::new(9, 0)..Point::new(10, 4),
11207                    primary: None,
11208                },
11209            ],
11210            cx,
11211        );
11212        multibuffer
11213    });
11214
11215    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11216    editor.update(cx, |editor, cx| {
11217        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");
11218        editor.select_all(&SelectAll, cx);
11219        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11220    });
11221    cx.executor().run_until_parked();
11222    // When all ranges are selected, all buffer hunks are reverted.
11223    editor.update(cx, |editor, cx| {
11224        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");
11225    });
11226    buffer_1.update(cx, |buffer, _| {
11227        assert_eq!(buffer.text(), sample_text_1);
11228    });
11229    buffer_2.update(cx, |buffer, _| {
11230        assert_eq!(buffer.text(), sample_text_2);
11231    });
11232    buffer_3.update(cx, |buffer, _| {
11233        assert_eq!(buffer.text(), sample_text_3);
11234    });
11235
11236    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11237    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11238    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11239    editor.update(cx, |editor, cx| {
11240        editor.change_selections(None, cx, |s| {
11241            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11242        });
11243        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11244    });
11245    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11246    // but not affect buffer_2 and its related excerpts.
11247    editor.update(cx, |editor, cx| {
11248        assert_eq!(
11249            editor.text(cx),
11250            "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"
11251        );
11252    });
11253    buffer_1.update(cx, |buffer, _| {
11254        assert_eq!(buffer.text(), sample_text_1);
11255    });
11256    buffer_2.update(cx, |buffer, _| {
11257        assert_eq!(
11258            buffer.text(),
11259            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11260        );
11261    });
11262    buffer_3.update(cx, |buffer, _| {
11263        assert_eq!(
11264            buffer.text(),
11265            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11266        );
11267    });
11268}
11269
11270#[gpui::test]
11271async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11272    init_test(cx, |_| {});
11273
11274    let cols = 4;
11275    let rows = 10;
11276    let sample_text_1 = sample_text(rows, cols, 'a');
11277    assert_eq!(
11278        sample_text_1,
11279        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11280    );
11281    let sample_text_2 = sample_text(rows, cols, 'l');
11282    assert_eq!(
11283        sample_text_2,
11284        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11285    );
11286    let sample_text_3 = sample_text(rows, cols, 'v');
11287    assert_eq!(
11288        sample_text_3,
11289        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11290    );
11291
11292    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11293    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11294    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11295
11296    let multi_buffer = cx.new_model(|cx| {
11297        let mut multibuffer = MultiBuffer::new(ReadWrite);
11298        multibuffer.push_excerpts(
11299            buffer_1.clone(),
11300            [
11301                ExcerptRange {
11302                    context: Point::new(0, 0)..Point::new(3, 0),
11303                    primary: None,
11304                },
11305                ExcerptRange {
11306                    context: Point::new(5, 0)..Point::new(7, 0),
11307                    primary: None,
11308                },
11309                ExcerptRange {
11310                    context: Point::new(9, 0)..Point::new(10, 4),
11311                    primary: None,
11312                },
11313            ],
11314            cx,
11315        );
11316        multibuffer.push_excerpts(
11317            buffer_2.clone(),
11318            [
11319                ExcerptRange {
11320                    context: Point::new(0, 0)..Point::new(3, 0),
11321                    primary: None,
11322                },
11323                ExcerptRange {
11324                    context: Point::new(5, 0)..Point::new(7, 0),
11325                    primary: None,
11326                },
11327                ExcerptRange {
11328                    context: Point::new(9, 0)..Point::new(10, 4),
11329                    primary: None,
11330                },
11331            ],
11332            cx,
11333        );
11334        multibuffer.push_excerpts(
11335            buffer_3.clone(),
11336            [
11337                ExcerptRange {
11338                    context: Point::new(0, 0)..Point::new(3, 0),
11339                    primary: None,
11340                },
11341                ExcerptRange {
11342                    context: Point::new(5, 0)..Point::new(7, 0),
11343                    primary: None,
11344                },
11345                ExcerptRange {
11346                    context: Point::new(9, 0)..Point::new(10, 4),
11347                    primary: None,
11348                },
11349            ],
11350            cx,
11351        );
11352        multibuffer
11353    });
11354
11355    let fs = FakeFs::new(cx.executor());
11356    fs.insert_tree(
11357        "/a",
11358        json!({
11359            "main.rs": sample_text_1,
11360            "other.rs": sample_text_2,
11361            "lib.rs": sample_text_3,
11362        }),
11363    )
11364    .await;
11365    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11366    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11367    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11368    let multi_buffer_editor = cx.new_view(|cx| {
11369        Editor::new(
11370            EditorMode::Full,
11371            multi_buffer,
11372            Some(project.clone()),
11373            true,
11374            cx,
11375        )
11376    });
11377    let multibuffer_item_id = workspace
11378        .update(cx, |workspace, cx| {
11379            assert!(
11380                workspace.active_item(cx).is_none(),
11381                "active item should be None before the first item is added"
11382            );
11383            workspace.add_item_to_active_pane(
11384                Box::new(multi_buffer_editor.clone()),
11385                None,
11386                true,
11387                cx,
11388            );
11389            let active_item = workspace
11390                .active_item(cx)
11391                .expect("should have an active item after adding the multi buffer");
11392            assert!(
11393                !active_item.is_singleton(cx),
11394                "A multi buffer was expected to active after adding"
11395            );
11396            active_item.item_id()
11397        })
11398        .unwrap();
11399    cx.executor().run_until_parked();
11400
11401    multi_buffer_editor.update(cx, |editor, cx| {
11402        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11403        editor.open_excerpts(&OpenExcerpts, cx);
11404    });
11405    cx.executor().run_until_parked();
11406    let first_item_id = workspace
11407        .update(cx, |workspace, cx| {
11408            let active_item = workspace
11409                .active_item(cx)
11410                .expect("should have an active item after navigating into the 1st buffer");
11411            let first_item_id = active_item.item_id();
11412            assert_ne!(
11413                first_item_id, multibuffer_item_id,
11414                "Should navigate into the 1st buffer and activate it"
11415            );
11416            assert!(
11417                active_item.is_singleton(cx),
11418                "New active item should be a singleton buffer"
11419            );
11420            assert_eq!(
11421                active_item
11422                    .act_as::<Editor>(cx)
11423                    .expect("should have navigated into an editor for the 1st buffer")
11424                    .read(cx)
11425                    .text(cx),
11426                sample_text_1
11427            );
11428
11429            workspace
11430                .go_back(workspace.active_pane().downgrade(), cx)
11431                .detach_and_log_err(cx);
11432
11433            first_item_id
11434        })
11435        .unwrap();
11436    cx.executor().run_until_parked();
11437    workspace
11438        .update(cx, |workspace, cx| {
11439            let active_item = workspace
11440                .active_item(cx)
11441                .expect("should have an active item after navigating back");
11442            assert_eq!(
11443                active_item.item_id(),
11444                multibuffer_item_id,
11445                "Should navigate back to the multi buffer"
11446            );
11447            assert!(!active_item.is_singleton(cx));
11448        })
11449        .unwrap();
11450
11451    multi_buffer_editor.update(cx, |editor, cx| {
11452        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11453            s.select_ranges(Some(39..40))
11454        });
11455        editor.open_excerpts(&OpenExcerpts, cx);
11456    });
11457    cx.executor().run_until_parked();
11458    let second_item_id = workspace
11459        .update(cx, |workspace, cx| {
11460            let active_item = workspace
11461                .active_item(cx)
11462                .expect("should have an active item after navigating into the 2nd buffer");
11463            let second_item_id = active_item.item_id();
11464            assert_ne!(
11465                second_item_id, multibuffer_item_id,
11466                "Should navigate away from the multibuffer"
11467            );
11468            assert_ne!(
11469                second_item_id, first_item_id,
11470                "Should navigate into the 2nd buffer and activate it"
11471            );
11472            assert!(
11473                active_item.is_singleton(cx),
11474                "New active item should be a singleton buffer"
11475            );
11476            assert_eq!(
11477                active_item
11478                    .act_as::<Editor>(cx)
11479                    .expect("should have navigated into an editor")
11480                    .read(cx)
11481                    .text(cx),
11482                sample_text_2
11483            );
11484
11485            workspace
11486                .go_back(workspace.active_pane().downgrade(), cx)
11487                .detach_and_log_err(cx);
11488
11489            second_item_id
11490        })
11491        .unwrap();
11492    cx.executor().run_until_parked();
11493    workspace
11494        .update(cx, |workspace, cx| {
11495            let active_item = workspace
11496                .active_item(cx)
11497                .expect("should have an active item after navigating back from the 2nd buffer");
11498            assert_eq!(
11499                active_item.item_id(),
11500                multibuffer_item_id,
11501                "Should navigate back from the 2nd buffer to the multi buffer"
11502            );
11503            assert!(!active_item.is_singleton(cx));
11504        })
11505        .unwrap();
11506
11507    multi_buffer_editor.update(cx, |editor, cx| {
11508        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11509            s.select_ranges(Some(60..70))
11510        });
11511        editor.open_excerpts(&OpenExcerpts, cx);
11512    });
11513    cx.executor().run_until_parked();
11514    workspace
11515        .update(cx, |workspace, cx| {
11516            let active_item = workspace
11517                .active_item(cx)
11518                .expect("should have an active item after navigating into the 3rd buffer");
11519            let third_item_id = active_item.item_id();
11520            assert_ne!(
11521                third_item_id, multibuffer_item_id,
11522                "Should navigate into the 3rd buffer and activate it"
11523            );
11524            assert_ne!(third_item_id, first_item_id);
11525            assert_ne!(third_item_id, second_item_id);
11526            assert!(
11527                active_item.is_singleton(cx),
11528                "New active item should be a singleton buffer"
11529            );
11530            assert_eq!(
11531                active_item
11532                    .act_as::<Editor>(cx)
11533                    .expect("should have navigated into an editor")
11534                    .read(cx)
11535                    .text(cx),
11536                sample_text_3
11537            );
11538
11539            workspace
11540                .go_back(workspace.active_pane().downgrade(), cx)
11541                .detach_and_log_err(cx);
11542        })
11543        .unwrap();
11544    cx.executor().run_until_parked();
11545    workspace
11546        .update(cx, |workspace, cx| {
11547            let active_item = workspace
11548                .active_item(cx)
11549                .expect("should have an active item after navigating back from the 3rd buffer");
11550            assert_eq!(
11551                active_item.item_id(),
11552                multibuffer_item_id,
11553                "Should navigate back from the 3rd buffer to the multi buffer"
11554            );
11555            assert!(!active_item.is_singleton(cx));
11556        })
11557        .unwrap();
11558}
11559
11560#[gpui::test]
11561async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11562    init_test(cx, |_| {});
11563
11564    let mut cx = EditorTestContext::new(cx).await;
11565
11566    let diff_base = r#"
11567        use some::mod;
11568
11569        const A: u32 = 42;
11570
11571        fn main() {
11572            println!("hello");
11573
11574            println!("world");
11575        }
11576        "#
11577    .unindent();
11578
11579    cx.set_state(
11580        &r#"
11581        use some::modified;
11582
11583        ˇ
11584        fn main() {
11585            println!("hello there");
11586
11587            println!("around the");
11588            println!("world");
11589        }
11590        "#
11591        .unindent(),
11592    );
11593
11594    cx.set_diff_base(Some(&diff_base));
11595    executor.run_until_parked();
11596
11597    cx.update_editor(|editor, cx| {
11598        editor.go_to_next_hunk(&GoToHunk, cx);
11599        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11600    });
11601    executor.run_until_parked();
11602    cx.assert_diff_hunks(
11603        r#"
11604          use some::modified;
11605
11606
11607          fn main() {
11608        -     println!("hello");
11609        +     println!("hello there");
11610
11611              println!("around the");
11612              println!("world");
11613          }
11614        "#
11615        .unindent(),
11616    );
11617
11618    cx.update_editor(|editor, cx| {
11619        for _ in 0..3 {
11620            editor.go_to_next_hunk(&GoToHunk, cx);
11621            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11622        }
11623    });
11624    executor.run_until_parked();
11625    cx.assert_editor_state(
11626        &r#"
11627        use some::modified;
11628
11629        ˇ
11630        fn main() {
11631            println!("hello there");
11632
11633            println!("around the");
11634            println!("world");
11635        }
11636        "#
11637        .unindent(),
11638    );
11639
11640    cx.assert_diff_hunks(
11641        r#"
11642        - use some::mod;
11643        + use some::modified;
11644
11645        - const A: u32 = 42;
11646
11647          fn main() {
11648        -     println!("hello");
11649        +     println!("hello there");
11650
11651        +     println!("around the");
11652              println!("world");
11653          }
11654        "#
11655        .unindent(),
11656    );
11657
11658    cx.update_editor(|editor, cx| {
11659        editor.cancel(&Cancel, cx);
11660    });
11661
11662    cx.assert_diff_hunks(
11663        r#"
11664          use some::modified;
11665
11666
11667          fn main() {
11668              println!("hello there");
11669
11670              println!("around the");
11671              println!("world");
11672          }
11673        "#
11674        .unindent(),
11675    );
11676}
11677
11678#[gpui::test]
11679async fn test_diff_base_change_with_expanded_diff_hunks(
11680    executor: BackgroundExecutor,
11681    cx: &mut gpui::TestAppContext,
11682) {
11683    init_test(cx, |_| {});
11684
11685    let mut cx = EditorTestContext::new(cx).await;
11686
11687    let diff_base = r#"
11688        use some::mod1;
11689        use some::mod2;
11690
11691        const A: u32 = 42;
11692        const B: u32 = 42;
11693        const C: u32 = 42;
11694
11695        fn main() {
11696            println!("hello");
11697
11698            println!("world");
11699        }
11700        "#
11701    .unindent();
11702
11703    cx.set_state(
11704        &r#"
11705        use some::mod2;
11706
11707        const A: u32 = 42;
11708        const C: u32 = 42;
11709
11710        fn main(ˇ) {
11711            //println!("hello");
11712
11713            println!("world");
11714            //
11715            //
11716        }
11717        "#
11718        .unindent(),
11719    );
11720
11721    cx.set_diff_base(Some(&diff_base));
11722    executor.run_until_parked();
11723
11724    cx.update_editor(|editor, cx| {
11725        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11726    });
11727    executor.run_until_parked();
11728    cx.assert_diff_hunks(
11729        r#"
11730        - use some::mod1;
11731          use some::mod2;
11732
11733          const A: u32 = 42;
11734        - const B: u32 = 42;
11735          const C: u32 = 42;
11736
11737          fn main() {
11738        -     println!("hello");
11739        +     //println!("hello");
11740
11741              println!("world");
11742        +     //
11743        +     //
11744          }
11745        "#
11746        .unindent(),
11747    );
11748
11749    cx.set_diff_base(Some("new diff base!"));
11750    executor.run_until_parked();
11751    cx.assert_diff_hunks(
11752        r#"
11753          use some::mod2;
11754
11755          const A: u32 = 42;
11756          const C: u32 = 42;
11757
11758          fn main() {
11759              //println!("hello");
11760
11761              println!("world");
11762              //
11763              //
11764          }
11765        "#
11766        .unindent(),
11767    );
11768
11769    cx.update_editor(|editor, cx| {
11770        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11771    });
11772    executor.run_until_parked();
11773    cx.assert_diff_hunks(
11774        r#"
11775        - new diff base!
11776        + use some::mod2;
11777        +
11778        + const A: u32 = 42;
11779        + const C: u32 = 42;
11780        +
11781        + fn main() {
11782        +     //println!("hello");
11783        +
11784        +     println!("world");
11785        +     //
11786        +     //
11787        + }
11788        "#
11789        .unindent(),
11790    );
11791}
11792
11793#[gpui::test]
11794async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11795    init_test(cx, |_| {});
11796
11797    let mut cx = EditorTestContext::new(cx).await;
11798
11799    let diff_base = r#"
11800        use some::mod1;
11801        use some::mod2;
11802
11803        const A: u32 = 42;
11804        const B: u32 = 42;
11805        const C: u32 = 42;
11806
11807        fn main() {
11808            println!("hello");
11809
11810            println!("world");
11811        }
11812
11813        fn another() {
11814            println!("another");
11815        }
11816
11817        fn another2() {
11818            println!("another2");
11819        }
11820        "#
11821    .unindent();
11822
11823    cx.set_state(
11824        &r#"
11825        «use some::mod2;
11826
11827        const A: u32 = 42;
11828        const C: u32 = 42;
11829
11830        fn main() {
11831            //println!("hello");
11832
11833            println!("world");
11834            //
11835            //ˇ»
11836        }
11837
11838        fn another() {
11839            println!("another");
11840            println!("another");
11841        }
11842
11843            println!("another2");
11844        }
11845        "#
11846        .unindent(),
11847    );
11848
11849    cx.set_diff_base(Some(&diff_base));
11850    executor.run_until_parked();
11851
11852    cx.update_editor(|editor, cx| {
11853        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11854    });
11855    executor.run_until_parked();
11856
11857    cx.assert_diff_hunks(
11858        r#"
11859        - use some::mod1;
11860          use some::mod2;
11861
11862          const A: u32 = 42;
11863        - const B: u32 = 42;
11864          const C: u32 = 42;
11865
11866          fn main() {
11867        -     println!("hello");
11868        +     //println!("hello");
11869
11870              println!("world");
11871        +     //
11872        +     //
11873          }
11874
11875          fn another() {
11876              println!("another");
11877        +     println!("another");
11878          }
11879
11880        - fn another2() {
11881              println!("another2");
11882          }
11883        "#
11884        .unindent(),
11885    );
11886
11887    // Fold across some of the diff hunks. They should no longer appear expanded.
11888    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11889    cx.executor().run_until_parked();
11890
11891    // Hunks are not shown if their position is within a fold
11892    cx.assert_diff_hunks(
11893        r#"
11894          use some::mod2;
11895
11896          const A: u32 = 42;
11897          const C: u32 = 42;
11898
11899          fn main() {
11900              //println!("hello");
11901
11902              println!("world");
11903              //
11904              //
11905          }
11906
11907          fn another() {
11908              println!("another");
11909        +     println!("another");
11910          }
11911
11912        - fn another2() {
11913              println!("another2");
11914          }
11915        "#
11916        .unindent(),
11917    );
11918
11919    cx.update_editor(|editor, cx| {
11920        editor.select_all(&SelectAll, cx);
11921        editor.unfold_lines(&UnfoldLines, cx);
11922    });
11923    cx.executor().run_until_parked();
11924
11925    // The deletions reappear when unfolding.
11926    cx.assert_diff_hunks(
11927        r#"
11928        - use some::mod1;
11929          use some::mod2;
11930
11931          const A: u32 = 42;
11932        - const B: u32 = 42;
11933          const C: u32 = 42;
11934
11935          fn main() {
11936        -     println!("hello");
11937        +     //println!("hello");
11938
11939              println!("world");
11940        +     //
11941        +     //
11942          }
11943
11944          fn another() {
11945              println!("another");
11946        +     println!("another");
11947          }
11948
11949        - fn another2() {
11950              println!("another2");
11951          }
11952        "#
11953        .unindent(),
11954    );
11955}
11956
11957#[gpui::test]
11958async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11959    init_test(cx, |_| {});
11960
11961    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11962    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11963    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11964    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11965    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11966    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11967
11968    let buffer_1 = cx.new_model(|cx| {
11969        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11970        buffer.set_diff_base(Some(file_1_old.into()), cx);
11971        buffer
11972    });
11973    let buffer_2 = cx.new_model(|cx| {
11974        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11975        buffer.set_diff_base(Some(file_2_old.into()), cx);
11976        buffer
11977    });
11978    let buffer_3 = cx.new_model(|cx| {
11979        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11980        buffer.set_diff_base(Some(file_3_old.into()), cx);
11981        buffer
11982    });
11983
11984    let multi_buffer = cx.new_model(|cx| {
11985        let mut multibuffer = MultiBuffer::new(ReadWrite);
11986        multibuffer.push_excerpts(
11987            buffer_1.clone(),
11988            [
11989                ExcerptRange {
11990                    context: Point::new(0, 0)..Point::new(3, 0),
11991                    primary: None,
11992                },
11993                ExcerptRange {
11994                    context: Point::new(5, 0)..Point::new(7, 0),
11995                    primary: None,
11996                },
11997                ExcerptRange {
11998                    context: Point::new(9, 0)..Point::new(10, 3),
11999                    primary: None,
12000                },
12001            ],
12002            cx,
12003        );
12004        multibuffer.push_excerpts(
12005            buffer_2.clone(),
12006            [
12007                ExcerptRange {
12008                    context: Point::new(0, 0)..Point::new(3, 0),
12009                    primary: None,
12010                },
12011                ExcerptRange {
12012                    context: Point::new(5, 0)..Point::new(7, 0),
12013                    primary: None,
12014                },
12015                ExcerptRange {
12016                    context: Point::new(9, 0)..Point::new(10, 3),
12017                    primary: None,
12018                },
12019            ],
12020            cx,
12021        );
12022        multibuffer.push_excerpts(
12023            buffer_3.clone(),
12024            [
12025                ExcerptRange {
12026                    context: Point::new(0, 0)..Point::new(3, 0),
12027                    primary: None,
12028                },
12029                ExcerptRange {
12030                    context: Point::new(5, 0)..Point::new(7, 0),
12031                    primary: None,
12032                },
12033                ExcerptRange {
12034                    context: Point::new(9, 0)..Point::new(10, 3),
12035                    primary: None,
12036                },
12037            ],
12038            cx,
12039        );
12040        multibuffer
12041    });
12042
12043    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12044    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12045    cx.run_until_parked();
12046
12047    cx.assert_editor_state(
12048        &"
12049            ˇaaa
12050            ccc
12051            ddd
12052
12053            ggg
12054            hhh
12055
12056
12057            lll
12058            mmm
12059            NNN
12060
12061            qqq
12062            rrr
12063
12064            uuu
12065            111
12066            222
12067            333
12068
12069            666
12070            777
12071
12072            000
12073            !!!"
12074        .unindent(),
12075    );
12076
12077    cx.update_editor(|editor, cx| {
12078        editor.select_all(&SelectAll, cx);
12079        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12080    });
12081    cx.executor().run_until_parked();
12082
12083    cx.assert_diff_hunks(
12084        "
12085            aaa
12086          - bbb
12087            ccc
12088            ddd
12089
12090            ggg
12091            hhh
12092
12093
12094            lll
12095            mmm
12096          - nnn
12097          + NNN
12098
12099            qqq
12100            rrr
12101
12102            uuu
12103            111
12104            222
12105            333
12106
12107          + 666
12108            777
12109
12110            000
12111            !!!"
12112        .unindent(),
12113    );
12114}
12115
12116#[gpui::test]
12117async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12118    init_test(cx, |_| {});
12119
12120    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12121    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12122
12123    let buffer = cx.new_model(|cx| {
12124        let mut buffer = Buffer::local(text.to_string(), cx);
12125        buffer.set_diff_base(Some(base.into()), cx);
12126        buffer
12127    });
12128
12129    let multi_buffer = cx.new_model(|cx| {
12130        let mut multibuffer = MultiBuffer::new(ReadWrite);
12131        multibuffer.push_excerpts(
12132            buffer.clone(),
12133            [
12134                ExcerptRange {
12135                    context: Point::new(0, 0)..Point::new(2, 0),
12136                    primary: None,
12137                },
12138                ExcerptRange {
12139                    context: Point::new(5, 0)..Point::new(7, 0),
12140                    primary: None,
12141                },
12142            ],
12143            cx,
12144        );
12145        multibuffer
12146    });
12147
12148    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12149    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12150    cx.run_until_parked();
12151
12152    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12153    cx.executor().run_until_parked();
12154
12155    cx.assert_diff_hunks(
12156        "
12157            aaa
12158          - bbb
12159          + BBB
12160
12161          - ddd
12162          - eee
12163          + EEE
12164            fff
12165        "
12166        .unindent(),
12167    );
12168}
12169
12170#[gpui::test]
12171async fn test_edits_around_expanded_insertion_hunks(
12172    executor: BackgroundExecutor,
12173    cx: &mut gpui::TestAppContext,
12174) {
12175    init_test(cx, |_| {});
12176
12177    let mut cx = EditorTestContext::new(cx).await;
12178
12179    let diff_base = r#"
12180        use some::mod1;
12181        use some::mod2;
12182
12183        const A: u32 = 42;
12184
12185        fn main() {
12186            println!("hello");
12187
12188            println!("world");
12189        }
12190        "#
12191    .unindent();
12192    executor.run_until_parked();
12193    cx.set_state(
12194        &r#"
12195        use some::mod1;
12196        use some::mod2;
12197
12198        const A: u32 = 42;
12199        const B: u32 = 42;
12200        const C: u32 = 42;
12201        ˇ
12202
12203        fn main() {
12204            println!("hello");
12205
12206            println!("world");
12207        }
12208        "#
12209        .unindent(),
12210    );
12211
12212    cx.set_diff_base(Some(&diff_base));
12213    executor.run_until_parked();
12214
12215    cx.update_editor(|editor, cx| {
12216        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12217    });
12218    executor.run_until_parked();
12219
12220    cx.assert_diff_hunks(
12221        r#"
12222        use some::mod1;
12223        use some::mod2;
12224
12225        const A: u32 = 42;
12226      + const B: u32 = 42;
12227      + const C: u32 = 42;
12228      +
12229
12230        fn main() {
12231            println!("hello");
12232
12233            println!("world");
12234        }
12235        "#
12236        .unindent(),
12237    );
12238
12239    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12240    executor.run_until_parked();
12241
12242    cx.assert_diff_hunks(
12243        r#"
12244        use some::mod1;
12245        use some::mod2;
12246
12247        const A: u32 = 42;
12248      + const B: u32 = 42;
12249      + const C: u32 = 42;
12250      + const D: u32 = 42;
12251      +
12252
12253        fn main() {
12254            println!("hello");
12255
12256            println!("world");
12257        }
12258        "#
12259        .unindent(),
12260    );
12261
12262    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12263    executor.run_until_parked();
12264
12265    cx.assert_diff_hunks(
12266        r#"
12267        use some::mod1;
12268        use some::mod2;
12269
12270        const A: u32 = 42;
12271      + const B: u32 = 42;
12272      + const C: u32 = 42;
12273      + const D: u32 = 42;
12274      + const E: u32 = 42;
12275      +
12276
12277        fn main() {
12278            println!("hello");
12279
12280            println!("world");
12281        }
12282        "#
12283        .unindent(),
12284    );
12285
12286    cx.update_editor(|editor, cx| {
12287        editor.delete_line(&DeleteLine, cx);
12288    });
12289    executor.run_until_parked();
12290
12291    cx.assert_diff_hunks(
12292        r#"
12293        use some::mod1;
12294        use some::mod2;
12295
12296        const A: u32 = 42;
12297      + const B: u32 = 42;
12298      + const C: u32 = 42;
12299      + const D: u32 = 42;
12300      + const E: u32 = 42;
12301
12302        fn main() {
12303            println!("hello");
12304
12305            println!("world");
12306        }
12307        "#
12308        .unindent(),
12309    );
12310
12311    cx.update_editor(|editor, cx| {
12312        editor.move_up(&MoveUp, cx);
12313        editor.delete_line(&DeleteLine, cx);
12314        editor.move_up(&MoveUp, cx);
12315        editor.delete_line(&DeleteLine, cx);
12316        editor.move_up(&MoveUp, cx);
12317        editor.delete_line(&DeleteLine, cx);
12318    });
12319    executor.run_until_parked();
12320    cx.assert_editor_state(
12321        &r#"
12322        use some::mod1;
12323        use some::mod2;
12324
12325        const A: u32 = 42;
12326        const B: u32 = 42;
12327        ˇ
12328        fn main() {
12329            println!("hello");
12330
12331            println!("world");
12332        }
12333        "#
12334        .unindent(),
12335    );
12336
12337    cx.assert_diff_hunks(
12338        r#"
12339        use some::mod1;
12340        use some::mod2;
12341
12342        const A: u32 = 42;
12343      + const B: u32 = 42;
12344
12345        fn main() {
12346            println!("hello");
12347
12348            println!("world");
12349        }
12350        "#
12351        .unindent(),
12352    );
12353
12354    cx.update_editor(|editor, cx| {
12355        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12356        editor.delete_line(&DeleteLine, cx);
12357    });
12358    executor.run_until_parked();
12359    cx.assert_diff_hunks(
12360        r#"
12361        use some::mod1;
12362      - use some::mod2;
12363      -
12364      - const A: u32 = 42;
12365
12366        fn main() {
12367            println!("hello");
12368
12369            println!("world");
12370        }
12371        "#
12372        .unindent(),
12373    );
12374}
12375
12376#[gpui::test]
12377async fn test_edits_around_expanded_deletion_hunks(
12378    executor: BackgroundExecutor,
12379    cx: &mut gpui::TestAppContext,
12380) {
12381    init_test(cx, |_| {});
12382
12383    let mut cx = EditorTestContext::new(cx).await;
12384
12385    let diff_base = r#"
12386        use some::mod1;
12387        use some::mod2;
12388
12389        const A: u32 = 42;
12390        const B: u32 = 42;
12391        const C: u32 = 42;
12392
12393
12394        fn main() {
12395            println!("hello");
12396
12397            println!("world");
12398        }
12399    "#
12400    .unindent();
12401    executor.run_until_parked();
12402    cx.set_state(
12403        &r#"
12404        use some::mod1;
12405        use some::mod2;
12406
12407        ˇconst B: u32 = 42;
12408        const C: u32 = 42;
12409
12410
12411        fn main() {
12412            println!("hello");
12413
12414            println!("world");
12415        }
12416        "#
12417        .unindent(),
12418    );
12419
12420    cx.set_diff_base(Some(&diff_base));
12421    executor.run_until_parked();
12422
12423    cx.update_editor(|editor, cx| {
12424        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12425    });
12426    executor.run_until_parked();
12427
12428    cx.assert_diff_hunks(
12429        r#"
12430        use some::mod1;
12431        use some::mod2;
12432
12433      - const A: u32 = 42;
12434        const B: u32 = 42;
12435        const C: u32 = 42;
12436
12437
12438        fn main() {
12439            println!("hello");
12440
12441            println!("world");
12442        }
12443        "#
12444        .unindent(),
12445    );
12446
12447    cx.update_editor(|editor, cx| {
12448        editor.delete_line(&DeleteLine, cx);
12449    });
12450    executor.run_until_parked();
12451    cx.assert_editor_state(
12452        &r#"
12453        use some::mod1;
12454        use some::mod2;
12455
12456        ˇconst C: u32 = 42;
12457
12458
12459        fn main() {
12460            println!("hello");
12461
12462            println!("world");
12463        }
12464        "#
12465        .unindent(),
12466    );
12467    cx.assert_diff_hunks(
12468        r#"
12469        use some::mod1;
12470        use some::mod2;
12471
12472      - const A: u32 = 42;
12473      - const B: u32 = 42;
12474        const C: u32 = 42;
12475
12476
12477        fn main() {
12478            println!("hello");
12479
12480            println!("world");
12481        }
12482        "#
12483        .unindent(),
12484    );
12485
12486    cx.update_editor(|editor, cx| {
12487        editor.delete_line(&DeleteLine, cx);
12488    });
12489    executor.run_until_parked();
12490    cx.assert_editor_state(
12491        &r#"
12492        use some::mod1;
12493        use some::mod2;
12494
12495        ˇ
12496
12497        fn main() {
12498            println!("hello");
12499
12500            println!("world");
12501        }
12502        "#
12503        .unindent(),
12504    );
12505    cx.assert_diff_hunks(
12506        r#"
12507        use some::mod1;
12508        use some::mod2;
12509
12510      - const A: u32 = 42;
12511      - const B: u32 = 42;
12512      - const C: u32 = 42;
12513
12514
12515        fn main() {
12516            println!("hello");
12517
12518            println!("world");
12519        }
12520        "#
12521        .unindent(),
12522    );
12523
12524    cx.update_editor(|editor, cx| {
12525        editor.handle_input("replacement", cx);
12526    });
12527    executor.run_until_parked();
12528    cx.assert_editor_state(
12529        &r#"
12530        use some::mod1;
12531        use some::mod2;
12532
12533        replacementˇ
12534
12535        fn main() {
12536            println!("hello");
12537
12538            println!("world");
12539        }
12540        "#
12541        .unindent(),
12542    );
12543    cx.assert_diff_hunks(
12544        r#"
12545        use some::mod1;
12546        use some::mod2;
12547
12548      - const A: u32 = 42;
12549      - const B: u32 = 42;
12550      - const C: u32 = 42;
12551      -
12552      + replacement
12553
12554        fn main() {
12555            println!("hello");
12556
12557            println!("world");
12558        }
12559        "#
12560        .unindent(),
12561    );
12562}
12563
12564#[gpui::test]
12565async fn test_edit_after_expanded_modification_hunk(
12566    executor: BackgroundExecutor,
12567    cx: &mut gpui::TestAppContext,
12568) {
12569    init_test(cx, |_| {});
12570
12571    let mut cx = EditorTestContext::new(cx).await;
12572
12573    let diff_base = r#"
12574        use some::mod1;
12575        use some::mod2;
12576
12577        const A: u32 = 42;
12578        const B: u32 = 42;
12579        const C: u32 = 42;
12580        const D: u32 = 42;
12581
12582
12583        fn main() {
12584            println!("hello");
12585
12586            println!("world");
12587        }"#
12588    .unindent();
12589
12590    cx.set_state(
12591        &r#"
12592        use some::mod1;
12593        use some::mod2;
12594
12595        const A: u32 = 42;
12596        const B: u32 = 42;
12597        const C: u32 = 43ˇ
12598        const D: u32 = 42;
12599
12600
12601        fn main() {
12602            println!("hello");
12603
12604            println!("world");
12605        }"#
12606        .unindent(),
12607    );
12608
12609    cx.set_diff_base(Some(&diff_base));
12610    executor.run_until_parked();
12611    cx.update_editor(|editor, cx| {
12612        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12613    });
12614    executor.run_until_parked();
12615
12616    cx.assert_diff_hunks(
12617        r#"
12618        use some::mod1;
12619        use some::mod2;
12620
12621        const A: u32 = 42;
12622        const B: u32 = 42;
12623      - const C: u32 = 42;
12624      + const C: u32 = 43
12625        const D: u32 = 42;
12626
12627
12628        fn main() {
12629            println!("hello");
12630
12631            println!("world");
12632        }"#
12633        .unindent(),
12634    );
12635
12636    cx.update_editor(|editor, cx| {
12637        editor.handle_input("\nnew_line\n", cx);
12638    });
12639    executor.run_until_parked();
12640
12641    cx.assert_diff_hunks(
12642        r#"
12643        use some::mod1;
12644        use some::mod2;
12645
12646        const A: u32 = 42;
12647        const B: u32 = 42;
12648      - const C: u32 = 42;
12649      + const C: u32 = 43
12650      + new_line
12651      +
12652        const D: u32 = 42;
12653
12654
12655        fn main() {
12656            println!("hello");
12657
12658            println!("world");
12659        }"#
12660        .unindent(),
12661    );
12662}
12663
12664async fn setup_indent_guides_editor(
12665    text: &str,
12666    cx: &mut gpui::TestAppContext,
12667) -> (BufferId, EditorTestContext) {
12668    init_test(cx, |_| {});
12669
12670    let mut cx = EditorTestContext::new(cx).await;
12671
12672    let buffer_id = cx.update_editor(|editor, cx| {
12673        editor.set_text(text, cx);
12674        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12675
12676        buffer_ids[0]
12677    });
12678
12679    (buffer_id, cx)
12680}
12681
12682fn assert_indent_guides(
12683    range: Range<u32>,
12684    expected: Vec<IndentGuide>,
12685    active_indices: Option<Vec<usize>>,
12686    cx: &mut EditorTestContext,
12687) {
12688    let indent_guides = cx.update_editor(|editor, cx| {
12689        let snapshot = editor.snapshot(cx).display_snapshot;
12690        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12691            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12692            true,
12693            &snapshot,
12694            cx,
12695        );
12696
12697        indent_guides.sort_by(|a, b| {
12698            a.depth.cmp(&b.depth).then(
12699                a.start_row
12700                    .cmp(&b.start_row)
12701                    .then(a.end_row.cmp(&b.end_row)),
12702            )
12703        });
12704        indent_guides
12705    });
12706
12707    if let Some(expected) = active_indices {
12708        let active_indices = cx.update_editor(|editor, cx| {
12709            let snapshot = editor.snapshot(cx).display_snapshot;
12710            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12711        });
12712
12713        assert_eq!(
12714            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12715            expected,
12716            "Active indent guide indices do not match"
12717        );
12718    }
12719
12720    let expected: Vec<_> = expected
12721        .into_iter()
12722        .map(|guide| MultiBufferIndentGuide {
12723            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12724            buffer: guide,
12725        })
12726        .collect();
12727
12728    assert_eq!(indent_guides, expected, "Indent guides do not match");
12729}
12730
12731fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12732    IndentGuide {
12733        buffer_id,
12734        start_row,
12735        end_row,
12736        depth,
12737        tab_size: 4,
12738        settings: IndentGuideSettings {
12739            enabled: true,
12740            line_width: 1,
12741            active_line_width: 1,
12742            ..Default::default()
12743        },
12744    }
12745}
12746
12747#[gpui::test]
12748async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12749    let (buffer_id, mut cx) = setup_indent_guides_editor(
12750        &"
12751    fn main() {
12752        let a = 1;
12753    }"
12754        .unindent(),
12755        cx,
12756    )
12757    .await;
12758
12759    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12760}
12761
12762#[gpui::test]
12763async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12764    let (buffer_id, mut cx) = setup_indent_guides_editor(
12765        &"
12766    fn main() {
12767        let a = 1;
12768        let b = 2;
12769    }"
12770        .unindent(),
12771        cx,
12772    )
12773    .await;
12774
12775    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12776}
12777
12778#[gpui::test]
12779async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12780    let (buffer_id, mut cx) = setup_indent_guides_editor(
12781        &"
12782    fn main() {
12783        let a = 1;
12784        if a == 3 {
12785            let b = 2;
12786        } else {
12787            let c = 3;
12788        }
12789    }"
12790        .unindent(),
12791        cx,
12792    )
12793    .await;
12794
12795    assert_indent_guides(
12796        0..8,
12797        vec![
12798            indent_guide(buffer_id, 1, 6, 0),
12799            indent_guide(buffer_id, 3, 3, 1),
12800            indent_guide(buffer_id, 5, 5, 1),
12801        ],
12802        None,
12803        &mut cx,
12804    );
12805}
12806
12807#[gpui::test]
12808async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12809    let (buffer_id, mut cx) = setup_indent_guides_editor(
12810        &"
12811    fn main() {
12812        let a = 1;
12813            let b = 2;
12814        let c = 3;
12815    }"
12816        .unindent(),
12817        cx,
12818    )
12819    .await;
12820
12821    assert_indent_guides(
12822        0..5,
12823        vec![
12824            indent_guide(buffer_id, 1, 3, 0),
12825            indent_guide(buffer_id, 2, 2, 1),
12826        ],
12827        None,
12828        &mut cx,
12829    );
12830}
12831
12832#[gpui::test]
12833async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12834    let (buffer_id, mut cx) = setup_indent_guides_editor(
12835        &"
12836        fn main() {
12837            let a = 1;
12838
12839            let c = 3;
12840        }"
12841        .unindent(),
12842        cx,
12843    )
12844    .await;
12845
12846    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12847}
12848
12849#[gpui::test]
12850async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12851    let (buffer_id, mut cx) = setup_indent_guides_editor(
12852        &"
12853        fn main() {
12854            let a = 1;
12855
12856            let c = 3;
12857
12858            if a == 3 {
12859                let b = 2;
12860            } else {
12861                let c = 3;
12862            }
12863        }"
12864        .unindent(),
12865        cx,
12866    )
12867    .await;
12868
12869    assert_indent_guides(
12870        0..11,
12871        vec![
12872            indent_guide(buffer_id, 1, 9, 0),
12873            indent_guide(buffer_id, 6, 6, 1),
12874            indent_guide(buffer_id, 8, 8, 1),
12875        ],
12876        None,
12877        &mut cx,
12878    );
12879}
12880
12881#[gpui::test]
12882async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12883    let (buffer_id, mut cx) = setup_indent_guides_editor(
12884        &"
12885        fn main() {
12886            let a = 1;
12887
12888            let c = 3;
12889
12890            if a == 3 {
12891                let b = 2;
12892            } else {
12893                let c = 3;
12894            }
12895        }"
12896        .unindent(),
12897        cx,
12898    )
12899    .await;
12900
12901    assert_indent_guides(
12902        1..11,
12903        vec![
12904            indent_guide(buffer_id, 1, 9, 0),
12905            indent_guide(buffer_id, 6, 6, 1),
12906            indent_guide(buffer_id, 8, 8, 1),
12907        ],
12908        None,
12909        &mut cx,
12910    );
12911}
12912
12913#[gpui::test]
12914async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12915    let (buffer_id, mut cx) = setup_indent_guides_editor(
12916        &"
12917        fn main() {
12918            let a = 1;
12919
12920            let c = 3;
12921
12922            if a == 3 {
12923                let b = 2;
12924            } else {
12925                let c = 3;
12926            }
12927        }"
12928        .unindent(),
12929        cx,
12930    )
12931    .await;
12932
12933    assert_indent_guides(
12934        1..10,
12935        vec![
12936            indent_guide(buffer_id, 1, 9, 0),
12937            indent_guide(buffer_id, 6, 6, 1),
12938            indent_guide(buffer_id, 8, 8, 1),
12939        ],
12940        None,
12941        &mut cx,
12942    );
12943}
12944
12945#[gpui::test]
12946async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12947    let (buffer_id, mut cx) = setup_indent_guides_editor(
12948        &"
12949        block1
12950            block2
12951                block3
12952                    block4
12953            block2
12954        block1
12955        block1"
12956            .unindent(),
12957        cx,
12958    )
12959    .await;
12960
12961    assert_indent_guides(
12962        1..10,
12963        vec![
12964            indent_guide(buffer_id, 1, 4, 0),
12965            indent_guide(buffer_id, 2, 3, 1),
12966            indent_guide(buffer_id, 3, 3, 2),
12967        ],
12968        None,
12969        &mut cx,
12970    );
12971}
12972
12973#[gpui::test]
12974async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12975    let (buffer_id, mut cx) = setup_indent_guides_editor(
12976        &"
12977        block1
12978            block2
12979                block3
12980
12981        block1
12982        block1"
12983            .unindent(),
12984        cx,
12985    )
12986    .await;
12987
12988    assert_indent_guides(
12989        0..6,
12990        vec![
12991            indent_guide(buffer_id, 1, 2, 0),
12992            indent_guide(buffer_id, 2, 2, 1),
12993        ],
12994        None,
12995        &mut cx,
12996    );
12997}
12998
12999#[gpui::test]
13000async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13001    let (buffer_id, mut cx) = setup_indent_guides_editor(
13002        &"
13003        block1
13004
13005
13006
13007            block2
13008        "
13009        .unindent(),
13010        cx,
13011    )
13012    .await;
13013
13014    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13015}
13016
13017#[gpui::test]
13018async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13019    let (buffer_id, mut cx) = setup_indent_guides_editor(
13020        &"
13021        def a:
13022        \tb = 3
13023        \tif True:
13024        \t\tc = 4
13025        \t\td = 5
13026        \tprint(b)
13027        "
13028        .unindent(),
13029        cx,
13030    )
13031    .await;
13032
13033    assert_indent_guides(
13034        0..6,
13035        vec![
13036            indent_guide(buffer_id, 1, 6, 0),
13037            indent_guide(buffer_id, 3, 4, 1),
13038        ],
13039        None,
13040        &mut cx,
13041    );
13042}
13043
13044#[gpui::test]
13045async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13046    let (buffer_id, mut cx) = setup_indent_guides_editor(
13047        &"
13048    fn main() {
13049        let a = 1;
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..3,
13064        vec![indent_guide(buffer_id, 1, 1, 0)],
13065        Some(vec![0]),
13066        &mut cx,
13067    );
13068}
13069
13070#[gpui::test]
13071async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13072    let (buffer_id, mut cx) = setup_indent_guides_editor(
13073        &"
13074    fn main() {
13075        if 1 == 2 {
13076            let a = 1;
13077        }
13078    }"
13079        .unindent(),
13080        cx,
13081    )
13082    .await;
13083
13084    cx.update_editor(|editor, cx| {
13085        editor.change_selections(None, cx, |s| {
13086            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13087        });
13088    });
13089
13090    assert_indent_guides(
13091        0..4,
13092        vec![
13093            indent_guide(buffer_id, 1, 3, 0),
13094            indent_guide(buffer_id, 2, 2, 1),
13095        ],
13096        Some(vec![1]),
13097        &mut cx,
13098    );
13099
13100    cx.update_editor(|editor, cx| {
13101        editor.change_selections(None, cx, |s| {
13102            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13103        });
13104    });
13105
13106    assert_indent_guides(
13107        0..4,
13108        vec![
13109            indent_guide(buffer_id, 1, 3, 0),
13110            indent_guide(buffer_id, 2, 2, 1),
13111        ],
13112        Some(vec![1]),
13113        &mut cx,
13114    );
13115
13116    cx.update_editor(|editor, cx| {
13117        editor.change_selections(None, cx, |s| {
13118            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13119        });
13120    });
13121
13122    assert_indent_guides(
13123        0..4,
13124        vec![
13125            indent_guide(buffer_id, 1, 3, 0),
13126            indent_guide(buffer_id, 2, 2, 1),
13127        ],
13128        Some(vec![0]),
13129        &mut cx,
13130    );
13131}
13132
13133#[gpui::test]
13134async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13135    let (buffer_id, mut cx) = setup_indent_guides_editor(
13136        &"
13137    fn main() {
13138        let a = 1;
13139
13140        let b = 2;
13141    }"
13142        .unindent(),
13143        cx,
13144    )
13145    .await;
13146
13147    cx.update_editor(|editor, cx| {
13148        editor.change_selections(None, cx, |s| {
13149            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13150        });
13151    });
13152
13153    assert_indent_guides(
13154        0..5,
13155        vec![indent_guide(buffer_id, 1, 3, 0)],
13156        Some(vec![0]),
13157        &mut cx,
13158    );
13159}
13160
13161#[gpui::test]
13162async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13163    let (buffer_id, mut cx) = setup_indent_guides_editor(
13164        &"
13165    def m:
13166        a = 1
13167        pass"
13168            .unindent(),
13169        cx,
13170    )
13171    .await;
13172
13173    cx.update_editor(|editor, cx| {
13174        editor.change_selections(None, cx, |s| {
13175            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13176        });
13177    });
13178
13179    assert_indent_guides(
13180        0..3,
13181        vec![indent_guide(buffer_id, 1, 2, 0)],
13182        Some(vec![0]),
13183        &mut cx,
13184    );
13185}
13186
13187#[gpui::test]
13188fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13189    init_test(cx, |_| {});
13190
13191    let editor = cx.add_window(|cx| {
13192        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13193        build_editor(buffer, cx)
13194    });
13195
13196    let render_args = Arc::new(Mutex::new(None));
13197    let snapshot = editor
13198        .update(cx, |editor, cx| {
13199            let snapshot = editor.buffer().read(cx).snapshot(cx);
13200            let range =
13201                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13202
13203            struct RenderArgs {
13204                row: MultiBufferRow,
13205                folded: bool,
13206                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13207            }
13208
13209            let crease = Crease::inline(
13210                range,
13211                FoldPlaceholder::test(),
13212                {
13213                    let toggle_callback = render_args.clone();
13214                    move |row, folded, callback, _cx| {
13215                        *toggle_callback.lock() = Some(RenderArgs {
13216                            row,
13217                            folded,
13218                            callback,
13219                        });
13220                        div()
13221                    }
13222                },
13223                |_row, _folded, _cx| div(),
13224            );
13225
13226            editor.insert_creases(Some(crease), cx);
13227            let snapshot = editor.snapshot(cx);
13228            let _div =
13229                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13230            snapshot
13231        })
13232        .unwrap();
13233
13234    let render_args = render_args.lock().take().unwrap();
13235    assert_eq!(render_args.row, MultiBufferRow(1));
13236    assert!(!render_args.folded);
13237    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13238
13239    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13240        .unwrap();
13241    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13242    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13243
13244    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13245        .unwrap();
13246    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13247    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13248}
13249
13250#[gpui::test]
13251async fn test_input_text(cx: &mut gpui::TestAppContext) {
13252    init_test(cx, |_| {});
13253    let mut cx = EditorTestContext::new(cx).await;
13254
13255    cx.set_state(
13256        &r#"ˇone
13257        two
13258
13259        three
13260        fourˇ
13261        five
13262
13263        siˇx"#
13264            .unindent(),
13265    );
13266
13267    cx.dispatch_action(HandleInput(String::new()));
13268    cx.assert_editor_state(
13269        &r#"ˇone
13270        two
13271
13272        three
13273        fourˇ
13274        five
13275
13276        siˇx"#
13277            .unindent(),
13278    );
13279
13280    cx.dispatch_action(HandleInput("AAAA".to_string()));
13281    cx.assert_editor_state(
13282        &r#"AAAAˇone
13283        two
13284
13285        three
13286        fourAAAAˇ
13287        five
13288
13289        siAAAAˇx"#
13290            .unindent(),
13291    );
13292}
13293
13294#[gpui::test]
13295async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13296    init_test(cx, |_| {});
13297
13298    let mut cx = EditorTestContext::new(cx).await;
13299    cx.set_state(
13300        r#"let foo = 1;
13301let foo = 2;
13302let foo = 3;
13303let fooˇ = 4;
13304let foo = 5;
13305let foo = 6;
13306let foo = 7;
13307let foo = 8;
13308let foo = 9;
13309let foo = 10;
13310let foo = 11;
13311let foo = 12;
13312let foo = 13;
13313let foo = 14;
13314let foo = 15;"#,
13315    );
13316
13317    cx.update_editor(|e, cx| {
13318        assert_eq!(
13319            e.next_scroll_position,
13320            NextScrollCursorCenterTopBottom::Center,
13321            "Default next scroll direction is center",
13322        );
13323
13324        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13325        assert_eq!(
13326            e.next_scroll_position,
13327            NextScrollCursorCenterTopBottom::Top,
13328            "After center, next scroll direction should be top",
13329        );
13330
13331        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13332        assert_eq!(
13333            e.next_scroll_position,
13334            NextScrollCursorCenterTopBottom::Bottom,
13335            "After top, next scroll direction should be bottom",
13336        );
13337
13338        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13339        assert_eq!(
13340            e.next_scroll_position,
13341            NextScrollCursorCenterTopBottom::Center,
13342            "After bottom, scrolling should start over",
13343        );
13344
13345        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13346        assert_eq!(
13347            e.next_scroll_position,
13348            NextScrollCursorCenterTopBottom::Top,
13349            "Scrolling continues if retriggered fast enough"
13350        );
13351    });
13352
13353    cx.executor()
13354        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13355    cx.executor().run_until_parked();
13356    cx.update_editor(|e, _| {
13357        assert_eq!(
13358            e.next_scroll_position,
13359            NextScrollCursorCenterTopBottom::Center,
13360            "If scrolling is not triggered fast enough, it should reset"
13361        );
13362    });
13363}
13364
13365#[gpui::test]
13366async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13367    init_test(cx, |_| {});
13368    let mut cx = EditorLspTestContext::new_rust(
13369        lsp::ServerCapabilities {
13370            definition_provider: Some(lsp::OneOf::Left(true)),
13371            references_provider: Some(lsp::OneOf::Left(true)),
13372            ..lsp::ServerCapabilities::default()
13373        },
13374        cx,
13375    )
13376    .await;
13377
13378    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13379        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13380            move |params, _| async move {
13381                if empty_go_to_definition {
13382                    Ok(None)
13383                } else {
13384                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13385                        uri: params.text_document_position_params.text_document.uri,
13386                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13387                    })))
13388                }
13389            },
13390        );
13391        let references =
13392            cx.lsp
13393                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13394                    Ok(Some(vec![lsp::Location {
13395                        uri: params.text_document_position.text_document.uri,
13396                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13397                    }]))
13398                });
13399        (go_to_definition, references)
13400    };
13401
13402    cx.set_state(
13403        &r#"fn one() {
13404            let mut a = ˇtwo();
13405        }
13406
13407        fn two() {}"#
13408            .unindent(),
13409    );
13410    set_up_lsp_handlers(false, &mut cx);
13411    let navigated = cx
13412        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13413        .await
13414        .expect("Failed to navigate to definition");
13415    assert_eq!(
13416        navigated,
13417        Navigated::Yes,
13418        "Should have navigated to definition from the GetDefinition response"
13419    );
13420    cx.assert_editor_state(
13421        &r#"fn one() {
13422            let mut a = two();
13423        }
13424
13425        fn «twoˇ»() {}"#
13426            .unindent(),
13427    );
13428
13429    let editors = cx.update_workspace(|workspace, cx| {
13430        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13431    });
13432    cx.update_editor(|_, test_editor_cx| {
13433        assert_eq!(
13434            editors.len(),
13435            1,
13436            "Initially, only one, test, editor should be open in the workspace"
13437        );
13438        assert_eq!(
13439            test_editor_cx.view(),
13440            editors.last().expect("Asserted len is 1")
13441        );
13442    });
13443
13444    set_up_lsp_handlers(true, &mut cx);
13445    let navigated = cx
13446        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13447        .await
13448        .expect("Failed to navigate to lookup references");
13449    assert_eq!(
13450        navigated,
13451        Navigated::Yes,
13452        "Should have navigated to references as a fallback after empty GoToDefinition response"
13453    );
13454    // We should not change the selections in the existing file,
13455    // if opening another milti buffer with the references
13456    cx.assert_editor_state(
13457        &r#"fn one() {
13458            let mut a = two();
13459        }
13460
13461        fn «twoˇ»() {}"#
13462            .unindent(),
13463    );
13464    let editors = cx.update_workspace(|workspace, cx| {
13465        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13466    });
13467    cx.update_editor(|_, test_editor_cx| {
13468        assert_eq!(
13469            editors.len(),
13470            2,
13471            "After falling back to references search, we open a new editor with the results"
13472        );
13473        let references_fallback_text = editors
13474            .into_iter()
13475            .find(|new_editor| new_editor != test_editor_cx.view())
13476            .expect("Should have one non-test editor now")
13477            .read(test_editor_cx)
13478            .text(test_editor_cx);
13479        assert_eq!(
13480            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13481            "Should use the range from the references response and not the GoToDefinition one"
13482        );
13483    });
13484}
13485
13486#[gpui::test]
13487async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13488    init_test(cx, |_| {});
13489
13490    let language = Arc::new(Language::new(
13491        LanguageConfig::default(),
13492        Some(tree_sitter_rust::LANGUAGE.into()),
13493    ));
13494
13495    let text = r#"
13496        #[cfg(test)]
13497        mod tests() {
13498            #[test]
13499            fn runnable_1() {
13500                let a = 1;
13501            }
13502
13503            #[test]
13504            fn runnable_2() {
13505                let a = 1;
13506                let b = 2;
13507            }
13508        }
13509    "#
13510    .unindent();
13511
13512    let fs = FakeFs::new(cx.executor());
13513    fs.insert_file("/file.rs", Default::default()).await;
13514
13515    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13516    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13517    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13518    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13519    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13520
13521    let editor = cx.new_view(|cx| {
13522        Editor::new(
13523            EditorMode::Full,
13524            multi_buffer,
13525            Some(project.clone()),
13526            true,
13527            cx,
13528        )
13529    });
13530
13531    editor.update(cx, |editor, cx| {
13532        editor.tasks.insert(
13533            (buffer.read(cx).remote_id(), 3),
13534            RunnableTasks {
13535                templates: vec![],
13536                offset: MultiBufferOffset(43),
13537                column: 0,
13538                extra_variables: HashMap::default(),
13539                context_range: BufferOffset(43)..BufferOffset(85),
13540            },
13541        );
13542        editor.tasks.insert(
13543            (buffer.read(cx).remote_id(), 8),
13544            RunnableTasks {
13545                templates: vec![],
13546                offset: MultiBufferOffset(86),
13547                column: 0,
13548                extra_variables: HashMap::default(),
13549                context_range: BufferOffset(86)..BufferOffset(191),
13550            },
13551        );
13552
13553        // Test finding task when cursor is inside function body
13554        editor.change_selections(None, cx, |s| {
13555            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13556        });
13557        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13558        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13559
13560        // Test finding task when cursor is on function name
13561        editor.change_selections(None, cx, |s| {
13562            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13563        });
13564        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13565        assert_eq!(row, 8, "Should find task when cursor is on function name");
13566    });
13567}
13568
13569fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13570    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13571    point..point
13572}
13573
13574fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13575    let (text, ranges) = marked_text_ranges(marked_text, true);
13576    assert_eq!(view.text(cx), text);
13577    assert_eq!(
13578        view.selections.ranges(cx),
13579        ranges,
13580        "Assert selections are {}",
13581        marked_text
13582    );
13583}
13584
13585pub fn handle_signature_help_request(
13586    cx: &mut EditorLspTestContext,
13587    mocked_response: lsp::SignatureHelp,
13588) -> impl Future<Output = ()> {
13589    let mut request =
13590        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13591            let mocked_response = mocked_response.clone();
13592            async move { Ok(Some(mocked_response)) }
13593        });
13594
13595    async move {
13596        request.next().await;
13597    }
13598}
13599
13600/// Handle completion request passing a marked string specifying where the completion
13601/// should be triggered from using '|' character, what range should be replaced, and what completions
13602/// should be returned using '<' and '>' to delimit the range
13603pub fn handle_completion_request(
13604    cx: &mut EditorLspTestContext,
13605    marked_string: &str,
13606    completions: Vec<&'static str>,
13607    counter: Arc<AtomicUsize>,
13608) -> impl Future<Output = ()> {
13609    let complete_from_marker: TextRangeMarker = '|'.into();
13610    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13611    let (_, mut marked_ranges) = marked_text_ranges_by(
13612        marked_string,
13613        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13614    );
13615
13616    let complete_from_position =
13617        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13618    let replace_range =
13619        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13620
13621    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13622        let completions = completions.clone();
13623        counter.fetch_add(1, atomic::Ordering::Release);
13624        async move {
13625            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13626            assert_eq!(
13627                params.text_document_position.position,
13628                complete_from_position
13629            );
13630            Ok(Some(lsp::CompletionResponse::Array(
13631                completions
13632                    .iter()
13633                    .map(|completion_text| lsp::CompletionItem {
13634                        label: completion_text.to_string(),
13635                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13636                            range: replace_range,
13637                            new_text: completion_text.to_string(),
13638                        })),
13639                        ..Default::default()
13640                    })
13641                    .collect(),
13642            )))
13643        }
13644    });
13645
13646    async move {
13647        request.next().await;
13648    }
13649}
13650
13651fn handle_resolve_completion_request(
13652    cx: &mut EditorLspTestContext,
13653    edits: Option<Vec<(&'static str, &'static str)>>,
13654) -> impl Future<Output = ()> {
13655    let edits = edits.map(|edits| {
13656        edits
13657            .iter()
13658            .map(|(marked_string, new_text)| {
13659                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13660                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13661                lsp::TextEdit::new(replace_range, new_text.to_string())
13662            })
13663            .collect::<Vec<_>>()
13664    });
13665
13666    let mut request =
13667        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13668            let edits = edits.clone();
13669            async move {
13670                Ok(lsp::CompletionItem {
13671                    additional_text_edits: edits,
13672                    ..Default::default()
13673                })
13674            }
13675        });
13676
13677    async move {
13678        request.next().await;
13679    }
13680}
13681
13682pub(crate) fn update_test_language_settings(
13683    cx: &mut TestAppContext,
13684    f: impl Fn(&mut AllLanguageSettingsContent),
13685) {
13686    cx.update(|cx| {
13687        SettingsStore::update_global(cx, |store, cx| {
13688            store.update_user_settings::<AllLanguageSettings>(cx, f);
13689        });
13690    });
13691}
13692
13693pub(crate) fn update_test_project_settings(
13694    cx: &mut TestAppContext,
13695    f: impl Fn(&mut ProjectSettings),
13696) {
13697    cx.update(|cx| {
13698        SettingsStore::update_global(cx, |store, cx| {
13699            store.update_user_settings::<ProjectSettings>(cx, f);
13700        });
13701    });
13702}
13703
13704pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13705    cx.update(|cx| {
13706        assets::Assets.load_test_fonts(cx);
13707        let store = SettingsStore::test(cx);
13708        cx.set_global(store);
13709        theme::init(theme::LoadThemes::JustBase, cx);
13710        release_channel::init(SemanticVersion::default(), cx);
13711        client::init_settings(cx);
13712        language::init(cx);
13713        Project::init_settings(cx);
13714        workspace::init_settings(cx);
13715        crate::init(cx);
13716    });
13717
13718    update_test_language_settings(cx, f);
13719}
13720
13721pub(crate) fn rust_lang() -> Arc<Language> {
13722    Arc::new(Language::new(
13723        LanguageConfig {
13724            name: "Rust".into(),
13725            matcher: LanguageMatcher {
13726                path_suffixes: vec!["rs".to_string()],
13727                ..Default::default()
13728            },
13729            ..Default::default()
13730        },
13731        Some(tree_sitter_rust::LANGUAGE.into()),
13732    ))
13733}
13734
13735#[track_caller]
13736fn assert_hunk_revert(
13737    not_reverted_text_with_selections: &str,
13738    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13739    expected_reverted_text_with_selections: &str,
13740    base_text: &str,
13741    cx: &mut EditorLspTestContext,
13742) {
13743    cx.set_state(not_reverted_text_with_selections);
13744    cx.update_editor(|editor, cx| {
13745        editor
13746            .buffer()
13747            .read(cx)
13748            .as_singleton()
13749            .unwrap()
13750            .update(cx, |buffer, cx| {
13751                buffer.set_diff_base(Some(base_text.into()), cx);
13752            });
13753    });
13754    cx.executor().run_until_parked();
13755
13756    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13757        let snapshot = editor.buffer().read(cx).snapshot(cx);
13758        let reverted_hunk_statuses = snapshot
13759            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13760            .map(|hunk| hunk_status(&hunk))
13761            .collect::<Vec<_>>();
13762
13763        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13764        reverted_hunk_statuses
13765    });
13766    cx.executor().run_until_parked();
13767    cx.assert_editor_state(expected_reverted_text_with_selections);
13768    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13769}