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::AtomicUsize;
   35use std::sync::atomic::{self, AtomicBool};
   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_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10546    init_test(cx, |_| {});
10547
10548    let mut cx = EditorLspTestContext::new_rust(
10549        lsp::ServerCapabilities {
10550            completion_provider: Some(lsp::CompletionOptions {
10551                trigger_characters: Some(vec![".".to_string()]),
10552                resolve_provider: Some(true),
10553                ..Default::default()
10554            }),
10555            ..Default::default()
10556        },
10557        cx,
10558    )
10559    .await;
10560
10561    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10562    cx.simulate_keystroke(".");
10563
10564    let default_commit_characters = vec!["?".to_string()];
10565    let default_data = json!({ "very": "special"});
10566    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10567    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10568    let default_edit_range = lsp::Range {
10569        start: lsp::Position {
10570            line: 0,
10571            character: 5,
10572        },
10573        end: lsp::Position {
10574            line: 0,
10575            character: 5,
10576        },
10577    };
10578
10579    let resolve_requests_number = Arc::new(AtomicUsize::new(0));
10580    let expect_first_item = Arc::new(AtomicBool::new(true));
10581    cx.lsp
10582        .server
10583        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10584            let closure_default_data = default_data.clone();
10585            let closure_resolve_requests_number = resolve_requests_number.clone();
10586            let closure_expect_first_item = expect_first_item.clone();
10587            let closure_default_commit_characters = default_commit_characters.clone();
10588            move |item_to_resolve, _| {
10589                closure_resolve_requests_number.fetch_add(1, atomic::Ordering::Release);
10590                let default_data = closure_default_data.clone();
10591                let default_commit_characters = closure_default_commit_characters.clone();
10592                let expect_first_item = closure_expect_first_item.clone();
10593                async move {
10594                    if expect_first_item.load(atomic::Ordering::Acquire) {
10595                        assert_eq!(
10596                            item_to_resolve.label, "Some(2)",
10597                            "Should have selected the first item"
10598                        );
10599                        assert_eq!(
10600                            item_to_resolve.data,
10601                            Some(json!({ "very": "special"})),
10602                            "First item should bring its own data for resolving"
10603                        );
10604                        assert_eq!(
10605                            item_to_resolve.commit_characters,
10606                            Some(default_commit_characters),
10607                            "First item had no own commit characters and should inherit the default ones"
10608                        );
10609                        assert!(
10610                            matches!(
10611                                item_to_resolve.text_edit,
10612                                Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10613                            ),
10614                            "First item should bring its own edit range for resolving"
10615                        );
10616                        assert_eq!(
10617                            item_to_resolve.insert_text_format,
10618                            Some(default_insert_text_format),
10619                            "First item had no own insert text format and should inherit the default one"
10620                        );
10621                        assert_eq!(
10622                            item_to_resolve.insert_text_mode,
10623                            Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10624                            "First item should bring its own insert text mode for resolving"
10625                        );
10626                        Ok(item_to_resolve)
10627                    } else {
10628                        assert_eq!(
10629                            item_to_resolve.label, "vec![2]",
10630                            "Should have selected the last item"
10631                        );
10632                        assert_eq!(
10633                            item_to_resolve.data,
10634                            Some(default_data),
10635                            "Last item has no own resolve data and should inherit the default one"
10636                        );
10637                        assert_eq!(
10638                            item_to_resolve.commit_characters,
10639                            Some(default_commit_characters),
10640                            "Last item had no own commit characters and should inherit the default ones"
10641                        );
10642                        assert_eq!(
10643                            item_to_resolve.text_edit,
10644                            Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10645                                range: default_edit_range,
10646                                new_text: "vec![2]".to_string()
10647                            })),
10648                            "Last item had no own edit range and should inherit the default one"
10649                        );
10650                        assert_eq!(
10651                            item_to_resolve.insert_text_format,
10652                            Some(lsp::InsertTextFormat::PLAIN_TEXT),
10653                            "Last item should bring its own insert text format for resolving"
10654                        );
10655                        assert_eq!(
10656                            item_to_resolve.insert_text_mode,
10657                            Some(default_insert_text_mode),
10658                            "Last item had no own insert text mode and should inherit the default one"
10659                        );
10660
10661                        Ok(item_to_resolve)
10662                    }
10663                }
10664            }
10665        }).detach();
10666
10667    let completion_data = default_data.clone();
10668    let completion_characters = default_commit_characters.clone();
10669    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10670        let default_data = completion_data.clone();
10671        let default_commit_characters = completion_characters.clone();
10672        async move {
10673            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10674                items: vec![
10675                    lsp::CompletionItem {
10676                        label: "Some(2)".into(),
10677                        insert_text: Some("Some(2)".into()),
10678                        data: Some(json!({ "very": "special"})),
10679                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10680                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10681                            lsp::InsertReplaceEdit {
10682                                new_text: "Some(2)".to_string(),
10683                                insert: lsp::Range::default(),
10684                                replace: lsp::Range::default(),
10685                            },
10686                        )),
10687                        ..lsp::CompletionItem::default()
10688                    },
10689                    lsp::CompletionItem {
10690                        label: "vec![2]".into(),
10691                        insert_text: Some("vec![2]".into()),
10692                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10693                        ..lsp::CompletionItem::default()
10694                    },
10695                ],
10696                item_defaults: Some(lsp::CompletionListItemDefaults {
10697                    data: Some(default_data.clone()),
10698                    commit_characters: Some(default_commit_characters.clone()),
10699                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10700                        default_edit_range,
10701                    )),
10702                    insert_text_format: Some(default_insert_text_format),
10703                    insert_text_mode: Some(default_insert_text_mode),
10704                }),
10705                ..lsp::CompletionList::default()
10706            })))
10707        }
10708    })
10709    .next()
10710    .await;
10711
10712    cx.condition(|editor, _| editor.context_menu_visible())
10713        .await;
10714    cx.run_until_parked();
10715    cx.update_editor(|editor, _| {
10716        let menu = editor.context_menu.read();
10717        match menu.as_ref().expect("should have the completions menu") {
10718            ContextMenu::Completions(completions_menu) => {
10719                assert_eq!(
10720                    completions_menu
10721                        .matches
10722                        .iter()
10723                        .map(|c| c.string.as_str())
10724                        .collect::<Vec<_>>(),
10725                    vec!["Some(2)", "vec![2]"]
10726                );
10727            }
10728            ContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10729        }
10730    });
10731    assert_eq!(
10732        resolve_requests_number.load(atomic::Ordering::Acquire),
10733        1,
10734        "While there are 2 items in the completion list, only 1 resolve request should have been sent, for the selected item"
10735    );
10736
10737    cx.update_editor(|editor, cx| {
10738        editor.context_menu_first(&ContextMenuFirst, cx);
10739    });
10740    cx.run_until_parked();
10741    assert_eq!(
10742        resolve_requests_number.load(atomic::Ordering::Acquire),
10743        2,
10744        "After re-selecting the first item, another resolve request should have been sent"
10745    );
10746
10747    expect_first_item.store(false, atomic::Ordering::Release);
10748    cx.update_editor(|editor, cx| {
10749        editor.context_menu_last(&ContextMenuLast, cx);
10750    });
10751    cx.run_until_parked();
10752    assert_eq!(
10753        resolve_requests_number.load(atomic::Ordering::Acquire),
10754        3,
10755        "After selecting the other item, another resolve request should have been sent"
10756    );
10757}
10758
10759#[gpui::test]
10760async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10761    init_test(cx, |_| {});
10762
10763    let mut cx = EditorLspTestContext::new(
10764        Language::new(
10765            LanguageConfig {
10766                matcher: LanguageMatcher {
10767                    path_suffixes: vec!["jsx".into()],
10768                    ..Default::default()
10769                },
10770                overrides: [(
10771                    "element".into(),
10772                    LanguageConfigOverride {
10773                        word_characters: Override::Set(['-'].into_iter().collect()),
10774                        ..Default::default()
10775                    },
10776                )]
10777                .into_iter()
10778                .collect(),
10779                ..Default::default()
10780            },
10781            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10782        )
10783        .with_override_query("(jsx_self_closing_element) @element")
10784        .unwrap(),
10785        lsp::ServerCapabilities {
10786            completion_provider: Some(lsp::CompletionOptions {
10787                trigger_characters: Some(vec![":".to_string()]),
10788                ..Default::default()
10789            }),
10790            ..Default::default()
10791        },
10792        cx,
10793    )
10794    .await;
10795
10796    cx.lsp
10797        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10798            Ok(Some(lsp::CompletionResponse::Array(vec![
10799                lsp::CompletionItem {
10800                    label: "bg-blue".into(),
10801                    ..Default::default()
10802                },
10803                lsp::CompletionItem {
10804                    label: "bg-red".into(),
10805                    ..Default::default()
10806                },
10807                lsp::CompletionItem {
10808                    label: "bg-yellow".into(),
10809                    ..Default::default()
10810                },
10811            ])))
10812        });
10813
10814    cx.set_state(r#"<p class="bgˇ" />"#);
10815
10816    // Trigger completion when typing a dash, because the dash is an extra
10817    // word character in the 'element' scope, which contains the cursor.
10818    cx.simulate_keystroke("-");
10819    cx.executor().run_until_parked();
10820    cx.update_editor(|editor, _| {
10821        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10822            assert_eq!(
10823                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10824                &["bg-red", "bg-blue", "bg-yellow"]
10825            );
10826        } else {
10827            panic!("expected completion menu to be open");
10828        }
10829    });
10830
10831    cx.simulate_keystroke("l");
10832    cx.executor().run_until_parked();
10833    cx.update_editor(|editor, _| {
10834        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10835            assert_eq!(
10836                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10837                &["bg-blue", "bg-yellow"]
10838            );
10839        } else {
10840            panic!("expected completion menu to be open");
10841        }
10842    });
10843
10844    // When filtering completions, consider the character after the '-' to
10845    // be the start of a subword.
10846    cx.set_state(r#"<p class="yelˇ" />"#);
10847    cx.simulate_keystroke("l");
10848    cx.executor().run_until_parked();
10849    cx.update_editor(|editor, _| {
10850        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10851            assert_eq!(
10852                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10853                &["bg-yellow"]
10854            );
10855        } else {
10856            panic!("expected completion menu to be open");
10857        }
10858    });
10859}
10860
10861#[gpui::test]
10862async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10863    init_test(cx, |settings| {
10864        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10865            FormatterList(vec![Formatter::Prettier].into()),
10866        ))
10867    });
10868
10869    let fs = FakeFs::new(cx.executor());
10870    fs.insert_file("/file.ts", Default::default()).await;
10871
10872    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10873    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10874
10875    language_registry.add(Arc::new(Language::new(
10876        LanguageConfig {
10877            name: "TypeScript".into(),
10878            matcher: LanguageMatcher {
10879                path_suffixes: vec!["ts".to_string()],
10880                ..Default::default()
10881            },
10882            ..Default::default()
10883        },
10884        Some(tree_sitter_rust::LANGUAGE.into()),
10885    )));
10886    update_test_language_settings(cx, |settings| {
10887        settings.defaults.prettier = Some(PrettierSettings {
10888            allowed: true,
10889            ..PrettierSettings::default()
10890        });
10891    });
10892
10893    let test_plugin = "test_plugin";
10894    let _ = language_registry.register_fake_lsp(
10895        "TypeScript",
10896        FakeLspAdapter {
10897            prettier_plugins: vec![test_plugin],
10898            ..Default::default()
10899        },
10900    );
10901
10902    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10903    let buffer = project
10904        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10905        .await
10906        .unwrap();
10907
10908    let buffer_text = "one\ntwo\nthree\n";
10909    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10910    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10911    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10912
10913    editor
10914        .update(cx, |editor, cx| {
10915            editor.perform_format(
10916                project.clone(),
10917                FormatTrigger::Manual,
10918                FormatTarget::Buffer,
10919                cx,
10920            )
10921        })
10922        .unwrap()
10923        .await;
10924    assert_eq!(
10925        editor.update(cx, |editor, cx| editor.text(cx)),
10926        buffer_text.to_string() + prettier_format_suffix,
10927        "Test prettier formatting was not applied to the original buffer text",
10928    );
10929
10930    update_test_language_settings(cx, |settings| {
10931        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10932    });
10933    let format = editor.update(cx, |editor, cx| {
10934        editor.perform_format(
10935            project.clone(),
10936            FormatTrigger::Manual,
10937            FormatTarget::Buffer,
10938            cx,
10939        )
10940    });
10941    format.await.unwrap();
10942    assert_eq!(
10943        editor.update(cx, |editor, cx| editor.text(cx)),
10944        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10945        "Autoformatting (via test prettier) was not applied to the original buffer text",
10946    );
10947}
10948
10949#[gpui::test]
10950async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10951    init_test(cx, |_| {});
10952    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10953    let base_text = indoc! {r#"struct Row;
10954struct Row1;
10955struct Row2;
10956
10957struct Row4;
10958struct Row5;
10959struct Row6;
10960
10961struct Row8;
10962struct Row9;
10963struct Row10;"#};
10964
10965    // When addition hunks are not adjacent to carets, no hunk revert is performed
10966    assert_hunk_revert(
10967        indoc! {r#"struct Row;
10968                   struct Row1;
10969                   struct Row1.1;
10970                   struct Row1.2;
10971                   struct Row2;ˇ
10972
10973                   struct Row4;
10974                   struct Row5;
10975                   struct Row6;
10976
10977                   struct Row8;
10978                   ˇstruct Row9;
10979                   struct Row9.1;
10980                   struct Row9.2;
10981                   struct Row9.3;
10982                   struct Row10;"#},
10983        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10984        indoc! {r#"struct Row;
10985                   struct Row1;
10986                   struct Row1.1;
10987                   struct Row1.2;
10988                   struct Row2;ˇ
10989
10990                   struct Row4;
10991                   struct Row5;
10992                   struct Row6;
10993
10994                   struct Row8;
10995                   ˇstruct Row9;
10996                   struct Row9.1;
10997                   struct Row9.2;
10998                   struct Row9.3;
10999                   struct Row10;"#},
11000        base_text,
11001        &mut cx,
11002    );
11003    // Same for selections
11004    assert_hunk_revert(
11005        indoc! {r#"struct Row;
11006                   struct Row1;
11007                   struct Row2;
11008                   struct Row2.1;
11009                   struct Row2.2;
11010                   «ˇ
11011                   struct Row4;
11012                   struct» Row5;
11013                   «struct Row6;
11014                   ˇ»
11015                   struct Row9.1;
11016                   struct Row9.2;
11017                   struct Row9.3;
11018                   struct Row8;
11019                   struct Row9;
11020                   struct Row10;"#},
11021        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11022        indoc! {r#"struct Row;
11023                   struct Row1;
11024                   struct Row2;
11025                   struct Row2.1;
11026                   struct Row2.2;
11027                   «ˇ
11028                   struct Row4;
11029                   struct» Row5;
11030                   «struct Row6;
11031                   ˇ»
11032                   struct Row9.1;
11033                   struct Row9.2;
11034                   struct Row9.3;
11035                   struct Row8;
11036                   struct Row9;
11037                   struct Row10;"#},
11038        base_text,
11039        &mut cx,
11040    );
11041
11042    // When carets and selections intersect the addition hunks, those are reverted.
11043    // Adjacent carets got merged.
11044    assert_hunk_revert(
11045        indoc! {r#"struct Row;
11046                   ˇ// something on the top
11047                   struct Row1;
11048                   struct Row2;
11049                   struct Roˇw3.1;
11050                   struct Row2.2;
11051                   struct Row2.3;ˇ
11052
11053                   struct Row4;
11054                   struct ˇRow5.1;
11055                   struct Row5.2;
11056                   struct «Rowˇ»5.3;
11057                   struct Row5;
11058                   struct Row6;
11059                   ˇ
11060                   struct Row9.1;
11061                   struct «Rowˇ»9.2;
11062                   struct «ˇRow»9.3;
11063                   struct Row8;
11064                   struct Row9;
11065                   «ˇ// something on bottom»
11066                   struct Row10;"#},
11067        vec![
11068            DiffHunkStatus::Added,
11069            DiffHunkStatus::Added,
11070            DiffHunkStatus::Added,
11071            DiffHunkStatus::Added,
11072            DiffHunkStatus::Added,
11073        ],
11074        indoc! {r#"struct Row;
11075                   ˇstruct Row1;
11076                   struct Row2;
11077                   ˇ
11078                   struct Row4;
11079                   ˇstruct Row5;
11080                   struct Row6;
11081                   ˇ
11082                   ˇstruct Row8;
11083                   struct Row9;
11084                   ˇstruct Row10;"#},
11085        base_text,
11086        &mut cx,
11087    );
11088}
11089
11090#[gpui::test]
11091async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11092    init_test(cx, |_| {});
11093    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11094    let base_text = indoc! {r#"struct Row;
11095struct Row1;
11096struct Row2;
11097
11098struct Row4;
11099struct Row5;
11100struct Row6;
11101
11102struct Row8;
11103struct Row9;
11104struct Row10;"#};
11105
11106    // Modification hunks behave the same as the addition ones.
11107    assert_hunk_revert(
11108        indoc! {r#"struct Row;
11109                   struct Row1;
11110                   struct Row33;
11111                   ˇ
11112                   struct Row4;
11113                   struct Row5;
11114                   struct Row6;
11115                   ˇ
11116                   struct Row99;
11117                   struct Row9;
11118                   struct Row10;"#},
11119        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11120        indoc! {r#"struct Row;
11121                   struct Row1;
11122                   struct Row33;
11123                   ˇ
11124                   struct Row4;
11125                   struct Row5;
11126                   struct Row6;
11127                   ˇ
11128                   struct Row99;
11129                   struct Row9;
11130                   struct Row10;"#},
11131        base_text,
11132        &mut cx,
11133    );
11134    assert_hunk_revert(
11135        indoc! {r#"struct Row;
11136                   struct Row1;
11137                   struct Row33;
11138                   «ˇ
11139                   struct Row4;
11140                   struct» Row5;
11141                   «struct Row6;
11142                   ˇ»
11143                   struct Row99;
11144                   struct Row9;
11145                   struct Row10;"#},
11146        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11147        indoc! {r#"struct Row;
11148                   struct Row1;
11149                   struct Row33;
11150                   «ˇ
11151                   struct Row4;
11152                   struct» Row5;
11153                   «struct Row6;
11154                   ˇ»
11155                   struct Row99;
11156                   struct Row9;
11157                   struct Row10;"#},
11158        base_text,
11159        &mut cx,
11160    );
11161
11162    assert_hunk_revert(
11163        indoc! {r#"ˇstruct Row1.1;
11164                   struct Row1;
11165                   «ˇstr»uct Row22;
11166
11167                   struct ˇRow44;
11168                   struct Row5;
11169                   struct «Rˇ»ow66;ˇ
11170
11171                   «struˇ»ct Row88;
11172                   struct Row9;
11173                   struct Row1011;ˇ"#},
11174        vec![
11175            DiffHunkStatus::Modified,
11176            DiffHunkStatus::Modified,
11177            DiffHunkStatus::Modified,
11178            DiffHunkStatus::Modified,
11179            DiffHunkStatus::Modified,
11180            DiffHunkStatus::Modified,
11181        ],
11182        indoc! {r#"struct Row;
11183                   ˇstruct Row1;
11184                   struct Row2;
11185                   ˇ
11186                   struct Row4;
11187                   ˇstruct Row5;
11188                   struct Row6;
11189                   ˇ
11190                   struct Row8;
11191                   ˇstruct Row9;
11192                   struct Row10;ˇ"#},
11193        base_text,
11194        &mut cx,
11195    );
11196}
11197
11198#[gpui::test]
11199async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11200    init_test(cx, |_| {});
11201    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11202    let base_text = indoc! {r#"struct Row;
11203struct Row1;
11204struct Row2;
11205
11206struct Row4;
11207struct Row5;
11208struct Row6;
11209
11210struct Row8;
11211struct Row9;
11212struct Row10;"#};
11213
11214    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11215    assert_hunk_revert(
11216        indoc! {r#"struct Row;
11217                   struct Row2;
11218
11219                   ˇstruct Row4;
11220                   struct Row5;
11221                   struct Row6;
11222                   ˇ
11223                   struct Row8;
11224                   struct Row10;"#},
11225        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11226        indoc! {r#"struct Row;
11227                   struct Row2;
11228
11229                   ˇstruct Row4;
11230                   struct Row5;
11231                   struct Row6;
11232                   ˇ
11233                   struct Row8;
11234                   struct Row10;"#},
11235        base_text,
11236        &mut cx,
11237    );
11238    assert_hunk_revert(
11239        indoc! {r#"struct Row;
11240                   struct Row2;
11241
11242                   «ˇstruct Row4;
11243                   struct» Row5;
11244                   «struct Row6;
11245                   ˇ»
11246                   struct Row8;
11247                   struct Row10;"#},
11248        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11249        indoc! {r#"struct Row;
11250                   struct Row2;
11251
11252                   «ˇstruct Row4;
11253                   struct» Row5;
11254                   «struct Row6;
11255                   ˇ»
11256                   struct Row8;
11257                   struct Row10;"#},
11258        base_text,
11259        &mut cx,
11260    );
11261
11262    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11263    assert_hunk_revert(
11264        indoc! {r#"struct Row;
11265                   ˇstruct Row2;
11266
11267                   struct Row4;
11268                   struct Row5;
11269                   struct Row6;
11270
11271                   struct Row8;ˇ
11272                   struct Row10;"#},
11273        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11274        indoc! {r#"struct Row;
11275                   struct Row1;
11276                   ˇstruct Row2;
11277
11278                   struct Row4;
11279                   struct Row5;
11280                   struct Row6;
11281
11282                   struct Row8;ˇ
11283                   struct Row9;
11284                   struct Row10;"#},
11285        base_text,
11286        &mut cx,
11287    );
11288    assert_hunk_revert(
11289        indoc! {r#"struct Row;
11290                   struct Row2«ˇ;
11291                   struct Row4;
11292                   struct» Row5;
11293                   «struct Row6;
11294
11295                   struct Row8;ˇ»
11296                   struct Row10;"#},
11297        vec![
11298            DiffHunkStatus::Removed,
11299            DiffHunkStatus::Removed,
11300            DiffHunkStatus::Removed,
11301        ],
11302        indoc! {r#"struct Row;
11303                   struct Row1;
11304                   struct Row2«ˇ;
11305
11306                   struct Row4;
11307                   struct» Row5;
11308                   «struct Row6;
11309
11310                   struct Row8;ˇ»
11311                   struct Row9;
11312                   struct Row10;"#},
11313        base_text,
11314        &mut cx,
11315    );
11316}
11317
11318#[gpui::test]
11319async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11320    init_test(cx, |_| {});
11321
11322    let cols = 4;
11323    let rows = 10;
11324    let sample_text_1 = sample_text(rows, cols, 'a');
11325    assert_eq!(
11326        sample_text_1,
11327        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11328    );
11329    let sample_text_2 = sample_text(rows, cols, 'l');
11330    assert_eq!(
11331        sample_text_2,
11332        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11333    );
11334    let sample_text_3 = sample_text(rows, cols, 'v');
11335    assert_eq!(
11336        sample_text_3,
11337        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11338    );
11339
11340    fn diff_every_buffer_row(
11341        buffer: &Model<Buffer>,
11342        sample_text: String,
11343        cols: usize,
11344        cx: &mut gpui::TestAppContext,
11345    ) {
11346        // revert first character in each row, creating one large diff hunk per buffer
11347        let is_first_char = |offset: usize| offset % cols == 0;
11348        buffer.update(cx, |buffer, cx| {
11349            buffer.set_text(
11350                sample_text
11351                    .chars()
11352                    .enumerate()
11353                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11354                    .collect::<String>(),
11355                cx,
11356            );
11357            buffer.set_diff_base(Some(sample_text), cx);
11358        });
11359        cx.executor().run_until_parked();
11360    }
11361
11362    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11363    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11364
11365    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11366    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11367
11368    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11369    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11370
11371    let multibuffer = cx.new_model(|cx| {
11372        let mut multibuffer = MultiBuffer::new(ReadWrite);
11373        multibuffer.push_excerpts(
11374            buffer_1.clone(),
11375            [
11376                ExcerptRange {
11377                    context: Point::new(0, 0)..Point::new(3, 0),
11378                    primary: None,
11379                },
11380                ExcerptRange {
11381                    context: Point::new(5, 0)..Point::new(7, 0),
11382                    primary: None,
11383                },
11384                ExcerptRange {
11385                    context: Point::new(9, 0)..Point::new(10, 4),
11386                    primary: None,
11387                },
11388            ],
11389            cx,
11390        );
11391        multibuffer.push_excerpts(
11392            buffer_2.clone(),
11393            [
11394                ExcerptRange {
11395                    context: Point::new(0, 0)..Point::new(3, 0),
11396                    primary: None,
11397                },
11398                ExcerptRange {
11399                    context: Point::new(5, 0)..Point::new(7, 0),
11400                    primary: None,
11401                },
11402                ExcerptRange {
11403                    context: Point::new(9, 0)..Point::new(10, 4),
11404                    primary: None,
11405                },
11406            ],
11407            cx,
11408        );
11409        multibuffer.push_excerpts(
11410            buffer_3.clone(),
11411            [
11412                ExcerptRange {
11413                    context: Point::new(0, 0)..Point::new(3, 0),
11414                    primary: None,
11415                },
11416                ExcerptRange {
11417                    context: Point::new(5, 0)..Point::new(7, 0),
11418                    primary: None,
11419                },
11420                ExcerptRange {
11421                    context: Point::new(9, 0)..Point::new(10, 4),
11422                    primary: None,
11423                },
11424            ],
11425            cx,
11426        );
11427        multibuffer
11428    });
11429
11430    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11431    editor.update(cx, |editor, cx| {
11432        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");
11433        editor.select_all(&SelectAll, cx);
11434        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11435    });
11436    cx.executor().run_until_parked();
11437    // When all ranges are selected, all buffer hunks are reverted.
11438    editor.update(cx, |editor, cx| {
11439        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");
11440    });
11441    buffer_1.update(cx, |buffer, _| {
11442        assert_eq!(buffer.text(), sample_text_1);
11443    });
11444    buffer_2.update(cx, |buffer, _| {
11445        assert_eq!(buffer.text(), sample_text_2);
11446    });
11447    buffer_3.update(cx, |buffer, _| {
11448        assert_eq!(buffer.text(), sample_text_3);
11449    });
11450
11451    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11452    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11453    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11454    editor.update(cx, |editor, cx| {
11455        editor.change_selections(None, cx, |s| {
11456            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11457        });
11458        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11459    });
11460    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11461    // but not affect buffer_2 and its related excerpts.
11462    editor.update(cx, |editor, cx| {
11463        assert_eq!(
11464            editor.text(cx),
11465            "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"
11466        );
11467    });
11468    buffer_1.update(cx, |buffer, _| {
11469        assert_eq!(buffer.text(), sample_text_1);
11470    });
11471    buffer_2.update(cx, |buffer, _| {
11472        assert_eq!(
11473            buffer.text(),
11474            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11475        );
11476    });
11477    buffer_3.update(cx, |buffer, _| {
11478        assert_eq!(
11479            buffer.text(),
11480            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11481        );
11482    });
11483}
11484
11485#[gpui::test]
11486async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11487    init_test(cx, |_| {});
11488
11489    let cols = 4;
11490    let rows = 10;
11491    let sample_text_1 = sample_text(rows, cols, 'a');
11492    assert_eq!(
11493        sample_text_1,
11494        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11495    );
11496    let sample_text_2 = sample_text(rows, cols, 'l');
11497    assert_eq!(
11498        sample_text_2,
11499        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11500    );
11501    let sample_text_3 = sample_text(rows, cols, 'v');
11502    assert_eq!(
11503        sample_text_3,
11504        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11505    );
11506
11507    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11508    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11509    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11510
11511    let multi_buffer = cx.new_model(|cx| {
11512        let mut multibuffer = MultiBuffer::new(ReadWrite);
11513        multibuffer.push_excerpts(
11514            buffer_1.clone(),
11515            [
11516                ExcerptRange {
11517                    context: Point::new(0, 0)..Point::new(3, 0),
11518                    primary: None,
11519                },
11520                ExcerptRange {
11521                    context: Point::new(5, 0)..Point::new(7, 0),
11522                    primary: None,
11523                },
11524                ExcerptRange {
11525                    context: Point::new(9, 0)..Point::new(10, 4),
11526                    primary: None,
11527                },
11528            ],
11529            cx,
11530        );
11531        multibuffer.push_excerpts(
11532            buffer_2.clone(),
11533            [
11534                ExcerptRange {
11535                    context: Point::new(0, 0)..Point::new(3, 0),
11536                    primary: None,
11537                },
11538                ExcerptRange {
11539                    context: Point::new(5, 0)..Point::new(7, 0),
11540                    primary: None,
11541                },
11542                ExcerptRange {
11543                    context: Point::new(9, 0)..Point::new(10, 4),
11544                    primary: None,
11545                },
11546            ],
11547            cx,
11548        );
11549        multibuffer.push_excerpts(
11550            buffer_3.clone(),
11551            [
11552                ExcerptRange {
11553                    context: Point::new(0, 0)..Point::new(3, 0),
11554                    primary: None,
11555                },
11556                ExcerptRange {
11557                    context: Point::new(5, 0)..Point::new(7, 0),
11558                    primary: None,
11559                },
11560                ExcerptRange {
11561                    context: Point::new(9, 0)..Point::new(10, 4),
11562                    primary: None,
11563                },
11564            ],
11565            cx,
11566        );
11567        multibuffer
11568    });
11569
11570    let fs = FakeFs::new(cx.executor());
11571    fs.insert_tree(
11572        "/a",
11573        json!({
11574            "main.rs": sample_text_1,
11575            "other.rs": sample_text_2,
11576            "lib.rs": sample_text_3,
11577        }),
11578    )
11579    .await;
11580    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11581    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11582    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11583    let multi_buffer_editor = cx.new_view(|cx| {
11584        Editor::new(
11585            EditorMode::Full,
11586            multi_buffer,
11587            Some(project.clone()),
11588            true,
11589            cx,
11590        )
11591    });
11592    let multibuffer_item_id = workspace
11593        .update(cx, |workspace, cx| {
11594            assert!(
11595                workspace.active_item(cx).is_none(),
11596                "active item should be None before the first item is added"
11597            );
11598            workspace.add_item_to_active_pane(
11599                Box::new(multi_buffer_editor.clone()),
11600                None,
11601                true,
11602                cx,
11603            );
11604            let active_item = workspace
11605                .active_item(cx)
11606                .expect("should have an active item after adding the multi buffer");
11607            assert!(
11608                !active_item.is_singleton(cx),
11609                "A multi buffer was expected to active after adding"
11610            );
11611            active_item.item_id()
11612        })
11613        .unwrap();
11614    cx.executor().run_until_parked();
11615
11616    multi_buffer_editor.update(cx, |editor, cx| {
11617        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11618        editor.open_excerpts(&OpenExcerpts, cx);
11619    });
11620    cx.executor().run_until_parked();
11621    let first_item_id = workspace
11622        .update(cx, |workspace, cx| {
11623            let active_item = workspace
11624                .active_item(cx)
11625                .expect("should have an active item after navigating into the 1st buffer");
11626            let first_item_id = active_item.item_id();
11627            assert_ne!(
11628                first_item_id, multibuffer_item_id,
11629                "Should navigate into the 1st buffer and activate it"
11630            );
11631            assert!(
11632                active_item.is_singleton(cx),
11633                "New active item should be a singleton buffer"
11634            );
11635            assert_eq!(
11636                active_item
11637                    .act_as::<Editor>(cx)
11638                    .expect("should have navigated into an editor for the 1st buffer")
11639                    .read(cx)
11640                    .text(cx),
11641                sample_text_1
11642            );
11643
11644            workspace
11645                .go_back(workspace.active_pane().downgrade(), cx)
11646                .detach_and_log_err(cx);
11647
11648            first_item_id
11649        })
11650        .unwrap();
11651    cx.executor().run_until_parked();
11652    workspace
11653        .update(cx, |workspace, cx| {
11654            let active_item = workspace
11655                .active_item(cx)
11656                .expect("should have an active item after navigating back");
11657            assert_eq!(
11658                active_item.item_id(),
11659                multibuffer_item_id,
11660                "Should navigate back to the multi buffer"
11661            );
11662            assert!(!active_item.is_singleton(cx));
11663        })
11664        .unwrap();
11665
11666    multi_buffer_editor.update(cx, |editor, cx| {
11667        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11668            s.select_ranges(Some(39..40))
11669        });
11670        editor.open_excerpts(&OpenExcerpts, cx);
11671    });
11672    cx.executor().run_until_parked();
11673    let second_item_id = workspace
11674        .update(cx, |workspace, cx| {
11675            let active_item = workspace
11676                .active_item(cx)
11677                .expect("should have an active item after navigating into the 2nd buffer");
11678            let second_item_id = active_item.item_id();
11679            assert_ne!(
11680                second_item_id, multibuffer_item_id,
11681                "Should navigate away from the multibuffer"
11682            );
11683            assert_ne!(
11684                second_item_id, first_item_id,
11685                "Should navigate into the 2nd buffer and activate it"
11686            );
11687            assert!(
11688                active_item.is_singleton(cx),
11689                "New active item should be a singleton buffer"
11690            );
11691            assert_eq!(
11692                active_item
11693                    .act_as::<Editor>(cx)
11694                    .expect("should have navigated into an editor")
11695                    .read(cx)
11696                    .text(cx),
11697                sample_text_2
11698            );
11699
11700            workspace
11701                .go_back(workspace.active_pane().downgrade(), cx)
11702                .detach_and_log_err(cx);
11703
11704            second_item_id
11705        })
11706        .unwrap();
11707    cx.executor().run_until_parked();
11708    workspace
11709        .update(cx, |workspace, cx| {
11710            let active_item = workspace
11711                .active_item(cx)
11712                .expect("should have an active item after navigating back from the 2nd buffer");
11713            assert_eq!(
11714                active_item.item_id(),
11715                multibuffer_item_id,
11716                "Should navigate back from the 2nd buffer to the multi buffer"
11717            );
11718            assert!(!active_item.is_singleton(cx));
11719        })
11720        .unwrap();
11721
11722    multi_buffer_editor.update(cx, |editor, cx| {
11723        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11724            s.select_ranges(Some(60..70))
11725        });
11726        editor.open_excerpts(&OpenExcerpts, cx);
11727    });
11728    cx.executor().run_until_parked();
11729    workspace
11730        .update(cx, |workspace, cx| {
11731            let active_item = workspace
11732                .active_item(cx)
11733                .expect("should have an active item after navigating into the 3rd buffer");
11734            let third_item_id = active_item.item_id();
11735            assert_ne!(
11736                third_item_id, multibuffer_item_id,
11737                "Should navigate into the 3rd buffer and activate it"
11738            );
11739            assert_ne!(third_item_id, first_item_id);
11740            assert_ne!(third_item_id, second_item_id);
11741            assert!(
11742                active_item.is_singleton(cx),
11743                "New active item should be a singleton buffer"
11744            );
11745            assert_eq!(
11746                active_item
11747                    .act_as::<Editor>(cx)
11748                    .expect("should have navigated into an editor")
11749                    .read(cx)
11750                    .text(cx),
11751                sample_text_3
11752            );
11753
11754            workspace
11755                .go_back(workspace.active_pane().downgrade(), cx)
11756                .detach_and_log_err(cx);
11757        })
11758        .unwrap();
11759    cx.executor().run_until_parked();
11760    workspace
11761        .update(cx, |workspace, cx| {
11762            let active_item = workspace
11763                .active_item(cx)
11764                .expect("should have an active item after navigating back from the 3rd buffer");
11765            assert_eq!(
11766                active_item.item_id(),
11767                multibuffer_item_id,
11768                "Should navigate back from the 3rd buffer to the multi buffer"
11769            );
11770            assert!(!active_item.is_singleton(cx));
11771        })
11772        .unwrap();
11773}
11774
11775#[gpui::test]
11776async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11777    init_test(cx, |_| {});
11778
11779    let mut cx = EditorTestContext::new(cx).await;
11780
11781    let diff_base = r#"
11782        use some::mod;
11783
11784        const A: u32 = 42;
11785
11786        fn main() {
11787            println!("hello");
11788
11789            println!("world");
11790        }
11791        "#
11792    .unindent();
11793
11794    cx.set_state(
11795        &r#"
11796        use some::modified;
11797
11798        ˇ
11799        fn main() {
11800            println!("hello there");
11801
11802            println!("around the");
11803            println!("world");
11804        }
11805        "#
11806        .unindent(),
11807    );
11808
11809    cx.set_diff_base(Some(&diff_base));
11810    executor.run_until_parked();
11811
11812    cx.update_editor(|editor, cx| {
11813        editor.go_to_next_hunk(&GoToHunk, cx);
11814        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11815    });
11816    executor.run_until_parked();
11817    cx.assert_diff_hunks(
11818        r#"
11819          use some::modified;
11820
11821
11822          fn main() {
11823        -     println!("hello");
11824        +     println!("hello there");
11825
11826              println!("around the");
11827              println!("world");
11828          }
11829        "#
11830        .unindent(),
11831    );
11832
11833    cx.update_editor(|editor, cx| {
11834        for _ in 0..3 {
11835            editor.go_to_next_hunk(&GoToHunk, cx);
11836            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11837        }
11838    });
11839    executor.run_until_parked();
11840    cx.assert_editor_state(
11841        &r#"
11842        use some::modified;
11843
11844        ˇ
11845        fn main() {
11846            println!("hello there");
11847
11848            println!("around the");
11849            println!("world");
11850        }
11851        "#
11852        .unindent(),
11853    );
11854
11855    cx.assert_diff_hunks(
11856        r#"
11857        - use some::mod;
11858        + use some::modified;
11859
11860        - const A: u32 = 42;
11861
11862          fn main() {
11863        -     println!("hello");
11864        +     println!("hello there");
11865
11866        +     println!("around the");
11867              println!("world");
11868          }
11869        "#
11870        .unindent(),
11871    );
11872
11873    cx.update_editor(|editor, cx| {
11874        editor.cancel(&Cancel, cx);
11875    });
11876
11877    cx.assert_diff_hunks(
11878        r#"
11879          use some::modified;
11880
11881
11882          fn main() {
11883              println!("hello there");
11884
11885              println!("around the");
11886              println!("world");
11887          }
11888        "#
11889        .unindent(),
11890    );
11891}
11892
11893#[gpui::test]
11894async fn test_diff_base_change_with_expanded_diff_hunks(
11895    executor: BackgroundExecutor,
11896    cx: &mut gpui::TestAppContext,
11897) {
11898    init_test(cx, |_| {});
11899
11900    let mut cx = EditorTestContext::new(cx).await;
11901
11902    let diff_base = r#"
11903        use some::mod1;
11904        use some::mod2;
11905
11906        const A: u32 = 42;
11907        const B: u32 = 42;
11908        const C: u32 = 42;
11909
11910        fn main() {
11911            println!("hello");
11912
11913            println!("world");
11914        }
11915        "#
11916    .unindent();
11917
11918    cx.set_state(
11919        &r#"
11920        use some::mod2;
11921
11922        const A: u32 = 42;
11923        const C: u32 = 42;
11924
11925        fn main(ˇ) {
11926            //println!("hello");
11927
11928            println!("world");
11929            //
11930            //
11931        }
11932        "#
11933        .unindent(),
11934    );
11935
11936    cx.set_diff_base(Some(&diff_base));
11937    executor.run_until_parked();
11938
11939    cx.update_editor(|editor, cx| {
11940        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11941    });
11942    executor.run_until_parked();
11943    cx.assert_diff_hunks(
11944        r#"
11945        - use some::mod1;
11946          use some::mod2;
11947
11948          const A: u32 = 42;
11949        - const B: u32 = 42;
11950          const C: u32 = 42;
11951
11952          fn main() {
11953        -     println!("hello");
11954        +     //println!("hello");
11955
11956              println!("world");
11957        +     //
11958        +     //
11959          }
11960        "#
11961        .unindent(),
11962    );
11963
11964    cx.set_diff_base(Some("new diff base!"));
11965    executor.run_until_parked();
11966    cx.assert_diff_hunks(
11967        r#"
11968          use some::mod2;
11969
11970          const A: u32 = 42;
11971          const C: u32 = 42;
11972
11973          fn main() {
11974              //println!("hello");
11975
11976              println!("world");
11977              //
11978              //
11979          }
11980        "#
11981        .unindent(),
11982    );
11983
11984    cx.update_editor(|editor, cx| {
11985        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11986    });
11987    executor.run_until_parked();
11988    cx.assert_diff_hunks(
11989        r#"
11990        - new diff base!
11991        + use some::mod2;
11992        +
11993        + const A: u32 = 42;
11994        + const C: u32 = 42;
11995        +
11996        + fn main() {
11997        +     //println!("hello");
11998        +
11999        +     println!("world");
12000        +     //
12001        +     //
12002        + }
12003        "#
12004        .unindent(),
12005    );
12006}
12007
12008#[gpui::test]
12009async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12010    init_test(cx, |_| {});
12011
12012    let mut cx = EditorTestContext::new(cx).await;
12013
12014    let diff_base = r#"
12015        use some::mod1;
12016        use some::mod2;
12017
12018        const A: u32 = 42;
12019        const B: u32 = 42;
12020        const C: u32 = 42;
12021
12022        fn main() {
12023            println!("hello");
12024
12025            println!("world");
12026        }
12027
12028        fn another() {
12029            println!("another");
12030        }
12031
12032        fn another2() {
12033            println!("another2");
12034        }
12035        "#
12036    .unindent();
12037
12038    cx.set_state(
12039        &r#"
12040        «use some::mod2;
12041
12042        const A: u32 = 42;
12043        const C: u32 = 42;
12044
12045        fn main() {
12046            //println!("hello");
12047
12048            println!("world");
12049            //
12050            //ˇ»
12051        }
12052
12053        fn another() {
12054            println!("another");
12055            println!("another");
12056        }
12057
12058            println!("another2");
12059        }
12060        "#
12061        .unindent(),
12062    );
12063
12064    cx.set_diff_base(Some(&diff_base));
12065    executor.run_until_parked();
12066
12067    cx.update_editor(|editor, cx| {
12068        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12069    });
12070    executor.run_until_parked();
12071
12072    cx.assert_diff_hunks(
12073        r#"
12074        - use some::mod1;
12075          use some::mod2;
12076
12077          const A: u32 = 42;
12078        - const B: u32 = 42;
12079          const C: u32 = 42;
12080
12081          fn main() {
12082        -     println!("hello");
12083        +     //println!("hello");
12084
12085              println!("world");
12086        +     //
12087        +     //
12088          }
12089
12090          fn another() {
12091              println!("another");
12092        +     println!("another");
12093          }
12094
12095        - fn another2() {
12096              println!("another2");
12097          }
12098        "#
12099        .unindent(),
12100    );
12101
12102    // Fold across some of the diff hunks. They should no longer appear expanded.
12103    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12104    cx.executor().run_until_parked();
12105
12106    // Hunks are not shown if their position is within a fold
12107    cx.assert_diff_hunks(
12108        r#"
12109          use some::mod2;
12110
12111          const A: u32 = 42;
12112          const C: u32 = 42;
12113
12114          fn main() {
12115              //println!("hello");
12116
12117              println!("world");
12118              //
12119              //
12120          }
12121
12122          fn another() {
12123              println!("another");
12124        +     println!("another");
12125          }
12126
12127        - fn another2() {
12128              println!("another2");
12129          }
12130        "#
12131        .unindent(),
12132    );
12133
12134    cx.update_editor(|editor, cx| {
12135        editor.select_all(&SelectAll, cx);
12136        editor.unfold_lines(&UnfoldLines, cx);
12137    });
12138    cx.executor().run_until_parked();
12139
12140    // The deletions reappear when unfolding.
12141    cx.assert_diff_hunks(
12142        r#"
12143        - use some::mod1;
12144          use some::mod2;
12145
12146          const A: u32 = 42;
12147        - const B: u32 = 42;
12148          const C: u32 = 42;
12149
12150          fn main() {
12151        -     println!("hello");
12152        +     //println!("hello");
12153
12154              println!("world");
12155        +     //
12156        +     //
12157          }
12158
12159          fn another() {
12160              println!("another");
12161        +     println!("another");
12162          }
12163
12164        - fn another2() {
12165              println!("another2");
12166          }
12167        "#
12168        .unindent(),
12169    );
12170}
12171
12172#[gpui::test]
12173async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12174    init_test(cx, |_| {});
12175
12176    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12177    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12178    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12179    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12180    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12181    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12182
12183    let buffer_1 = cx.new_model(|cx| {
12184        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
12185        buffer.set_diff_base(Some(file_1_old.into()), cx);
12186        buffer
12187    });
12188    let buffer_2 = cx.new_model(|cx| {
12189        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
12190        buffer.set_diff_base(Some(file_2_old.into()), cx);
12191        buffer
12192    });
12193    let buffer_3 = cx.new_model(|cx| {
12194        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
12195        buffer.set_diff_base(Some(file_3_old.into()), cx);
12196        buffer
12197    });
12198
12199    let multi_buffer = cx.new_model(|cx| {
12200        let mut multibuffer = MultiBuffer::new(ReadWrite);
12201        multibuffer.push_excerpts(
12202            buffer_1.clone(),
12203            [
12204                ExcerptRange {
12205                    context: Point::new(0, 0)..Point::new(3, 0),
12206                    primary: None,
12207                },
12208                ExcerptRange {
12209                    context: Point::new(5, 0)..Point::new(7, 0),
12210                    primary: None,
12211                },
12212                ExcerptRange {
12213                    context: Point::new(9, 0)..Point::new(10, 3),
12214                    primary: None,
12215                },
12216            ],
12217            cx,
12218        );
12219        multibuffer.push_excerpts(
12220            buffer_2.clone(),
12221            [
12222                ExcerptRange {
12223                    context: Point::new(0, 0)..Point::new(3, 0),
12224                    primary: None,
12225                },
12226                ExcerptRange {
12227                    context: Point::new(5, 0)..Point::new(7, 0),
12228                    primary: None,
12229                },
12230                ExcerptRange {
12231                    context: Point::new(9, 0)..Point::new(10, 3),
12232                    primary: None,
12233                },
12234            ],
12235            cx,
12236        );
12237        multibuffer.push_excerpts(
12238            buffer_3.clone(),
12239            [
12240                ExcerptRange {
12241                    context: Point::new(0, 0)..Point::new(3, 0),
12242                    primary: None,
12243                },
12244                ExcerptRange {
12245                    context: Point::new(5, 0)..Point::new(7, 0),
12246                    primary: None,
12247                },
12248                ExcerptRange {
12249                    context: Point::new(9, 0)..Point::new(10, 3),
12250                    primary: None,
12251                },
12252            ],
12253            cx,
12254        );
12255        multibuffer
12256    });
12257
12258    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12259    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12260    cx.run_until_parked();
12261
12262    cx.assert_editor_state(
12263        &"
12264            ˇaaa
12265            ccc
12266            ddd
12267
12268            ggg
12269            hhh
12270
12271
12272            lll
12273            mmm
12274            NNN
12275
12276            qqq
12277            rrr
12278
12279            uuu
12280            111
12281            222
12282            333
12283
12284            666
12285            777
12286
12287            000
12288            !!!"
12289        .unindent(),
12290    );
12291
12292    cx.update_editor(|editor, cx| {
12293        editor.select_all(&SelectAll, cx);
12294        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12295    });
12296    cx.executor().run_until_parked();
12297
12298    cx.assert_diff_hunks(
12299        "
12300            aaa
12301          - bbb
12302            ccc
12303            ddd
12304
12305            ggg
12306            hhh
12307
12308
12309            lll
12310            mmm
12311          - nnn
12312          + NNN
12313
12314            qqq
12315            rrr
12316
12317            uuu
12318            111
12319            222
12320            333
12321
12322          + 666
12323            777
12324
12325            000
12326            !!!"
12327        .unindent(),
12328    );
12329}
12330
12331#[gpui::test]
12332async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12333    init_test(cx, |_| {});
12334
12335    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12336    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12337
12338    let buffer = cx.new_model(|cx| {
12339        let mut buffer = Buffer::local(text.to_string(), cx);
12340        buffer.set_diff_base(Some(base.into()), cx);
12341        buffer
12342    });
12343
12344    let multi_buffer = cx.new_model(|cx| {
12345        let mut multibuffer = MultiBuffer::new(ReadWrite);
12346        multibuffer.push_excerpts(
12347            buffer.clone(),
12348            [
12349                ExcerptRange {
12350                    context: Point::new(0, 0)..Point::new(2, 0),
12351                    primary: None,
12352                },
12353                ExcerptRange {
12354                    context: Point::new(5, 0)..Point::new(7, 0),
12355                    primary: None,
12356                },
12357            ],
12358            cx,
12359        );
12360        multibuffer
12361    });
12362
12363    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12364    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12365    cx.run_until_parked();
12366
12367    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12368    cx.executor().run_until_parked();
12369
12370    cx.assert_diff_hunks(
12371        "
12372            aaa
12373          - bbb
12374          + BBB
12375
12376          - ddd
12377          - eee
12378          + EEE
12379            fff
12380        "
12381        .unindent(),
12382    );
12383}
12384
12385#[gpui::test]
12386async fn test_edits_around_expanded_insertion_hunks(
12387    executor: BackgroundExecutor,
12388    cx: &mut gpui::TestAppContext,
12389) {
12390    init_test(cx, |_| {});
12391
12392    let mut cx = EditorTestContext::new(cx).await;
12393
12394    let diff_base = r#"
12395        use some::mod1;
12396        use some::mod2;
12397
12398        const A: u32 = 42;
12399
12400        fn main() {
12401            println!("hello");
12402
12403            println!("world");
12404        }
12405        "#
12406    .unindent();
12407    executor.run_until_parked();
12408    cx.set_state(
12409        &r#"
12410        use some::mod1;
12411        use some::mod2;
12412
12413        const A: u32 = 42;
12414        const B: u32 = 42;
12415        const C: u32 = 42;
12416        ˇ
12417
12418        fn main() {
12419            println!("hello");
12420
12421            println!("world");
12422        }
12423        "#
12424        .unindent(),
12425    );
12426
12427    cx.set_diff_base(Some(&diff_base));
12428    executor.run_until_parked();
12429
12430    cx.update_editor(|editor, cx| {
12431        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12432    });
12433    executor.run_until_parked();
12434
12435    cx.assert_diff_hunks(
12436        r#"
12437        use some::mod1;
12438        use some::mod2;
12439
12440        const A: u32 = 42;
12441      + const B: u32 = 42;
12442      + const C: u32 = 42;
12443      +
12444
12445        fn main() {
12446            println!("hello");
12447
12448            println!("world");
12449        }
12450        "#
12451        .unindent(),
12452    );
12453
12454    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12455    executor.run_until_parked();
12456
12457    cx.assert_diff_hunks(
12458        r#"
12459        use some::mod1;
12460        use some::mod2;
12461
12462        const A: u32 = 42;
12463      + const B: u32 = 42;
12464      + const C: u32 = 42;
12465      + const D: u32 = 42;
12466      +
12467
12468        fn main() {
12469            println!("hello");
12470
12471            println!("world");
12472        }
12473        "#
12474        .unindent(),
12475    );
12476
12477    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12478    executor.run_until_parked();
12479
12480    cx.assert_diff_hunks(
12481        r#"
12482        use some::mod1;
12483        use some::mod2;
12484
12485        const A: u32 = 42;
12486      + const B: u32 = 42;
12487      + const C: u32 = 42;
12488      + const D: u32 = 42;
12489      + const E: u32 = 42;
12490      +
12491
12492        fn main() {
12493            println!("hello");
12494
12495            println!("world");
12496        }
12497        "#
12498        .unindent(),
12499    );
12500
12501    cx.update_editor(|editor, cx| {
12502        editor.delete_line(&DeleteLine, cx);
12503    });
12504    executor.run_until_parked();
12505
12506    cx.assert_diff_hunks(
12507        r#"
12508        use some::mod1;
12509        use some::mod2;
12510
12511        const A: u32 = 42;
12512      + const B: u32 = 42;
12513      + const C: u32 = 42;
12514      + const D: u32 = 42;
12515      + const E: u32 = 42;
12516
12517        fn main() {
12518            println!("hello");
12519
12520            println!("world");
12521        }
12522        "#
12523        .unindent(),
12524    );
12525
12526    cx.update_editor(|editor, cx| {
12527        editor.move_up(&MoveUp, cx);
12528        editor.delete_line(&DeleteLine, cx);
12529        editor.move_up(&MoveUp, cx);
12530        editor.delete_line(&DeleteLine, cx);
12531        editor.move_up(&MoveUp, cx);
12532        editor.delete_line(&DeleteLine, cx);
12533    });
12534    executor.run_until_parked();
12535    cx.assert_editor_state(
12536        &r#"
12537        use some::mod1;
12538        use some::mod2;
12539
12540        const A: u32 = 42;
12541        const B: u32 = 42;
12542        ˇ
12543        fn main() {
12544            println!("hello");
12545
12546            println!("world");
12547        }
12548        "#
12549        .unindent(),
12550    );
12551
12552    cx.assert_diff_hunks(
12553        r#"
12554        use some::mod1;
12555        use some::mod2;
12556
12557        const A: u32 = 42;
12558      + const B: u32 = 42;
12559
12560        fn main() {
12561            println!("hello");
12562
12563            println!("world");
12564        }
12565        "#
12566        .unindent(),
12567    );
12568
12569    cx.update_editor(|editor, cx| {
12570        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12571        editor.delete_line(&DeleteLine, cx);
12572    });
12573    executor.run_until_parked();
12574    cx.assert_diff_hunks(
12575        r#"
12576        use some::mod1;
12577      - use some::mod2;
12578      -
12579      - const A: u32 = 42;
12580
12581        fn main() {
12582            println!("hello");
12583
12584            println!("world");
12585        }
12586        "#
12587        .unindent(),
12588    );
12589}
12590
12591#[gpui::test]
12592async fn test_edits_around_expanded_deletion_hunks(
12593    executor: BackgroundExecutor,
12594    cx: &mut gpui::TestAppContext,
12595) {
12596    init_test(cx, |_| {});
12597
12598    let mut cx = EditorTestContext::new(cx).await;
12599
12600    let diff_base = r#"
12601        use some::mod1;
12602        use some::mod2;
12603
12604        const A: u32 = 42;
12605        const B: u32 = 42;
12606        const C: u32 = 42;
12607
12608
12609        fn main() {
12610            println!("hello");
12611
12612            println!("world");
12613        }
12614    "#
12615    .unindent();
12616    executor.run_until_parked();
12617    cx.set_state(
12618        &r#"
12619        use some::mod1;
12620        use some::mod2;
12621
12622        ˇconst B: u32 = 42;
12623        const C: u32 = 42;
12624
12625
12626        fn main() {
12627            println!("hello");
12628
12629            println!("world");
12630        }
12631        "#
12632        .unindent(),
12633    );
12634
12635    cx.set_diff_base(Some(&diff_base));
12636    executor.run_until_parked();
12637
12638    cx.update_editor(|editor, cx| {
12639        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12640    });
12641    executor.run_until_parked();
12642
12643    cx.assert_diff_hunks(
12644        r#"
12645        use some::mod1;
12646        use some::mod2;
12647
12648      - const A: u32 = 42;
12649        const B: u32 = 42;
12650        const C: u32 = 42;
12651
12652
12653        fn main() {
12654            println!("hello");
12655
12656            println!("world");
12657        }
12658        "#
12659        .unindent(),
12660    );
12661
12662    cx.update_editor(|editor, cx| {
12663        editor.delete_line(&DeleteLine, cx);
12664    });
12665    executor.run_until_parked();
12666    cx.assert_editor_state(
12667        &r#"
12668        use some::mod1;
12669        use some::mod2;
12670
12671        ˇconst C: u32 = 42;
12672
12673
12674        fn main() {
12675            println!("hello");
12676
12677            println!("world");
12678        }
12679        "#
12680        .unindent(),
12681    );
12682    cx.assert_diff_hunks(
12683        r#"
12684        use some::mod1;
12685        use some::mod2;
12686
12687      - const A: u32 = 42;
12688      - const B: u32 = 42;
12689        const C: u32 = 42;
12690
12691
12692        fn main() {
12693            println!("hello");
12694
12695            println!("world");
12696        }
12697        "#
12698        .unindent(),
12699    );
12700
12701    cx.update_editor(|editor, cx| {
12702        editor.delete_line(&DeleteLine, cx);
12703    });
12704    executor.run_until_parked();
12705    cx.assert_editor_state(
12706        &r#"
12707        use some::mod1;
12708        use some::mod2;
12709
12710        ˇ
12711
12712        fn main() {
12713            println!("hello");
12714
12715            println!("world");
12716        }
12717        "#
12718        .unindent(),
12719    );
12720    cx.assert_diff_hunks(
12721        r#"
12722        use some::mod1;
12723        use some::mod2;
12724
12725      - const A: u32 = 42;
12726      - const B: u32 = 42;
12727      - const C: u32 = 42;
12728
12729
12730        fn main() {
12731            println!("hello");
12732
12733            println!("world");
12734        }
12735        "#
12736        .unindent(),
12737    );
12738
12739    cx.update_editor(|editor, cx| {
12740        editor.handle_input("replacement", cx);
12741    });
12742    executor.run_until_parked();
12743    cx.assert_editor_state(
12744        &r#"
12745        use some::mod1;
12746        use some::mod2;
12747
12748        replacementˇ
12749
12750        fn main() {
12751            println!("hello");
12752
12753            println!("world");
12754        }
12755        "#
12756        .unindent(),
12757    );
12758    cx.assert_diff_hunks(
12759        r#"
12760        use some::mod1;
12761        use some::mod2;
12762
12763      - const A: u32 = 42;
12764      - const B: u32 = 42;
12765      - const C: u32 = 42;
12766      -
12767      + replacement
12768
12769        fn main() {
12770            println!("hello");
12771
12772            println!("world");
12773        }
12774        "#
12775        .unindent(),
12776    );
12777}
12778
12779#[gpui::test]
12780async fn test_edit_after_expanded_modification_hunk(
12781    executor: BackgroundExecutor,
12782    cx: &mut gpui::TestAppContext,
12783) {
12784    init_test(cx, |_| {});
12785
12786    let mut cx = EditorTestContext::new(cx).await;
12787
12788    let diff_base = r#"
12789        use some::mod1;
12790        use some::mod2;
12791
12792        const A: u32 = 42;
12793        const B: u32 = 42;
12794        const C: u32 = 42;
12795        const D: u32 = 42;
12796
12797
12798        fn main() {
12799            println!("hello");
12800
12801            println!("world");
12802        }"#
12803    .unindent();
12804
12805    cx.set_state(
12806        &r#"
12807        use some::mod1;
12808        use some::mod2;
12809
12810        const A: u32 = 42;
12811        const B: u32 = 42;
12812        const C: u32 = 43ˇ
12813        const D: u32 = 42;
12814
12815
12816        fn main() {
12817            println!("hello");
12818
12819            println!("world");
12820        }"#
12821        .unindent(),
12822    );
12823
12824    cx.set_diff_base(Some(&diff_base));
12825    executor.run_until_parked();
12826    cx.update_editor(|editor, cx| {
12827        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12828    });
12829    executor.run_until_parked();
12830
12831    cx.assert_diff_hunks(
12832        r#"
12833        use some::mod1;
12834        use some::mod2;
12835
12836        const A: u32 = 42;
12837        const B: u32 = 42;
12838      - const C: u32 = 42;
12839      + const C: u32 = 43
12840        const D: u32 = 42;
12841
12842
12843        fn main() {
12844            println!("hello");
12845
12846            println!("world");
12847        }"#
12848        .unindent(),
12849    );
12850
12851    cx.update_editor(|editor, cx| {
12852        editor.handle_input("\nnew_line\n", cx);
12853    });
12854    executor.run_until_parked();
12855
12856    cx.assert_diff_hunks(
12857        r#"
12858        use some::mod1;
12859        use some::mod2;
12860
12861        const A: u32 = 42;
12862        const B: u32 = 42;
12863      - const C: u32 = 42;
12864      + const C: u32 = 43
12865      + new_line
12866      +
12867        const D: u32 = 42;
12868
12869
12870        fn main() {
12871            println!("hello");
12872
12873            println!("world");
12874        }"#
12875        .unindent(),
12876    );
12877}
12878
12879async fn setup_indent_guides_editor(
12880    text: &str,
12881    cx: &mut gpui::TestAppContext,
12882) -> (BufferId, EditorTestContext) {
12883    init_test(cx, |_| {});
12884
12885    let mut cx = EditorTestContext::new(cx).await;
12886
12887    let buffer_id = cx.update_editor(|editor, cx| {
12888        editor.set_text(text, cx);
12889        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12890
12891        buffer_ids[0]
12892    });
12893
12894    (buffer_id, cx)
12895}
12896
12897fn assert_indent_guides(
12898    range: Range<u32>,
12899    expected: Vec<IndentGuide>,
12900    active_indices: Option<Vec<usize>>,
12901    cx: &mut EditorTestContext,
12902) {
12903    let indent_guides = cx.update_editor(|editor, cx| {
12904        let snapshot = editor.snapshot(cx).display_snapshot;
12905        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12906            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12907            true,
12908            &snapshot,
12909            cx,
12910        );
12911
12912        indent_guides.sort_by(|a, b| {
12913            a.depth.cmp(&b.depth).then(
12914                a.start_row
12915                    .cmp(&b.start_row)
12916                    .then(a.end_row.cmp(&b.end_row)),
12917            )
12918        });
12919        indent_guides
12920    });
12921
12922    if let Some(expected) = active_indices {
12923        let active_indices = cx.update_editor(|editor, cx| {
12924            let snapshot = editor.snapshot(cx).display_snapshot;
12925            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12926        });
12927
12928        assert_eq!(
12929            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12930            expected,
12931            "Active indent guide indices do not match"
12932        );
12933    }
12934
12935    let expected: Vec<_> = expected
12936        .into_iter()
12937        .map(|guide| MultiBufferIndentGuide {
12938            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12939            buffer: guide,
12940        })
12941        .collect();
12942
12943    assert_eq!(indent_guides, expected, "Indent guides do not match");
12944}
12945
12946fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12947    IndentGuide {
12948        buffer_id,
12949        start_row,
12950        end_row,
12951        depth,
12952        tab_size: 4,
12953        settings: IndentGuideSettings {
12954            enabled: true,
12955            line_width: 1,
12956            active_line_width: 1,
12957            ..Default::default()
12958        },
12959    }
12960}
12961
12962#[gpui::test]
12963async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12964    let (buffer_id, mut cx) = setup_indent_guides_editor(
12965        &"
12966    fn main() {
12967        let a = 1;
12968    }"
12969        .unindent(),
12970        cx,
12971    )
12972    .await;
12973
12974    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12975}
12976
12977#[gpui::test]
12978async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12979    let (buffer_id, mut cx) = setup_indent_guides_editor(
12980        &"
12981    fn main() {
12982        let a = 1;
12983        let b = 2;
12984    }"
12985        .unindent(),
12986        cx,
12987    )
12988    .await;
12989
12990    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12991}
12992
12993#[gpui::test]
12994async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12995    let (buffer_id, mut cx) = setup_indent_guides_editor(
12996        &"
12997    fn main() {
12998        let a = 1;
12999        if a == 3 {
13000            let b = 2;
13001        } else {
13002            let c = 3;
13003        }
13004    }"
13005        .unindent(),
13006        cx,
13007    )
13008    .await;
13009
13010    assert_indent_guides(
13011        0..8,
13012        vec![
13013            indent_guide(buffer_id, 1, 6, 0),
13014            indent_guide(buffer_id, 3, 3, 1),
13015            indent_guide(buffer_id, 5, 5, 1),
13016        ],
13017        None,
13018        &mut cx,
13019    );
13020}
13021
13022#[gpui::test]
13023async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13024    let (buffer_id, mut cx) = setup_indent_guides_editor(
13025        &"
13026    fn main() {
13027        let a = 1;
13028            let b = 2;
13029        let c = 3;
13030    }"
13031        .unindent(),
13032        cx,
13033    )
13034    .await;
13035
13036    assert_indent_guides(
13037        0..5,
13038        vec![
13039            indent_guide(buffer_id, 1, 3, 0),
13040            indent_guide(buffer_id, 2, 2, 1),
13041        ],
13042        None,
13043        &mut cx,
13044    );
13045}
13046
13047#[gpui::test]
13048async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13049    let (buffer_id, mut cx) = setup_indent_guides_editor(
13050        &"
13051        fn main() {
13052            let a = 1;
13053
13054            let c = 3;
13055        }"
13056        .unindent(),
13057        cx,
13058    )
13059    .await;
13060
13061    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13062}
13063
13064#[gpui::test]
13065async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13066    let (buffer_id, mut cx) = setup_indent_guides_editor(
13067        &"
13068        fn main() {
13069            let a = 1;
13070
13071            let c = 3;
13072
13073            if a == 3 {
13074                let b = 2;
13075            } else {
13076                let c = 3;
13077            }
13078        }"
13079        .unindent(),
13080        cx,
13081    )
13082    .await;
13083
13084    assert_indent_guides(
13085        0..11,
13086        vec![
13087            indent_guide(buffer_id, 1, 9, 0),
13088            indent_guide(buffer_id, 6, 6, 1),
13089            indent_guide(buffer_id, 8, 8, 1),
13090        ],
13091        None,
13092        &mut cx,
13093    );
13094}
13095
13096#[gpui::test]
13097async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13098    let (buffer_id, mut cx) = setup_indent_guides_editor(
13099        &"
13100        fn main() {
13101            let a = 1;
13102
13103            let c = 3;
13104
13105            if a == 3 {
13106                let b = 2;
13107            } else {
13108                let c = 3;
13109            }
13110        }"
13111        .unindent(),
13112        cx,
13113    )
13114    .await;
13115
13116    assert_indent_guides(
13117        1..11,
13118        vec![
13119            indent_guide(buffer_id, 1, 9, 0),
13120            indent_guide(buffer_id, 6, 6, 1),
13121            indent_guide(buffer_id, 8, 8, 1),
13122        ],
13123        None,
13124        &mut cx,
13125    );
13126}
13127
13128#[gpui::test]
13129async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13130    let (buffer_id, mut cx) = setup_indent_guides_editor(
13131        &"
13132        fn main() {
13133            let a = 1;
13134
13135            let c = 3;
13136
13137            if a == 3 {
13138                let b = 2;
13139            } else {
13140                let c = 3;
13141            }
13142        }"
13143        .unindent(),
13144        cx,
13145    )
13146    .await;
13147
13148    assert_indent_guides(
13149        1..10,
13150        vec![
13151            indent_guide(buffer_id, 1, 9, 0),
13152            indent_guide(buffer_id, 6, 6, 1),
13153            indent_guide(buffer_id, 8, 8, 1),
13154        ],
13155        None,
13156        &mut cx,
13157    );
13158}
13159
13160#[gpui::test]
13161async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13162    let (buffer_id, mut cx) = setup_indent_guides_editor(
13163        &"
13164        block1
13165            block2
13166                block3
13167                    block4
13168            block2
13169        block1
13170        block1"
13171            .unindent(),
13172        cx,
13173    )
13174    .await;
13175
13176    assert_indent_guides(
13177        1..10,
13178        vec![
13179            indent_guide(buffer_id, 1, 4, 0),
13180            indent_guide(buffer_id, 2, 3, 1),
13181            indent_guide(buffer_id, 3, 3, 2),
13182        ],
13183        None,
13184        &mut cx,
13185    );
13186}
13187
13188#[gpui::test]
13189async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13190    let (buffer_id, mut cx) = setup_indent_guides_editor(
13191        &"
13192        block1
13193            block2
13194                block3
13195
13196        block1
13197        block1"
13198            .unindent(),
13199        cx,
13200    )
13201    .await;
13202
13203    assert_indent_guides(
13204        0..6,
13205        vec![
13206            indent_guide(buffer_id, 1, 2, 0),
13207            indent_guide(buffer_id, 2, 2, 1),
13208        ],
13209        None,
13210        &mut cx,
13211    );
13212}
13213
13214#[gpui::test]
13215async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13216    let (buffer_id, mut cx) = setup_indent_guides_editor(
13217        &"
13218        block1
13219
13220
13221
13222            block2
13223        "
13224        .unindent(),
13225        cx,
13226    )
13227    .await;
13228
13229    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13230}
13231
13232#[gpui::test]
13233async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13234    let (buffer_id, mut cx) = setup_indent_guides_editor(
13235        &"
13236        def a:
13237        \tb = 3
13238        \tif True:
13239        \t\tc = 4
13240        \t\td = 5
13241        \tprint(b)
13242        "
13243        .unindent(),
13244        cx,
13245    )
13246    .await;
13247
13248    assert_indent_guides(
13249        0..6,
13250        vec![
13251            indent_guide(buffer_id, 1, 6, 0),
13252            indent_guide(buffer_id, 3, 4, 1),
13253        ],
13254        None,
13255        &mut cx,
13256    );
13257}
13258
13259#[gpui::test]
13260async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13261    let (buffer_id, mut cx) = setup_indent_guides_editor(
13262        &"
13263    fn main() {
13264        let a = 1;
13265    }"
13266        .unindent(),
13267        cx,
13268    )
13269    .await;
13270
13271    cx.update_editor(|editor, cx| {
13272        editor.change_selections(None, cx, |s| {
13273            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13274        });
13275    });
13276
13277    assert_indent_guides(
13278        0..3,
13279        vec![indent_guide(buffer_id, 1, 1, 0)],
13280        Some(vec![0]),
13281        &mut cx,
13282    );
13283}
13284
13285#[gpui::test]
13286async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13287    let (buffer_id, mut cx) = setup_indent_guides_editor(
13288        &"
13289    fn main() {
13290        if 1 == 2 {
13291            let a = 1;
13292        }
13293    }"
13294        .unindent(),
13295        cx,
13296    )
13297    .await;
13298
13299    cx.update_editor(|editor, cx| {
13300        editor.change_selections(None, cx, |s| {
13301            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13302        });
13303    });
13304
13305    assert_indent_guides(
13306        0..4,
13307        vec![
13308            indent_guide(buffer_id, 1, 3, 0),
13309            indent_guide(buffer_id, 2, 2, 1),
13310        ],
13311        Some(vec![1]),
13312        &mut cx,
13313    );
13314
13315    cx.update_editor(|editor, cx| {
13316        editor.change_selections(None, cx, |s| {
13317            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13318        });
13319    });
13320
13321    assert_indent_guides(
13322        0..4,
13323        vec![
13324            indent_guide(buffer_id, 1, 3, 0),
13325            indent_guide(buffer_id, 2, 2, 1),
13326        ],
13327        Some(vec![1]),
13328        &mut cx,
13329    );
13330
13331    cx.update_editor(|editor, cx| {
13332        editor.change_selections(None, cx, |s| {
13333            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13334        });
13335    });
13336
13337    assert_indent_guides(
13338        0..4,
13339        vec![
13340            indent_guide(buffer_id, 1, 3, 0),
13341            indent_guide(buffer_id, 2, 2, 1),
13342        ],
13343        Some(vec![0]),
13344        &mut cx,
13345    );
13346}
13347
13348#[gpui::test]
13349async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13350    let (buffer_id, mut cx) = setup_indent_guides_editor(
13351        &"
13352    fn main() {
13353        let a = 1;
13354
13355        let b = 2;
13356    }"
13357        .unindent(),
13358        cx,
13359    )
13360    .await;
13361
13362    cx.update_editor(|editor, cx| {
13363        editor.change_selections(None, cx, |s| {
13364            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13365        });
13366    });
13367
13368    assert_indent_guides(
13369        0..5,
13370        vec![indent_guide(buffer_id, 1, 3, 0)],
13371        Some(vec![0]),
13372        &mut cx,
13373    );
13374}
13375
13376#[gpui::test]
13377async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13378    let (buffer_id, mut cx) = setup_indent_guides_editor(
13379        &"
13380    def m:
13381        a = 1
13382        pass"
13383            .unindent(),
13384        cx,
13385    )
13386    .await;
13387
13388    cx.update_editor(|editor, cx| {
13389        editor.change_selections(None, cx, |s| {
13390            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13391        });
13392    });
13393
13394    assert_indent_guides(
13395        0..3,
13396        vec![indent_guide(buffer_id, 1, 2, 0)],
13397        Some(vec![0]),
13398        &mut cx,
13399    );
13400}
13401
13402#[gpui::test]
13403fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13404    init_test(cx, |_| {});
13405
13406    let editor = cx.add_window(|cx| {
13407        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13408        build_editor(buffer, cx)
13409    });
13410
13411    let render_args = Arc::new(Mutex::new(None));
13412    let snapshot = editor
13413        .update(cx, |editor, cx| {
13414            let snapshot = editor.buffer().read(cx).snapshot(cx);
13415            let range =
13416                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13417
13418            struct RenderArgs {
13419                row: MultiBufferRow,
13420                folded: bool,
13421                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13422            }
13423
13424            let crease = Crease::inline(
13425                range,
13426                FoldPlaceholder::test(),
13427                {
13428                    let toggle_callback = render_args.clone();
13429                    move |row, folded, callback, _cx| {
13430                        *toggle_callback.lock() = Some(RenderArgs {
13431                            row,
13432                            folded,
13433                            callback,
13434                        });
13435                        div()
13436                    }
13437                },
13438                |_row, _folded, _cx| div(),
13439            );
13440
13441            editor.insert_creases(Some(crease), cx);
13442            let snapshot = editor.snapshot(cx);
13443            let _div =
13444                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13445            snapshot
13446        })
13447        .unwrap();
13448
13449    let render_args = render_args.lock().take().unwrap();
13450    assert_eq!(render_args.row, MultiBufferRow(1));
13451    assert!(!render_args.folded);
13452    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13453
13454    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13455        .unwrap();
13456    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13457    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13458
13459    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13460        .unwrap();
13461    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13462    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13463}
13464
13465#[gpui::test]
13466async fn test_input_text(cx: &mut gpui::TestAppContext) {
13467    init_test(cx, |_| {});
13468    let mut cx = EditorTestContext::new(cx).await;
13469
13470    cx.set_state(
13471        &r#"ˇone
13472        two
13473
13474        three
13475        fourˇ
13476        five
13477
13478        siˇx"#
13479            .unindent(),
13480    );
13481
13482    cx.dispatch_action(HandleInput(String::new()));
13483    cx.assert_editor_state(
13484        &r#"ˇone
13485        two
13486
13487        three
13488        fourˇ
13489        five
13490
13491        siˇx"#
13492            .unindent(),
13493    );
13494
13495    cx.dispatch_action(HandleInput("AAAA".to_string()));
13496    cx.assert_editor_state(
13497        &r#"AAAAˇone
13498        two
13499
13500        three
13501        fourAAAAˇ
13502        five
13503
13504        siAAAAˇx"#
13505            .unindent(),
13506    );
13507}
13508
13509#[gpui::test]
13510async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13511    init_test(cx, |_| {});
13512
13513    let mut cx = EditorTestContext::new(cx).await;
13514    cx.set_state(
13515        r#"let foo = 1;
13516let foo = 2;
13517let foo = 3;
13518let fooˇ = 4;
13519let foo = 5;
13520let foo = 6;
13521let foo = 7;
13522let foo = 8;
13523let foo = 9;
13524let foo = 10;
13525let foo = 11;
13526let foo = 12;
13527let foo = 13;
13528let foo = 14;
13529let foo = 15;"#,
13530    );
13531
13532    cx.update_editor(|e, cx| {
13533        assert_eq!(
13534            e.next_scroll_position,
13535            NextScrollCursorCenterTopBottom::Center,
13536            "Default next scroll direction is center",
13537        );
13538
13539        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13540        assert_eq!(
13541            e.next_scroll_position,
13542            NextScrollCursorCenterTopBottom::Top,
13543            "After center, next scroll direction should be top",
13544        );
13545
13546        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13547        assert_eq!(
13548            e.next_scroll_position,
13549            NextScrollCursorCenterTopBottom::Bottom,
13550            "After top, next scroll direction should be bottom",
13551        );
13552
13553        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13554        assert_eq!(
13555            e.next_scroll_position,
13556            NextScrollCursorCenterTopBottom::Center,
13557            "After bottom, scrolling should start over",
13558        );
13559
13560        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13561        assert_eq!(
13562            e.next_scroll_position,
13563            NextScrollCursorCenterTopBottom::Top,
13564            "Scrolling continues if retriggered fast enough"
13565        );
13566    });
13567
13568    cx.executor()
13569        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13570    cx.executor().run_until_parked();
13571    cx.update_editor(|e, _| {
13572        assert_eq!(
13573            e.next_scroll_position,
13574            NextScrollCursorCenterTopBottom::Center,
13575            "If scrolling is not triggered fast enough, it should reset"
13576        );
13577    });
13578}
13579
13580#[gpui::test]
13581async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13582    init_test(cx, |_| {});
13583    let mut cx = EditorLspTestContext::new_rust(
13584        lsp::ServerCapabilities {
13585            definition_provider: Some(lsp::OneOf::Left(true)),
13586            references_provider: Some(lsp::OneOf::Left(true)),
13587            ..lsp::ServerCapabilities::default()
13588        },
13589        cx,
13590    )
13591    .await;
13592
13593    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13594        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13595            move |params, _| async move {
13596                if empty_go_to_definition {
13597                    Ok(None)
13598                } else {
13599                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13600                        uri: params.text_document_position_params.text_document.uri,
13601                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13602                    })))
13603                }
13604            },
13605        );
13606        let references =
13607            cx.lsp
13608                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13609                    Ok(Some(vec![lsp::Location {
13610                        uri: params.text_document_position.text_document.uri,
13611                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13612                    }]))
13613                });
13614        (go_to_definition, references)
13615    };
13616
13617    cx.set_state(
13618        &r#"fn one() {
13619            let mut a = ˇtwo();
13620        }
13621
13622        fn two() {}"#
13623            .unindent(),
13624    );
13625    set_up_lsp_handlers(false, &mut cx);
13626    let navigated = cx
13627        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13628        .await
13629        .expect("Failed to navigate to definition");
13630    assert_eq!(
13631        navigated,
13632        Navigated::Yes,
13633        "Should have navigated to definition from the GetDefinition response"
13634    );
13635    cx.assert_editor_state(
13636        &r#"fn one() {
13637            let mut a = two();
13638        }
13639
13640        fn «twoˇ»() {}"#
13641            .unindent(),
13642    );
13643
13644    let editors = cx.update_workspace(|workspace, cx| {
13645        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13646    });
13647    cx.update_editor(|_, test_editor_cx| {
13648        assert_eq!(
13649            editors.len(),
13650            1,
13651            "Initially, only one, test, editor should be open in the workspace"
13652        );
13653        assert_eq!(
13654            test_editor_cx.view(),
13655            editors.last().expect("Asserted len is 1")
13656        );
13657    });
13658
13659    set_up_lsp_handlers(true, &mut cx);
13660    let navigated = cx
13661        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13662        .await
13663        .expect("Failed to navigate to lookup references");
13664    assert_eq!(
13665        navigated,
13666        Navigated::Yes,
13667        "Should have navigated to references as a fallback after empty GoToDefinition response"
13668    );
13669    // We should not change the selections in the existing file,
13670    // if opening another milti buffer with the references
13671    cx.assert_editor_state(
13672        &r#"fn one() {
13673            let mut a = two();
13674        }
13675
13676        fn «twoˇ»() {}"#
13677            .unindent(),
13678    );
13679    let editors = cx.update_workspace(|workspace, cx| {
13680        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13681    });
13682    cx.update_editor(|_, test_editor_cx| {
13683        assert_eq!(
13684            editors.len(),
13685            2,
13686            "After falling back to references search, we open a new editor with the results"
13687        );
13688        let references_fallback_text = editors
13689            .into_iter()
13690            .find(|new_editor| new_editor != test_editor_cx.view())
13691            .expect("Should have one non-test editor now")
13692            .read(test_editor_cx)
13693            .text(test_editor_cx);
13694        assert_eq!(
13695            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13696            "Should use the range from the references response and not the GoToDefinition one"
13697        );
13698    });
13699}
13700
13701#[gpui::test]
13702async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13703    init_test(cx, |_| {});
13704
13705    let language = Arc::new(Language::new(
13706        LanguageConfig::default(),
13707        Some(tree_sitter_rust::LANGUAGE.into()),
13708    ));
13709
13710    let text = r#"
13711        #[cfg(test)]
13712        mod tests() {
13713            #[test]
13714            fn runnable_1() {
13715                let a = 1;
13716            }
13717
13718            #[test]
13719            fn runnable_2() {
13720                let a = 1;
13721                let b = 2;
13722            }
13723        }
13724    "#
13725    .unindent();
13726
13727    let fs = FakeFs::new(cx.executor());
13728    fs.insert_file("/file.rs", Default::default()).await;
13729
13730    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13731    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13732    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13733    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13734    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13735
13736    let editor = cx.new_view(|cx| {
13737        Editor::new(
13738            EditorMode::Full,
13739            multi_buffer,
13740            Some(project.clone()),
13741            true,
13742            cx,
13743        )
13744    });
13745
13746    editor.update(cx, |editor, cx| {
13747        editor.tasks.insert(
13748            (buffer.read(cx).remote_id(), 3),
13749            RunnableTasks {
13750                templates: vec![],
13751                offset: MultiBufferOffset(43),
13752                column: 0,
13753                extra_variables: HashMap::default(),
13754                context_range: BufferOffset(43)..BufferOffset(85),
13755            },
13756        );
13757        editor.tasks.insert(
13758            (buffer.read(cx).remote_id(), 8),
13759            RunnableTasks {
13760                templates: vec![],
13761                offset: MultiBufferOffset(86),
13762                column: 0,
13763                extra_variables: HashMap::default(),
13764                context_range: BufferOffset(86)..BufferOffset(191),
13765            },
13766        );
13767
13768        // Test finding task when cursor is inside function body
13769        editor.change_selections(None, cx, |s| {
13770            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13771        });
13772        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13773        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13774
13775        // Test finding task when cursor is on function name
13776        editor.change_selections(None, cx, |s| {
13777            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13778        });
13779        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13780        assert_eq!(row, 8, "Should find task when cursor is on function name");
13781    });
13782}
13783
13784fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13785    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13786    point..point
13787}
13788
13789fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13790    let (text, ranges) = marked_text_ranges(marked_text, true);
13791    assert_eq!(view.text(cx), text);
13792    assert_eq!(
13793        view.selections.ranges(cx),
13794        ranges,
13795        "Assert selections are {}",
13796        marked_text
13797    );
13798}
13799
13800pub fn handle_signature_help_request(
13801    cx: &mut EditorLspTestContext,
13802    mocked_response: lsp::SignatureHelp,
13803) -> impl Future<Output = ()> {
13804    let mut request =
13805        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13806            let mocked_response = mocked_response.clone();
13807            async move { Ok(Some(mocked_response)) }
13808        });
13809
13810    async move {
13811        request.next().await;
13812    }
13813}
13814
13815/// Handle completion request passing a marked string specifying where the completion
13816/// should be triggered from using '|' character, what range should be replaced, and what completions
13817/// should be returned using '<' and '>' to delimit the range
13818pub fn handle_completion_request(
13819    cx: &mut EditorLspTestContext,
13820    marked_string: &str,
13821    completions: Vec<&'static str>,
13822    counter: Arc<AtomicUsize>,
13823) -> impl Future<Output = ()> {
13824    let complete_from_marker: TextRangeMarker = '|'.into();
13825    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13826    let (_, mut marked_ranges) = marked_text_ranges_by(
13827        marked_string,
13828        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13829    );
13830
13831    let complete_from_position =
13832        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13833    let replace_range =
13834        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13835
13836    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13837        let completions = completions.clone();
13838        counter.fetch_add(1, atomic::Ordering::Release);
13839        async move {
13840            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13841            assert_eq!(
13842                params.text_document_position.position,
13843                complete_from_position
13844            );
13845            Ok(Some(lsp::CompletionResponse::Array(
13846                completions
13847                    .iter()
13848                    .map(|completion_text| lsp::CompletionItem {
13849                        label: completion_text.to_string(),
13850                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13851                            range: replace_range,
13852                            new_text: completion_text.to_string(),
13853                        })),
13854                        ..Default::default()
13855                    })
13856                    .collect(),
13857            )))
13858        }
13859    });
13860
13861    async move {
13862        request.next().await;
13863    }
13864}
13865
13866fn handle_resolve_completion_request(
13867    cx: &mut EditorLspTestContext,
13868    edits: Option<Vec<(&'static str, &'static str)>>,
13869) -> impl Future<Output = ()> {
13870    let edits = edits.map(|edits| {
13871        edits
13872            .iter()
13873            .map(|(marked_string, new_text)| {
13874                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13875                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13876                lsp::TextEdit::new(replace_range, new_text.to_string())
13877            })
13878            .collect::<Vec<_>>()
13879    });
13880
13881    let mut request =
13882        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13883            let edits = edits.clone();
13884            async move {
13885                Ok(lsp::CompletionItem {
13886                    additional_text_edits: edits,
13887                    ..Default::default()
13888                })
13889            }
13890        });
13891
13892    async move {
13893        request.next().await;
13894    }
13895}
13896
13897pub(crate) fn update_test_language_settings(
13898    cx: &mut TestAppContext,
13899    f: impl Fn(&mut AllLanguageSettingsContent),
13900) {
13901    cx.update(|cx| {
13902        SettingsStore::update_global(cx, |store, cx| {
13903            store.update_user_settings::<AllLanguageSettings>(cx, f);
13904        });
13905    });
13906}
13907
13908pub(crate) fn update_test_project_settings(
13909    cx: &mut TestAppContext,
13910    f: impl Fn(&mut ProjectSettings),
13911) {
13912    cx.update(|cx| {
13913        SettingsStore::update_global(cx, |store, cx| {
13914            store.update_user_settings::<ProjectSettings>(cx, f);
13915        });
13916    });
13917}
13918
13919pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13920    cx.update(|cx| {
13921        assets::Assets.load_test_fonts(cx);
13922        let store = SettingsStore::test(cx);
13923        cx.set_global(store);
13924        theme::init(theme::LoadThemes::JustBase, cx);
13925        release_channel::init(SemanticVersion::default(), cx);
13926        client::init_settings(cx);
13927        language::init(cx);
13928        Project::init_settings(cx);
13929        workspace::init_settings(cx);
13930        crate::init(cx);
13931    });
13932
13933    update_test_language_settings(cx, f);
13934}
13935
13936pub(crate) fn rust_lang() -> Arc<Language> {
13937    Arc::new(Language::new(
13938        LanguageConfig {
13939            name: "Rust".into(),
13940            matcher: LanguageMatcher {
13941                path_suffixes: vec!["rs".to_string()],
13942                ..Default::default()
13943            },
13944            ..Default::default()
13945        },
13946        Some(tree_sitter_rust::LANGUAGE.into()),
13947    ))
13948}
13949
13950#[track_caller]
13951fn assert_hunk_revert(
13952    not_reverted_text_with_selections: &str,
13953    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13954    expected_reverted_text_with_selections: &str,
13955    base_text: &str,
13956    cx: &mut EditorLspTestContext,
13957) {
13958    cx.set_state(not_reverted_text_with_selections);
13959    cx.update_editor(|editor, cx| {
13960        editor
13961            .buffer()
13962            .read(cx)
13963            .as_singleton()
13964            .unwrap()
13965            .update(cx, |buffer, cx| {
13966                buffer.set_diff_base(Some(base_text.into()), cx);
13967            });
13968    });
13969    cx.executor().run_until_parked();
13970
13971    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13972        let snapshot = editor.buffer().read(cx).snapshot(cx);
13973        let reverted_hunk_statuses = snapshot
13974            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13975            .map(|hunk| hunk_status(&hunk))
13976            .collect::<Vec<_>>();
13977
13978        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13979        reverted_hunk_statuses
13980    });
13981    cx.executor().run_until_parked();
13982    cx.assert_editor_state(expected_reverted_text_with_selections);
13983    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13984}