editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use project::FakeFs;
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic;
   35use std::sync::atomic::AtomicUsize;
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use unindent::Unindent;
   38use util::{
   39    assert_set_eq,
   40    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   41};
   42use workspace::{
   43    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   44    NavigationEntry, ViewId,
   45};
   46
   47#[gpui::test]
   48fn test_edit_events(cx: &mut TestAppContext) {
   49    init_test(cx, |_| {});
   50
   51    let buffer = cx.new_model(|cx| {
   52        let mut buffer = language::Buffer::local("123456", cx);
   53        buffer.set_group_interval(Duration::from_secs(1));
   54        buffer
   55    });
   56
   57    let events = Rc::new(RefCell::new(Vec::new()));
   58    let editor1 = cx.add_window({
   59        let events = events.clone();
   60        |cx| {
   61            let view = cx.view().clone();
   62            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   63                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   64                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   65                _ => {}
   66            })
   67            .detach();
   68            Editor::for_buffer(buffer.clone(), None, cx)
   69        }
   70    });
   71
   72    let editor2 = cx.add_window({
   73        let events = events.clone();
   74        |cx| {
   75            cx.subscribe(
   76                &cx.view().clone(),
   77                move |_, _, event: &EditorEvent, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor2", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, cx)
   87        }
   88    });
   89
   90    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   91
   92    // Mutating editor 1 will emit an `Edited` event only for that editor.
   93    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   94    assert_eq!(
   95        mem::take(&mut *events.borrow_mut()),
   96        [
   97            ("editor1", "edited"),
   98            ("editor1", "buffer edited"),
   99            ("editor2", "buffer edited"),
  100        ]
  101    );
  102
  103    // Mutating editor 2 will emit an `Edited` event only for that editor.
  104    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor2", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  137    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor2", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // No event is emitted when the mutation is a no-op.
  159    _ = editor2.update(cx, |editor, cx| {
  160        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  161
  162        editor.backspace(&Backspace, cx);
  163    });
  164    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  165}
  166
  167#[gpui::test]
  168fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  169    init_test(cx, |_| {});
  170
  171    let mut now = Instant::now();
  172    let group_interval = Duration::from_millis(1);
  173    let buffer = cx.new_model(|cx| {
  174        let mut buf = language::Buffer::local("123456", cx);
  175        buf.set_group_interval(group_interval);
  176        buf
  177    });
  178    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  179    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  180
  181    _ = editor.update(cx, |editor, cx| {
  182        editor.start_transaction_at(now, cx);
  183        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  184
  185        editor.insert("cd", cx);
  186        editor.end_transaction_at(now, cx);
  187        assert_eq!(editor.text(cx), "12cd56");
  188        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  189
  190        editor.start_transaction_at(now, cx);
  191        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  192        editor.insert("e", cx);
  193        editor.end_transaction_at(now, cx);
  194        assert_eq!(editor.text(cx), "12cde6");
  195        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  196
  197        now += group_interval + Duration::from_millis(1);
  198        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  199
  200        // Simulate an edit in another editor
  201        buffer.update(cx, |buffer, cx| {
  202            buffer.start_transaction_at(now, cx);
  203            buffer.edit([(0..1, "a")], None, cx);
  204            buffer.edit([(1..1, "b")], None, cx);
  205            buffer.end_transaction_at(now, cx);
  206        });
  207
  208        assert_eq!(editor.text(cx), "ab2cde6");
  209        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  210
  211        // Last transaction happened past the group interval in a different editor.
  212        // Undo it individually and don't restore selections.
  213        editor.undo(&Undo, cx);
  214        assert_eq!(editor.text(cx), "12cde6");
  215        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  216
  217        // First two transactions happened within the group interval in this editor.
  218        // Undo them together and restore selections.
  219        editor.undo(&Undo, cx);
  220        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  221        assert_eq!(editor.text(cx), "123456");
  222        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  223
  224        // Redo the first two transactions together.
  225        editor.redo(&Redo, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  228
  229        // Redo the last transaction on its own.
  230        editor.redo(&Redo, cx);
  231        assert_eq!(editor.text(cx), "ab2cde6");
  232        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  233
  234        // Test empty transactions.
  235        editor.start_transaction_at(now, cx);
  236        editor.end_transaction_at(now, cx);
  237        editor.undo(&Undo, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239    });
  240}
  241
  242#[gpui::test]
  243fn test_ime_composition(cx: &mut TestAppContext) {
  244    init_test(cx, |_| {});
  245
  246    let buffer = cx.new_model(|cx| {
  247        let mut buffer = language::Buffer::local("abcde", cx);
  248        // Ensure automatic grouping doesn't occur.
  249        buffer.set_group_interval(Duration::ZERO);
  250        buffer
  251    });
  252
  253    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  254    cx.add_window(|cx| {
  255        let mut editor = build_editor(buffer.clone(), cx);
  256
  257        // Start a new IME composition.
  258        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  259        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  260        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  261        assert_eq!(editor.text(cx), "äbcde");
  262        assert_eq!(
  263            editor.marked_text_ranges(cx),
  264            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  265        );
  266
  267        // Finalize IME composition.
  268        editor.replace_text_in_range(None, "ā", cx);
  269        assert_eq!(editor.text(cx), "ābcde");
  270        assert_eq!(editor.marked_text_ranges(cx), None);
  271
  272        // IME composition edits are grouped and are undone/redone at once.
  273        editor.undo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "abcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276        editor.redo(&Default::default(), cx);
  277        assert_eq!(editor.text(cx), "ābcde");
  278        assert_eq!(editor.marked_text_ranges(cx), None);
  279
  280        // Start a new IME composition.
  281        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  282        assert_eq!(
  283            editor.marked_text_ranges(cx),
  284            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  285        );
  286
  287        // Undoing during an IME composition cancels it.
  288        editor.undo(&Default::default(), cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  293        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  294        assert_eq!(editor.text(cx), "ābcdè");
  295        assert_eq!(
  296            editor.marked_text_ranges(cx),
  297            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  298        );
  299
  300        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  301        editor.replace_text_in_range(Some(4..999), "ę", cx);
  302        assert_eq!(editor.text(cx), "ābcdę");
  303        assert_eq!(editor.marked_text_ranges(cx), None);
  304
  305        // Start a new IME composition with multiple cursors.
  306        editor.change_selections(None, cx, |s| {
  307            s.select_ranges([
  308                OffsetUtf16(1)..OffsetUtf16(1),
  309                OffsetUtf16(3)..OffsetUtf16(3),
  310                OffsetUtf16(5)..OffsetUtf16(5),
  311            ])
  312        });
  313        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  314        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  315        assert_eq!(
  316            editor.marked_text_ranges(cx),
  317            Some(vec![
  318                OffsetUtf16(0)..OffsetUtf16(3),
  319                OffsetUtf16(4)..OffsetUtf16(7),
  320                OffsetUtf16(8)..OffsetUtf16(11)
  321            ])
  322        );
  323
  324        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  325        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  326        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(1)..OffsetUtf16(2),
  331                OffsetUtf16(5)..OffsetUtf16(6),
  332                OffsetUtf16(9)..OffsetUtf16(10)
  333            ])
  334        );
  335
  336        // Finalize IME composition with multiple cursors.
  337        editor.replace_text_in_range(Some(9..10), "2", cx);
  338        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  339        assert_eq!(editor.marked_text_ranges(cx), None);
  340
  341        editor
  342    });
  343}
  344
  345#[gpui::test]
  346fn test_selection_with_mouse(cx: &mut TestAppContext) {
  347    init_test(cx, |_| {});
  348
  349    let editor = cx.add_window(|cx| {
  350        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  351        build_editor(buffer, cx)
  352    });
  353
  354    _ = editor.update(cx, |view, cx| {
  355        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  356    });
  357    assert_eq!(
  358        editor
  359            .update(cx, |view, cx| view.selections.display_ranges(cx))
  360            .unwrap(),
  361        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  362    );
  363
  364    _ = editor.update(cx, |view, cx| {
  365        view.update_selection(
  366            DisplayPoint::new(DisplayRow(3), 3),
  367            0,
  368            gpui::Point::<f32>::default(),
  369            cx,
  370        );
  371    });
  372
  373    assert_eq!(
  374        editor
  375            .update(cx, |view, cx| view.selections.display_ranges(cx))
  376            .unwrap(),
  377        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  378    );
  379
  380    _ = editor.update(cx, |view, cx| {
  381        view.update_selection(
  382            DisplayPoint::new(DisplayRow(1), 1),
  383            0,
  384            gpui::Point::<f32>::default(),
  385            cx,
  386        );
  387    });
  388
  389    assert_eq!(
  390        editor
  391            .update(cx, |view, cx| view.selections.display_ranges(cx))
  392            .unwrap(),
  393        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  394    );
  395
  396    _ = editor.update(cx, |view, cx| {
  397        view.end_selection(cx);
  398        view.update_selection(
  399            DisplayPoint::new(DisplayRow(3), 3),
  400            0,
  401            gpui::Point::<f32>::default(),
  402            cx,
  403        );
  404    });
  405
  406    assert_eq!(
  407        editor
  408            .update(cx, |view, cx| view.selections.display_ranges(cx))
  409            .unwrap(),
  410        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  411    );
  412
  413    _ = editor.update(cx, |view, cx| {
  414        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  415        view.update_selection(
  416            DisplayPoint::new(DisplayRow(0), 0),
  417            0,
  418            gpui::Point::<f32>::default(),
  419            cx,
  420        );
  421    });
  422
  423    assert_eq!(
  424        editor
  425            .update(cx, |view, cx| view.selections.display_ranges(cx))
  426            .unwrap(),
  427        [
  428            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  429            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  430        ]
  431    );
  432
  433    _ = editor.update(cx, |view, cx| {
  434        view.end_selection(cx);
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |view, cx| view.selections.display_ranges(cx))
  440            .unwrap(),
  441        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  442    );
  443}
  444
  445#[gpui::test]
  446fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  447    init_test(cx, |_| {});
  448
  449    let editor = cx.add_window(|cx| {
  450        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  451        build_editor(buffer, cx)
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.end_selection(cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.end_selection(cx);
  468    });
  469
  470    assert_eq!(
  471        editor
  472            .update(cx, |view, cx| view.selections.display_ranges(cx))
  473            .unwrap(),
  474        [
  475            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  476            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  477        ]
  478    );
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  482    });
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.end_selection(cx);
  486    });
  487
  488    assert_eq!(
  489        editor
  490            .update(cx, |view, cx| view.selections.display_ranges(cx))
  491            .unwrap(),
  492        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  493    );
  494}
  495
  496#[gpui::test]
  497fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  498    init_test(cx, |_| {});
  499
  500    let view = cx.add_window(|cx| {
  501        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  502        build_editor(buffer, cx)
  503    });
  504
  505    _ = view.update(cx, |view, cx| {
  506        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  507        assert_eq!(
  508            view.selections.display_ranges(cx),
  509            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  510        );
  511    });
  512
  513    _ = view.update(cx, |view, cx| {
  514        view.update_selection(
  515            DisplayPoint::new(DisplayRow(3), 3),
  516            0,
  517            gpui::Point::<f32>::default(),
  518            cx,
  519        );
  520        assert_eq!(
  521            view.selections.display_ranges(cx),
  522            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  523        );
  524    });
  525
  526    _ = view.update(cx, |view, cx| {
  527        view.cancel(&Cancel, cx);
  528        view.update_selection(
  529            DisplayPoint::new(DisplayRow(1), 1),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            cx,
  533        );
  534        assert_eq!(
  535            view.selections.display_ranges(cx),
  536            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  537        );
  538    });
  539}
  540
  541#[gpui::test]
  542fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  543    init_test(cx, |_| {});
  544
  545    let view = cx.add_window(|cx| {
  546        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  547        build_editor(buffer, cx)
  548    });
  549
  550    _ = view.update(cx, |view, cx| {
  551        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  552        assert_eq!(
  553            view.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  555        );
  556
  557        view.move_down(&Default::default(), cx);
  558        assert_eq!(
  559            view.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  561        );
  562
  563        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  564        assert_eq!(
  565            view.selections.display_ranges(cx),
  566            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  567        );
  568
  569        view.move_up(&Default::default(), cx);
  570        assert_eq!(
  571            view.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  573        );
  574    });
  575}
  576
  577#[gpui::test]
  578fn test_clone(cx: &mut TestAppContext) {
  579    init_test(cx, |_| {});
  580
  581    let (text, selection_ranges) = marked_text_ranges(
  582        indoc! {"
  583            one
  584            two
  585            threeˇ
  586            four
  587            fiveˇ
  588        "},
  589        true,
  590    );
  591
  592    let editor = cx.add_window(|cx| {
  593        let buffer = MultiBuffer::build_simple(&text, cx);
  594        build_editor(buffer, cx)
  595    });
  596
  597    _ = editor.update(cx, |editor, cx| {
  598        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  599        editor.fold_creases(
  600            vec![
  601                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  602                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  603            ],
  604            true,
  605            cx,
  606        );
  607    });
  608
  609    let cloned_editor = editor
  610        .update(cx, |editor, cx| {
  611            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  612        })
  613        .unwrap()
  614        .unwrap();
  615
  616    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  617    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  618
  619    assert_eq!(
  620        cloned_editor
  621            .update(cx, |e, cx| e.display_text(cx))
  622            .unwrap(),
  623        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  624    );
  625    assert_eq!(
  626        cloned_snapshot
  627            .folds_in_range(0..text.len())
  628            .collect::<Vec<_>>(),
  629        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  630    );
  631    assert_set_eq!(
  632        cloned_editor
  633            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  634            .unwrap(),
  635        editor
  636            .update(cx, |editor, cx| editor.selections.ranges(cx))
  637            .unwrap()
  638    );
  639    assert_set_eq!(
  640        cloned_editor
  641            .update(cx, |e, cx| e.selections.display_ranges(cx))
  642            .unwrap(),
  643        editor
  644            .update(cx, |e, cx| e.selections.display_ranges(cx))
  645            .unwrap()
  646    );
  647}
  648
  649#[gpui::test]
  650async fn test_navigation_history(cx: &mut TestAppContext) {
  651    init_test(cx, |_| {});
  652
  653    use workspace::item::Item;
  654
  655    let fs = FakeFs::new(cx.executor());
  656    let project = Project::test(fs, [], cx).await;
  657    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  658    let pane = workspace
  659        .update(cx, |workspace, _| workspace.active_pane().clone())
  660        .unwrap();
  661
  662    _ = workspace.update(cx, |_v, cx| {
  663        cx.new_view(|cx| {
  664            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  665            let mut editor = build_editor(buffer.clone(), cx);
  666            let handle = cx.view();
  667            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  668
  669            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  670                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  671            }
  672
  673            // Move the cursor a small distance.
  674            // Nothing is added to the navigation history.
  675            editor.change_selections(None, cx, |s| {
  676                s.select_display_ranges([
  677                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  678                ])
  679            });
  680            editor.change_selections(None, cx, |s| {
  681                s.select_display_ranges([
  682                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  683                ])
  684            });
  685            assert!(pop_history(&mut editor, cx).is_none());
  686
  687            // Move the cursor a large distance.
  688            // The history can jump back to the previous position.
  689            editor.change_selections(None, cx, |s| {
  690                s.select_display_ranges([
  691                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  692                ])
  693            });
  694            let nav_entry = pop_history(&mut editor, cx).unwrap();
  695            editor.navigate(nav_entry.data.unwrap(), cx);
  696            assert_eq!(nav_entry.item.id(), cx.entity_id());
  697            assert_eq!(
  698                editor.selections.display_ranges(cx),
  699                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  700            );
  701            assert!(pop_history(&mut editor, cx).is_none());
  702
  703            // Move the cursor a small distance via the mouse.
  704            // Nothing is added to the navigation history.
  705            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  706            editor.end_selection(cx);
  707            assert_eq!(
  708                editor.selections.display_ranges(cx),
  709                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  710            );
  711            assert!(pop_history(&mut editor, cx).is_none());
  712
  713            // Move the cursor a large distance via the mouse.
  714            // The history can jump back to the previous position.
  715            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  716            editor.end_selection(cx);
  717            assert_eq!(
  718                editor.selections.display_ranges(cx),
  719                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  720            );
  721            let nav_entry = pop_history(&mut editor, cx).unwrap();
  722            editor.navigate(nav_entry.data.unwrap(), cx);
  723            assert_eq!(nav_entry.item.id(), cx.entity_id());
  724            assert_eq!(
  725                editor.selections.display_ranges(cx),
  726                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  727            );
  728            assert!(pop_history(&mut editor, cx).is_none());
  729
  730            // Set scroll position to check later
  731            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  732            let original_scroll_position = editor.scroll_manager.anchor();
  733
  734            // Jump to the end of the document and adjust scroll
  735            editor.move_to_end(&MoveToEnd, cx);
  736            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  737            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            let nav_entry = pop_history(&mut editor, cx).unwrap();
  740            editor.navigate(nav_entry.data.unwrap(), cx);
  741            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  742
  743            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  744            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  745            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  746            let invalid_point = Point::new(9999, 0);
  747            editor.navigate(
  748                Box::new(NavigationData {
  749                    cursor_anchor: invalid_anchor,
  750                    cursor_position: invalid_point,
  751                    scroll_anchor: ScrollAnchor {
  752                        anchor: invalid_anchor,
  753                        offset: Default::default(),
  754                    },
  755                    scroll_top_row: invalid_point.row,
  756                }),
  757                cx,
  758            );
  759            assert_eq!(
  760                editor.selections.display_ranges(cx),
  761                &[editor.max_point(cx)..editor.max_point(cx)]
  762            );
  763            assert_eq!(
  764                editor.scroll_position(cx),
  765                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  766            );
  767
  768            editor
  769        })
  770    });
  771}
  772
  773#[gpui::test]
  774fn test_cancel(cx: &mut TestAppContext) {
  775    init_test(cx, |_| {});
  776
  777    let view = cx.add_window(|cx| {
  778        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  779        build_editor(buffer, cx)
  780    });
  781
  782    _ = view.update(cx, |view, cx| {
  783        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  784        view.update_selection(
  785            DisplayPoint::new(DisplayRow(1), 1),
  786            0,
  787            gpui::Point::<f32>::default(),
  788            cx,
  789        );
  790        view.end_selection(cx);
  791
  792        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  793        view.update_selection(
  794            DisplayPoint::new(DisplayRow(0), 3),
  795            0,
  796            gpui::Point::<f32>::default(),
  797            cx,
  798        );
  799        view.end_selection(cx);
  800        assert_eq!(
  801            view.selections.display_ranges(cx),
  802            [
  803                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  804                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  805            ]
  806        );
  807    });
  808
  809    _ = view.update(cx, |view, cx| {
  810        view.cancel(&Cancel, cx);
  811        assert_eq!(
  812            view.selections.display_ranges(cx),
  813            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  814        );
  815    });
  816
  817    _ = view.update(cx, |view, cx| {
  818        view.cancel(&Cancel, cx);
  819        assert_eq!(
  820            view.selections.display_ranges(cx),
  821            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  822        );
  823    });
  824}
  825
  826#[gpui::test]
  827fn test_fold_action(cx: &mut TestAppContext) {
  828    init_test(cx, |_| {});
  829
  830    let view = cx.add_window(|cx| {
  831        let buffer = MultiBuffer::build_simple(
  832            &"
  833                impl Foo {
  834                    // Hello!
  835
  836                    fn a() {
  837                        1
  838                    }
  839
  840                    fn b() {
  841                        2
  842                    }
  843
  844                    fn c() {
  845                        3
  846                    }
  847                }
  848            "
  849            .unindent(),
  850            cx,
  851        );
  852        build_editor(buffer.clone(), cx)
  853    });
  854
  855    _ = view.update(cx, |view, cx| {
  856        view.change_selections(None, cx, |s| {
  857            s.select_display_ranges([
  858                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  859            ]);
  860        });
  861        view.fold(&Fold, cx);
  862        assert_eq!(
  863            view.display_text(cx),
  864            "
  865                impl Foo {
  866                    // Hello!
  867
  868                    fn a() {
  869                        1
  870                    }
  871
  872                    fn b() {⋯
  873                    }
  874
  875                    fn c() {⋯
  876                    }
  877                }
  878            "
  879            .unindent(),
  880        );
  881
  882        view.fold(&Fold, cx);
  883        assert_eq!(
  884            view.display_text(cx),
  885            "
  886                impl Foo {⋯
  887                }
  888            "
  889            .unindent(),
  890        );
  891
  892        view.unfold_lines(&UnfoldLines, cx);
  893        assert_eq!(
  894            view.display_text(cx),
  895            "
  896                impl Foo {
  897                    // Hello!
  898
  899                    fn a() {
  900                        1
  901                    }
  902
  903                    fn b() {⋯
  904                    }
  905
  906                    fn c() {⋯
  907                    }
  908                }
  909            "
  910            .unindent(),
  911        );
  912
  913        view.unfold_lines(&UnfoldLines, cx);
  914        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  915    });
  916}
  917
  918#[gpui::test]
  919fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  920    init_test(cx, |_| {});
  921
  922    let view = cx.add_window(|cx| {
  923        let buffer = MultiBuffer::build_simple(
  924            &"
  925                class Foo:
  926                    # Hello!
  927
  928                    def a():
  929                        print(1)
  930
  931                    def b():
  932                        print(2)
  933
  934                    def c():
  935                        print(3)
  936            "
  937            .unindent(),
  938            cx,
  939        );
  940        build_editor(buffer.clone(), cx)
  941    });
  942
  943    _ = view.update(cx, |view, cx| {
  944        view.change_selections(None, cx, |s| {
  945            s.select_display_ranges([
  946                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  947            ]);
  948        });
  949        view.fold(&Fold, cx);
  950        assert_eq!(
  951            view.display_text(cx),
  952            "
  953                class Foo:
  954                    # Hello!
  955
  956                    def a():
  957                        print(1)
  958
  959                    def b():⋯
  960
  961                    def c():⋯
  962            "
  963            .unindent(),
  964        );
  965
  966        view.fold(&Fold, cx);
  967        assert_eq!(
  968            view.display_text(cx),
  969            "
  970                class Foo:⋯
  971            "
  972            .unindent(),
  973        );
  974
  975        view.unfold_lines(&UnfoldLines, cx);
  976        assert_eq!(
  977            view.display_text(cx),
  978            "
  979                class Foo:
  980                    # Hello!
  981
  982                    def a():
  983                        print(1)
  984
  985                    def b():⋯
  986
  987                    def c():⋯
  988            "
  989            .unindent(),
  990        );
  991
  992        view.unfold_lines(&UnfoldLines, cx);
  993        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  994    });
  995}
  996
  997#[gpui::test]
  998fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  999    init_test(cx, |_| {});
 1000
 1001    let view = cx.add_window(|cx| {
 1002        let buffer = MultiBuffer::build_simple(
 1003            &"
 1004                class Foo:
 1005                    # Hello!
 1006
 1007                    def a():
 1008                        print(1)
 1009
 1010                    def b():
 1011                        print(2)
 1012
 1013
 1014                    def c():
 1015                        print(3)
 1016
 1017
 1018            "
 1019            .unindent(),
 1020            cx,
 1021        );
 1022        build_editor(buffer.clone(), cx)
 1023    });
 1024
 1025    _ = view.update(cx, |view, cx| {
 1026        view.change_selections(None, cx, |s| {
 1027            s.select_display_ranges([
 1028                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1029            ]);
 1030        });
 1031        view.fold(&Fold, cx);
 1032        assert_eq!(
 1033            view.display_text(cx),
 1034            "
 1035                class Foo:
 1036                    # Hello!
 1037
 1038                    def a():
 1039                        print(1)
 1040
 1041                    def b():⋯
 1042
 1043
 1044                    def c():⋯
 1045
 1046
 1047            "
 1048            .unindent(),
 1049        );
 1050
 1051        view.fold(&Fold, cx);
 1052        assert_eq!(
 1053            view.display_text(cx),
 1054            "
 1055                class Foo:⋯
 1056
 1057
 1058            "
 1059            .unindent(),
 1060        );
 1061
 1062        view.unfold_lines(&UnfoldLines, cx);
 1063        assert_eq!(
 1064            view.display_text(cx),
 1065            "
 1066                class Foo:
 1067                    # Hello!
 1068
 1069                    def a():
 1070                        print(1)
 1071
 1072                    def b():⋯
 1073
 1074
 1075                    def c():⋯
 1076
 1077
 1078            "
 1079            .unindent(),
 1080        );
 1081
 1082        view.unfold_lines(&UnfoldLines, cx);
 1083        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1084    });
 1085}
 1086
 1087#[gpui::test]
 1088fn test_fold_at_level(cx: &mut TestAppContext) {
 1089    init_test(cx, |_| {});
 1090
 1091    let view = cx.add_window(|cx| {
 1092        let buffer = MultiBuffer::build_simple(
 1093            &"
 1094                class Foo:
 1095                    # Hello!
 1096
 1097                    def a():
 1098                        print(1)
 1099
 1100                    def b():
 1101                        print(2)
 1102
 1103
 1104                class Bar:
 1105                    # World!
 1106
 1107                    def a():
 1108                        print(1)
 1109
 1110                    def b():
 1111                        print(2)
 1112
 1113
 1114            "
 1115            .unindent(),
 1116            cx,
 1117        );
 1118        build_editor(buffer.clone(), cx)
 1119    });
 1120
 1121    _ = view.update(cx, |view, cx| {
 1122        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1123        assert_eq!(
 1124            view.display_text(cx),
 1125            "
 1126                class Foo:
 1127                    # Hello!
 1128
 1129                    def a():⋯
 1130
 1131                    def b():⋯
 1132
 1133
 1134                class Bar:
 1135                    # World!
 1136
 1137                    def a():⋯
 1138
 1139                    def b():⋯
 1140
 1141
 1142            "
 1143            .unindent(),
 1144        );
 1145
 1146        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1147        assert_eq!(
 1148            view.display_text(cx),
 1149            "
 1150                class Foo:⋯
 1151
 1152
 1153                class Bar:⋯
 1154
 1155
 1156            "
 1157            .unindent(),
 1158        );
 1159
 1160        view.unfold_all(&UnfoldAll, cx);
 1161        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1162        assert_eq!(
 1163            view.display_text(cx),
 1164            "
 1165                class Foo:
 1166                    # Hello!
 1167
 1168                    def a():
 1169                        print(1)
 1170
 1171                    def b():
 1172                        print(2)
 1173
 1174
 1175                class Bar:
 1176                    # World!
 1177
 1178                    def a():
 1179                        print(1)
 1180
 1181                    def b():
 1182                        print(2)
 1183
 1184
 1185            "
 1186            .unindent(),
 1187        );
 1188
 1189        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1190    });
 1191}
 1192
 1193#[gpui::test]
 1194fn test_move_cursor(cx: &mut TestAppContext) {
 1195    init_test(cx, |_| {});
 1196
 1197    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1198    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1199
 1200    buffer.update(cx, |buffer, cx| {
 1201        buffer.edit(
 1202            vec![
 1203                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1204                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1205            ],
 1206            None,
 1207            cx,
 1208        );
 1209    });
 1210    _ = view.update(cx, |view, cx| {
 1211        assert_eq!(
 1212            view.selections.display_ranges(cx),
 1213            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1214        );
 1215
 1216        view.move_down(&MoveDown, cx);
 1217        assert_eq!(
 1218            view.selections.display_ranges(cx),
 1219            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1220        );
 1221
 1222        view.move_right(&MoveRight, cx);
 1223        assert_eq!(
 1224            view.selections.display_ranges(cx),
 1225            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1226        );
 1227
 1228        view.move_left(&MoveLeft, cx);
 1229        assert_eq!(
 1230            view.selections.display_ranges(cx),
 1231            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1232        );
 1233
 1234        view.move_up(&MoveUp, cx);
 1235        assert_eq!(
 1236            view.selections.display_ranges(cx),
 1237            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1238        );
 1239
 1240        view.move_to_end(&MoveToEnd, cx);
 1241        assert_eq!(
 1242            view.selections.display_ranges(cx),
 1243            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1244        );
 1245
 1246        view.move_to_beginning(&MoveToBeginning, cx);
 1247        assert_eq!(
 1248            view.selections.display_ranges(cx),
 1249            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1250        );
 1251
 1252        view.change_selections(None, cx, |s| {
 1253            s.select_display_ranges([
 1254                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1255            ]);
 1256        });
 1257        view.select_to_beginning(&SelectToBeginning, cx);
 1258        assert_eq!(
 1259            view.selections.display_ranges(cx),
 1260            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1261        );
 1262
 1263        view.select_to_end(&SelectToEnd, cx);
 1264        assert_eq!(
 1265            view.selections.display_ranges(cx),
 1266            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1267        );
 1268    });
 1269}
 1270
 1271// TODO: Re-enable this test
 1272#[cfg(target_os = "macos")]
 1273#[gpui::test]
 1274fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1275    init_test(cx, |_| {});
 1276
 1277    let view = cx.add_window(|cx| {
 1278        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1279        build_editor(buffer.clone(), cx)
 1280    });
 1281
 1282    assert_eq!('ⓐ'.len_utf8(), 3);
 1283    assert_eq!('α'.len_utf8(), 2);
 1284
 1285    _ = view.update(cx, |view, cx| {
 1286        view.fold_creases(
 1287            vec![
 1288                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1289                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1290                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1291            ],
 1292            true,
 1293            cx,
 1294        );
 1295        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1296
 1297        view.move_right(&MoveRight, cx);
 1298        assert_eq!(
 1299            view.selections.display_ranges(cx),
 1300            &[empty_range(0, "".len())]
 1301        );
 1302        view.move_right(&MoveRight, cx);
 1303        assert_eq!(
 1304            view.selections.display_ranges(cx),
 1305            &[empty_range(0, "ⓐⓑ".len())]
 1306        );
 1307        view.move_right(&MoveRight, cx);
 1308        assert_eq!(
 1309            view.selections.display_ranges(cx),
 1310            &[empty_range(0, "ⓐⓑ⋯".len())]
 1311        );
 1312
 1313        view.move_down(&MoveDown, cx);
 1314        assert_eq!(
 1315            view.selections.display_ranges(cx),
 1316            &[empty_range(1, "ab⋯e".len())]
 1317        );
 1318        view.move_left(&MoveLeft, cx);
 1319        assert_eq!(
 1320            view.selections.display_ranges(cx),
 1321            &[empty_range(1, "ab⋯".len())]
 1322        );
 1323        view.move_left(&MoveLeft, cx);
 1324        assert_eq!(
 1325            view.selections.display_ranges(cx),
 1326            &[empty_range(1, "ab".len())]
 1327        );
 1328        view.move_left(&MoveLeft, cx);
 1329        assert_eq!(
 1330            view.selections.display_ranges(cx),
 1331            &[empty_range(1, "a".len())]
 1332        );
 1333
 1334        view.move_down(&MoveDown, cx);
 1335        assert_eq!(
 1336            view.selections.display_ranges(cx),
 1337            &[empty_range(2, "α".len())]
 1338        );
 1339        view.move_right(&MoveRight, cx);
 1340        assert_eq!(
 1341            view.selections.display_ranges(cx),
 1342            &[empty_range(2, "αβ".len())]
 1343        );
 1344        view.move_right(&MoveRight, cx);
 1345        assert_eq!(
 1346            view.selections.display_ranges(cx),
 1347            &[empty_range(2, "αβ⋯".len())]
 1348        );
 1349        view.move_right(&MoveRight, cx);
 1350        assert_eq!(
 1351            view.selections.display_ranges(cx),
 1352            &[empty_range(2, "αβ⋯ε".len())]
 1353        );
 1354
 1355        view.move_up(&MoveUp, cx);
 1356        assert_eq!(
 1357            view.selections.display_ranges(cx),
 1358            &[empty_range(1, "ab⋯e".len())]
 1359        );
 1360        view.move_down(&MoveDown, cx);
 1361        assert_eq!(
 1362            view.selections.display_ranges(cx),
 1363            &[empty_range(2, "αβ⋯ε".len())]
 1364        );
 1365        view.move_up(&MoveUp, cx);
 1366        assert_eq!(
 1367            view.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯e".len())]
 1369        );
 1370
 1371        view.move_up(&MoveUp, cx);
 1372        assert_eq!(
 1373            view.selections.display_ranges(cx),
 1374            &[empty_range(0, "ⓐⓑ".len())]
 1375        );
 1376        view.move_left(&MoveLeft, cx);
 1377        assert_eq!(
 1378            view.selections.display_ranges(cx),
 1379            &[empty_range(0, "".len())]
 1380        );
 1381        view.move_left(&MoveLeft, cx);
 1382        assert_eq!(
 1383            view.selections.display_ranges(cx),
 1384            &[empty_range(0, "".len())]
 1385        );
 1386    });
 1387}
 1388
 1389#[gpui::test]
 1390fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1391    init_test(cx, |_| {});
 1392
 1393    let view = cx.add_window(|cx| {
 1394        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1395        build_editor(buffer.clone(), cx)
 1396    });
 1397    _ = view.update(cx, |view, cx| {
 1398        view.change_selections(None, cx, |s| {
 1399            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1400        });
 1401
 1402        // moving above start of document should move selection to start of document,
 1403        // but the next move down should still be at the original goal_x
 1404        view.move_up(&MoveUp, cx);
 1405        assert_eq!(
 1406            view.selections.display_ranges(cx),
 1407            &[empty_range(0, "".len())]
 1408        );
 1409
 1410        view.move_down(&MoveDown, cx);
 1411        assert_eq!(
 1412            view.selections.display_ranges(cx),
 1413            &[empty_range(1, "abcd".len())]
 1414        );
 1415
 1416        view.move_down(&MoveDown, cx);
 1417        assert_eq!(
 1418            view.selections.display_ranges(cx),
 1419            &[empty_range(2, "αβγ".len())]
 1420        );
 1421
 1422        view.move_down(&MoveDown, cx);
 1423        assert_eq!(
 1424            view.selections.display_ranges(cx),
 1425            &[empty_range(3, "abcd".len())]
 1426        );
 1427
 1428        view.move_down(&MoveDown, cx);
 1429        assert_eq!(
 1430            view.selections.display_ranges(cx),
 1431            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1432        );
 1433
 1434        // moving past end of document should not change goal_x
 1435        view.move_down(&MoveDown, cx);
 1436        assert_eq!(
 1437            view.selections.display_ranges(cx),
 1438            &[empty_range(5, "".len())]
 1439        );
 1440
 1441        view.move_down(&MoveDown, cx);
 1442        assert_eq!(
 1443            view.selections.display_ranges(cx),
 1444            &[empty_range(5, "".len())]
 1445        );
 1446
 1447        view.move_up(&MoveUp, cx);
 1448        assert_eq!(
 1449            view.selections.display_ranges(cx),
 1450            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1451        );
 1452
 1453        view.move_up(&MoveUp, cx);
 1454        assert_eq!(
 1455            view.selections.display_ranges(cx),
 1456            &[empty_range(3, "abcd".len())]
 1457        );
 1458
 1459        view.move_up(&MoveUp, cx);
 1460        assert_eq!(
 1461            view.selections.display_ranges(cx),
 1462            &[empty_range(2, "αβγ".len())]
 1463        );
 1464    });
 1465}
 1466
 1467#[gpui::test]
 1468fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1469    init_test(cx, |_| {});
 1470    let move_to_beg = MoveToBeginningOfLine {
 1471        stop_at_soft_wraps: true,
 1472    };
 1473
 1474    let move_to_end = MoveToEndOfLine {
 1475        stop_at_soft_wraps: true,
 1476    };
 1477
 1478    let view = cx.add_window(|cx| {
 1479        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1480        build_editor(buffer, cx)
 1481    });
 1482    _ = view.update(cx, |view, cx| {
 1483        view.change_selections(None, cx, |s| {
 1484            s.select_display_ranges([
 1485                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1486                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1487            ]);
 1488        });
 1489    });
 1490
 1491    _ = view.update(cx, |view, cx| {
 1492        view.move_to_beginning_of_line(&move_to_beg, cx);
 1493        assert_eq!(
 1494            view.selections.display_ranges(cx),
 1495            &[
 1496                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1497                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1498            ]
 1499        );
 1500    });
 1501
 1502    _ = view.update(cx, |view, cx| {
 1503        view.move_to_beginning_of_line(&move_to_beg, cx);
 1504        assert_eq!(
 1505            view.selections.display_ranges(cx),
 1506            &[
 1507                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1508                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1509            ]
 1510        );
 1511    });
 1512
 1513    _ = view.update(cx, |view, cx| {
 1514        view.move_to_beginning_of_line(&move_to_beg, cx);
 1515        assert_eq!(
 1516            view.selections.display_ranges(cx),
 1517            &[
 1518                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1519                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1520            ]
 1521        );
 1522    });
 1523
 1524    _ = view.update(cx, |view, cx| {
 1525        view.move_to_end_of_line(&move_to_end, cx);
 1526        assert_eq!(
 1527            view.selections.display_ranges(cx),
 1528            &[
 1529                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1530                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1531            ]
 1532        );
 1533    });
 1534
 1535    // Moving to the end of line again is a no-op.
 1536    _ = view.update(cx, |view, cx| {
 1537        view.move_to_end_of_line(&move_to_end, cx);
 1538        assert_eq!(
 1539            view.selections.display_ranges(cx),
 1540            &[
 1541                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1542                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1543            ]
 1544        );
 1545    });
 1546
 1547    _ = view.update(cx, |view, cx| {
 1548        view.move_left(&MoveLeft, cx);
 1549        view.select_to_beginning_of_line(
 1550            &SelectToBeginningOfLine {
 1551                stop_at_soft_wraps: true,
 1552            },
 1553            cx,
 1554        );
 1555        assert_eq!(
 1556            view.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = view.update(cx, |view, cx| {
 1565        view.select_to_beginning_of_line(
 1566            &SelectToBeginningOfLine {
 1567                stop_at_soft_wraps: true,
 1568            },
 1569            cx,
 1570        );
 1571        assert_eq!(
 1572            view.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1575                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = view.update(cx, |view, cx| {
 1581        view.select_to_beginning_of_line(
 1582            &SelectToBeginningOfLine {
 1583                stop_at_soft_wraps: true,
 1584            },
 1585            cx,
 1586        );
 1587        assert_eq!(
 1588            view.selections.display_ranges(cx),
 1589            &[
 1590                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1591                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1592            ]
 1593        );
 1594    });
 1595
 1596    _ = view.update(cx, |view, cx| {
 1597        view.select_to_end_of_line(
 1598            &SelectToEndOfLine {
 1599                stop_at_soft_wraps: true,
 1600            },
 1601            cx,
 1602        );
 1603        assert_eq!(
 1604            view.selections.display_ranges(cx),
 1605            &[
 1606                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1607                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1608            ]
 1609        );
 1610    });
 1611
 1612    _ = view.update(cx, |view, cx| {
 1613        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1614        assert_eq!(view.display_text(cx), "ab\n  de");
 1615        assert_eq!(
 1616            view.selections.display_ranges(cx),
 1617            &[
 1618                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1619                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1620            ]
 1621        );
 1622    });
 1623
 1624    _ = view.update(cx, |view, cx| {
 1625        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1626        assert_eq!(view.display_text(cx), "\n");
 1627        assert_eq!(
 1628            view.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635}
 1636
 1637#[gpui::test]
 1638fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1639    init_test(cx, |_| {});
 1640    let move_to_beg = MoveToBeginningOfLine {
 1641        stop_at_soft_wraps: false,
 1642    };
 1643
 1644    let move_to_end = MoveToEndOfLine {
 1645        stop_at_soft_wraps: false,
 1646    };
 1647
 1648    let view = cx.add_window(|cx| {
 1649        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1650        build_editor(buffer, cx)
 1651    });
 1652
 1653    _ = view.update(cx, |view, cx| {
 1654        view.set_wrap_width(Some(140.0.into()), cx);
 1655
 1656        // We expect the following lines after wrapping
 1657        // ```
 1658        // thequickbrownfox
 1659        // jumpedoverthelazydo
 1660        // gs
 1661        // ```
 1662        // The final `gs` was soft-wrapped onto a new line.
 1663        assert_eq!(
 1664            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1665            view.display_text(cx),
 1666        );
 1667
 1668        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1669        // Start the cursor at the `k` on the first line
 1670        view.change_selections(None, cx, |s| {
 1671            s.select_display_ranges([
 1672                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1673            ]);
 1674        });
 1675
 1676        // Moving to the beginning of the line should put us at the beginning of the line.
 1677        view.move_to_beginning_of_line(&move_to_beg, cx);
 1678        assert_eq!(
 1679            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1680            view.selections.display_ranges(cx)
 1681        );
 1682
 1683        // Moving to the end of the line should put us at the end of the line.
 1684        view.move_to_end_of_line(&move_to_end, cx);
 1685        assert_eq!(
 1686            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1687            view.selections.display_ranges(cx)
 1688        );
 1689
 1690        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1691        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1692        view.change_selections(None, cx, |s| {
 1693            s.select_display_ranges([
 1694                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1695            ]);
 1696        });
 1697
 1698        // Moving to the beginning of the line should put us at the start of the second line of
 1699        // display text, i.e., the `j`.
 1700        view.move_to_beginning_of_line(&move_to_beg, cx);
 1701        assert_eq!(
 1702            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1703            view.selections.display_ranges(cx)
 1704        );
 1705
 1706        // Moving to the beginning of the line again should be a no-op.
 1707        view.move_to_beginning_of_line(&move_to_beg, cx);
 1708        assert_eq!(
 1709            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1710            view.selections.display_ranges(cx)
 1711        );
 1712
 1713        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1714        // next display line.
 1715        view.move_to_end_of_line(&move_to_end, cx);
 1716        assert_eq!(
 1717            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1718            view.selections.display_ranges(cx)
 1719        );
 1720
 1721        // Moving to the end of the line again should be a no-op.
 1722        view.move_to_end_of_line(&move_to_end, cx);
 1723        assert_eq!(
 1724            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1725            view.selections.display_ranges(cx)
 1726        );
 1727    });
 1728}
 1729
 1730#[gpui::test]
 1731fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1732    init_test(cx, |_| {});
 1733
 1734    let view = cx.add_window(|cx| {
 1735        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1736        build_editor(buffer, cx)
 1737    });
 1738    _ = view.update(cx, |view, cx| {
 1739        view.change_selections(None, cx, |s| {
 1740            s.select_display_ranges([
 1741                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1742                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1743            ])
 1744        });
 1745
 1746        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1747        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1748
 1749        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1750        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1751
 1752        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1753        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1754
 1755        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1756        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1757
 1758        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1759        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1760
 1761        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1762        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1763
 1764        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1765        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1766
 1767        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1768        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1769
 1770        view.move_right(&MoveRight, cx);
 1771        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1772        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1773
 1774        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1775        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1776
 1777        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1778        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1779    });
 1780}
 1781
 1782#[gpui::test]
 1783fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1784    init_test(cx, |_| {});
 1785
 1786    let view = cx.add_window(|cx| {
 1787        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1788        build_editor(buffer, cx)
 1789    });
 1790
 1791    _ = view.update(cx, |view, cx| {
 1792        view.set_wrap_width(Some(140.0.into()), cx);
 1793        assert_eq!(
 1794            view.display_text(cx),
 1795            "use one::{\n    two::three::\n    four::five\n};"
 1796        );
 1797
 1798        view.change_selections(None, cx, |s| {
 1799            s.select_display_ranges([
 1800                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1801            ]);
 1802        });
 1803
 1804        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1805        assert_eq!(
 1806            view.selections.display_ranges(cx),
 1807            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1808        );
 1809
 1810        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1811        assert_eq!(
 1812            view.selections.display_ranges(cx),
 1813            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1814        );
 1815
 1816        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1817        assert_eq!(
 1818            view.selections.display_ranges(cx),
 1819            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1820        );
 1821
 1822        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1823        assert_eq!(
 1824            view.selections.display_ranges(cx),
 1825            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1826        );
 1827
 1828        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1829        assert_eq!(
 1830            view.selections.display_ranges(cx),
 1831            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1832        );
 1833
 1834        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1835        assert_eq!(
 1836            view.selections.display_ranges(cx),
 1837            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1838        );
 1839    });
 1840}
 1841
 1842#[gpui::test]
 1843async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1844    init_test(cx, |_| {});
 1845    let mut cx = EditorTestContext::new(cx).await;
 1846
 1847    let line_height = cx.editor(|editor, cx| {
 1848        editor
 1849            .style()
 1850            .unwrap()
 1851            .text
 1852            .line_height_in_pixels(cx.rem_size())
 1853    });
 1854    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1855
 1856    cx.set_state(
 1857        &r#"ˇone
 1858        two
 1859
 1860        three
 1861        fourˇ
 1862        five
 1863
 1864        six"#
 1865            .unindent(),
 1866    );
 1867
 1868    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1869    cx.assert_editor_state(
 1870        &r#"one
 1871        two
 1872        ˇ
 1873        three
 1874        four
 1875        five
 1876        ˇ
 1877        six"#
 1878            .unindent(),
 1879    );
 1880
 1881    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1882    cx.assert_editor_state(
 1883        &r#"one
 1884        two
 1885
 1886        three
 1887        four
 1888        five
 1889        ˇ
 1890        sixˇ"#
 1891            .unindent(),
 1892    );
 1893
 1894    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1895    cx.assert_editor_state(
 1896        &r#"one
 1897        two
 1898
 1899        three
 1900        four
 1901        five
 1902
 1903        sixˇ"#
 1904            .unindent(),
 1905    );
 1906
 1907    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1908    cx.assert_editor_state(
 1909        &r#"one
 1910        two
 1911
 1912        three
 1913        four
 1914        five
 1915        ˇ
 1916        six"#
 1917            .unindent(),
 1918    );
 1919
 1920    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1921    cx.assert_editor_state(
 1922        &r#"one
 1923        two
 1924        ˇ
 1925        three
 1926        four
 1927        five
 1928
 1929        six"#
 1930            .unindent(),
 1931    );
 1932
 1933    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1934    cx.assert_editor_state(
 1935        &r#"ˇone
 1936        two
 1937
 1938        three
 1939        four
 1940        five
 1941
 1942        six"#
 1943            .unindent(),
 1944    );
 1945}
 1946
 1947#[gpui::test]
 1948async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1949    init_test(cx, |_| {});
 1950    let mut cx = EditorTestContext::new(cx).await;
 1951    let line_height = cx.editor(|editor, cx| {
 1952        editor
 1953            .style()
 1954            .unwrap()
 1955            .text
 1956            .line_height_in_pixels(cx.rem_size())
 1957    });
 1958    let window = cx.window;
 1959    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1960
 1961    cx.set_state(
 1962        r#"ˇone
 1963        two
 1964        three
 1965        four
 1966        five
 1967        six
 1968        seven
 1969        eight
 1970        nine
 1971        ten
 1972        "#,
 1973    );
 1974
 1975    cx.update_editor(|editor, cx| {
 1976        assert_eq!(
 1977            editor.snapshot(cx).scroll_position(),
 1978            gpui::Point::new(0., 0.)
 1979        );
 1980        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1981        assert_eq!(
 1982            editor.snapshot(cx).scroll_position(),
 1983            gpui::Point::new(0., 3.)
 1984        );
 1985        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1986        assert_eq!(
 1987            editor.snapshot(cx).scroll_position(),
 1988            gpui::Point::new(0., 6.)
 1989        );
 1990        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1991        assert_eq!(
 1992            editor.snapshot(cx).scroll_position(),
 1993            gpui::Point::new(0., 3.)
 1994        );
 1995
 1996        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1997        assert_eq!(
 1998            editor.snapshot(cx).scroll_position(),
 1999            gpui::Point::new(0., 1.)
 2000        );
 2001        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2002        assert_eq!(
 2003            editor.snapshot(cx).scroll_position(),
 2004            gpui::Point::new(0., 3.)
 2005        );
 2006    });
 2007}
 2008
 2009#[gpui::test]
 2010async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2011    init_test(cx, |_| {});
 2012    let mut cx = EditorTestContext::new(cx).await;
 2013
 2014    let line_height = cx.update_editor(|editor, cx| {
 2015        editor.set_vertical_scroll_margin(2, cx);
 2016        editor
 2017            .style()
 2018            .unwrap()
 2019            .text
 2020            .line_height_in_pixels(cx.rem_size())
 2021    });
 2022    let window = cx.window;
 2023    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2024
 2025    cx.set_state(
 2026        r#"ˇone
 2027            two
 2028            three
 2029            four
 2030            five
 2031            six
 2032            seven
 2033            eight
 2034            nine
 2035            ten
 2036        "#,
 2037    );
 2038    cx.update_editor(|editor, cx| {
 2039        assert_eq!(
 2040            editor.snapshot(cx).scroll_position(),
 2041            gpui::Point::new(0., 0.0)
 2042        );
 2043    });
 2044
 2045    // Add a cursor below the visible area. Since both cursors cannot fit
 2046    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2047    // allows the vertical scroll margin below that cursor.
 2048    cx.update_editor(|editor, cx| {
 2049        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2050            selections.select_ranges([
 2051                Point::new(0, 0)..Point::new(0, 0),
 2052                Point::new(6, 0)..Point::new(6, 0),
 2053            ]);
 2054        })
 2055    });
 2056    cx.update_editor(|editor, cx| {
 2057        assert_eq!(
 2058            editor.snapshot(cx).scroll_position(),
 2059            gpui::Point::new(0., 3.0)
 2060        );
 2061    });
 2062
 2063    // Move down. The editor cursor scrolls down to track the newest cursor.
 2064    cx.update_editor(|editor, cx| {
 2065        editor.move_down(&Default::default(), cx);
 2066    });
 2067    cx.update_editor(|editor, cx| {
 2068        assert_eq!(
 2069            editor.snapshot(cx).scroll_position(),
 2070            gpui::Point::new(0., 4.0)
 2071        );
 2072    });
 2073
 2074    // Add a cursor above the visible area. Since both cursors fit on screen,
 2075    // the editor scrolls to show both.
 2076    cx.update_editor(|editor, cx| {
 2077        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2078            selections.select_ranges([
 2079                Point::new(1, 0)..Point::new(1, 0),
 2080                Point::new(6, 0)..Point::new(6, 0),
 2081            ]);
 2082        })
 2083    });
 2084    cx.update_editor(|editor, cx| {
 2085        assert_eq!(
 2086            editor.snapshot(cx).scroll_position(),
 2087            gpui::Point::new(0., 1.0)
 2088        );
 2089    });
 2090}
 2091
 2092#[gpui::test]
 2093async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2094    init_test(cx, |_| {});
 2095    let mut cx = EditorTestContext::new(cx).await;
 2096
 2097    let line_height = cx.editor(|editor, cx| {
 2098        editor
 2099            .style()
 2100            .unwrap()
 2101            .text
 2102            .line_height_in_pixels(cx.rem_size())
 2103    });
 2104    let window = cx.window;
 2105    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2106    cx.set_state(
 2107        &r#"
 2108        ˇone
 2109        two
 2110        threeˇ
 2111        four
 2112        five
 2113        six
 2114        seven
 2115        eight
 2116        nine
 2117        ten
 2118        "#
 2119        .unindent(),
 2120    );
 2121
 2122    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2123    cx.assert_editor_state(
 2124        &r#"
 2125        one
 2126        two
 2127        three
 2128        ˇfour
 2129        five
 2130        sixˇ
 2131        seven
 2132        eight
 2133        nine
 2134        ten
 2135        "#
 2136        .unindent(),
 2137    );
 2138
 2139    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2140    cx.assert_editor_state(
 2141        &r#"
 2142        one
 2143        two
 2144        three
 2145        four
 2146        five
 2147        six
 2148        ˇseven
 2149        eight
 2150        nineˇ
 2151        ten
 2152        "#
 2153        .unindent(),
 2154    );
 2155
 2156    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2157    cx.assert_editor_state(
 2158        &r#"
 2159        one
 2160        two
 2161        three
 2162        ˇfour
 2163        five
 2164        sixˇ
 2165        seven
 2166        eight
 2167        nine
 2168        ten
 2169        "#
 2170        .unindent(),
 2171    );
 2172
 2173    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2174    cx.assert_editor_state(
 2175        &r#"
 2176        ˇone
 2177        two
 2178        threeˇ
 2179        four
 2180        five
 2181        six
 2182        seven
 2183        eight
 2184        nine
 2185        ten
 2186        "#
 2187        .unindent(),
 2188    );
 2189
 2190    // Test select collapsing
 2191    cx.update_editor(|editor, cx| {
 2192        editor.move_page_down(&MovePageDown::default(), cx);
 2193        editor.move_page_down(&MovePageDown::default(), cx);
 2194        editor.move_page_down(&MovePageDown::default(), cx);
 2195    });
 2196    cx.assert_editor_state(
 2197        &r#"
 2198        one
 2199        two
 2200        three
 2201        four
 2202        five
 2203        six
 2204        seven
 2205        eight
 2206        nine
 2207        ˇten
 2208        ˇ"#
 2209        .unindent(),
 2210    );
 2211}
 2212
 2213#[gpui::test]
 2214async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2215    init_test(cx, |_| {});
 2216    let mut cx = EditorTestContext::new(cx).await;
 2217    cx.set_state("one «two threeˇ» four");
 2218    cx.update_editor(|editor, cx| {
 2219        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2220        assert_eq!(editor.text(cx), " four");
 2221    });
 2222}
 2223
 2224#[gpui::test]
 2225fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2226    init_test(cx, |_| {});
 2227
 2228    let view = cx.add_window(|cx| {
 2229        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2230        build_editor(buffer.clone(), cx)
 2231    });
 2232
 2233    _ = view.update(cx, |view, cx| {
 2234        view.change_selections(None, cx, |s| {
 2235            s.select_display_ranges([
 2236                // an empty selection - the preceding word fragment is deleted
 2237                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2238                // characters selected - they are deleted
 2239                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2240            ])
 2241        });
 2242        view.delete_to_previous_word_start(
 2243            &DeleteToPreviousWordStart {
 2244                ignore_newlines: false,
 2245            },
 2246            cx,
 2247        );
 2248        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2249    });
 2250
 2251    _ = view.update(cx, |view, cx| {
 2252        view.change_selections(None, cx, |s| {
 2253            s.select_display_ranges([
 2254                // an empty selection - the following word fragment is deleted
 2255                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2256                // characters selected - they are deleted
 2257                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2258            ])
 2259        });
 2260        view.delete_to_next_word_end(
 2261            &DeleteToNextWordEnd {
 2262                ignore_newlines: false,
 2263            },
 2264            cx,
 2265        );
 2266        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2267    });
 2268}
 2269
 2270#[gpui::test]
 2271fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2272    init_test(cx, |_| {});
 2273
 2274    let view = cx.add_window(|cx| {
 2275        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2276        build_editor(buffer.clone(), cx)
 2277    });
 2278    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2279        ignore_newlines: false,
 2280    };
 2281    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2282        ignore_newlines: true,
 2283    };
 2284
 2285    _ = view.update(cx, |view, cx| {
 2286        view.change_selections(None, cx, |s| {
 2287            s.select_display_ranges([
 2288                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2289            ])
 2290        });
 2291        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2292        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2293        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2294        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2295        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2296        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2297        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2298        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2299        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2300        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2301        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2302        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2303    });
 2304}
 2305
 2306#[gpui::test]
 2307fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2308    init_test(cx, |_| {});
 2309
 2310    let view = cx.add_window(|cx| {
 2311        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2312        build_editor(buffer.clone(), cx)
 2313    });
 2314    let del_to_next_word_end = DeleteToNextWordEnd {
 2315        ignore_newlines: false,
 2316    };
 2317    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2318        ignore_newlines: true,
 2319    };
 2320
 2321    _ = view.update(cx, |view, cx| {
 2322        view.change_selections(None, cx, |s| {
 2323            s.select_display_ranges([
 2324                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2325            ])
 2326        });
 2327        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2328        assert_eq!(
 2329            view.buffer.read(cx).read(cx).text(),
 2330            "one\n   two\nthree\n   four"
 2331        );
 2332        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2333        assert_eq!(
 2334            view.buffer.read(cx).read(cx).text(),
 2335            "\n   two\nthree\n   four"
 2336        );
 2337        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2338        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2339        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2340        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2341        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2342        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2343        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2344        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2345    });
 2346}
 2347
 2348#[gpui::test]
 2349fn test_newline(cx: &mut TestAppContext) {
 2350    init_test(cx, |_| {});
 2351
 2352    let view = cx.add_window(|cx| {
 2353        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2354        build_editor(buffer.clone(), cx)
 2355    });
 2356
 2357    _ = view.update(cx, |view, cx| {
 2358        view.change_selections(None, cx, |s| {
 2359            s.select_display_ranges([
 2360                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2361                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2362                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2363            ])
 2364        });
 2365
 2366        view.newline(&Newline, cx);
 2367        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2368    });
 2369}
 2370
 2371#[gpui::test]
 2372fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2373    init_test(cx, |_| {});
 2374
 2375    let editor = cx.add_window(|cx| {
 2376        let buffer = MultiBuffer::build_simple(
 2377            "
 2378                a
 2379                b(
 2380                    X
 2381                )
 2382                c(
 2383                    X
 2384                )
 2385            "
 2386            .unindent()
 2387            .as_str(),
 2388            cx,
 2389        );
 2390        let mut editor = build_editor(buffer.clone(), cx);
 2391        editor.change_selections(None, cx, |s| {
 2392            s.select_ranges([
 2393                Point::new(2, 4)..Point::new(2, 5),
 2394                Point::new(5, 4)..Point::new(5, 5),
 2395            ])
 2396        });
 2397        editor
 2398    });
 2399
 2400    _ = editor.update(cx, |editor, cx| {
 2401        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2402        editor.buffer.update(cx, |buffer, cx| {
 2403            buffer.edit(
 2404                [
 2405                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2406                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2407                ],
 2408                None,
 2409                cx,
 2410            );
 2411            assert_eq!(
 2412                buffer.read(cx).text(),
 2413                "
 2414                    a
 2415                    b()
 2416                    c()
 2417                "
 2418                .unindent()
 2419            );
 2420        });
 2421        assert_eq!(
 2422            editor.selections.ranges(cx),
 2423            &[
 2424                Point::new(1, 2)..Point::new(1, 2),
 2425                Point::new(2, 2)..Point::new(2, 2),
 2426            ],
 2427        );
 2428
 2429        editor.newline(&Newline, cx);
 2430        assert_eq!(
 2431            editor.text(cx),
 2432            "
 2433                a
 2434                b(
 2435                )
 2436                c(
 2437                )
 2438            "
 2439            .unindent()
 2440        );
 2441
 2442        // The selections are moved after the inserted newlines
 2443        assert_eq!(
 2444            editor.selections.ranges(cx),
 2445            &[
 2446                Point::new(2, 0)..Point::new(2, 0),
 2447                Point::new(4, 0)..Point::new(4, 0),
 2448            ],
 2449        );
 2450    });
 2451}
 2452
 2453#[gpui::test]
 2454async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2455    init_test(cx, |settings| {
 2456        settings.defaults.tab_size = NonZeroU32::new(4)
 2457    });
 2458
 2459    let language = Arc::new(
 2460        Language::new(
 2461            LanguageConfig::default(),
 2462            Some(tree_sitter_rust::LANGUAGE.into()),
 2463        )
 2464        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2465        .unwrap(),
 2466    );
 2467
 2468    let mut cx = EditorTestContext::new(cx).await;
 2469    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2470    cx.set_state(indoc! {"
 2471        const a: ˇA = (
 2472 2473                «const_functionˇ»(ˇ),
 2474                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2475 2476        ˇ);ˇ
 2477    "});
 2478
 2479    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2480    cx.assert_editor_state(indoc! {"
 2481        ˇ
 2482        const a: A = (
 2483            ˇ
 2484            (
 2485                ˇ
 2486                ˇ
 2487                const_function(),
 2488                ˇ
 2489                ˇ
 2490                ˇ
 2491                ˇ
 2492                something_else,
 2493                ˇ
 2494            )
 2495            ˇ
 2496            ˇ
 2497        );
 2498    "});
 2499}
 2500
 2501#[gpui::test]
 2502async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2503    init_test(cx, |settings| {
 2504        settings.defaults.tab_size = NonZeroU32::new(4)
 2505    });
 2506
 2507    let language = Arc::new(
 2508        Language::new(
 2509            LanguageConfig::default(),
 2510            Some(tree_sitter_rust::LANGUAGE.into()),
 2511        )
 2512        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2513        .unwrap(),
 2514    );
 2515
 2516    let mut cx = EditorTestContext::new(cx).await;
 2517    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2518    cx.set_state(indoc! {"
 2519        const a: ˇA = (
 2520 2521                «const_functionˇ»(ˇ),
 2522                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2523 2524        ˇ);ˇ
 2525    "});
 2526
 2527    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2528    cx.assert_editor_state(indoc! {"
 2529        const a: A = (
 2530            ˇ
 2531            (
 2532                ˇ
 2533                const_function(),
 2534                ˇ
 2535                ˇ
 2536                something_else,
 2537                ˇ
 2538                ˇ
 2539                ˇ
 2540                ˇ
 2541            )
 2542            ˇ
 2543        );
 2544        ˇ
 2545        ˇ
 2546    "});
 2547}
 2548
 2549#[gpui::test]
 2550async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2551    init_test(cx, |settings| {
 2552        settings.defaults.tab_size = NonZeroU32::new(4)
 2553    });
 2554
 2555    let language = Arc::new(Language::new(
 2556        LanguageConfig {
 2557            line_comments: vec!["//".into()],
 2558            ..LanguageConfig::default()
 2559        },
 2560        None,
 2561    ));
 2562    {
 2563        let mut cx = EditorTestContext::new(cx).await;
 2564        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2565        cx.set_state(indoc! {"
 2566        // Fooˇ
 2567    "});
 2568
 2569        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2570        cx.assert_editor_state(indoc! {"
 2571        // Foo
 2572        //ˇ
 2573    "});
 2574        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2575        cx.set_state(indoc! {"
 2576        ˇ// Foo
 2577    "});
 2578        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2579        cx.assert_editor_state(indoc! {"
 2580
 2581        ˇ// Foo
 2582    "});
 2583    }
 2584    // Ensure that comment continuations can be disabled.
 2585    update_test_language_settings(cx, |settings| {
 2586        settings.defaults.extend_comment_on_newline = Some(false);
 2587    });
 2588    let mut cx = EditorTestContext::new(cx).await;
 2589    cx.set_state(indoc! {"
 2590        // Fooˇ
 2591    "});
 2592    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2593    cx.assert_editor_state(indoc! {"
 2594        // Foo
 2595        ˇ
 2596    "});
 2597}
 2598
 2599#[gpui::test]
 2600fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2601    init_test(cx, |_| {});
 2602
 2603    let editor = cx.add_window(|cx| {
 2604        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2605        let mut editor = build_editor(buffer.clone(), cx);
 2606        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2607        editor
 2608    });
 2609
 2610    _ = editor.update(cx, |editor, cx| {
 2611        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2612        editor.buffer.update(cx, |buffer, cx| {
 2613            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2614            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2615        });
 2616        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2617
 2618        editor.insert("Z", cx);
 2619        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2620
 2621        // The selections are moved after the inserted characters
 2622        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2623    });
 2624}
 2625
 2626#[gpui::test]
 2627async fn test_tab(cx: &mut gpui::TestAppContext) {
 2628    init_test(cx, |settings| {
 2629        settings.defaults.tab_size = NonZeroU32::new(3)
 2630    });
 2631
 2632    let mut cx = EditorTestContext::new(cx).await;
 2633    cx.set_state(indoc! {"
 2634        ˇabˇc
 2635        ˇ🏀ˇ🏀ˇefg
 2636 2637    "});
 2638    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2639    cx.assert_editor_state(indoc! {"
 2640           ˇab ˇc
 2641           ˇ🏀  ˇ🏀  ˇefg
 2642        d  ˇ
 2643    "});
 2644
 2645    cx.set_state(indoc! {"
 2646        a
 2647        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2648    "});
 2649    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2650    cx.assert_editor_state(indoc! {"
 2651        a
 2652           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2653    "});
 2654}
 2655
 2656#[gpui::test]
 2657async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2658    init_test(cx, |_| {});
 2659
 2660    let mut cx = EditorTestContext::new(cx).await;
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670
 2671    // cursors that are already at the suggested indent level insert
 2672    // a soft tab. cursors that are to the left of the suggested indent
 2673    // auto-indent their line.
 2674    cx.set_state(indoc! {"
 2675        ˇ
 2676        const a: B = (
 2677            c(
 2678                d(
 2679        ˇ
 2680                )
 2681        ˇ
 2682        ˇ    )
 2683        );
 2684    "});
 2685    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2686    cx.assert_editor_state(indoc! {"
 2687            ˇ
 2688        const a: B = (
 2689            c(
 2690                d(
 2691                    ˇ
 2692                )
 2693                ˇ
 2694            ˇ)
 2695        );
 2696    "});
 2697
 2698    // handle auto-indent when there are multiple cursors on the same line
 2699    cx.set_state(indoc! {"
 2700        const a: B = (
 2701            c(
 2702        ˇ    ˇ
 2703        ˇ    )
 2704        );
 2705    "});
 2706    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2707    cx.assert_editor_state(indoc! {"
 2708        const a: B = (
 2709            c(
 2710                ˇ
 2711            ˇ)
 2712        );
 2713    "});
 2714}
 2715
 2716#[gpui::test]
 2717async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2718    init_test(cx, |settings| {
 2719        settings.defaults.tab_size = NonZeroU32::new(4)
 2720    });
 2721
 2722    let language = Arc::new(
 2723        Language::new(
 2724            LanguageConfig::default(),
 2725            Some(tree_sitter_rust::LANGUAGE.into()),
 2726        )
 2727        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2728        .unwrap(),
 2729    );
 2730
 2731    let mut cx = EditorTestContext::new(cx).await;
 2732    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2733    cx.set_state(indoc! {"
 2734        fn a() {
 2735            if b {
 2736        \t ˇc
 2737            }
 2738        }
 2739    "});
 2740
 2741    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2742    cx.assert_editor_state(indoc! {"
 2743        fn a() {
 2744            if b {
 2745                ˇc
 2746            }
 2747        }
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4);
 2755    });
 2756
 2757    let mut cx = EditorTestContext::new(cx).await;
 2758
 2759    cx.set_state(indoc! {"
 2760          «oneˇ» «twoˇ»
 2761        three
 2762         four
 2763    "});
 2764    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2765    cx.assert_editor_state(indoc! {"
 2766            «oneˇ» «twoˇ»
 2767        three
 2768         four
 2769    "});
 2770
 2771    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2772    cx.assert_editor_state(indoc! {"
 2773        «oneˇ» «twoˇ»
 2774        three
 2775         four
 2776    "});
 2777
 2778    // select across line ending
 2779    cx.set_state(indoc! {"
 2780        one two
 2781        t«hree
 2782        ˇ» four
 2783    "});
 2784    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2785    cx.assert_editor_state(indoc! {"
 2786        one two
 2787            t«hree
 2788        ˇ» four
 2789    "});
 2790
 2791    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2792    cx.assert_editor_state(indoc! {"
 2793        one two
 2794        t«hree
 2795        ˇ» four
 2796    "});
 2797
 2798    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2799    cx.set_state(indoc! {"
 2800        one two
 2801        ˇthree
 2802            four
 2803    "});
 2804    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2805    cx.assert_editor_state(indoc! {"
 2806        one two
 2807            ˇthree
 2808            four
 2809    "});
 2810
 2811    cx.set_state(indoc! {"
 2812        one two
 2813        ˇ    three
 2814            four
 2815    "});
 2816    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2817    cx.assert_editor_state(indoc! {"
 2818        one two
 2819        ˇthree
 2820            four
 2821    "});
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.hard_tabs = Some(true);
 2828    });
 2829
 2830    let mut cx = EditorTestContext::new(cx).await;
 2831
 2832    // select two ranges on one line
 2833    cx.set_state(indoc! {"
 2834        «oneˇ» «twoˇ»
 2835        three
 2836        four
 2837    "});
 2838    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2839    cx.assert_editor_state(indoc! {"
 2840        \t«oneˇ» «twoˇ»
 2841        three
 2842        four
 2843    "});
 2844    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2845    cx.assert_editor_state(indoc! {"
 2846        \t\t«oneˇ» «twoˇ»
 2847        three
 2848        four
 2849    "});
 2850    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2851    cx.assert_editor_state(indoc! {"
 2852        \t«oneˇ» «twoˇ»
 2853        three
 2854        four
 2855    "});
 2856    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2857    cx.assert_editor_state(indoc! {"
 2858        «oneˇ» «twoˇ»
 2859        three
 2860        four
 2861    "});
 2862
 2863    // select across a line ending
 2864    cx.set_state(indoc! {"
 2865        one two
 2866        t«hree
 2867        ˇ»four
 2868    "});
 2869    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2870    cx.assert_editor_state(indoc! {"
 2871        one two
 2872        \tt«hree
 2873        ˇ»four
 2874    "});
 2875    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2876    cx.assert_editor_state(indoc! {"
 2877        one two
 2878        \t\tt«hree
 2879        ˇ»four
 2880    "});
 2881    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2882    cx.assert_editor_state(indoc! {"
 2883        one two
 2884        \tt«hree
 2885        ˇ»four
 2886    "});
 2887    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889        one two
 2890        t«hree
 2891        ˇ»four
 2892    "});
 2893
 2894    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2895    cx.set_state(indoc! {"
 2896        one two
 2897        ˇthree
 2898        four
 2899    "});
 2900    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2901    cx.assert_editor_state(indoc! {"
 2902        one two
 2903        ˇthree
 2904        four
 2905    "});
 2906    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2907    cx.assert_editor_state(indoc! {"
 2908        one two
 2909        \tˇthree
 2910        four
 2911    "});
 2912    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2913    cx.assert_editor_state(indoc! {"
 2914        one two
 2915        ˇthree
 2916        four
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.languages.extend([
 2924            (
 2925                "TOML".into(),
 2926                LanguageSettingsContent {
 2927                    tab_size: NonZeroU32::new(2),
 2928                    ..Default::default()
 2929                },
 2930            ),
 2931            (
 2932                "Rust".into(),
 2933                LanguageSettingsContent {
 2934                    tab_size: NonZeroU32::new(4),
 2935                    ..Default::default()
 2936                },
 2937            ),
 2938        ]);
 2939    });
 2940
 2941    let toml_language = Arc::new(Language::new(
 2942        LanguageConfig {
 2943            name: "TOML".into(),
 2944            ..Default::default()
 2945        },
 2946        None,
 2947    ));
 2948    let rust_language = Arc::new(Language::new(
 2949        LanguageConfig {
 2950            name: "Rust".into(),
 2951            ..Default::default()
 2952        },
 2953        None,
 2954    ));
 2955
 2956    let toml_buffer =
 2957        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2958    let rust_buffer = cx.new_model(|cx| {
 2959        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2960    });
 2961    let multibuffer = cx.new_model(|cx| {
 2962        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2963        multibuffer.push_excerpts(
 2964            toml_buffer.clone(),
 2965            [ExcerptRange {
 2966                context: Point::new(0, 0)..Point::new(2, 0),
 2967                primary: None,
 2968            }],
 2969            cx,
 2970        );
 2971        multibuffer.push_excerpts(
 2972            rust_buffer.clone(),
 2973            [ExcerptRange {
 2974                context: Point::new(0, 0)..Point::new(1, 0),
 2975                primary: None,
 2976            }],
 2977            cx,
 2978        );
 2979        multibuffer
 2980    });
 2981
 2982    cx.add_window(|cx| {
 2983        let mut editor = build_editor(multibuffer, cx);
 2984
 2985        assert_eq!(
 2986            editor.text(cx),
 2987            indoc! {"
 2988                a = 1
 2989                b = 2
 2990
 2991                const c: usize = 3;
 2992            "}
 2993        );
 2994
 2995        select_ranges(
 2996            &mut editor,
 2997            indoc! {"
 2998                «aˇ» = 1
 2999                b = 2
 3000
 3001                «const c:ˇ» usize = 3;
 3002            "},
 3003            cx,
 3004        );
 3005
 3006        editor.tab(&Tab, cx);
 3007        assert_text_with_selections(
 3008            &mut editor,
 3009            indoc! {"
 3010                  «aˇ» = 1
 3011                b = 2
 3012
 3013                    «const c:ˇ» usize = 3;
 3014            "},
 3015            cx,
 3016        );
 3017        editor.tab_prev(&TabPrev, cx);
 3018        assert_text_with_selections(
 3019            &mut editor,
 3020            indoc! {"
 3021                «aˇ» = 1
 3022                b = 2
 3023
 3024                «const c:ˇ» usize = 3;
 3025            "},
 3026            cx,
 3027        );
 3028
 3029        editor
 3030    });
 3031}
 3032
 3033#[gpui::test]
 3034async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3035    init_test(cx, |_| {});
 3036
 3037    let mut cx = EditorTestContext::new(cx).await;
 3038
 3039    // Basic backspace
 3040    cx.set_state(indoc! {"
 3041        onˇe two three
 3042        fou«rˇ» five six
 3043        seven «ˇeight nine
 3044        »ten
 3045    "});
 3046    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3047    cx.assert_editor_state(indoc! {"
 3048        oˇe two three
 3049        fouˇ five six
 3050        seven ˇten
 3051    "});
 3052
 3053    // Test backspace inside and around indents
 3054    cx.set_state(indoc! {"
 3055        zero
 3056            ˇone
 3057                ˇtwo
 3058            ˇ ˇ ˇ  three
 3059        ˇ  ˇ  four
 3060    "});
 3061    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3062    cx.assert_editor_state(indoc! {"
 3063        zero
 3064        ˇone
 3065            ˇtwo
 3066        ˇ  threeˇ  four
 3067    "});
 3068
 3069    // Test backspace with line_mode set to true
 3070    cx.update_editor(|e, _| e.selections.line_mode = true);
 3071    cx.set_state(indoc! {"
 3072        The ˇquick ˇbrown
 3073        fox jumps over
 3074        the lazy dog
 3075        ˇThe qu«ick bˇ»rown"});
 3076    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3077    cx.assert_editor_state(indoc! {"
 3078        ˇfox jumps over
 3079        the lazy dogˇ"});
 3080}
 3081
 3082#[gpui::test]
 3083async fn test_delete(cx: &mut gpui::TestAppContext) {
 3084    init_test(cx, |_| {});
 3085
 3086    let mut cx = EditorTestContext::new(cx).await;
 3087    cx.set_state(indoc! {"
 3088        onˇe two three
 3089        fou«rˇ» five six
 3090        seven «ˇeight nine
 3091        »ten
 3092    "});
 3093    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3094    cx.assert_editor_state(indoc! {"
 3095        onˇ two three
 3096        fouˇ five six
 3097        seven ˇten
 3098    "});
 3099
 3100    // Test backspace with line_mode set to true
 3101    cx.update_editor(|e, _| e.selections.line_mode = true);
 3102    cx.set_state(indoc! {"
 3103        The ˇquick ˇbrown
 3104        fox «ˇjum»ps over
 3105        the lazy dog
 3106        ˇThe qu«ick bˇ»rown"});
 3107    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3108    cx.assert_editor_state("ˇthe lazy dogˇ");
 3109}
 3110
 3111#[gpui::test]
 3112fn test_delete_line(cx: &mut TestAppContext) {
 3113    init_test(cx, |_| {});
 3114
 3115    let view = cx.add_window(|cx| {
 3116        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3117        build_editor(buffer, cx)
 3118    });
 3119    _ = view.update(cx, |view, cx| {
 3120        view.change_selections(None, cx, |s| {
 3121            s.select_display_ranges([
 3122                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3123                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3124                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3125            ])
 3126        });
 3127        view.delete_line(&DeleteLine, cx);
 3128        assert_eq!(view.display_text(cx), "ghi");
 3129        assert_eq!(
 3130            view.selections.display_ranges(cx),
 3131            vec![
 3132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3133                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3134            ]
 3135        );
 3136    });
 3137
 3138    let view = cx.add_window(|cx| {
 3139        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3140        build_editor(buffer, cx)
 3141    });
 3142    _ = view.update(cx, |view, cx| {
 3143        view.change_selections(None, cx, |s| {
 3144            s.select_display_ranges([
 3145                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3146            ])
 3147        });
 3148        view.delete_line(&DeleteLine, cx);
 3149        assert_eq!(view.display_text(cx), "ghi\n");
 3150        assert_eq!(
 3151            view.selections.display_ranges(cx),
 3152            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3153        );
 3154    });
 3155}
 3156
 3157#[gpui::test]
 3158fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3159    init_test(cx, |_| {});
 3160
 3161    cx.add_window(|cx| {
 3162        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3163        let mut editor = build_editor(buffer.clone(), cx);
 3164        let buffer = buffer.read(cx).as_singleton().unwrap();
 3165
 3166        assert_eq!(
 3167            editor.selections.ranges::<Point>(cx),
 3168            &[Point::new(0, 0)..Point::new(0, 0)]
 3169        );
 3170
 3171        // When on single line, replace newline at end by space
 3172        editor.join_lines(&JoinLines, cx);
 3173        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3174        assert_eq!(
 3175            editor.selections.ranges::<Point>(cx),
 3176            &[Point::new(0, 3)..Point::new(0, 3)]
 3177        );
 3178
 3179        // When multiple lines are selected, remove newlines that are spanned by the selection
 3180        editor.change_selections(None, cx, |s| {
 3181            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3182        });
 3183        editor.join_lines(&JoinLines, cx);
 3184        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3185        assert_eq!(
 3186            editor.selections.ranges::<Point>(cx),
 3187            &[Point::new(0, 11)..Point::new(0, 11)]
 3188        );
 3189
 3190        // Undo should be transactional
 3191        editor.undo(&Undo, cx);
 3192        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3193        assert_eq!(
 3194            editor.selections.ranges::<Point>(cx),
 3195            &[Point::new(0, 5)..Point::new(2, 2)]
 3196        );
 3197
 3198        // When joining an empty line don't insert a space
 3199        editor.change_selections(None, cx, |s| {
 3200            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3201        });
 3202        editor.join_lines(&JoinLines, cx);
 3203        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3204        assert_eq!(
 3205            editor.selections.ranges::<Point>(cx),
 3206            [Point::new(2, 3)..Point::new(2, 3)]
 3207        );
 3208
 3209        // We can remove trailing newlines
 3210        editor.join_lines(&JoinLines, cx);
 3211        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3212        assert_eq!(
 3213            editor.selections.ranges::<Point>(cx),
 3214            [Point::new(2, 3)..Point::new(2, 3)]
 3215        );
 3216
 3217        // We don't blow up on the last line
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3220        assert_eq!(
 3221            editor.selections.ranges::<Point>(cx),
 3222            [Point::new(2, 3)..Point::new(2, 3)]
 3223        );
 3224
 3225        // reset to test indentation
 3226        editor.buffer.update(cx, |buffer, cx| {
 3227            buffer.edit(
 3228                [
 3229                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3230                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3231                ],
 3232                None,
 3233                cx,
 3234            )
 3235        });
 3236
 3237        // We remove any leading spaces
 3238        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3239        editor.change_selections(None, cx, |s| {
 3240            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3241        });
 3242        editor.join_lines(&JoinLines, cx);
 3243        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3244
 3245        // We don't insert a space for a line containing only spaces
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3248
 3249        // We ignore any leading tabs
 3250        editor.join_lines(&JoinLines, cx);
 3251        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3252
 3253        editor
 3254    });
 3255}
 3256
 3257#[gpui::test]
 3258fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3259    init_test(cx, |_| {});
 3260
 3261    cx.add_window(|cx| {
 3262        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3263        let mut editor = build_editor(buffer.clone(), cx);
 3264        let buffer = buffer.read(cx).as_singleton().unwrap();
 3265
 3266        editor.change_selections(None, cx, |s| {
 3267            s.select_ranges([
 3268                Point::new(0, 2)..Point::new(1, 1),
 3269                Point::new(1, 2)..Point::new(1, 2),
 3270                Point::new(3, 1)..Point::new(3, 2),
 3271            ])
 3272        });
 3273
 3274        editor.join_lines(&JoinLines, cx);
 3275        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3276
 3277        assert_eq!(
 3278            editor.selections.ranges::<Point>(cx),
 3279            [
 3280                Point::new(0, 7)..Point::new(0, 7),
 3281                Point::new(1, 3)..Point::new(1, 3)
 3282            ]
 3283        );
 3284        editor
 3285    });
 3286}
 3287
 3288#[gpui::test]
 3289async fn test_join_lines_with_git_diff_base(
 3290    executor: BackgroundExecutor,
 3291    cx: &mut gpui::TestAppContext,
 3292) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296
 3297    let diff_base = r#"
 3298        Line 0
 3299        Line 1
 3300        Line 2
 3301        Line 3
 3302        "#
 3303    .unindent();
 3304
 3305    cx.set_state(
 3306        &r#"
 3307        ˇLine 0
 3308        Line 1
 3309        Line 2
 3310        Line 3
 3311        "#
 3312        .unindent(),
 3313    );
 3314
 3315    cx.set_diff_base(Some(&diff_base));
 3316    executor.run_until_parked();
 3317
 3318    // Join lines
 3319    cx.update_editor(|editor, cx| {
 3320        editor.join_lines(&JoinLines, cx);
 3321    });
 3322    executor.run_until_parked();
 3323
 3324    cx.assert_editor_state(
 3325        &r#"
 3326        Line 0ˇ Line 1
 3327        Line 2
 3328        Line 3
 3329        "#
 3330        .unindent(),
 3331    );
 3332    // Join again
 3333    cx.update_editor(|editor, cx| {
 3334        editor.join_lines(&JoinLines, cx);
 3335    });
 3336    executor.run_until_parked();
 3337
 3338    cx.assert_editor_state(
 3339        &r#"
 3340        Line 0 Line 1ˇ Line 2
 3341        Line 3
 3342        "#
 3343        .unindent(),
 3344    );
 3345}
 3346
 3347#[gpui::test]
 3348async fn test_custom_newlines_cause_no_false_positive_diffs(
 3349    executor: BackgroundExecutor,
 3350    cx: &mut gpui::TestAppContext,
 3351) {
 3352    init_test(cx, |_| {});
 3353    let mut cx = EditorTestContext::new(cx).await;
 3354    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3355    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3356    executor.run_until_parked();
 3357
 3358    cx.update_editor(|editor, cx| {
 3359        assert_eq!(
 3360            editor
 3361                .buffer()
 3362                .read(cx)
 3363                .snapshot(cx)
 3364                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3365                .collect::<Vec<_>>(),
 3366            Vec::new(),
 3367            "Should not have any diffs for files with custom newlines"
 3368        );
 3369    });
 3370}
 3371
 3372#[gpui::test]
 3373async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3374    init_test(cx, |_| {});
 3375
 3376    let mut cx = EditorTestContext::new(cx).await;
 3377
 3378    // Test sort_lines_case_insensitive()
 3379    cx.set_state(indoc! {"
 3380        «z
 3381        y
 3382        x
 3383        Z
 3384        Y
 3385        Xˇ»
 3386    "});
 3387    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3388    cx.assert_editor_state(indoc! {"
 3389        «x
 3390        X
 3391        y
 3392        Y
 3393        z
 3394        Zˇ»
 3395    "});
 3396
 3397    // Test reverse_lines()
 3398    cx.set_state(indoc! {"
 3399        «5
 3400        4
 3401        3
 3402        2
 3403        1ˇ»
 3404    "});
 3405    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3406    cx.assert_editor_state(indoc! {"
 3407        «1
 3408        2
 3409        3
 3410        4
 3411        5ˇ»
 3412    "});
 3413
 3414    // Skip testing shuffle_line()
 3415
 3416    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3417    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3418
 3419    // Don't manipulate when cursor is on single line, but expand the selection
 3420    cx.set_state(indoc! {"
 3421        ddˇdd
 3422        ccc
 3423        bb
 3424        a
 3425    "});
 3426    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3427    cx.assert_editor_state(indoc! {"
 3428        «ddddˇ»
 3429        ccc
 3430        bb
 3431        a
 3432    "});
 3433
 3434    // Basic manipulate case
 3435    // Start selection moves to column 0
 3436    // End of selection shrinks to fit shorter line
 3437    cx.set_state(indoc! {"
 3438        dd«d
 3439        ccc
 3440        bb
 3441        aaaaaˇ»
 3442    "});
 3443    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3444    cx.assert_editor_state(indoc! {"
 3445        «aaaaa
 3446        bb
 3447        ccc
 3448        dddˇ»
 3449    "});
 3450
 3451    // Manipulate case with newlines
 3452    cx.set_state(indoc! {"
 3453        dd«d
 3454        ccc
 3455
 3456        bb
 3457        aaaaa
 3458
 3459        ˇ»
 3460    "});
 3461    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3462    cx.assert_editor_state(indoc! {"
 3463        «
 3464
 3465        aaaaa
 3466        bb
 3467        ccc
 3468        dddˇ»
 3469
 3470    "});
 3471
 3472    // Adding new line
 3473    cx.set_state(indoc! {"
 3474        aa«a
 3475        bbˇ»b
 3476    "});
 3477    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3478    cx.assert_editor_state(indoc! {"
 3479        «aaa
 3480        bbb
 3481        added_lineˇ»
 3482    "});
 3483
 3484    // Removing line
 3485    cx.set_state(indoc! {"
 3486        aa«a
 3487        bbbˇ»
 3488    "});
 3489    cx.update_editor(|e, cx| {
 3490        e.manipulate_lines(cx, |lines| {
 3491            lines.pop();
 3492        })
 3493    });
 3494    cx.assert_editor_state(indoc! {"
 3495        «aaaˇ»
 3496    "});
 3497
 3498    // Removing all lines
 3499    cx.set_state(indoc! {"
 3500        aa«a
 3501        bbbˇ»
 3502    "});
 3503    cx.update_editor(|e, cx| {
 3504        e.manipulate_lines(cx, |lines| {
 3505            lines.drain(..);
 3506        })
 3507    });
 3508    cx.assert_editor_state(indoc! {"
 3509        ˇ
 3510    "});
 3511}
 3512
 3513#[gpui::test]
 3514async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3515    init_test(cx, |_| {});
 3516
 3517    let mut cx = EditorTestContext::new(cx).await;
 3518
 3519    // Consider continuous selection as single selection
 3520    cx.set_state(indoc! {"
 3521        Aaa«aa
 3522        cˇ»c«c
 3523        bb
 3524        aaaˇ»aa
 3525    "});
 3526    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3527    cx.assert_editor_state(indoc! {"
 3528        «Aaaaa
 3529        ccc
 3530        bb
 3531        aaaaaˇ»
 3532    "});
 3533
 3534    cx.set_state(indoc! {"
 3535        Aaa«aa
 3536        cˇ»c«c
 3537        bb
 3538        aaaˇ»aa
 3539    "});
 3540    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3541    cx.assert_editor_state(indoc! {"
 3542        «Aaaaa
 3543        ccc
 3544        bbˇ»
 3545    "});
 3546
 3547    // Consider non continuous selection as distinct dedup operations
 3548    cx.set_state(indoc! {"
 3549        «aaaaa
 3550        bb
 3551        aaaaa
 3552        aaaaaˇ»
 3553
 3554        aaa«aaˇ»
 3555    "});
 3556    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3557    cx.assert_editor_state(indoc! {"
 3558        «aaaaa
 3559        bbˇ»
 3560
 3561        «aaaaaˇ»
 3562    "});
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    cx.set_state(indoc! {"
 3572        «Aaa
 3573        aAa
 3574        Aaaˇ»
 3575    "});
 3576    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3577    cx.assert_editor_state(indoc! {"
 3578        «Aaa
 3579        aAaˇ»
 3580    "});
 3581
 3582    cx.set_state(indoc! {"
 3583        «Aaa
 3584        aAa
 3585        aaAˇ»
 3586    "});
 3587    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3588    cx.assert_editor_state(indoc! {"
 3589        «Aaaˇ»
 3590    "});
 3591}
 3592
 3593#[gpui::test]
 3594async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3595    init_test(cx, |_| {});
 3596
 3597    let mut cx = EditorTestContext::new(cx).await;
 3598
 3599    // Manipulate with multiple selections on a single line
 3600    cx.set_state(indoc! {"
 3601        dd«dd
 3602        cˇ»c«c
 3603        bb
 3604        aaaˇ»aa
 3605    "});
 3606    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3607    cx.assert_editor_state(indoc! {"
 3608        «aaaaa
 3609        bb
 3610        ccc
 3611        ddddˇ»
 3612    "});
 3613
 3614    // Manipulate with multiple disjoin selections
 3615    cx.set_state(indoc! {"
 3616 3617        4
 3618        3
 3619        2
 3620        1ˇ»
 3621
 3622        dd«dd
 3623        ccc
 3624        bb
 3625        aaaˇ»aa
 3626    "});
 3627    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3628    cx.assert_editor_state(indoc! {"
 3629        «1
 3630        2
 3631        3
 3632        4
 3633        5ˇ»
 3634
 3635        «aaaaa
 3636        bb
 3637        ccc
 3638        ddddˇ»
 3639    "});
 3640
 3641    // Adding lines on each selection
 3642    cx.set_state(indoc! {"
 3643 3644        1ˇ»
 3645
 3646        bb«bb
 3647        aaaˇ»aa
 3648    "});
 3649    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3650    cx.assert_editor_state(indoc! {"
 3651        «2
 3652        1
 3653        added lineˇ»
 3654
 3655        «bbbb
 3656        aaaaa
 3657        added lineˇ»
 3658    "});
 3659
 3660    // Removing lines on each selection
 3661    cx.set_state(indoc! {"
 3662 3663        1ˇ»
 3664
 3665        bb«bb
 3666        aaaˇ»aa
 3667    "});
 3668    cx.update_editor(|e, cx| {
 3669        e.manipulate_lines(cx, |lines| {
 3670            lines.pop();
 3671        })
 3672    });
 3673    cx.assert_editor_state(indoc! {"
 3674        «2ˇ»
 3675
 3676        «bbbbˇ»
 3677    "});
 3678}
 3679
 3680#[gpui::test]
 3681async fn test_manipulate_text(cx: &mut TestAppContext) {
 3682    init_test(cx, |_| {});
 3683
 3684    let mut cx = EditorTestContext::new(cx).await;
 3685
 3686    // Test convert_to_upper_case()
 3687    cx.set_state(indoc! {"
 3688        «hello worldˇ»
 3689    "});
 3690    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3691    cx.assert_editor_state(indoc! {"
 3692        «HELLO WORLDˇ»
 3693    "});
 3694
 3695    // Test convert_to_lower_case()
 3696    cx.set_state(indoc! {"
 3697        «HELLO WORLDˇ»
 3698    "});
 3699    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3700    cx.assert_editor_state(indoc! {"
 3701        «hello worldˇ»
 3702    "});
 3703
 3704    // Test multiple line, single selection case
 3705    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3706    cx.set_state(indoc! {"
 3707        «The quick brown
 3708        fox jumps over
 3709        the lazy dogˇ»
 3710    "});
 3711    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3712    cx.assert_editor_state(indoc! {"
 3713        «The Quick Brown
 3714        Fox Jumps Over
 3715        The Lazy Dogˇ»
 3716    "});
 3717
 3718    // Test multiple line, single selection case
 3719    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3720    cx.set_state(indoc! {"
 3721        «The quick brown
 3722        fox jumps over
 3723        the lazy dogˇ»
 3724    "});
 3725    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3726    cx.assert_editor_state(indoc! {"
 3727        «TheQuickBrown
 3728        FoxJumpsOver
 3729        TheLazyDogˇ»
 3730    "});
 3731
 3732    // From here on out, test more complex cases of manipulate_text()
 3733
 3734    // Test no selection case - should affect words cursors are in
 3735    // Cursor at beginning, middle, and end of word
 3736    cx.set_state(indoc! {"
 3737        ˇhello big beauˇtiful worldˇ
 3738    "});
 3739    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3740    cx.assert_editor_state(indoc! {"
 3741        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3742    "});
 3743
 3744    // Test multiple selections on a single line and across multiple lines
 3745    cx.set_state(indoc! {"
 3746        «Theˇ» quick «brown
 3747        foxˇ» jumps «overˇ»
 3748        the «lazyˇ» dog
 3749    "});
 3750    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3751    cx.assert_editor_state(indoc! {"
 3752        «THEˇ» quick «BROWN
 3753        FOXˇ» jumps «OVERˇ»
 3754        the «LAZYˇ» dog
 3755    "});
 3756
 3757    // Test case where text length grows
 3758    cx.set_state(indoc! {"
 3759        «tschüߡ»
 3760    "});
 3761    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3762    cx.assert_editor_state(indoc! {"
 3763        «TSCHÜSSˇ»
 3764    "});
 3765
 3766    // Test to make sure we don't crash when text shrinks
 3767    cx.set_state(indoc! {"
 3768        aaa_bbbˇ
 3769    "});
 3770    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3771    cx.assert_editor_state(indoc! {"
 3772        «aaaBbbˇ»
 3773    "});
 3774
 3775    // Test to make sure we all aware of the fact that each word can grow and shrink
 3776    // Final selections should be aware of this fact
 3777    cx.set_state(indoc! {"
 3778        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3779    "});
 3780    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3781    cx.assert_editor_state(indoc! {"
 3782        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3783    "});
 3784
 3785    cx.set_state(indoc! {"
 3786        «hElLo, WoRld!ˇ»
 3787    "});
 3788    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3789    cx.assert_editor_state(indoc! {"
 3790        «HeLlO, wOrLD!ˇ»
 3791    "});
 3792}
 3793
 3794#[gpui::test]
 3795fn test_duplicate_line(cx: &mut TestAppContext) {
 3796    init_test(cx, |_| {});
 3797
 3798    let view = cx.add_window(|cx| {
 3799        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3800        build_editor(buffer, cx)
 3801    });
 3802    _ = view.update(cx, |view, cx| {
 3803        view.change_selections(None, cx, |s| {
 3804            s.select_display_ranges([
 3805                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3806                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3807                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3808                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3809            ])
 3810        });
 3811        view.duplicate_line_down(&DuplicateLineDown, cx);
 3812        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3813        assert_eq!(
 3814            view.selections.display_ranges(cx),
 3815            vec![
 3816                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3817                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3818                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3819                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3820            ]
 3821        );
 3822    });
 3823
 3824    let view = cx.add_window(|cx| {
 3825        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3826        build_editor(buffer, cx)
 3827    });
 3828    _ = view.update(cx, |view, cx| {
 3829        view.change_selections(None, cx, |s| {
 3830            s.select_display_ranges([
 3831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3833            ])
 3834        });
 3835        view.duplicate_line_down(&DuplicateLineDown, cx);
 3836        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3837        assert_eq!(
 3838            view.selections.display_ranges(cx),
 3839            vec![
 3840                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3841                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3842            ]
 3843        );
 3844    });
 3845
 3846    // With `move_upwards` the selections stay in place, except for
 3847    // the lines inserted above them
 3848    let view = cx.add_window(|cx| {
 3849        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3850        build_editor(buffer, cx)
 3851    });
 3852    _ = view.update(cx, |view, cx| {
 3853        view.change_selections(None, cx, |s| {
 3854            s.select_display_ranges([
 3855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3856                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3857                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3858                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3859            ])
 3860        });
 3861        view.duplicate_line_up(&DuplicateLineUp, cx);
 3862        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3863        assert_eq!(
 3864            view.selections.display_ranges(cx),
 3865            vec![
 3866                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3868                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3869                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3870            ]
 3871        );
 3872    });
 3873
 3874    let view = cx.add_window(|cx| {
 3875        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3876        build_editor(buffer, cx)
 3877    });
 3878    _ = view.update(cx, |view, cx| {
 3879        view.change_selections(None, cx, |s| {
 3880            s.select_display_ranges([
 3881                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3882                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3883            ])
 3884        });
 3885        view.duplicate_line_up(&DuplicateLineUp, cx);
 3886        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3887        assert_eq!(
 3888            view.selections.display_ranges(cx),
 3889            vec![
 3890                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3891                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3892            ]
 3893        );
 3894    });
 3895}
 3896
 3897#[gpui::test]
 3898fn test_move_line_up_down(cx: &mut TestAppContext) {
 3899    init_test(cx, |_| {});
 3900
 3901    let view = cx.add_window(|cx| {
 3902        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3903        build_editor(buffer, cx)
 3904    });
 3905    _ = view.update(cx, |view, cx| {
 3906        view.fold_creases(
 3907            vec![
 3908                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3909                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3910                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3911            ],
 3912            true,
 3913            cx,
 3914        );
 3915        view.change_selections(None, cx, |s| {
 3916            s.select_display_ranges([
 3917                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3918                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3919                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3920                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3921            ])
 3922        });
 3923        assert_eq!(
 3924            view.display_text(cx),
 3925            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3926        );
 3927
 3928        view.move_line_up(&MoveLineUp, cx);
 3929        assert_eq!(
 3930            view.display_text(cx),
 3931            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3932        );
 3933        assert_eq!(
 3934            view.selections.display_ranges(cx),
 3935            vec![
 3936                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3937                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3938                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3939                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3940            ]
 3941        );
 3942    });
 3943
 3944    _ = view.update(cx, |view, cx| {
 3945        view.move_line_down(&MoveLineDown, cx);
 3946        assert_eq!(
 3947            view.display_text(cx),
 3948            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3949        );
 3950        assert_eq!(
 3951            view.selections.display_ranges(cx),
 3952            vec![
 3953                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3955                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3956                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3957            ]
 3958        );
 3959    });
 3960
 3961    _ = view.update(cx, |view, cx| {
 3962        view.move_line_down(&MoveLineDown, cx);
 3963        assert_eq!(
 3964            view.display_text(cx),
 3965            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3966        );
 3967        assert_eq!(
 3968            view.selections.display_ranges(cx),
 3969            vec![
 3970                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3971                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3972                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3973                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3974            ]
 3975        );
 3976    });
 3977
 3978    _ = view.update(cx, |view, cx| {
 3979        view.move_line_up(&MoveLineUp, cx);
 3980        assert_eq!(
 3981            view.display_text(cx),
 3982            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3983        );
 3984        assert_eq!(
 3985            view.selections.display_ranges(cx),
 3986            vec![
 3987                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3988                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3989                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3990                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3991            ]
 3992        );
 3993    });
 3994}
 3995
 3996#[gpui::test]
 3997fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3998    init_test(cx, |_| {});
 3999
 4000    let editor = cx.add_window(|cx| {
 4001        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4002        build_editor(buffer, cx)
 4003    });
 4004    _ = editor.update(cx, |editor, cx| {
 4005        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4006        editor.insert_blocks(
 4007            [BlockProperties {
 4008                style: BlockStyle::Fixed,
 4009                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4010                height: 1,
 4011                render: Arc::new(|_| div().into_any()),
 4012                priority: 0,
 4013            }],
 4014            Some(Autoscroll::fit()),
 4015            cx,
 4016        );
 4017        editor.change_selections(None, cx, |s| {
 4018            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4019        });
 4020        editor.move_line_down(&MoveLineDown, cx);
 4021    });
 4022}
 4023
 4024#[gpui::test]
 4025async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4026    init_test(cx, |_| {});
 4027
 4028    let mut cx = EditorTestContext::new(cx).await;
 4029    cx.set_state(
 4030        &"
 4031            ˇzero
 4032            one
 4033            two
 4034            three
 4035            four
 4036            five
 4037        "
 4038        .unindent(),
 4039    );
 4040
 4041    // Create a four-line block that replaces three lines of text.
 4042    cx.update_editor(|editor, cx| {
 4043        let snapshot = editor.snapshot(cx);
 4044        let snapshot = &snapshot.buffer_snapshot;
 4045        let placement = BlockPlacement::Replace(
 4046            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4047        );
 4048        editor.insert_blocks(
 4049            [BlockProperties {
 4050                placement,
 4051                height: 4,
 4052                style: BlockStyle::Sticky,
 4053                render: Arc::new(|_| gpui::div().into_any_element()),
 4054                priority: 0,
 4055            }],
 4056            None,
 4057            cx,
 4058        );
 4059    });
 4060
 4061    // Move down so that the cursor touches the block.
 4062    cx.update_editor(|editor, cx| {
 4063        editor.move_down(&Default::default(), cx);
 4064    });
 4065    cx.assert_editor_state(
 4066        &"
 4067            zero
 4068            «one
 4069            two
 4070            threeˇ»
 4071            four
 4072            five
 4073        "
 4074        .unindent(),
 4075    );
 4076
 4077    // Move down past the block.
 4078    cx.update_editor(|editor, cx| {
 4079        editor.move_down(&Default::default(), cx);
 4080    });
 4081    cx.assert_editor_state(
 4082        &"
 4083            zero
 4084            one
 4085            two
 4086            three
 4087            ˇfour
 4088            five
 4089        "
 4090        .unindent(),
 4091    );
 4092}
 4093
 4094#[gpui::test]
 4095fn test_transpose(cx: &mut TestAppContext) {
 4096    init_test(cx, |_| {});
 4097
 4098    _ = cx.add_window(|cx| {
 4099        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4100        editor.set_style(EditorStyle::default(), cx);
 4101        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "bac");
 4104        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4105
 4106        editor.transpose(&Default::default(), cx);
 4107        assert_eq!(editor.text(cx), "bca");
 4108        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4109
 4110        editor.transpose(&Default::default(), cx);
 4111        assert_eq!(editor.text(cx), "bac");
 4112        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4113
 4114        editor
 4115    });
 4116
 4117    _ = cx.add_window(|cx| {
 4118        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4119        editor.set_style(EditorStyle::default(), cx);
 4120        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "acb\nde");
 4123        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4124
 4125        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4126        editor.transpose(&Default::default(), cx);
 4127        assert_eq!(editor.text(cx), "acbd\ne");
 4128        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4129
 4130        editor.transpose(&Default::default(), cx);
 4131        assert_eq!(editor.text(cx), "acbde\n");
 4132        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4133
 4134        editor.transpose(&Default::default(), cx);
 4135        assert_eq!(editor.text(cx), "acbd\ne");
 4136        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4137
 4138        editor
 4139    });
 4140
 4141    _ = cx.add_window(|cx| {
 4142        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4143        editor.set_style(EditorStyle::default(), cx);
 4144        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4145        editor.transpose(&Default::default(), cx);
 4146        assert_eq!(editor.text(cx), "bacd\ne");
 4147        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4148
 4149        editor.transpose(&Default::default(), cx);
 4150        assert_eq!(editor.text(cx), "bcade\n");
 4151        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4152
 4153        editor.transpose(&Default::default(), cx);
 4154        assert_eq!(editor.text(cx), "bcda\ne");
 4155        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4156
 4157        editor.transpose(&Default::default(), cx);
 4158        assert_eq!(editor.text(cx), "bcade\n");
 4159        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4160
 4161        editor.transpose(&Default::default(), cx);
 4162        assert_eq!(editor.text(cx), "bcaed\n");
 4163        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4164
 4165        editor
 4166    });
 4167
 4168    _ = cx.add_window(|cx| {
 4169        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4170        editor.set_style(EditorStyle::default(), cx);
 4171        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4172        editor.transpose(&Default::default(), cx);
 4173        assert_eq!(editor.text(cx), "🏀🍐✋");
 4174        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4175
 4176        editor.transpose(&Default::default(), cx);
 4177        assert_eq!(editor.text(cx), "🏀✋🍐");
 4178        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4179
 4180        editor.transpose(&Default::default(), cx);
 4181        assert_eq!(editor.text(cx), "🏀🍐✋");
 4182        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4183
 4184        editor
 4185    });
 4186}
 4187
 4188#[gpui::test]
 4189async fn test_rewrap(cx: &mut TestAppContext) {
 4190    init_test(cx, |_| {});
 4191
 4192    let mut cx = EditorTestContext::new(cx).await;
 4193
 4194    let language_with_c_comments = Arc::new(Language::new(
 4195        LanguageConfig {
 4196            line_comments: vec!["// ".into()],
 4197            ..LanguageConfig::default()
 4198        },
 4199        None,
 4200    ));
 4201    let language_with_pound_comments = Arc::new(Language::new(
 4202        LanguageConfig {
 4203            line_comments: vec!["# ".into()],
 4204            ..LanguageConfig::default()
 4205        },
 4206        None,
 4207    ));
 4208    let markdown_language = Arc::new(Language::new(
 4209        LanguageConfig {
 4210            name: "Markdown".into(),
 4211            ..LanguageConfig::default()
 4212        },
 4213        None,
 4214    ));
 4215    let language_with_doc_comments = Arc::new(Language::new(
 4216        LanguageConfig {
 4217            line_comments: vec!["// ".into(), "/// ".into()],
 4218            ..LanguageConfig::default()
 4219        },
 4220        Some(tree_sitter_rust::LANGUAGE.into()),
 4221    ));
 4222
 4223    let plaintext_language = Arc::new(Language::new(
 4224        LanguageConfig {
 4225            name: "Plain Text".into(),
 4226            ..LanguageConfig::default()
 4227        },
 4228        None,
 4229    ));
 4230
 4231    assert_rewrap(
 4232        indoc! {"
 4233            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4234        "},
 4235        indoc! {"
 4236            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4237            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4238            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4239            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4240            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4241            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4242            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4243            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4244            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4245            // porttitor id. Aliquam id accumsan eros.
 4246        "},
 4247        language_with_c_comments.clone(),
 4248        &mut cx,
 4249    );
 4250
 4251    // Test that rewrapping works inside of a selection
 4252    assert_rewrap(
 4253        indoc! {"
 4254            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4255        "},
 4256        indoc! {"
 4257            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4258            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4259            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4260            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4261            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4262            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4263            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4264            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4265            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4266            // porttitor id. Aliquam id accumsan eros.ˇ»
 4267        "},
 4268        language_with_c_comments.clone(),
 4269        &mut cx,
 4270    );
 4271
 4272    // Test that cursors that expand to the same region are collapsed.
 4273    assert_rewrap(
 4274        indoc! {"
 4275            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4276            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4277            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4278            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4279        "},
 4280        indoc! {"
 4281            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4282            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4283            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4284            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4285            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4286            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4287            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4288            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4289            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4290            // porttitor id. Aliquam id accumsan eros.
 4291        "},
 4292        language_with_c_comments.clone(),
 4293        &mut cx,
 4294    );
 4295
 4296    // Test that non-contiguous selections are treated separately.
 4297    assert_rewrap(
 4298        indoc! {"
 4299            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4300            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4301            //
 4302            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4303            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4304        "},
 4305        indoc! {"
 4306            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4307            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4308            // auctor, eu lacinia sapien scelerisque.
 4309            //
 4310            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4311            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4312            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4313            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4314            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4315            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4316            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4317        "},
 4318        language_with_c_comments.clone(),
 4319        &mut cx,
 4320    );
 4321
 4322    // Test that different comment prefixes are supported.
 4323    assert_rewrap(
 4324        indoc! {"
 4325            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4326        "},
 4327        indoc! {"
 4328            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4329            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4330            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4331            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4332            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4333            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4334            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4335            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4336            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4337            # accumsan eros.
 4338        "},
 4339        language_with_pound_comments.clone(),
 4340        &mut cx,
 4341    );
 4342
 4343    // Test that rewrapping is ignored outside of comments in most languages.
 4344    assert_rewrap(
 4345        indoc! {"
 4346            /// Adds two numbers.
 4347            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4348            fn add(a: u32, b: u32) -> u32 {
 4349                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4350            }
 4351        "},
 4352        indoc! {"
 4353            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4354            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4355            fn add(a: u32, b: u32) -> u32 {
 4356                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4357            }
 4358        "},
 4359        language_with_doc_comments.clone(),
 4360        &mut cx,
 4361    );
 4362
 4363    // Test that rewrapping works in Markdown and Plain Text languages.
 4364    assert_rewrap(
 4365        indoc! {"
 4366            # Hello
 4367
 4368            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4369        "},
 4370        indoc! {"
 4371            # Hello
 4372
 4373            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4374            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4375            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4376            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4377            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4378            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4379            Integer sit amet scelerisque nisi.
 4380        "},
 4381        markdown_language,
 4382        &mut cx,
 4383    );
 4384
 4385    assert_rewrap(
 4386        indoc! {"
 4387            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4388        "},
 4389        indoc! {"
 4390            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4391            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4392            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4393            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4394            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4395            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4396            Integer sit amet scelerisque nisi.
 4397        "},
 4398        plaintext_language,
 4399        &mut cx,
 4400    );
 4401
 4402    // Test rewrapping unaligned comments in a selection.
 4403    assert_rewrap(
 4404        indoc! {"
 4405            fn foo() {
 4406                if true {
 4407            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4408            // Praesent semper egestas tellus id dignissim.ˇ»
 4409                    do_something();
 4410                } else {
 4411                    //
 4412                }
 4413            }
 4414        "},
 4415        indoc! {"
 4416            fn foo() {
 4417                if true {
 4418            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4419                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4420                    // egestas tellus id dignissim.ˇ»
 4421                    do_something();
 4422                } else {
 4423                    //
 4424                }
 4425            }
 4426        "},
 4427        language_with_doc_comments.clone(),
 4428        &mut cx,
 4429    );
 4430
 4431    assert_rewrap(
 4432        indoc! {"
 4433            fn foo() {
 4434                if true {
 4435            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4436            // Praesent semper egestas tellus id dignissim.»
 4437                    do_something();
 4438                } else {
 4439                    //
 4440                }
 4441
 4442            }
 4443        "},
 4444        indoc! {"
 4445            fn foo() {
 4446                if true {
 4447            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4448                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4449                    // egestas tellus id dignissim.»
 4450                    do_something();
 4451                } else {
 4452                    //
 4453                }
 4454
 4455            }
 4456        "},
 4457        language_with_doc_comments.clone(),
 4458        &mut cx,
 4459    );
 4460
 4461    #[track_caller]
 4462    fn assert_rewrap(
 4463        unwrapped_text: &str,
 4464        wrapped_text: &str,
 4465        language: Arc<Language>,
 4466        cx: &mut EditorTestContext,
 4467    ) {
 4468        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4469        cx.set_state(unwrapped_text);
 4470        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4471        cx.assert_editor_state(wrapped_text);
 4472    }
 4473}
 4474
 4475#[gpui::test]
 4476async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4477    init_test(cx, |_| {});
 4478
 4479    let mut cx = EditorTestContext::new(cx).await;
 4480
 4481    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4482    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4483    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4484
 4485    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4486    cx.set_state("two ˇfour ˇsix ˇ");
 4487    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4488    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4489
 4490    // Paste again but with only two cursors. Since the number of cursors doesn't
 4491    // match the number of slices in the clipboard, the entire clipboard text
 4492    // is pasted at each cursor.
 4493    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4494    cx.update_editor(|e, cx| {
 4495        e.handle_input("( ", cx);
 4496        e.paste(&Paste, cx);
 4497        e.handle_input(") ", cx);
 4498    });
 4499    cx.assert_editor_state(
 4500        &([
 4501            "( one✅ ",
 4502            "three ",
 4503            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4504            "three ",
 4505            "five ) ˇ",
 4506        ]
 4507        .join("\n")),
 4508    );
 4509
 4510    // Cut with three selections, one of which is full-line.
 4511    cx.set_state(indoc! {"
 4512        1«2ˇ»3
 4513        4ˇ567
 4514        «8ˇ»9"});
 4515    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4516    cx.assert_editor_state(indoc! {"
 4517        1ˇ3
 4518        ˇ9"});
 4519
 4520    // Paste with three selections, noticing how the copied selection that was full-line
 4521    // gets inserted before the second cursor.
 4522    cx.set_state(indoc! {"
 4523        1ˇ3
 4524 4525        «oˇ»ne"});
 4526    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4527    cx.assert_editor_state(indoc! {"
 4528        12ˇ3
 4529        4567
 4530 4531        8ˇne"});
 4532
 4533    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4534    cx.set_state(indoc! {"
 4535        The quick brown
 4536        fox juˇmps over
 4537        the lazy dog"});
 4538    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4539    assert_eq!(
 4540        cx.read_from_clipboard()
 4541            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4542        Some("fox jumps over\n".to_string())
 4543    );
 4544
 4545    // Paste with three selections, noticing how the copied full-line selection is inserted
 4546    // before the empty selections but replaces the selection that is non-empty.
 4547    cx.set_state(indoc! {"
 4548        Tˇhe quick brown
 4549        «foˇ»x jumps over
 4550        tˇhe lazy dog"});
 4551    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4552    cx.assert_editor_state(indoc! {"
 4553        fox jumps over
 4554        Tˇhe quick brown
 4555        fox jumps over
 4556        ˇx jumps over
 4557        fox jumps over
 4558        tˇhe lazy dog"});
 4559}
 4560
 4561#[gpui::test]
 4562async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4563    init_test(cx, |_| {});
 4564
 4565    let mut cx = EditorTestContext::new(cx).await;
 4566    let language = Arc::new(Language::new(
 4567        LanguageConfig::default(),
 4568        Some(tree_sitter_rust::LANGUAGE.into()),
 4569    ));
 4570    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4571
 4572    // Cut an indented block, without the leading whitespace.
 4573    cx.set_state(indoc! {"
 4574        const a: B = (
 4575            c(),
 4576            «d(
 4577                e,
 4578                f
 4579            )ˇ»
 4580        );
 4581    "});
 4582    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4583    cx.assert_editor_state(indoc! {"
 4584        const a: B = (
 4585            c(),
 4586            ˇ
 4587        );
 4588    "});
 4589
 4590    // Paste it at the same position.
 4591    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4592    cx.assert_editor_state(indoc! {"
 4593        const a: B = (
 4594            c(),
 4595            d(
 4596                e,
 4597                f
 4598 4599        );
 4600    "});
 4601
 4602    // Paste it at a line with a lower indent level.
 4603    cx.set_state(indoc! {"
 4604        ˇ
 4605        const a: B = (
 4606            c(),
 4607        );
 4608    "});
 4609    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4610    cx.assert_editor_state(indoc! {"
 4611        d(
 4612            e,
 4613            f
 4614 4615        const a: B = (
 4616            c(),
 4617        );
 4618    "});
 4619
 4620    // Cut an indented block, with the leading whitespace.
 4621    cx.set_state(indoc! {"
 4622        const a: B = (
 4623            c(),
 4624        «    d(
 4625                e,
 4626                f
 4627            )
 4628        ˇ»);
 4629    "});
 4630    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4631    cx.assert_editor_state(indoc! {"
 4632        const a: B = (
 4633            c(),
 4634        ˇ);
 4635    "});
 4636
 4637    // Paste it at the same position.
 4638    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4639    cx.assert_editor_state(indoc! {"
 4640        const a: B = (
 4641            c(),
 4642            d(
 4643                e,
 4644                f
 4645            )
 4646        ˇ);
 4647    "});
 4648
 4649    // Paste it at a line with a higher indent level.
 4650    cx.set_state(indoc! {"
 4651        const a: B = (
 4652            c(),
 4653            d(
 4654                e,
 4655 4656            )
 4657        );
 4658    "});
 4659    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4660    cx.assert_editor_state(indoc! {"
 4661        const a: B = (
 4662            c(),
 4663            d(
 4664                e,
 4665                f    d(
 4666                    e,
 4667                    f
 4668                )
 4669        ˇ
 4670            )
 4671        );
 4672    "});
 4673}
 4674
 4675#[gpui::test]
 4676fn test_select_all(cx: &mut TestAppContext) {
 4677    init_test(cx, |_| {});
 4678
 4679    let view = cx.add_window(|cx| {
 4680        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4681        build_editor(buffer, cx)
 4682    });
 4683    _ = view.update(cx, |view, cx| {
 4684        view.select_all(&SelectAll, cx);
 4685        assert_eq!(
 4686            view.selections.display_ranges(cx),
 4687            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4688        );
 4689    });
 4690}
 4691
 4692#[gpui::test]
 4693fn test_select_line(cx: &mut TestAppContext) {
 4694    init_test(cx, |_| {});
 4695
 4696    let view = cx.add_window(|cx| {
 4697        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4698        build_editor(buffer, cx)
 4699    });
 4700    _ = view.update(cx, |view, cx| {
 4701        view.change_selections(None, cx, |s| {
 4702            s.select_display_ranges([
 4703                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4704                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4705                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4706                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4707            ])
 4708        });
 4709        view.select_line(&SelectLine, cx);
 4710        assert_eq!(
 4711            view.selections.display_ranges(cx),
 4712            vec![
 4713                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4714                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4715            ]
 4716        );
 4717    });
 4718
 4719    _ = view.update(cx, |view, cx| {
 4720        view.select_line(&SelectLine, cx);
 4721        assert_eq!(
 4722            view.selections.display_ranges(cx),
 4723            vec![
 4724                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4725                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4726            ]
 4727        );
 4728    });
 4729
 4730    _ = view.update(cx, |view, cx| {
 4731        view.select_line(&SelectLine, cx);
 4732        assert_eq!(
 4733            view.selections.display_ranges(cx),
 4734            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4735        );
 4736    });
 4737}
 4738
 4739#[gpui::test]
 4740fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4741    init_test(cx, |_| {});
 4742
 4743    let view = cx.add_window(|cx| {
 4744        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4745        build_editor(buffer, cx)
 4746    });
 4747    _ = view.update(cx, |view, cx| {
 4748        view.fold_creases(
 4749            vec![
 4750                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4751                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4752                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4753            ],
 4754            true,
 4755            cx,
 4756        );
 4757        view.change_selections(None, cx, |s| {
 4758            s.select_display_ranges([
 4759                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4760                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4761                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4762                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4763            ])
 4764        });
 4765        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4766    });
 4767
 4768    _ = view.update(cx, |view, cx| {
 4769        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4770        assert_eq!(
 4771            view.display_text(cx),
 4772            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4773        );
 4774        assert_eq!(
 4775            view.selections.display_ranges(cx),
 4776            [
 4777                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4778                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4779                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4780                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4781            ]
 4782        );
 4783    });
 4784
 4785    _ = view.update(cx, |view, cx| {
 4786        view.change_selections(None, cx, |s| {
 4787            s.select_display_ranges([
 4788                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4789            ])
 4790        });
 4791        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4792        assert_eq!(
 4793            view.display_text(cx),
 4794            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4795        );
 4796        assert_eq!(
 4797            view.selections.display_ranges(cx),
 4798            [
 4799                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4800                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4801                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4802                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4803                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4804                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4805                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4806                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4807            ]
 4808        );
 4809    });
 4810}
 4811
 4812#[gpui::test]
 4813async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4814    init_test(cx, |_| {});
 4815
 4816    let mut cx = EditorTestContext::new(cx).await;
 4817
 4818    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4819    cx.set_state(indoc!(
 4820        r#"abc
 4821           defˇghi
 4822
 4823           jk
 4824           nlmo
 4825           "#
 4826    ));
 4827
 4828    cx.update_editor(|editor, cx| {
 4829        editor.add_selection_above(&Default::default(), cx);
 4830    });
 4831
 4832    cx.assert_editor_state(indoc!(
 4833        r#"abcˇ
 4834           defˇghi
 4835
 4836           jk
 4837           nlmo
 4838           "#
 4839    ));
 4840
 4841    cx.update_editor(|editor, cx| {
 4842        editor.add_selection_above(&Default::default(), cx);
 4843    });
 4844
 4845    cx.assert_editor_state(indoc!(
 4846        r#"abcˇ
 4847            defˇghi
 4848
 4849            jk
 4850            nlmo
 4851            "#
 4852    ));
 4853
 4854    cx.update_editor(|view, cx| {
 4855        view.add_selection_below(&Default::default(), cx);
 4856    });
 4857
 4858    cx.assert_editor_state(indoc!(
 4859        r#"abc
 4860           defˇghi
 4861
 4862           jk
 4863           nlmo
 4864           "#
 4865    ));
 4866
 4867    cx.update_editor(|view, cx| {
 4868        view.undo_selection(&Default::default(), cx);
 4869    });
 4870
 4871    cx.assert_editor_state(indoc!(
 4872        r#"abcˇ
 4873           defˇghi
 4874
 4875           jk
 4876           nlmo
 4877           "#
 4878    ));
 4879
 4880    cx.update_editor(|view, cx| {
 4881        view.redo_selection(&Default::default(), cx);
 4882    });
 4883
 4884    cx.assert_editor_state(indoc!(
 4885        r#"abc
 4886           defˇghi
 4887
 4888           jk
 4889           nlmo
 4890           "#
 4891    ));
 4892
 4893    cx.update_editor(|view, cx| {
 4894        view.add_selection_below(&Default::default(), cx);
 4895    });
 4896
 4897    cx.assert_editor_state(indoc!(
 4898        r#"abc
 4899           defˇghi
 4900
 4901           jk
 4902           nlmˇo
 4903           "#
 4904    ));
 4905
 4906    cx.update_editor(|view, cx| {
 4907        view.add_selection_below(&Default::default(), cx);
 4908    });
 4909
 4910    cx.assert_editor_state(indoc!(
 4911        r#"abc
 4912           defˇghi
 4913
 4914           jk
 4915           nlmˇo
 4916           "#
 4917    ));
 4918
 4919    // change selections
 4920    cx.set_state(indoc!(
 4921        r#"abc
 4922           def«ˇg»hi
 4923
 4924           jk
 4925           nlmo
 4926           "#
 4927    ));
 4928
 4929    cx.update_editor(|view, cx| {
 4930        view.add_selection_below(&Default::default(), cx);
 4931    });
 4932
 4933    cx.assert_editor_state(indoc!(
 4934        r#"abc
 4935           def«ˇg»hi
 4936
 4937           jk
 4938           nlm«ˇo»
 4939           "#
 4940    ));
 4941
 4942    cx.update_editor(|view, cx| {
 4943        view.add_selection_below(&Default::default(), cx);
 4944    });
 4945
 4946    cx.assert_editor_state(indoc!(
 4947        r#"abc
 4948           def«ˇg»hi
 4949
 4950           jk
 4951           nlm«ˇo»
 4952           "#
 4953    ));
 4954
 4955    cx.update_editor(|view, cx| {
 4956        view.add_selection_above(&Default::default(), cx);
 4957    });
 4958
 4959    cx.assert_editor_state(indoc!(
 4960        r#"abc
 4961           def«ˇg»hi
 4962
 4963           jk
 4964           nlmo
 4965           "#
 4966    ));
 4967
 4968    cx.update_editor(|view, cx| {
 4969        view.add_selection_above(&Default::default(), cx);
 4970    });
 4971
 4972    cx.assert_editor_state(indoc!(
 4973        r#"abc
 4974           def«ˇg»hi
 4975
 4976           jk
 4977           nlmo
 4978           "#
 4979    ));
 4980
 4981    // Change selections again
 4982    cx.set_state(indoc!(
 4983        r#"a«bc
 4984           defgˇ»hi
 4985
 4986           jk
 4987           nlmo
 4988           "#
 4989    ));
 4990
 4991    cx.update_editor(|view, cx| {
 4992        view.add_selection_below(&Default::default(), cx);
 4993    });
 4994
 4995    cx.assert_editor_state(indoc!(
 4996        r#"a«bcˇ»
 4997           d«efgˇ»hi
 4998
 4999           j«kˇ»
 5000           nlmo
 5001           "#
 5002    ));
 5003
 5004    cx.update_editor(|view, cx| {
 5005        view.add_selection_below(&Default::default(), cx);
 5006    });
 5007    cx.assert_editor_state(indoc!(
 5008        r#"a«bcˇ»
 5009           d«efgˇ»hi
 5010
 5011           j«kˇ»
 5012           n«lmoˇ»
 5013           "#
 5014    ));
 5015    cx.update_editor(|view, cx| {
 5016        view.add_selection_above(&Default::default(), cx);
 5017    });
 5018
 5019    cx.assert_editor_state(indoc!(
 5020        r#"a«bcˇ»
 5021           d«efgˇ»hi
 5022
 5023           j«kˇ»
 5024           nlmo
 5025           "#
 5026    ));
 5027
 5028    // Change selections again
 5029    cx.set_state(indoc!(
 5030        r#"abc
 5031           d«ˇefghi
 5032
 5033           jk
 5034           nlm»o
 5035           "#
 5036    ));
 5037
 5038    cx.update_editor(|view, cx| {
 5039        view.add_selection_above(&Default::default(), cx);
 5040    });
 5041
 5042    cx.assert_editor_state(indoc!(
 5043        r#"a«ˇbc»
 5044           d«ˇef»ghi
 5045
 5046           j«ˇk»
 5047           n«ˇlm»o
 5048           "#
 5049    ));
 5050
 5051    cx.update_editor(|view, cx| {
 5052        view.add_selection_below(&Default::default(), cx);
 5053    });
 5054
 5055    cx.assert_editor_state(indoc!(
 5056        r#"abc
 5057           d«ˇef»ghi
 5058
 5059           j«ˇk»
 5060           n«ˇlm»o
 5061           "#
 5062    ));
 5063}
 5064
 5065#[gpui::test]
 5066async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5067    init_test(cx, |_| {});
 5068
 5069    let mut cx = EditorTestContext::new(cx).await;
 5070    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5071
 5072    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5073        .unwrap();
 5074    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5075
 5076    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5077        .unwrap();
 5078    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5079
 5080    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5081    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5082
 5083    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5084    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5085
 5086    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5087        .unwrap();
 5088    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5089
 5090    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5091        .unwrap();
 5092    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5093}
 5094
 5095#[gpui::test]
 5096async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5097    init_test(cx, |_| {});
 5098
 5099    let mut cx = EditorTestContext::new(cx).await;
 5100    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5101
 5102    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5103        .unwrap();
 5104    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5105}
 5106
 5107#[gpui::test]
 5108async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5109    init_test(cx, |_| {});
 5110
 5111    let mut cx = EditorTestContext::new(cx).await;
 5112    cx.set_state(
 5113        r#"let foo = 2;
 5114lˇet foo = 2;
 5115let fooˇ = 2;
 5116let foo = 2;
 5117let foo = ˇ2;"#,
 5118    );
 5119
 5120    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5121        .unwrap();
 5122    cx.assert_editor_state(
 5123        r#"let foo = 2;
 5124«letˇ» foo = 2;
 5125let «fooˇ» = 2;
 5126let foo = 2;
 5127let foo = «2ˇ»;"#,
 5128    );
 5129
 5130    // noop for multiple selections with different contents
 5131    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5132        .unwrap();
 5133    cx.assert_editor_state(
 5134        r#"let foo = 2;
 5135«letˇ» foo = 2;
 5136let «fooˇ» = 2;
 5137let foo = 2;
 5138let foo = «2ˇ»;"#,
 5139    );
 5140}
 5141
 5142#[gpui::test]
 5143async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5144    init_test(cx, |_| {});
 5145
 5146    let mut cx =
 5147        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5148
 5149    cx.assert_editor_state(indoc! {"
 5150        ˇbbb
 5151        ccc
 5152
 5153        bbb
 5154        ccc
 5155        "});
 5156    cx.dispatch_action(SelectPrevious::default());
 5157    cx.assert_editor_state(indoc! {"
 5158                «bbbˇ»
 5159                ccc
 5160
 5161                bbb
 5162                ccc
 5163                "});
 5164    cx.dispatch_action(SelectPrevious::default());
 5165    cx.assert_editor_state(indoc! {"
 5166                «bbbˇ»
 5167                ccc
 5168
 5169                «bbbˇ»
 5170                ccc
 5171                "});
 5172}
 5173
 5174#[gpui::test]
 5175async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5176    init_test(cx, |_| {});
 5177
 5178    let mut cx = EditorTestContext::new(cx).await;
 5179    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5180
 5181    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5182        .unwrap();
 5183    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5184
 5185    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5186        .unwrap();
 5187    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5188
 5189    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5190    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5191
 5192    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5193    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5194
 5195    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5196        .unwrap();
 5197    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5198
 5199    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5200        .unwrap();
 5201    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5202
 5203    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5204        .unwrap();
 5205    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5206}
 5207
 5208#[gpui::test]
 5209async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5210    init_test(cx, |_| {});
 5211
 5212    let mut cx = EditorTestContext::new(cx).await;
 5213    cx.set_state(
 5214        r#"let foo = 2;
 5215lˇet foo = 2;
 5216let fooˇ = 2;
 5217let foo = 2;
 5218let foo = ˇ2;"#,
 5219    );
 5220
 5221    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5222        .unwrap();
 5223    cx.assert_editor_state(
 5224        r#"let foo = 2;
 5225«letˇ» foo = 2;
 5226let «fooˇ» = 2;
 5227let foo = 2;
 5228let foo = «2ˇ»;"#,
 5229    );
 5230
 5231    // noop for multiple selections with different contents
 5232    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5233        .unwrap();
 5234    cx.assert_editor_state(
 5235        r#"let foo = 2;
 5236«letˇ» foo = 2;
 5237let «fooˇ» = 2;
 5238let foo = 2;
 5239let foo = «2ˇ»;"#,
 5240    );
 5241}
 5242
 5243#[gpui::test]
 5244async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5245    init_test(cx, |_| {});
 5246
 5247    let mut cx = EditorTestContext::new(cx).await;
 5248    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5249
 5250    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5251        .unwrap();
 5252    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5253
 5254    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5255        .unwrap();
 5256    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5257
 5258    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5259    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5260
 5261    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5262    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5263
 5264    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5265        .unwrap();
 5266    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5267
 5268    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5269        .unwrap();
 5270    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5271}
 5272
 5273#[gpui::test]
 5274async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5275    init_test(cx, |_| {});
 5276
 5277    let language = Arc::new(Language::new(
 5278        LanguageConfig::default(),
 5279        Some(tree_sitter_rust::LANGUAGE.into()),
 5280    ));
 5281
 5282    let text = r#"
 5283        use mod1::mod2::{mod3, mod4};
 5284
 5285        fn fn_1(param1: bool, param2: &str) {
 5286            let var1 = "text";
 5287        }
 5288    "#
 5289    .unindent();
 5290
 5291    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5293    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5294
 5295    editor
 5296        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5297        .await;
 5298
 5299    editor.update(cx, |view, cx| {
 5300        view.change_selections(None, cx, |s| {
 5301            s.select_display_ranges([
 5302                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5303                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5304                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5305            ]);
 5306        });
 5307        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5308    });
 5309    editor.update(cx, |editor, cx| {
 5310        assert_text_with_selections(
 5311            editor,
 5312            indoc! {r#"
 5313                use mod1::mod2::{mod3, «mod4ˇ»};
 5314
 5315                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5316                    let var1 = "«textˇ»";
 5317                }
 5318            "#},
 5319            cx,
 5320        );
 5321    });
 5322
 5323    editor.update(cx, |view, cx| {
 5324        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5325    });
 5326    editor.update(cx, |editor, cx| {
 5327        assert_text_with_selections(
 5328            editor,
 5329            indoc! {r#"
 5330                use mod1::mod2::«{mod3, mod4}ˇ»;
 5331
 5332                «ˇfn fn_1(param1: bool, param2: &str) {
 5333                    let var1 = "text";
 5334 5335            "#},
 5336            cx,
 5337        );
 5338    });
 5339
 5340    editor.update(cx, |view, cx| {
 5341        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5342    });
 5343    assert_eq!(
 5344        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5345        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5346    );
 5347
 5348    // Trying to expand the selected syntax node one more time has no effect.
 5349    editor.update(cx, |view, cx| {
 5350        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5351    });
 5352    assert_eq!(
 5353        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5354        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5355    );
 5356
 5357    editor.update(cx, |view, cx| {
 5358        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5359    });
 5360    editor.update(cx, |editor, cx| {
 5361        assert_text_with_selections(
 5362            editor,
 5363            indoc! {r#"
 5364                use mod1::mod2::«{mod3, mod4}ˇ»;
 5365
 5366                «ˇfn fn_1(param1: bool, param2: &str) {
 5367                    let var1 = "text";
 5368 5369            "#},
 5370            cx,
 5371        );
 5372    });
 5373
 5374    editor.update(cx, |view, cx| {
 5375        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5376    });
 5377    editor.update(cx, |editor, cx| {
 5378        assert_text_with_selections(
 5379            editor,
 5380            indoc! {r#"
 5381                use mod1::mod2::{mod3, «mod4ˇ»};
 5382
 5383                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5384                    let var1 = "«textˇ»";
 5385                }
 5386            "#},
 5387            cx,
 5388        );
 5389    });
 5390
 5391    editor.update(cx, |view, cx| {
 5392        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5393    });
 5394    editor.update(cx, |editor, cx| {
 5395        assert_text_with_selections(
 5396            editor,
 5397            indoc! {r#"
 5398                use mod1::mod2::{mod3, mo«ˇ»d4};
 5399
 5400                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5401                    let var1 = "te«ˇ»xt";
 5402                }
 5403            "#},
 5404            cx,
 5405        );
 5406    });
 5407
 5408    // Trying to shrink the selected syntax node one more time has no effect.
 5409    editor.update(cx, |view, cx| {
 5410        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5411    });
 5412    editor.update(cx, |editor, cx| {
 5413        assert_text_with_selections(
 5414            editor,
 5415            indoc! {r#"
 5416                use mod1::mod2::{mod3, mo«ˇ»d4};
 5417
 5418                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5419                    let var1 = "te«ˇ»xt";
 5420                }
 5421            "#},
 5422            cx,
 5423        );
 5424    });
 5425
 5426    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5427    // a fold.
 5428    editor.update(cx, |view, cx| {
 5429        view.fold_creases(
 5430            vec![
 5431                Crease::simple(
 5432                    Point::new(0, 21)..Point::new(0, 24),
 5433                    FoldPlaceholder::test(),
 5434                ),
 5435                Crease::simple(
 5436                    Point::new(3, 20)..Point::new(3, 22),
 5437                    FoldPlaceholder::test(),
 5438                ),
 5439            ],
 5440            true,
 5441            cx,
 5442        );
 5443        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5444    });
 5445    editor.update(cx, |editor, cx| {
 5446        assert_text_with_selections(
 5447            editor,
 5448            indoc! {r#"
 5449                use mod1::mod2::«{mod3, mod4}ˇ»;
 5450
 5451                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5452                    «let var1 = "text";ˇ»
 5453                }
 5454            "#},
 5455            cx,
 5456        );
 5457    });
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let language = Arc::new(
 5465        Language::new(
 5466            LanguageConfig {
 5467                brackets: BracketPairConfig {
 5468                    pairs: vec![
 5469                        BracketPair {
 5470                            start: "{".to_string(),
 5471                            end: "}".to_string(),
 5472                            close: false,
 5473                            surround: false,
 5474                            newline: true,
 5475                        },
 5476                        BracketPair {
 5477                            start: "(".to_string(),
 5478                            end: ")".to_string(),
 5479                            close: false,
 5480                            surround: false,
 5481                            newline: true,
 5482                        },
 5483                    ],
 5484                    ..Default::default()
 5485                },
 5486                ..Default::default()
 5487            },
 5488            Some(tree_sitter_rust::LANGUAGE.into()),
 5489        )
 5490        .with_indents_query(
 5491            r#"
 5492                (_ "(" ")" @end) @indent
 5493                (_ "{" "}" @end) @indent
 5494            "#,
 5495        )
 5496        .unwrap(),
 5497    );
 5498
 5499    let text = "fn a() {}";
 5500
 5501    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5502    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5503    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5504    editor
 5505        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5506        .await;
 5507
 5508    editor.update(cx, |editor, cx| {
 5509        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5510        editor.newline(&Newline, cx);
 5511        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5512        assert_eq!(
 5513            editor.selections.ranges(cx),
 5514            &[
 5515                Point::new(1, 4)..Point::new(1, 4),
 5516                Point::new(3, 4)..Point::new(3, 4),
 5517                Point::new(5, 0)..Point::new(5, 0)
 5518            ]
 5519        );
 5520    });
 5521}
 5522
 5523#[gpui::test]
 5524async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5525    init_test(cx, |_| {});
 5526
 5527    let mut cx = EditorTestContext::new(cx).await;
 5528
 5529    let language = Arc::new(Language::new(
 5530        LanguageConfig {
 5531            brackets: BracketPairConfig {
 5532                pairs: vec![
 5533                    BracketPair {
 5534                        start: "{".to_string(),
 5535                        end: "}".to_string(),
 5536                        close: true,
 5537                        surround: true,
 5538                        newline: true,
 5539                    },
 5540                    BracketPair {
 5541                        start: "(".to_string(),
 5542                        end: ")".to_string(),
 5543                        close: true,
 5544                        surround: true,
 5545                        newline: true,
 5546                    },
 5547                    BracketPair {
 5548                        start: "/*".to_string(),
 5549                        end: " */".to_string(),
 5550                        close: true,
 5551                        surround: true,
 5552                        newline: true,
 5553                    },
 5554                    BracketPair {
 5555                        start: "[".to_string(),
 5556                        end: "]".to_string(),
 5557                        close: false,
 5558                        surround: false,
 5559                        newline: true,
 5560                    },
 5561                    BracketPair {
 5562                        start: "\"".to_string(),
 5563                        end: "\"".to_string(),
 5564                        close: true,
 5565                        surround: true,
 5566                        newline: false,
 5567                    },
 5568                    BracketPair {
 5569                        start: "<".to_string(),
 5570                        end: ">".to_string(),
 5571                        close: false,
 5572                        surround: true,
 5573                        newline: true,
 5574                    },
 5575                ],
 5576                ..Default::default()
 5577            },
 5578            autoclose_before: "})]".to_string(),
 5579            ..Default::default()
 5580        },
 5581        Some(tree_sitter_rust::LANGUAGE.into()),
 5582    ));
 5583
 5584    cx.language_registry().add(language.clone());
 5585    cx.update_buffer(|buffer, cx| {
 5586        buffer.set_language(Some(language), cx);
 5587    });
 5588
 5589    cx.set_state(
 5590        &r#"
 5591            🏀ˇ
 5592            εˇ
 5593            ❤️ˇ
 5594        "#
 5595        .unindent(),
 5596    );
 5597
 5598    // autoclose multiple nested brackets at multiple cursors
 5599    cx.update_editor(|view, cx| {
 5600        view.handle_input("{", cx);
 5601        view.handle_input("{", cx);
 5602        view.handle_input("{", cx);
 5603    });
 5604    cx.assert_editor_state(
 5605        &"
 5606            🏀{{{ˇ}}}
 5607            ε{{{ˇ}}}
 5608            ❤️{{{ˇ}}}
 5609        "
 5610        .unindent(),
 5611    );
 5612
 5613    // insert a different closing bracket
 5614    cx.update_editor(|view, cx| {
 5615        view.handle_input(")", cx);
 5616    });
 5617    cx.assert_editor_state(
 5618        &"
 5619            🏀{{{)ˇ}}}
 5620            ε{{{)ˇ}}}
 5621            ❤️{{{)ˇ}}}
 5622        "
 5623        .unindent(),
 5624    );
 5625
 5626    // skip over the auto-closed brackets when typing a closing bracket
 5627    cx.update_editor(|view, cx| {
 5628        view.move_right(&MoveRight, cx);
 5629        view.handle_input("}", cx);
 5630        view.handle_input("}", cx);
 5631        view.handle_input("}", cx);
 5632    });
 5633    cx.assert_editor_state(
 5634        &"
 5635            🏀{{{)}}}}ˇ
 5636            ε{{{)}}}}ˇ
 5637            ❤️{{{)}}}}ˇ
 5638        "
 5639        .unindent(),
 5640    );
 5641
 5642    // autoclose multi-character pairs
 5643    cx.set_state(
 5644        &"
 5645            ˇ
 5646            ˇ
 5647        "
 5648        .unindent(),
 5649    );
 5650    cx.update_editor(|view, cx| {
 5651        view.handle_input("/", cx);
 5652        view.handle_input("*", cx);
 5653    });
 5654    cx.assert_editor_state(
 5655        &"
 5656            /*ˇ */
 5657            /*ˇ */
 5658        "
 5659        .unindent(),
 5660    );
 5661
 5662    // one cursor autocloses a multi-character pair, one cursor
 5663    // does not autoclose.
 5664    cx.set_state(
 5665        &"
 5666 5667            ˇ
 5668        "
 5669        .unindent(),
 5670    );
 5671    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5672    cx.assert_editor_state(
 5673        &"
 5674            /*ˇ */
 5675 5676        "
 5677        .unindent(),
 5678    );
 5679
 5680    // Don't autoclose if the next character isn't whitespace and isn't
 5681    // listed in the language's "autoclose_before" section.
 5682    cx.set_state("ˇa b");
 5683    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5684    cx.assert_editor_state("{ˇa b");
 5685
 5686    // Don't autoclose if `close` is false for the bracket pair
 5687    cx.set_state("ˇ");
 5688    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5689    cx.assert_editor_state("");
 5690
 5691    // Surround with brackets if text is selected
 5692    cx.set_state("«aˇ» b");
 5693    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5694    cx.assert_editor_state("{«aˇ»} b");
 5695
 5696    // Autclose pair where the start and end characters are the same
 5697    cx.set_state("");
 5698    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5699    cx.assert_editor_state("a\"ˇ\"");
 5700    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5701    cx.assert_editor_state("a\"\"ˇ");
 5702
 5703    // Don't autoclose pair if autoclose is disabled
 5704    cx.set_state("ˇ");
 5705    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5706    cx.assert_editor_state("");
 5707
 5708    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5709    cx.set_state("«aˇ» b");
 5710    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5711    cx.assert_editor_state("<«aˇ»> b");
 5712}
 5713
 5714#[gpui::test]
 5715async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5716    init_test(cx, |settings| {
 5717        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5718    });
 5719
 5720    let mut cx = EditorTestContext::new(cx).await;
 5721
 5722    let language = Arc::new(Language::new(
 5723        LanguageConfig {
 5724            brackets: BracketPairConfig {
 5725                pairs: vec![
 5726                    BracketPair {
 5727                        start: "{".to_string(),
 5728                        end: "}".to_string(),
 5729                        close: true,
 5730                        surround: true,
 5731                        newline: true,
 5732                    },
 5733                    BracketPair {
 5734                        start: "(".to_string(),
 5735                        end: ")".to_string(),
 5736                        close: true,
 5737                        surround: true,
 5738                        newline: true,
 5739                    },
 5740                    BracketPair {
 5741                        start: "[".to_string(),
 5742                        end: "]".to_string(),
 5743                        close: false,
 5744                        surround: false,
 5745                        newline: true,
 5746                    },
 5747                ],
 5748                ..Default::default()
 5749            },
 5750            autoclose_before: "})]".to_string(),
 5751            ..Default::default()
 5752        },
 5753        Some(tree_sitter_rust::LANGUAGE.into()),
 5754    ));
 5755
 5756    cx.language_registry().add(language.clone());
 5757    cx.update_buffer(|buffer, cx| {
 5758        buffer.set_language(Some(language), cx);
 5759    });
 5760
 5761    cx.set_state(
 5762        &"
 5763            ˇ
 5764            ˇ
 5765            ˇ
 5766        "
 5767        .unindent(),
 5768    );
 5769
 5770    // ensure only matching closing brackets are skipped over
 5771    cx.update_editor(|view, cx| {
 5772        view.handle_input("}", cx);
 5773        view.move_left(&MoveLeft, cx);
 5774        view.handle_input(")", cx);
 5775        view.move_left(&MoveLeft, cx);
 5776    });
 5777    cx.assert_editor_state(
 5778        &"
 5779            ˇ)}
 5780            ˇ)}
 5781            ˇ)}
 5782        "
 5783        .unindent(),
 5784    );
 5785
 5786    // skip-over closing brackets at multiple cursors
 5787    cx.update_editor(|view, cx| {
 5788        view.handle_input(")", cx);
 5789        view.handle_input("}", cx);
 5790    });
 5791    cx.assert_editor_state(
 5792        &"
 5793            )}ˇ
 5794            )}ˇ
 5795            )}ˇ
 5796        "
 5797        .unindent(),
 5798    );
 5799
 5800    // ignore non-close brackets
 5801    cx.update_editor(|view, cx| {
 5802        view.handle_input("]", cx);
 5803        view.move_left(&MoveLeft, cx);
 5804        view.handle_input("]", cx);
 5805    });
 5806    cx.assert_editor_state(
 5807        &"
 5808            )}]ˇ]
 5809            )}]ˇ]
 5810            )}]ˇ]
 5811        "
 5812        .unindent(),
 5813    );
 5814}
 5815
 5816#[gpui::test]
 5817async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5818    init_test(cx, |_| {});
 5819
 5820    let mut cx = EditorTestContext::new(cx).await;
 5821
 5822    let html_language = Arc::new(
 5823        Language::new(
 5824            LanguageConfig {
 5825                name: "HTML".into(),
 5826                brackets: BracketPairConfig {
 5827                    pairs: vec![
 5828                        BracketPair {
 5829                            start: "<".into(),
 5830                            end: ">".into(),
 5831                            close: true,
 5832                            ..Default::default()
 5833                        },
 5834                        BracketPair {
 5835                            start: "{".into(),
 5836                            end: "}".into(),
 5837                            close: true,
 5838                            ..Default::default()
 5839                        },
 5840                        BracketPair {
 5841                            start: "(".into(),
 5842                            end: ")".into(),
 5843                            close: true,
 5844                            ..Default::default()
 5845                        },
 5846                    ],
 5847                    ..Default::default()
 5848                },
 5849                autoclose_before: "})]>".into(),
 5850                ..Default::default()
 5851            },
 5852            Some(tree_sitter_html::language()),
 5853        )
 5854        .with_injection_query(
 5855            r#"
 5856            (script_element
 5857                (raw_text) @content
 5858                (#set! "language" "javascript"))
 5859            "#,
 5860        )
 5861        .unwrap(),
 5862    );
 5863
 5864    let javascript_language = Arc::new(Language::new(
 5865        LanguageConfig {
 5866            name: "JavaScript".into(),
 5867            brackets: BracketPairConfig {
 5868                pairs: vec![
 5869                    BracketPair {
 5870                        start: "/*".into(),
 5871                        end: " */".into(),
 5872                        close: true,
 5873                        ..Default::default()
 5874                    },
 5875                    BracketPair {
 5876                        start: "{".into(),
 5877                        end: "}".into(),
 5878                        close: true,
 5879                        ..Default::default()
 5880                    },
 5881                    BracketPair {
 5882                        start: "(".into(),
 5883                        end: ")".into(),
 5884                        close: true,
 5885                        ..Default::default()
 5886                    },
 5887                ],
 5888                ..Default::default()
 5889            },
 5890            autoclose_before: "})]>".into(),
 5891            ..Default::default()
 5892        },
 5893        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5894    ));
 5895
 5896    cx.language_registry().add(html_language.clone());
 5897    cx.language_registry().add(javascript_language.clone());
 5898
 5899    cx.update_buffer(|buffer, cx| {
 5900        buffer.set_language(Some(html_language), cx);
 5901    });
 5902
 5903    cx.set_state(
 5904        &r#"
 5905            <body>ˇ
 5906                <script>
 5907                    var x = 1;ˇ
 5908                </script>
 5909            </body>ˇ
 5910        "#
 5911        .unindent(),
 5912    );
 5913
 5914    // Precondition: different languages are active at different locations.
 5915    cx.update_editor(|editor, cx| {
 5916        let snapshot = editor.snapshot(cx);
 5917        let cursors = editor.selections.ranges::<usize>(cx);
 5918        let languages = cursors
 5919            .iter()
 5920            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5921            .collect::<Vec<_>>();
 5922        assert_eq!(
 5923            languages,
 5924            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5925        );
 5926    });
 5927
 5928    // Angle brackets autoclose in HTML, but not JavaScript.
 5929    cx.update_editor(|editor, cx| {
 5930        editor.handle_input("<", cx);
 5931        editor.handle_input("a", cx);
 5932    });
 5933    cx.assert_editor_state(
 5934        &r#"
 5935            <body><aˇ>
 5936                <script>
 5937                    var x = 1;<aˇ
 5938                </script>
 5939            </body><aˇ>
 5940        "#
 5941        .unindent(),
 5942    );
 5943
 5944    // Curly braces and parens autoclose in both HTML and JavaScript.
 5945    cx.update_editor(|editor, cx| {
 5946        editor.handle_input(" b=", cx);
 5947        editor.handle_input("{", cx);
 5948        editor.handle_input("c", cx);
 5949        editor.handle_input("(", cx);
 5950    });
 5951    cx.assert_editor_state(
 5952        &r#"
 5953            <body><a b={c(ˇ)}>
 5954                <script>
 5955                    var x = 1;<a b={c(ˇ)}
 5956                </script>
 5957            </body><a b={c(ˇ)}>
 5958        "#
 5959        .unindent(),
 5960    );
 5961
 5962    // Brackets that were already autoclosed are skipped.
 5963    cx.update_editor(|editor, cx| {
 5964        editor.handle_input(")", cx);
 5965        editor.handle_input("d", cx);
 5966        editor.handle_input("}", cx);
 5967    });
 5968    cx.assert_editor_state(
 5969        &r#"
 5970            <body><a b={c()d}ˇ>
 5971                <script>
 5972                    var x = 1;<a b={c()d}ˇ
 5973                </script>
 5974            </body><a b={c()d}ˇ>
 5975        "#
 5976        .unindent(),
 5977    );
 5978    cx.update_editor(|editor, cx| {
 5979        editor.handle_input(">", cx);
 5980    });
 5981    cx.assert_editor_state(
 5982        &r#"
 5983            <body><a b={c()d}>ˇ
 5984                <script>
 5985                    var x = 1;<a b={c()d}>ˇ
 5986                </script>
 5987            </body><a b={c()d}>ˇ
 5988        "#
 5989        .unindent(),
 5990    );
 5991
 5992    // Reset
 5993    cx.set_state(
 5994        &r#"
 5995            <body>ˇ
 5996                <script>
 5997                    var x = 1;ˇ
 5998                </script>
 5999            </body>ˇ
 6000        "#
 6001        .unindent(),
 6002    );
 6003
 6004    cx.update_editor(|editor, cx| {
 6005        editor.handle_input("<", cx);
 6006    });
 6007    cx.assert_editor_state(
 6008        &r#"
 6009            <body><ˇ>
 6010                <script>
 6011                    var x = 1;<ˇ
 6012                </script>
 6013            </body><ˇ>
 6014        "#
 6015        .unindent(),
 6016    );
 6017
 6018    // When backspacing, the closing angle brackets are removed.
 6019    cx.update_editor(|editor, cx| {
 6020        editor.backspace(&Backspace, cx);
 6021    });
 6022    cx.assert_editor_state(
 6023        &r#"
 6024            <body>ˇ
 6025                <script>
 6026                    var x = 1;ˇ
 6027                </script>
 6028            </body>ˇ
 6029        "#
 6030        .unindent(),
 6031    );
 6032
 6033    // Block comments autoclose in JavaScript, but not HTML.
 6034    cx.update_editor(|editor, cx| {
 6035        editor.handle_input("/", cx);
 6036        editor.handle_input("*", cx);
 6037    });
 6038    cx.assert_editor_state(
 6039        &r#"
 6040            <body>/*ˇ
 6041                <script>
 6042                    var x = 1;/*ˇ */
 6043                </script>
 6044            </body>/*ˇ
 6045        "#
 6046        .unindent(),
 6047    );
 6048}
 6049
 6050#[gpui::test]
 6051async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6052    init_test(cx, |_| {});
 6053
 6054    let mut cx = EditorTestContext::new(cx).await;
 6055
 6056    let rust_language = Arc::new(
 6057        Language::new(
 6058            LanguageConfig {
 6059                name: "Rust".into(),
 6060                brackets: serde_json::from_value(json!([
 6061                    { "start": "{", "end": "}", "close": true, "newline": true },
 6062                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6063                ]))
 6064                .unwrap(),
 6065                autoclose_before: "})]>".into(),
 6066                ..Default::default()
 6067            },
 6068            Some(tree_sitter_rust::LANGUAGE.into()),
 6069        )
 6070        .with_override_query("(string_literal) @string")
 6071        .unwrap(),
 6072    );
 6073
 6074    cx.language_registry().add(rust_language.clone());
 6075    cx.update_buffer(|buffer, cx| {
 6076        buffer.set_language(Some(rust_language), cx);
 6077    });
 6078
 6079    cx.set_state(
 6080        &r#"
 6081            let x = ˇ
 6082        "#
 6083        .unindent(),
 6084    );
 6085
 6086    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6087    cx.update_editor(|editor, cx| {
 6088        editor.handle_input("\"", cx);
 6089    });
 6090    cx.assert_editor_state(
 6091        &r#"
 6092            let x = "ˇ"
 6093        "#
 6094        .unindent(),
 6095    );
 6096
 6097    // Inserting another quotation mark. The cursor moves across the existing
 6098    // automatically-inserted quotation mark.
 6099    cx.update_editor(|editor, cx| {
 6100        editor.handle_input("\"", cx);
 6101    });
 6102    cx.assert_editor_state(
 6103        &r#"
 6104            let x = ""ˇ
 6105        "#
 6106        .unindent(),
 6107    );
 6108
 6109    // Reset
 6110    cx.set_state(
 6111        &r#"
 6112            let x = ˇ
 6113        "#
 6114        .unindent(),
 6115    );
 6116
 6117    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6118    cx.update_editor(|editor, cx| {
 6119        editor.handle_input("\"", cx);
 6120        editor.handle_input(" ", cx);
 6121        editor.move_left(&Default::default(), cx);
 6122        editor.handle_input("\\", cx);
 6123        editor.handle_input("\"", cx);
 6124    });
 6125    cx.assert_editor_state(
 6126        &r#"
 6127            let x = "\"ˇ "
 6128        "#
 6129        .unindent(),
 6130    );
 6131
 6132    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6133    // mark. Nothing is inserted.
 6134    cx.update_editor(|editor, cx| {
 6135        editor.move_right(&Default::default(), cx);
 6136        editor.handle_input("\"", cx);
 6137    });
 6138    cx.assert_editor_state(
 6139        &r#"
 6140            let x = "\" "ˇ
 6141        "#
 6142        .unindent(),
 6143    );
 6144}
 6145
 6146#[gpui::test]
 6147async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6148    init_test(cx, |_| {});
 6149
 6150    let language = Arc::new(Language::new(
 6151        LanguageConfig {
 6152            brackets: BracketPairConfig {
 6153                pairs: vec![
 6154                    BracketPair {
 6155                        start: "{".to_string(),
 6156                        end: "}".to_string(),
 6157                        close: true,
 6158                        surround: true,
 6159                        newline: true,
 6160                    },
 6161                    BracketPair {
 6162                        start: "/* ".to_string(),
 6163                        end: "*/".to_string(),
 6164                        close: true,
 6165                        surround: true,
 6166                        ..Default::default()
 6167                    },
 6168                ],
 6169                ..Default::default()
 6170            },
 6171            ..Default::default()
 6172        },
 6173        Some(tree_sitter_rust::LANGUAGE.into()),
 6174    ));
 6175
 6176    let text = r#"
 6177        a
 6178        b
 6179        c
 6180    "#
 6181    .unindent();
 6182
 6183    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6184    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6185    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6186    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6187        .await;
 6188
 6189    view.update(cx, |view, cx| {
 6190        view.change_selections(None, cx, |s| {
 6191            s.select_display_ranges([
 6192                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6193                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6194                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6195            ])
 6196        });
 6197
 6198        view.handle_input("{", cx);
 6199        view.handle_input("{", cx);
 6200        view.handle_input("{", cx);
 6201        assert_eq!(
 6202            view.text(cx),
 6203            "
 6204                {{{a}}}
 6205                {{{b}}}
 6206                {{{c}}}
 6207            "
 6208            .unindent()
 6209        );
 6210        assert_eq!(
 6211            view.selections.display_ranges(cx),
 6212            [
 6213                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6214                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6215                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6216            ]
 6217        );
 6218
 6219        view.undo(&Undo, cx);
 6220        view.undo(&Undo, cx);
 6221        view.undo(&Undo, cx);
 6222        assert_eq!(
 6223            view.text(cx),
 6224            "
 6225                a
 6226                b
 6227                c
 6228            "
 6229            .unindent()
 6230        );
 6231        assert_eq!(
 6232            view.selections.display_ranges(cx),
 6233            [
 6234                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6235                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6236                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6237            ]
 6238        );
 6239
 6240        // Ensure inserting the first character of a multi-byte bracket pair
 6241        // doesn't surround the selections with the bracket.
 6242        view.handle_input("/", cx);
 6243        assert_eq!(
 6244            view.text(cx),
 6245            "
 6246                /
 6247                /
 6248                /
 6249            "
 6250            .unindent()
 6251        );
 6252        assert_eq!(
 6253            view.selections.display_ranges(cx),
 6254            [
 6255                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6256                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6257                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6258            ]
 6259        );
 6260
 6261        view.undo(&Undo, cx);
 6262        assert_eq!(
 6263            view.text(cx),
 6264            "
 6265                a
 6266                b
 6267                c
 6268            "
 6269            .unindent()
 6270        );
 6271        assert_eq!(
 6272            view.selections.display_ranges(cx),
 6273            [
 6274                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6275                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6276                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6277            ]
 6278        );
 6279
 6280        // Ensure inserting the last character of a multi-byte bracket pair
 6281        // doesn't surround the selections with the bracket.
 6282        view.handle_input("*", cx);
 6283        assert_eq!(
 6284            view.text(cx),
 6285            "
 6286                *
 6287                *
 6288                *
 6289            "
 6290            .unindent()
 6291        );
 6292        assert_eq!(
 6293            view.selections.display_ranges(cx),
 6294            [
 6295                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6296                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6297                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6298            ]
 6299        );
 6300    });
 6301}
 6302
 6303#[gpui::test]
 6304async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6305    init_test(cx, |_| {});
 6306
 6307    let language = Arc::new(Language::new(
 6308        LanguageConfig {
 6309            brackets: BracketPairConfig {
 6310                pairs: vec![BracketPair {
 6311                    start: "{".to_string(),
 6312                    end: "}".to_string(),
 6313                    close: true,
 6314                    surround: true,
 6315                    newline: true,
 6316                }],
 6317                ..Default::default()
 6318            },
 6319            autoclose_before: "}".to_string(),
 6320            ..Default::default()
 6321        },
 6322        Some(tree_sitter_rust::LANGUAGE.into()),
 6323    ));
 6324
 6325    let text = r#"
 6326        a
 6327        b
 6328        c
 6329    "#
 6330    .unindent();
 6331
 6332    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6333    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6334    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6335    editor
 6336        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6337        .await;
 6338
 6339    editor.update(cx, |editor, cx| {
 6340        editor.change_selections(None, cx, |s| {
 6341            s.select_ranges([
 6342                Point::new(0, 1)..Point::new(0, 1),
 6343                Point::new(1, 1)..Point::new(1, 1),
 6344                Point::new(2, 1)..Point::new(2, 1),
 6345            ])
 6346        });
 6347
 6348        editor.handle_input("{", cx);
 6349        editor.handle_input("{", cx);
 6350        editor.handle_input("_", cx);
 6351        assert_eq!(
 6352            editor.text(cx),
 6353            "
 6354                a{{_}}
 6355                b{{_}}
 6356                c{{_}}
 6357            "
 6358            .unindent()
 6359        );
 6360        assert_eq!(
 6361            editor.selections.ranges::<Point>(cx),
 6362            [
 6363                Point::new(0, 4)..Point::new(0, 4),
 6364                Point::new(1, 4)..Point::new(1, 4),
 6365                Point::new(2, 4)..Point::new(2, 4)
 6366            ]
 6367        );
 6368
 6369        editor.backspace(&Default::default(), cx);
 6370        editor.backspace(&Default::default(), cx);
 6371        assert_eq!(
 6372            editor.text(cx),
 6373            "
 6374                a{}
 6375                b{}
 6376                c{}
 6377            "
 6378            .unindent()
 6379        );
 6380        assert_eq!(
 6381            editor.selections.ranges::<Point>(cx),
 6382            [
 6383                Point::new(0, 2)..Point::new(0, 2),
 6384                Point::new(1, 2)..Point::new(1, 2),
 6385                Point::new(2, 2)..Point::new(2, 2)
 6386            ]
 6387        );
 6388
 6389        editor.delete_to_previous_word_start(&Default::default(), cx);
 6390        assert_eq!(
 6391            editor.text(cx),
 6392            "
 6393                a
 6394                b
 6395                c
 6396            "
 6397            .unindent()
 6398        );
 6399        assert_eq!(
 6400            editor.selections.ranges::<Point>(cx),
 6401            [
 6402                Point::new(0, 1)..Point::new(0, 1),
 6403                Point::new(1, 1)..Point::new(1, 1),
 6404                Point::new(2, 1)..Point::new(2, 1)
 6405            ]
 6406        );
 6407    });
 6408}
 6409
 6410#[gpui::test]
 6411async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6412    init_test(cx, |settings| {
 6413        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6414    });
 6415
 6416    let mut cx = EditorTestContext::new(cx).await;
 6417
 6418    let language = Arc::new(Language::new(
 6419        LanguageConfig {
 6420            brackets: BracketPairConfig {
 6421                pairs: vec![
 6422                    BracketPair {
 6423                        start: "{".to_string(),
 6424                        end: "}".to_string(),
 6425                        close: true,
 6426                        surround: true,
 6427                        newline: true,
 6428                    },
 6429                    BracketPair {
 6430                        start: "(".to_string(),
 6431                        end: ")".to_string(),
 6432                        close: true,
 6433                        surround: true,
 6434                        newline: true,
 6435                    },
 6436                    BracketPair {
 6437                        start: "[".to_string(),
 6438                        end: "]".to_string(),
 6439                        close: false,
 6440                        surround: true,
 6441                        newline: true,
 6442                    },
 6443                ],
 6444                ..Default::default()
 6445            },
 6446            autoclose_before: "})]".to_string(),
 6447            ..Default::default()
 6448        },
 6449        Some(tree_sitter_rust::LANGUAGE.into()),
 6450    ));
 6451
 6452    cx.language_registry().add(language.clone());
 6453    cx.update_buffer(|buffer, cx| {
 6454        buffer.set_language(Some(language), cx);
 6455    });
 6456
 6457    cx.set_state(
 6458        &"
 6459            {(ˇ)}
 6460            [[ˇ]]
 6461            {(ˇ)}
 6462        "
 6463        .unindent(),
 6464    );
 6465
 6466    cx.update_editor(|view, cx| {
 6467        view.backspace(&Default::default(), cx);
 6468        view.backspace(&Default::default(), cx);
 6469    });
 6470
 6471    cx.assert_editor_state(
 6472        &"
 6473            ˇ
 6474            ˇ]]
 6475            ˇ
 6476        "
 6477        .unindent(),
 6478    );
 6479
 6480    cx.update_editor(|view, cx| {
 6481        view.handle_input("{", cx);
 6482        view.handle_input("{", cx);
 6483        view.move_right(&MoveRight, cx);
 6484        view.move_right(&MoveRight, cx);
 6485        view.move_left(&MoveLeft, cx);
 6486        view.move_left(&MoveLeft, cx);
 6487        view.backspace(&Default::default(), cx);
 6488    });
 6489
 6490    cx.assert_editor_state(
 6491        &"
 6492            {ˇ}
 6493            {ˇ}]]
 6494            {ˇ}
 6495        "
 6496        .unindent(),
 6497    );
 6498
 6499    cx.update_editor(|view, cx| {
 6500        view.backspace(&Default::default(), cx);
 6501    });
 6502
 6503    cx.assert_editor_state(
 6504        &"
 6505            ˇ
 6506            ˇ]]
 6507            ˇ
 6508        "
 6509        .unindent(),
 6510    );
 6511}
 6512
 6513#[gpui::test]
 6514async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6515    init_test(cx, |_| {});
 6516
 6517    let language = Arc::new(Language::new(
 6518        LanguageConfig::default(),
 6519        Some(tree_sitter_rust::LANGUAGE.into()),
 6520    ));
 6521
 6522    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6523    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6524    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6525    editor
 6526        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6527        .await;
 6528
 6529    editor.update(cx, |editor, cx| {
 6530        editor.set_auto_replace_emoji_shortcode(true);
 6531
 6532        editor.handle_input("Hello ", cx);
 6533        editor.handle_input(":wave", cx);
 6534        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6535
 6536        editor.handle_input(":", cx);
 6537        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6538
 6539        editor.handle_input(" :smile", cx);
 6540        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6541
 6542        editor.handle_input(":", cx);
 6543        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6544
 6545        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6546        editor.handle_input(":wave", cx);
 6547        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6548
 6549        editor.handle_input(":", cx);
 6550        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6551
 6552        editor.handle_input(":1", cx);
 6553        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6554
 6555        editor.handle_input(":", cx);
 6556        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6557
 6558        // Ensure shortcode does not get replaced when it is part of a word
 6559        editor.handle_input(" Test:wave", cx);
 6560        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6561
 6562        editor.handle_input(":", cx);
 6563        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6564
 6565        editor.set_auto_replace_emoji_shortcode(false);
 6566
 6567        // Ensure shortcode does not get replaced when auto replace is off
 6568        editor.handle_input(" :wave", cx);
 6569        assert_eq!(
 6570            editor.text(cx),
 6571            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6572        );
 6573
 6574        editor.handle_input(":", cx);
 6575        assert_eq!(
 6576            editor.text(cx),
 6577            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6578        );
 6579    });
 6580}
 6581
 6582#[gpui::test]
 6583async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6584    init_test(cx, |_| {});
 6585
 6586    let (text, insertion_ranges) = marked_text_ranges(
 6587        indoc! {"
 6588            ˇ
 6589        "},
 6590        false,
 6591    );
 6592
 6593    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6594    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6595
 6596    _ = editor.update(cx, |editor, cx| {
 6597        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6598
 6599        editor
 6600            .insert_snippet(&insertion_ranges, snippet, cx)
 6601            .unwrap();
 6602
 6603        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6604            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6605            assert_eq!(editor.text(cx), expected_text);
 6606            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6607        }
 6608
 6609        assert(
 6610            editor,
 6611            cx,
 6612            indoc! {"
 6613            type «» =•
 6614            "},
 6615        );
 6616
 6617        assert!(editor.context_menu_visible(), "There should be a matches");
 6618    });
 6619}
 6620
 6621#[gpui::test]
 6622async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6623    init_test(cx, |_| {});
 6624
 6625    let (text, insertion_ranges) = marked_text_ranges(
 6626        indoc! {"
 6627            a.ˇ b
 6628            a.ˇ b
 6629            a.ˇ b
 6630        "},
 6631        false,
 6632    );
 6633
 6634    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6635    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6636
 6637    editor.update(cx, |editor, cx| {
 6638        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6639
 6640        editor
 6641            .insert_snippet(&insertion_ranges, snippet, cx)
 6642            .unwrap();
 6643
 6644        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6645            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6646            assert_eq!(editor.text(cx), expected_text);
 6647            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6648        }
 6649
 6650        assert(
 6651            editor,
 6652            cx,
 6653            indoc! {"
 6654                a.f(«one», two, «three») b
 6655                a.f(«one», two, «three») b
 6656                a.f(«one», two, «three») b
 6657            "},
 6658        );
 6659
 6660        // Can't move earlier than the first tab stop
 6661        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6662        assert(
 6663            editor,
 6664            cx,
 6665            indoc! {"
 6666                a.f(«one», two, «three») b
 6667                a.f(«one», two, «three») b
 6668                a.f(«one», two, «three») b
 6669            "},
 6670        );
 6671
 6672        assert!(editor.move_to_next_snippet_tabstop(cx));
 6673        assert(
 6674            editor,
 6675            cx,
 6676            indoc! {"
 6677                a.f(one, «two», three) b
 6678                a.f(one, «two», three) b
 6679                a.f(one, «two», three) b
 6680            "},
 6681        );
 6682
 6683        editor.move_to_prev_snippet_tabstop(cx);
 6684        assert(
 6685            editor,
 6686            cx,
 6687            indoc! {"
 6688                a.f(«one», two, «three») b
 6689                a.f(«one», two, «three») b
 6690                a.f(«one», two, «three») b
 6691            "},
 6692        );
 6693
 6694        assert!(editor.move_to_next_snippet_tabstop(cx));
 6695        assert(
 6696            editor,
 6697            cx,
 6698            indoc! {"
 6699                a.f(one, «two», three) b
 6700                a.f(one, «two», three) b
 6701                a.f(one, «two», three) b
 6702            "},
 6703        );
 6704        assert!(editor.move_to_next_snippet_tabstop(cx));
 6705        assert(
 6706            editor,
 6707            cx,
 6708            indoc! {"
 6709                a.f(one, two, three)ˇ b
 6710                a.f(one, two, three)ˇ b
 6711                a.f(one, two, three)ˇ b
 6712            "},
 6713        );
 6714
 6715        // As soon as the last tab stop is reached, snippet state is gone
 6716        editor.move_to_prev_snippet_tabstop(cx);
 6717        assert(
 6718            editor,
 6719            cx,
 6720            indoc! {"
 6721                a.f(one, two, three)ˇ b
 6722                a.f(one, two, three)ˇ b
 6723                a.f(one, two, three)ˇ b
 6724            "},
 6725        );
 6726    });
 6727}
 6728
 6729#[gpui::test]
 6730async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6731    init_test(cx, |_| {});
 6732
 6733    let fs = FakeFs::new(cx.executor());
 6734    fs.insert_file("/file.rs", Default::default()).await;
 6735
 6736    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6737
 6738    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6739    language_registry.add(rust_lang());
 6740    let mut fake_servers = language_registry.register_fake_lsp(
 6741        "Rust",
 6742        FakeLspAdapter {
 6743            capabilities: lsp::ServerCapabilities {
 6744                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6745                ..Default::default()
 6746            },
 6747            ..Default::default()
 6748        },
 6749    );
 6750
 6751    let buffer = project
 6752        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6753        .await
 6754        .unwrap();
 6755
 6756    cx.executor().start_waiting();
 6757    let fake_server = fake_servers.next().await.unwrap();
 6758
 6759    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6760    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6761    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6762    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6763
 6764    let save = editor
 6765        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6766        .unwrap();
 6767    fake_server
 6768        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6769            assert_eq!(
 6770                params.text_document.uri,
 6771                lsp::Url::from_file_path("/file.rs").unwrap()
 6772            );
 6773            assert_eq!(params.options.tab_size, 4);
 6774            Ok(Some(vec![lsp::TextEdit::new(
 6775                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6776                ", ".to_string(),
 6777            )]))
 6778        })
 6779        .next()
 6780        .await;
 6781    cx.executor().start_waiting();
 6782    save.await;
 6783
 6784    assert_eq!(
 6785        editor.update(cx, |editor, cx| editor.text(cx)),
 6786        "one, two\nthree\n"
 6787    );
 6788    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6789
 6790    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6791    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6792
 6793    // Ensure we can still save even if formatting hangs.
 6794    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6795        assert_eq!(
 6796            params.text_document.uri,
 6797            lsp::Url::from_file_path("/file.rs").unwrap()
 6798        );
 6799        futures::future::pending::<()>().await;
 6800        unreachable!()
 6801    });
 6802    let save = editor
 6803        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6804        .unwrap();
 6805    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6806    cx.executor().start_waiting();
 6807    save.await;
 6808    assert_eq!(
 6809        editor.update(cx, |editor, cx| editor.text(cx)),
 6810        "one\ntwo\nthree\n"
 6811    );
 6812    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6813
 6814    // For non-dirty buffer, no formatting request should be sent
 6815    let save = editor
 6816        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6817        .unwrap();
 6818    let _pending_format_request = fake_server
 6819        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6820            panic!("Should not be invoked on non-dirty buffer");
 6821        })
 6822        .next();
 6823    cx.executor().start_waiting();
 6824    save.await;
 6825
 6826    // Set rust language override and assert overridden tabsize is sent to language server
 6827    update_test_language_settings(cx, |settings| {
 6828        settings.languages.insert(
 6829            "Rust".into(),
 6830            LanguageSettingsContent {
 6831                tab_size: NonZeroU32::new(8),
 6832                ..Default::default()
 6833            },
 6834        );
 6835    });
 6836
 6837    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6838    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6839    let save = editor
 6840        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6841        .unwrap();
 6842    fake_server
 6843        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6844            assert_eq!(
 6845                params.text_document.uri,
 6846                lsp::Url::from_file_path("/file.rs").unwrap()
 6847            );
 6848            assert_eq!(params.options.tab_size, 8);
 6849            Ok(Some(vec![]))
 6850        })
 6851        .next()
 6852        .await;
 6853    cx.executor().start_waiting();
 6854    save.await;
 6855}
 6856
 6857#[gpui::test]
 6858async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6859    init_test(cx, |_| {});
 6860
 6861    let cols = 4;
 6862    let rows = 10;
 6863    let sample_text_1 = sample_text(rows, cols, 'a');
 6864    assert_eq!(
 6865        sample_text_1,
 6866        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6867    );
 6868    let sample_text_2 = sample_text(rows, cols, 'l');
 6869    assert_eq!(
 6870        sample_text_2,
 6871        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6872    );
 6873    let sample_text_3 = sample_text(rows, cols, 'v');
 6874    assert_eq!(
 6875        sample_text_3,
 6876        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6877    );
 6878
 6879    let fs = FakeFs::new(cx.executor());
 6880    fs.insert_tree(
 6881        "/a",
 6882        json!({
 6883            "main.rs": sample_text_1,
 6884            "other.rs": sample_text_2,
 6885            "lib.rs": sample_text_3,
 6886        }),
 6887    )
 6888    .await;
 6889
 6890    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6891    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6892    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6893
 6894    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6895    language_registry.add(rust_lang());
 6896    let mut fake_servers = language_registry.register_fake_lsp(
 6897        "Rust",
 6898        FakeLspAdapter {
 6899            capabilities: lsp::ServerCapabilities {
 6900                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6901                ..Default::default()
 6902            },
 6903            ..Default::default()
 6904        },
 6905    );
 6906
 6907    let worktree = project.update(cx, |project, cx| {
 6908        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6909        assert_eq!(worktrees.len(), 1);
 6910        worktrees.pop().unwrap()
 6911    });
 6912    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6913
 6914    let buffer_1 = project
 6915        .update(cx, |project, cx| {
 6916            project.open_buffer((worktree_id, "main.rs"), cx)
 6917        })
 6918        .await
 6919        .unwrap();
 6920    let buffer_2 = project
 6921        .update(cx, |project, cx| {
 6922            project.open_buffer((worktree_id, "other.rs"), cx)
 6923        })
 6924        .await
 6925        .unwrap();
 6926    let buffer_3 = project
 6927        .update(cx, |project, cx| {
 6928            project.open_buffer((worktree_id, "lib.rs"), cx)
 6929        })
 6930        .await
 6931        .unwrap();
 6932
 6933    let multi_buffer = cx.new_model(|cx| {
 6934        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6935        multi_buffer.push_excerpts(
 6936            buffer_1.clone(),
 6937            [
 6938                ExcerptRange {
 6939                    context: Point::new(0, 0)..Point::new(3, 0),
 6940                    primary: None,
 6941                },
 6942                ExcerptRange {
 6943                    context: Point::new(5, 0)..Point::new(7, 0),
 6944                    primary: None,
 6945                },
 6946                ExcerptRange {
 6947                    context: Point::new(9, 0)..Point::new(10, 4),
 6948                    primary: None,
 6949                },
 6950            ],
 6951            cx,
 6952        );
 6953        multi_buffer.push_excerpts(
 6954            buffer_2.clone(),
 6955            [
 6956                ExcerptRange {
 6957                    context: Point::new(0, 0)..Point::new(3, 0),
 6958                    primary: None,
 6959                },
 6960                ExcerptRange {
 6961                    context: Point::new(5, 0)..Point::new(7, 0),
 6962                    primary: None,
 6963                },
 6964                ExcerptRange {
 6965                    context: Point::new(9, 0)..Point::new(10, 4),
 6966                    primary: None,
 6967                },
 6968            ],
 6969            cx,
 6970        );
 6971        multi_buffer.push_excerpts(
 6972            buffer_3.clone(),
 6973            [
 6974                ExcerptRange {
 6975                    context: Point::new(0, 0)..Point::new(3, 0),
 6976                    primary: None,
 6977                },
 6978                ExcerptRange {
 6979                    context: Point::new(5, 0)..Point::new(7, 0),
 6980                    primary: None,
 6981                },
 6982                ExcerptRange {
 6983                    context: Point::new(9, 0)..Point::new(10, 4),
 6984                    primary: None,
 6985                },
 6986            ],
 6987            cx,
 6988        );
 6989        multi_buffer
 6990    });
 6991    let multi_buffer_editor = cx.new_view(|cx| {
 6992        Editor::new(
 6993            EditorMode::Full,
 6994            multi_buffer,
 6995            Some(project.clone()),
 6996            true,
 6997            cx,
 6998        )
 6999    });
 7000
 7001    multi_buffer_editor.update(cx, |editor, cx| {
 7002        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7003        editor.insert("|one|two|three|", cx);
 7004    });
 7005    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7006    multi_buffer_editor.update(cx, |editor, cx| {
 7007        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7008            s.select_ranges(Some(60..70))
 7009        });
 7010        editor.insert("|four|five|six|", cx);
 7011    });
 7012    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7013
 7014    // First two buffers should be edited, but not the third one.
 7015    assert_eq!(
 7016        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7017        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7018    );
 7019    buffer_1.update(cx, |buffer, _| {
 7020        assert!(buffer.is_dirty());
 7021        assert_eq!(
 7022            buffer.text(),
 7023            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7024        )
 7025    });
 7026    buffer_2.update(cx, |buffer, _| {
 7027        assert!(buffer.is_dirty());
 7028        assert_eq!(
 7029            buffer.text(),
 7030            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7031        )
 7032    });
 7033    buffer_3.update(cx, |buffer, _| {
 7034        assert!(!buffer.is_dirty());
 7035        assert_eq!(buffer.text(), sample_text_3,)
 7036    });
 7037
 7038    cx.executor().start_waiting();
 7039    let save = multi_buffer_editor
 7040        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7041        .unwrap();
 7042
 7043    let fake_server = fake_servers.next().await.unwrap();
 7044    fake_server
 7045        .server
 7046        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7047            Ok(Some(vec![lsp::TextEdit::new(
 7048                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7049                format!("[{} formatted]", params.text_document.uri),
 7050            )]))
 7051        })
 7052        .detach();
 7053    save.await;
 7054
 7055    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7056    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7057    assert_eq!(
 7058        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7059        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7060    );
 7061    buffer_1.update(cx, |buffer, _| {
 7062        assert!(!buffer.is_dirty());
 7063        assert_eq!(
 7064            buffer.text(),
 7065            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7066        )
 7067    });
 7068    buffer_2.update(cx, |buffer, _| {
 7069        assert!(!buffer.is_dirty());
 7070        assert_eq!(
 7071            buffer.text(),
 7072            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7073        )
 7074    });
 7075    buffer_3.update(cx, |buffer, _| {
 7076        assert!(!buffer.is_dirty());
 7077        assert_eq!(buffer.text(), sample_text_3,)
 7078    });
 7079}
 7080
 7081#[gpui::test]
 7082async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7083    init_test(cx, |_| {});
 7084
 7085    let fs = FakeFs::new(cx.executor());
 7086    fs.insert_file("/file.rs", Default::default()).await;
 7087
 7088    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7089
 7090    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7091    language_registry.add(rust_lang());
 7092    let mut fake_servers = language_registry.register_fake_lsp(
 7093        "Rust",
 7094        FakeLspAdapter {
 7095            capabilities: lsp::ServerCapabilities {
 7096                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7097                ..Default::default()
 7098            },
 7099            ..Default::default()
 7100        },
 7101    );
 7102
 7103    let buffer = project
 7104        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7105        .await
 7106        .unwrap();
 7107
 7108    cx.executor().start_waiting();
 7109    let fake_server = fake_servers.next().await.unwrap();
 7110
 7111    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7112    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7113    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7114    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7115
 7116    let save = editor
 7117        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7118        .unwrap();
 7119    fake_server
 7120        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7121            assert_eq!(
 7122                params.text_document.uri,
 7123                lsp::Url::from_file_path("/file.rs").unwrap()
 7124            );
 7125            assert_eq!(params.options.tab_size, 4);
 7126            Ok(Some(vec![lsp::TextEdit::new(
 7127                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7128                ", ".to_string(),
 7129            )]))
 7130        })
 7131        .next()
 7132        .await;
 7133    cx.executor().start_waiting();
 7134    save.await;
 7135    assert_eq!(
 7136        editor.update(cx, |editor, cx| editor.text(cx)),
 7137        "one, two\nthree\n"
 7138    );
 7139    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7140
 7141    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7142    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7143
 7144    // Ensure we can still save even if formatting hangs.
 7145    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7146        move |params, _| async move {
 7147            assert_eq!(
 7148                params.text_document.uri,
 7149                lsp::Url::from_file_path("/file.rs").unwrap()
 7150            );
 7151            futures::future::pending::<()>().await;
 7152            unreachable!()
 7153        },
 7154    );
 7155    let save = editor
 7156        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7157        .unwrap();
 7158    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7159    cx.executor().start_waiting();
 7160    save.await;
 7161    assert_eq!(
 7162        editor.update(cx, |editor, cx| editor.text(cx)),
 7163        "one\ntwo\nthree\n"
 7164    );
 7165    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7166
 7167    // For non-dirty buffer, no formatting request should be sent
 7168    let save = editor
 7169        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7170        .unwrap();
 7171    let _pending_format_request = fake_server
 7172        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7173            panic!("Should not be invoked on non-dirty buffer");
 7174        })
 7175        .next();
 7176    cx.executor().start_waiting();
 7177    save.await;
 7178
 7179    // Set Rust language override and assert overridden tabsize is sent to language server
 7180    update_test_language_settings(cx, |settings| {
 7181        settings.languages.insert(
 7182            "Rust".into(),
 7183            LanguageSettingsContent {
 7184                tab_size: NonZeroU32::new(8),
 7185                ..Default::default()
 7186            },
 7187        );
 7188    });
 7189
 7190    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7191    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7192    let save = editor
 7193        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7194        .unwrap();
 7195    fake_server
 7196        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7197            assert_eq!(
 7198                params.text_document.uri,
 7199                lsp::Url::from_file_path("/file.rs").unwrap()
 7200            );
 7201            assert_eq!(params.options.tab_size, 8);
 7202            Ok(Some(vec![]))
 7203        })
 7204        .next()
 7205        .await;
 7206    cx.executor().start_waiting();
 7207    save.await;
 7208}
 7209
 7210#[gpui::test]
 7211async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7212    init_test(cx, |settings| {
 7213        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7214            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7215        ))
 7216    });
 7217
 7218    let fs = FakeFs::new(cx.executor());
 7219    fs.insert_file("/file.rs", Default::default()).await;
 7220
 7221    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7222
 7223    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7224    language_registry.add(Arc::new(Language::new(
 7225        LanguageConfig {
 7226            name: "Rust".into(),
 7227            matcher: LanguageMatcher {
 7228                path_suffixes: vec!["rs".to_string()],
 7229                ..Default::default()
 7230            },
 7231            ..LanguageConfig::default()
 7232        },
 7233        Some(tree_sitter_rust::LANGUAGE.into()),
 7234    )));
 7235    update_test_language_settings(cx, |settings| {
 7236        // Enable Prettier formatting for the same buffer, and ensure
 7237        // LSP is called instead of Prettier.
 7238        settings.defaults.prettier = Some(PrettierSettings {
 7239            allowed: true,
 7240            ..PrettierSettings::default()
 7241        });
 7242    });
 7243    let mut fake_servers = language_registry.register_fake_lsp(
 7244        "Rust",
 7245        FakeLspAdapter {
 7246            capabilities: lsp::ServerCapabilities {
 7247                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7248                ..Default::default()
 7249            },
 7250            ..Default::default()
 7251        },
 7252    );
 7253
 7254    let buffer = project
 7255        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7256        .await
 7257        .unwrap();
 7258
 7259    cx.executor().start_waiting();
 7260    let fake_server = fake_servers.next().await.unwrap();
 7261
 7262    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7263    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7264    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7265
 7266    let format = editor
 7267        .update(cx, |editor, cx| {
 7268            editor.perform_format(
 7269                project.clone(),
 7270                FormatTrigger::Manual,
 7271                FormatTarget::Buffer,
 7272                cx,
 7273            )
 7274        })
 7275        .unwrap();
 7276    fake_server
 7277        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7278            assert_eq!(
 7279                params.text_document.uri,
 7280                lsp::Url::from_file_path("/file.rs").unwrap()
 7281            );
 7282            assert_eq!(params.options.tab_size, 4);
 7283            Ok(Some(vec![lsp::TextEdit::new(
 7284                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7285                ", ".to_string(),
 7286            )]))
 7287        })
 7288        .next()
 7289        .await;
 7290    cx.executor().start_waiting();
 7291    format.await;
 7292    assert_eq!(
 7293        editor.update(cx, |editor, cx| editor.text(cx)),
 7294        "one, two\nthree\n"
 7295    );
 7296
 7297    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7298    // Ensure we don't lock if formatting hangs.
 7299    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7300        assert_eq!(
 7301            params.text_document.uri,
 7302            lsp::Url::from_file_path("/file.rs").unwrap()
 7303        );
 7304        futures::future::pending::<()>().await;
 7305        unreachable!()
 7306    });
 7307    let format = editor
 7308        .update(cx, |editor, cx| {
 7309            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7310        })
 7311        .unwrap();
 7312    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7313    cx.executor().start_waiting();
 7314    format.await;
 7315    assert_eq!(
 7316        editor.update(cx, |editor, cx| editor.text(cx)),
 7317        "one\ntwo\nthree\n"
 7318    );
 7319}
 7320
 7321#[gpui::test]
 7322async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7323    init_test(cx, |_| {});
 7324
 7325    let mut cx = EditorLspTestContext::new_rust(
 7326        lsp::ServerCapabilities {
 7327            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7328            ..Default::default()
 7329        },
 7330        cx,
 7331    )
 7332    .await;
 7333
 7334    cx.set_state(indoc! {"
 7335        one.twoˇ
 7336    "});
 7337
 7338    // The format request takes a long time. When it completes, it inserts
 7339    // a newline and an indent before the `.`
 7340    cx.lsp
 7341        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7342            let executor = cx.background_executor().clone();
 7343            async move {
 7344                executor.timer(Duration::from_millis(100)).await;
 7345                Ok(Some(vec![lsp::TextEdit {
 7346                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7347                    new_text: "\n    ".into(),
 7348                }]))
 7349            }
 7350        });
 7351
 7352    // Submit a format request.
 7353    let format_1 = cx
 7354        .update_editor(|editor, cx| editor.format(&Format, cx))
 7355        .unwrap();
 7356    cx.executor().run_until_parked();
 7357
 7358    // Submit a second format request.
 7359    let format_2 = cx
 7360        .update_editor(|editor, cx| editor.format(&Format, cx))
 7361        .unwrap();
 7362    cx.executor().run_until_parked();
 7363
 7364    // Wait for both format requests to complete
 7365    cx.executor().advance_clock(Duration::from_millis(200));
 7366    cx.executor().start_waiting();
 7367    format_1.await.unwrap();
 7368    cx.executor().start_waiting();
 7369    format_2.await.unwrap();
 7370
 7371    // The formatting edits only happens once.
 7372    cx.assert_editor_state(indoc! {"
 7373        one
 7374            .twoˇ
 7375    "});
 7376}
 7377
 7378#[gpui::test]
 7379async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7380    init_test(cx, |settings| {
 7381        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7382    });
 7383
 7384    let mut cx = EditorLspTestContext::new_rust(
 7385        lsp::ServerCapabilities {
 7386            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7387            ..Default::default()
 7388        },
 7389        cx,
 7390    )
 7391    .await;
 7392
 7393    // Set up a buffer white some trailing whitespace and no trailing newline.
 7394    cx.set_state(
 7395        &[
 7396            "one ",   //
 7397            "twoˇ",   //
 7398            "three ", //
 7399            "four",   //
 7400        ]
 7401        .join("\n"),
 7402    );
 7403
 7404    // Submit a format request.
 7405    let format = cx
 7406        .update_editor(|editor, cx| editor.format(&Format, cx))
 7407        .unwrap();
 7408
 7409    // Record which buffer changes have been sent to the language server
 7410    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7411    cx.lsp
 7412        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7413            let buffer_changes = buffer_changes.clone();
 7414            move |params, _| {
 7415                buffer_changes.lock().extend(
 7416                    params
 7417                        .content_changes
 7418                        .into_iter()
 7419                        .map(|e| (e.range.unwrap(), e.text)),
 7420                );
 7421            }
 7422        });
 7423
 7424    // Handle formatting requests to the language server.
 7425    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7426        let buffer_changes = buffer_changes.clone();
 7427        move |_, _| {
 7428            // When formatting is requested, trailing whitespace has already been stripped,
 7429            // and the trailing newline has already been added.
 7430            assert_eq!(
 7431                &buffer_changes.lock()[1..],
 7432                &[
 7433                    (
 7434                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7435                        "".into()
 7436                    ),
 7437                    (
 7438                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7439                        "".into()
 7440                    ),
 7441                    (
 7442                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7443                        "\n".into()
 7444                    ),
 7445                ]
 7446            );
 7447
 7448            // Insert blank lines between each line of the buffer.
 7449            async move {
 7450                Ok(Some(vec![
 7451                    lsp::TextEdit {
 7452                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7453                        new_text: "\n".into(),
 7454                    },
 7455                    lsp::TextEdit {
 7456                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7457                        new_text: "\n".into(),
 7458                    },
 7459                ]))
 7460            }
 7461        }
 7462    });
 7463
 7464    // After formatting the buffer, the trailing whitespace is stripped,
 7465    // a newline is appended, and the edits provided by the language server
 7466    // have been applied.
 7467    format.await.unwrap();
 7468    cx.assert_editor_state(
 7469        &[
 7470            "one",   //
 7471            "",      //
 7472            "twoˇ",  //
 7473            "",      //
 7474            "three", //
 7475            "four",  //
 7476            "",      //
 7477        ]
 7478        .join("\n"),
 7479    );
 7480
 7481    // Undoing the formatting undoes the trailing whitespace removal, the
 7482    // trailing newline, and the LSP edits.
 7483    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7484    cx.assert_editor_state(
 7485        &[
 7486            "one ",   //
 7487            "twoˇ",   //
 7488            "three ", //
 7489            "four",   //
 7490        ]
 7491        .join("\n"),
 7492    );
 7493}
 7494
 7495#[gpui::test]
 7496async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7497    cx: &mut gpui::TestAppContext,
 7498) {
 7499    init_test(cx, |_| {});
 7500
 7501    cx.update(|cx| {
 7502        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7503            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7504                settings.auto_signature_help = Some(true);
 7505            });
 7506        });
 7507    });
 7508
 7509    let mut cx = EditorLspTestContext::new_rust(
 7510        lsp::ServerCapabilities {
 7511            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7512                ..Default::default()
 7513            }),
 7514            ..Default::default()
 7515        },
 7516        cx,
 7517    )
 7518    .await;
 7519
 7520    let language = Language::new(
 7521        LanguageConfig {
 7522            name: "Rust".into(),
 7523            brackets: BracketPairConfig {
 7524                pairs: vec![
 7525                    BracketPair {
 7526                        start: "{".to_string(),
 7527                        end: "}".to_string(),
 7528                        close: true,
 7529                        surround: true,
 7530                        newline: true,
 7531                    },
 7532                    BracketPair {
 7533                        start: "(".to_string(),
 7534                        end: ")".to_string(),
 7535                        close: true,
 7536                        surround: true,
 7537                        newline: true,
 7538                    },
 7539                    BracketPair {
 7540                        start: "/*".to_string(),
 7541                        end: " */".to_string(),
 7542                        close: true,
 7543                        surround: true,
 7544                        newline: true,
 7545                    },
 7546                    BracketPair {
 7547                        start: "[".to_string(),
 7548                        end: "]".to_string(),
 7549                        close: false,
 7550                        surround: false,
 7551                        newline: true,
 7552                    },
 7553                    BracketPair {
 7554                        start: "\"".to_string(),
 7555                        end: "\"".to_string(),
 7556                        close: true,
 7557                        surround: true,
 7558                        newline: false,
 7559                    },
 7560                    BracketPair {
 7561                        start: "<".to_string(),
 7562                        end: ">".to_string(),
 7563                        close: false,
 7564                        surround: true,
 7565                        newline: true,
 7566                    },
 7567                ],
 7568                ..Default::default()
 7569            },
 7570            autoclose_before: "})]".to_string(),
 7571            ..Default::default()
 7572        },
 7573        Some(tree_sitter_rust::LANGUAGE.into()),
 7574    );
 7575    let language = Arc::new(language);
 7576
 7577    cx.language_registry().add(language.clone());
 7578    cx.update_buffer(|buffer, cx| {
 7579        buffer.set_language(Some(language), cx);
 7580    });
 7581
 7582    cx.set_state(
 7583        &r#"
 7584            fn main() {
 7585                sampleˇ
 7586            }
 7587        "#
 7588        .unindent(),
 7589    );
 7590
 7591    cx.update_editor(|view, cx| {
 7592        view.handle_input("(", cx);
 7593    });
 7594    cx.assert_editor_state(
 7595        &"
 7596            fn main() {
 7597                sample(ˇ)
 7598            }
 7599        "
 7600        .unindent(),
 7601    );
 7602
 7603    let mocked_response = lsp::SignatureHelp {
 7604        signatures: vec![lsp::SignatureInformation {
 7605            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7606            documentation: None,
 7607            parameters: Some(vec![
 7608                lsp::ParameterInformation {
 7609                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7610                    documentation: None,
 7611                },
 7612                lsp::ParameterInformation {
 7613                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7614                    documentation: None,
 7615                },
 7616            ]),
 7617            active_parameter: None,
 7618        }],
 7619        active_signature: Some(0),
 7620        active_parameter: Some(0),
 7621    };
 7622    handle_signature_help_request(&mut cx, mocked_response).await;
 7623
 7624    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7625        .await;
 7626
 7627    cx.editor(|editor, _| {
 7628        let signature_help_state = editor.signature_help_state.popover().cloned();
 7629        assert!(signature_help_state.is_some());
 7630        let ParsedMarkdown {
 7631            text, highlights, ..
 7632        } = signature_help_state.unwrap().parsed_content;
 7633        assert_eq!(text, "param1: u8, param2: u8");
 7634        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7635    });
 7636}
 7637
 7638#[gpui::test]
 7639async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7640    init_test(cx, |_| {});
 7641
 7642    cx.update(|cx| {
 7643        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7644            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7645                settings.auto_signature_help = Some(false);
 7646                settings.show_signature_help_after_edits = Some(false);
 7647            });
 7648        });
 7649    });
 7650
 7651    let mut cx = EditorLspTestContext::new_rust(
 7652        lsp::ServerCapabilities {
 7653            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7654                ..Default::default()
 7655            }),
 7656            ..Default::default()
 7657        },
 7658        cx,
 7659    )
 7660    .await;
 7661
 7662    let language = Language::new(
 7663        LanguageConfig {
 7664            name: "Rust".into(),
 7665            brackets: BracketPairConfig {
 7666                pairs: vec![
 7667                    BracketPair {
 7668                        start: "{".to_string(),
 7669                        end: "}".to_string(),
 7670                        close: true,
 7671                        surround: true,
 7672                        newline: true,
 7673                    },
 7674                    BracketPair {
 7675                        start: "(".to_string(),
 7676                        end: ")".to_string(),
 7677                        close: true,
 7678                        surround: true,
 7679                        newline: true,
 7680                    },
 7681                    BracketPair {
 7682                        start: "/*".to_string(),
 7683                        end: " */".to_string(),
 7684                        close: true,
 7685                        surround: true,
 7686                        newline: true,
 7687                    },
 7688                    BracketPair {
 7689                        start: "[".to_string(),
 7690                        end: "]".to_string(),
 7691                        close: false,
 7692                        surround: false,
 7693                        newline: true,
 7694                    },
 7695                    BracketPair {
 7696                        start: "\"".to_string(),
 7697                        end: "\"".to_string(),
 7698                        close: true,
 7699                        surround: true,
 7700                        newline: false,
 7701                    },
 7702                    BracketPair {
 7703                        start: "<".to_string(),
 7704                        end: ">".to_string(),
 7705                        close: false,
 7706                        surround: true,
 7707                        newline: true,
 7708                    },
 7709                ],
 7710                ..Default::default()
 7711            },
 7712            autoclose_before: "})]".to_string(),
 7713            ..Default::default()
 7714        },
 7715        Some(tree_sitter_rust::LANGUAGE.into()),
 7716    );
 7717    let language = Arc::new(language);
 7718
 7719    cx.language_registry().add(language.clone());
 7720    cx.update_buffer(|buffer, cx| {
 7721        buffer.set_language(Some(language), cx);
 7722    });
 7723
 7724    // Ensure that signature_help is not called when no signature help is enabled.
 7725    cx.set_state(
 7726        &r#"
 7727            fn main() {
 7728                sampleˇ
 7729            }
 7730        "#
 7731        .unindent(),
 7732    );
 7733    cx.update_editor(|view, cx| {
 7734        view.handle_input("(", cx);
 7735    });
 7736    cx.assert_editor_state(
 7737        &"
 7738            fn main() {
 7739                sample(ˇ)
 7740            }
 7741        "
 7742        .unindent(),
 7743    );
 7744    cx.editor(|editor, _| {
 7745        assert!(editor.signature_help_state.task().is_none());
 7746    });
 7747
 7748    let mocked_response = lsp::SignatureHelp {
 7749        signatures: vec![lsp::SignatureInformation {
 7750            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7751            documentation: None,
 7752            parameters: Some(vec![
 7753                lsp::ParameterInformation {
 7754                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7755                    documentation: None,
 7756                },
 7757                lsp::ParameterInformation {
 7758                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7759                    documentation: None,
 7760                },
 7761            ]),
 7762            active_parameter: None,
 7763        }],
 7764        active_signature: Some(0),
 7765        active_parameter: Some(0),
 7766    };
 7767
 7768    // Ensure that signature_help is called when enabled afte edits
 7769    cx.update(|cx| {
 7770        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7771            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7772                settings.auto_signature_help = Some(false);
 7773                settings.show_signature_help_after_edits = Some(true);
 7774            });
 7775        });
 7776    });
 7777    cx.set_state(
 7778        &r#"
 7779            fn main() {
 7780                sampleˇ
 7781            }
 7782        "#
 7783        .unindent(),
 7784    );
 7785    cx.update_editor(|view, cx| {
 7786        view.handle_input("(", cx);
 7787    });
 7788    cx.assert_editor_state(
 7789        &"
 7790            fn main() {
 7791                sample(ˇ)
 7792            }
 7793        "
 7794        .unindent(),
 7795    );
 7796    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7797    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7798        .await;
 7799    cx.update_editor(|editor, _| {
 7800        let signature_help_state = editor.signature_help_state.popover().cloned();
 7801        assert!(signature_help_state.is_some());
 7802        let ParsedMarkdown {
 7803            text, highlights, ..
 7804        } = signature_help_state.unwrap().parsed_content;
 7805        assert_eq!(text, "param1: u8, param2: u8");
 7806        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7807        editor.signature_help_state = SignatureHelpState::default();
 7808    });
 7809
 7810    // Ensure that signature_help is called when auto signature help override is enabled
 7811    cx.update(|cx| {
 7812        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7813            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7814                settings.auto_signature_help = Some(true);
 7815                settings.show_signature_help_after_edits = Some(false);
 7816            });
 7817        });
 7818    });
 7819    cx.set_state(
 7820        &r#"
 7821            fn main() {
 7822                sampleˇ
 7823            }
 7824        "#
 7825        .unindent(),
 7826    );
 7827    cx.update_editor(|view, cx| {
 7828        view.handle_input("(", cx);
 7829    });
 7830    cx.assert_editor_state(
 7831        &"
 7832            fn main() {
 7833                sample(ˇ)
 7834            }
 7835        "
 7836        .unindent(),
 7837    );
 7838    handle_signature_help_request(&mut cx, mocked_response).await;
 7839    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7840        .await;
 7841    cx.editor(|editor, _| {
 7842        let signature_help_state = editor.signature_help_state.popover().cloned();
 7843        assert!(signature_help_state.is_some());
 7844        let ParsedMarkdown {
 7845            text, highlights, ..
 7846        } = signature_help_state.unwrap().parsed_content;
 7847        assert_eq!(text, "param1: u8, param2: u8");
 7848        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7849    });
 7850}
 7851
 7852#[gpui::test]
 7853async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7854    init_test(cx, |_| {});
 7855    cx.update(|cx| {
 7856        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7857            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7858                settings.auto_signature_help = Some(true);
 7859            });
 7860        });
 7861    });
 7862
 7863    let mut cx = EditorLspTestContext::new_rust(
 7864        lsp::ServerCapabilities {
 7865            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7866                ..Default::default()
 7867            }),
 7868            ..Default::default()
 7869        },
 7870        cx,
 7871    )
 7872    .await;
 7873
 7874    // A test that directly calls `show_signature_help`
 7875    cx.update_editor(|editor, cx| {
 7876        editor.show_signature_help(&ShowSignatureHelp, cx);
 7877    });
 7878
 7879    let mocked_response = lsp::SignatureHelp {
 7880        signatures: vec![lsp::SignatureInformation {
 7881            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7882            documentation: None,
 7883            parameters: Some(vec![
 7884                lsp::ParameterInformation {
 7885                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7886                    documentation: None,
 7887                },
 7888                lsp::ParameterInformation {
 7889                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7890                    documentation: None,
 7891                },
 7892            ]),
 7893            active_parameter: None,
 7894        }],
 7895        active_signature: Some(0),
 7896        active_parameter: Some(0),
 7897    };
 7898    handle_signature_help_request(&mut cx, mocked_response).await;
 7899
 7900    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7901        .await;
 7902
 7903    cx.editor(|editor, _| {
 7904        let signature_help_state = editor.signature_help_state.popover().cloned();
 7905        assert!(signature_help_state.is_some());
 7906        let ParsedMarkdown {
 7907            text, highlights, ..
 7908        } = signature_help_state.unwrap().parsed_content;
 7909        assert_eq!(text, "param1: u8, param2: u8");
 7910        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7911    });
 7912
 7913    // When exiting outside from inside the brackets, `signature_help` is closed.
 7914    cx.set_state(indoc! {"
 7915        fn main() {
 7916            sample(ˇ);
 7917        }
 7918
 7919        fn sample(param1: u8, param2: u8) {}
 7920    "});
 7921
 7922    cx.update_editor(|editor, cx| {
 7923        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7924    });
 7925
 7926    let mocked_response = lsp::SignatureHelp {
 7927        signatures: Vec::new(),
 7928        active_signature: None,
 7929        active_parameter: None,
 7930    };
 7931    handle_signature_help_request(&mut cx, mocked_response).await;
 7932
 7933    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7934        .await;
 7935
 7936    cx.editor(|editor, _| {
 7937        assert!(!editor.signature_help_state.is_shown());
 7938    });
 7939
 7940    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7941    cx.set_state(indoc! {"
 7942        fn main() {
 7943            sample(ˇ);
 7944        }
 7945
 7946        fn sample(param1: u8, param2: u8) {}
 7947    "});
 7948
 7949    let mocked_response = lsp::SignatureHelp {
 7950        signatures: vec![lsp::SignatureInformation {
 7951            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7952            documentation: None,
 7953            parameters: Some(vec![
 7954                lsp::ParameterInformation {
 7955                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7956                    documentation: None,
 7957                },
 7958                lsp::ParameterInformation {
 7959                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7960                    documentation: None,
 7961                },
 7962            ]),
 7963            active_parameter: None,
 7964        }],
 7965        active_signature: Some(0),
 7966        active_parameter: Some(0),
 7967    };
 7968    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7969    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7970        .await;
 7971    cx.editor(|editor, _| {
 7972        assert!(editor.signature_help_state.is_shown());
 7973    });
 7974
 7975    // Restore the popover with more parameter input
 7976    cx.set_state(indoc! {"
 7977        fn main() {
 7978            sample(param1, param2ˇ);
 7979        }
 7980
 7981        fn sample(param1: u8, param2: u8) {}
 7982    "});
 7983
 7984    let mocked_response = lsp::SignatureHelp {
 7985        signatures: vec![lsp::SignatureInformation {
 7986            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7987            documentation: None,
 7988            parameters: Some(vec![
 7989                lsp::ParameterInformation {
 7990                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7991                    documentation: None,
 7992                },
 7993                lsp::ParameterInformation {
 7994                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7995                    documentation: None,
 7996                },
 7997            ]),
 7998            active_parameter: None,
 7999        }],
 8000        active_signature: Some(0),
 8001        active_parameter: Some(1),
 8002    };
 8003    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8004    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8005        .await;
 8006
 8007    // When selecting a range, the popover is gone.
 8008    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8009    cx.update_editor(|editor, cx| {
 8010        editor.change_selections(None, cx, |s| {
 8011            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8012        })
 8013    });
 8014    cx.assert_editor_state(indoc! {"
 8015        fn main() {
 8016            sample(param1, «ˇparam2»);
 8017        }
 8018
 8019        fn sample(param1: u8, param2: u8) {}
 8020    "});
 8021    cx.editor(|editor, _| {
 8022        assert!(!editor.signature_help_state.is_shown());
 8023    });
 8024
 8025    // When unselecting again, the popover is back if within the brackets.
 8026    cx.update_editor(|editor, cx| {
 8027        editor.change_selections(None, cx, |s| {
 8028            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8029        })
 8030    });
 8031    cx.assert_editor_state(indoc! {"
 8032        fn main() {
 8033            sample(param1, ˇparam2);
 8034        }
 8035
 8036        fn sample(param1: u8, param2: u8) {}
 8037    "});
 8038    handle_signature_help_request(&mut cx, mocked_response).await;
 8039    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8040        .await;
 8041    cx.editor(|editor, _| {
 8042        assert!(editor.signature_help_state.is_shown());
 8043    });
 8044
 8045    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8046    cx.update_editor(|editor, cx| {
 8047        editor.change_selections(None, cx, |s| {
 8048            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8049            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8050        })
 8051    });
 8052    cx.assert_editor_state(indoc! {"
 8053        fn main() {
 8054            sample(param1, ˇparam2);
 8055        }
 8056
 8057        fn sample(param1: u8, param2: u8) {}
 8058    "});
 8059
 8060    let mocked_response = lsp::SignatureHelp {
 8061        signatures: vec![lsp::SignatureInformation {
 8062            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8063            documentation: None,
 8064            parameters: Some(vec![
 8065                lsp::ParameterInformation {
 8066                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8067                    documentation: None,
 8068                },
 8069                lsp::ParameterInformation {
 8070                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8071                    documentation: None,
 8072                },
 8073            ]),
 8074            active_parameter: None,
 8075        }],
 8076        active_signature: Some(0),
 8077        active_parameter: Some(1),
 8078    };
 8079    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8080    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8081        .await;
 8082    cx.update_editor(|editor, cx| {
 8083        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8084    });
 8085    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8086        .await;
 8087    cx.update_editor(|editor, cx| {
 8088        editor.change_selections(None, cx, |s| {
 8089            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8090        })
 8091    });
 8092    cx.assert_editor_state(indoc! {"
 8093        fn main() {
 8094            sample(param1, «ˇparam2»);
 8095        }
 8096
 8097        fn sample(param1: u8, param2: u8) {}
 8098    "});
 8099    cx.update_editor(|editor, cx| {
 8100        editor.change_selections(None, cx, |s| {
 8101            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8102        })
 8103    });
 8104    cx.assert_editor_state(indoc! {"
 8105        fn main() {
 8106            sample(param1, ˇparam2);
 8107        }
 8108
 8109        fn sample(param1: u8, param2: u8) {}
 8110    "});
 8111    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8112        .await;
 8113}
 8114
 8115#[gpui::test]
 8116async fn test_completion(cx: &mut gpui::TestAppContext) {
 8117    init_test(cx, |_| {});
 8118
 8119    let mut cx = EditorLspTestContext::new_rust(
 8120        lsp::ServerCapabilities {
 8121            completion_provider: Some(lsp::CompletionOptions {
 8122                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8123                resolve_provider: Some(true),
 8124                ..Default::default()
 8125            }),
 8126            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8127            ..Default::default()
 8128        },
 8129        cx,
 8130    )
 8131    .await;
 8132    let counter = Arc::new(AtomicUsize::new(0));
 8133
 8134    cx.set_state(indoc! {"
 8135        oneˇ
 8136        two
 8137        three
 8138    "});
 8139    cx.simulate_keystroke(".");
 8140    handle_completion_request(
 8141        &mut cx,
 8142        indoc! {"
 8143            one.|<>
 8144            two
 8145            three
 8146        "},
 8147        vec!["first_completion", "second_completion"],
 8148        counter.clone(),
 8149    )
 8150    .await;
 8151    cx.condition(|editor, _| editor.context_menu_visible())
 8152        .await;
 8153    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8154
 8155    let _handler = handle_signature_help_request(
 8156        &mut cx,
 8157        lsp::SignatureHelp {
 8158            signatures: vec![lsp::SignatureInformation {
 8159                label: "test signature".to_string(),
 8160                documentation: None,
 8161                parameters: Some(vec![lsp::ParameterInformation {
 8162                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8163                    documentation: None,
 8164                }]),
 8165                active_parameter: None,
 8166            }],
 8167            active_signature: None,
 8168            active_parameter: None,
 8169        },
 8170    );
 8171    cx.update_editor(|editor, cx| {
 8172        assert!(
 8173            !editor.signature_help_state.is_shown(),
 8174            "No signature help was called for"
 8175        );
 8176        editor.show_signature_help(&ShowSignatureHelp, cx);
 8177    });
 8178    cx.run_until_parked();
 8179    cx.update_editor(|editor, _| {
 8180        assert!(
 8181            !editor.signature_help_state.is_shown(),
 8182            "No signature help should be shown when completions menu is open"
 8183        );
 8184    });
 8185
 8186    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8187        editor.context_menu_next(&Default::default(), cx);
 8188        editor
 8189            .confirm_completion(&ConfirmCompletion::default(), cx)
 8190            .unwrap()
 8191    });
 8192    cx.assert_editor_state(indoc! {"
 8193        one.second_completionˇ
 8194        two
 8195        three
 8196    "});
 8197
 8198    handle_resolve_completion_request(
 8199        &mut cx,
 8200        Some(vec![
 8201            (
 8202                //This overlaps with the primary completion edit which is
 8203                //misbehavior from the LSP spec, test that we filter it out
 8204                indoc! {"
 8205                    one.second_ˇcompletion
 8206                    two
 8207                    threeˇ
 8208                "},
 8209                "overlapping additional edit",
 8210            ),
 8211            (
 8212                indoc! {"
 8213                    one.second_completion
 8214                    two
 8215                    threeˇ
 8216                "},
 8217                "\nadditional edit",
 8218            ),
 8219        ]),
 8220    )
 8221    .await;
 8222    apply_additional_edits.await.unwrap();
 8223    cx.assert_editor_state(indoc! {"
 8224        one.second_completionˇ
 8225        two
 8226        three
 8227        additional edit
 8228    "});
 8229
 8230    cx.set_state(indoc! {"
 8231        one.second_completion
 8232        twoˇ
 8233        threeˇ
 8234        additional edit
 8235    "});
 8236    cx.simulate_keystroke(" ");
 8237    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8238    cx.simulate_keystroke("s");
 8239    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8240
 8241    cx.assert_editor_state(indoc! {"
 8242        one.second_completion
 8243        two sˇ
 8244        three sˇ
 8245        additional edit
 8246    "});
 8247    handle_completion_request(
 8248        &mut cx,
 8249        indoc! {"
 8250            one.second_completion
 8251            two s
 8252            three <s|>
 8253            additional edit
 8254        "},
 8255        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8256        counter.clone(),
 8257    )
 8258    .await;
 8259    cx.condition(|editor, _| editor.context_menu_visible())
 8260        .await;
 8261    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8262
 8263    cx.simulate_keystroke("i");
 8264
 8265    handle_completion_request(
 8266        &mut cx,
 8267        indoc! {"
 8268            one.second_completion
 8269            two si
 8270            three <si|>
 8271            additional edit
 8272        "},
 8273        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8274        counter.clone(),
 8275    )
 8276    .await;
 8277    cx.condition(|editor, _| editor.context_menu_visible())
 8278        .await;
 8279    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8280
 8281    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8282        editor
 8283            .confirm_completion(&ConfirmCompletion::default(), cx)
 8284            .unwrap()
 8285    });
 8286    cx.assert_editor_state(indoc! {"
 8287        one.second_completion
 8288        two sixth_completionˇ
 8289        three sixth_completionˇ
 8290        additional edit
 8291    "});
 8292
 8293    handle_resolve_completion_request(&mut cx, None).await;
 8294    apply_additional_edits.await.unwrap();
 8295
 8296    cx.update(|cx| {
 8297        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8298            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8299                settings.show_completions_on_input = Some(false);
 8300            });
 8301        })
 8302    });
 8303    cx.set_state("editorˇ");
 8304    cx.simulate_keystroke(".");
 8305    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8306    cx.simulate_keystroke("c");
 8307    cx.simulate_keystroke("l");
 8308    cx.simulate_keystroke("o");
 8309    cx.assert_editor_state("editor.cloˇ");
 8310    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8311    cx.update_editor(|editor, cx| {
 8312        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8313    });
 8314    handle_completion_request(
 8315        &mut cx,
 8316        "editor.<clo|>",
 8317        vec!["close", "clobber"],
 8318        counter.clone(),
 8319    )
 8320    .await;
 8321    cx.condition(|editor, _| editor.context_menu_visible())
 8322        .await;
 8323    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8324
 8325    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8326        editor
 8327            .confirm_completion(&ConfirmCompletion::default(), cx)
 8328            .unwrap()
 8329    });
 8330    cx.assert_editor_state("editor.closeˇ");
 8331    handle_resolve_completion_request(&mut cx, None).await;
 8332    apply_additional_edits.await.unwrap();
 8333}
 8334
 8335#[gpui::test]
 8336async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8337    init_test(cx, |_| {});
 8338    let mut cx = EditorLspTestContext::new_rust(
 8339        lsp::ServerCapabilities {
 8340            completion_provider: Some(lsp::CompletionOptions {
 8341                trigger_characters: Some(vec![".".to_string()]),
 8342                ..Default::default()
 8343            }),
 8344            ..Default::default()
 8345        },
 8346        cx,
 8347    )
 8348    .await;
 8349    cx.lsp
 8350        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8351            Ok(Some(lsp::CompletionResponse::Array(vec![
 8352                lsp::CompletionItem {
 8353                    label: "first".into(),
 8354                    ..Default::default()
 8355                },
 8356                lsp::CompletionItem {
 8357                    label: "last".into(),
 8358                    ..Default::default()
 8359                },
 8360            ])))
 8361        });
 8362    cx.set_state("variableˇ");
 8363    cx.simulate_keystroke(".");
 8364    cx.executor().run_until_parked();
 8365
 8366    cx.update_editor(|editor, _| {
 8367        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8368            assert_eq!(
 8369                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8370                &["first", "last"]
 8371            );
 8372        } else {
 8373            panic!("expected completion menu to be open");
 8374        }
 8375    });
 8376
 8377    cx.update_editor(|editor, cx| {
 8378        editor.move_page_down(&MovePageDown::default(), cx);
 8379        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8380            assert!(
 8381                menu.selected_item == 1,
 8382                "expected PageDown to select the last item from the context menu"
 8383            );
 8384        } else {
 8385            panic!("expected completion menu to stay open after PageDown");
 8386        }
 8387    });
 8388
 8389    cx.update_editor(|editor, cx| {
 8390        editor.move_page_up(&MovePageUp::default(), cx);
 8391        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8392            assert!(
 8393                menu.selected_item == 0,
 8394                "expected PageUp to select the first item from the context menu"
 8395            );
 8396        } else {
 8397            panic!("expected completion menu to stay open after PageUp");
 8398        }
 8399    });
 8400}
 8401
 8402#[gpui::test]
 8403async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8404    init_test(cx, |_| {});
 8405    let mut cx = EditorLspTestContext::new_rust(
 8406        lsp::ServerCapabilities {
 8407            completion_provider: Some(lsp::CompletionOptions {
 8408                trigger_characters: Some(vec![".".to_string()]),
 8409                ..Default::default()
 8410            }),
 8411            ..Default::default()
 8412        },
 8413        cx,
 8414    )
 8415    .await;
 8416    cx.lsp
 8417        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8418            Ok(Some(lsp::CompletionResponse::Array(vec![
 8419                lsp::CompletionItem {
 8420                    label: "Range".into(),
 8421                    sort_text: Some("a".into()),
 8422                    ..Default::default()
 8423                },
 8424                lsp::CompletionItem {
 8425                    label: "r".into(),
 8426                    sort_text: Some("b".into()),
 8427                    ..Default::default()
 8428                },
 8429                lsp::CompletionItem {
 8430                    label: "ret".into(),
 8431                    sort_text: Some("c".into()),
 8432                    ..Default::default()
 8433                },
 8434                lsp::CompletionItem {
 8435                    label: "return".into(),
 8436                    sort_text: Some("d".into()),
 8437                    ..Default::default()
 8438                },
 8439                lsp::CompletionItem {
 8440                    label: "slice".into(),
 8441                    sort_text: Some("d".into()),
 8442                    ..Default::default()
 8443                },
 8444            ])))
 8445        });
 8446    cx.set_state("");
 8447    cx.executor().run_until_parked();
 8448    cx.update_editor(|editor, cx| {
 8449        editor.show_completions(
 8450            &ShowCompletions {
 8451                trigger: Some("r".into()),
 8452            },
 8453            cx,
 8454        );
 8455    });
 8456    cx.executor().run_until_parked();
 8457
 8458    cx.update_editor(|editor, _| {
 8459        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8460            assert_eq!(
 8461                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8462                &["r", "ret", "Range", "return"]
 8463            );
 8464        } else {
 8465            panic!("expected completion menu to be open");
 8466        }
 8467    });
 8468}
 8469
 8470#[gpui::test]
 8471async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8472    init_test(cx, |_| {});
 8473
 8474    let mut cx = EditorLspTestContext::new_rust(
 8475        lsp::ServerCapabilities {
 8476            completion_provider: Some(lsp::CompletionOptions {
 8477                trigger_characters: Some(vec![".".to_string()]),
 8478                resolve_provider: Some(true),
 8479                ..Default::default()
 8480            }),
 8481            ..Default::default()
 8482        },
 8483        cx,
 8484    )
 8485    .await;
 8486
 8487    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8488    cx.simulate_keystroke(".");
 8489    let completion_item = lsp::CompletionItem {
 8490        label: "Some".into(),
 8491        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8492        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8493        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8494            kind: lsp::MarkupKind::Markdown,
 8495            value: "```rust\nSome(2)\n```".to_string(),
 8496        })),
 8497        deprecated: Some(false),
 8498        sort_text: Some("Some".to_string()),
 8499        filter_text: Some("Some".to_string()),
 8500        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8501        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8502            range: lsp::Range {
 8503                start: lsp::Position {
 8504                    line: 0,
 8505                    character: 22,
 8506                },
 8507                end: lsp::Position {
 8508                    line: 0,
 8509                    character: 22,
 8510                },
 8511            },
 8512            new_text: "Some(2)".to_string(),
 8513        })),
 8514        additional_text_edits: Some(vec![lsp::TextEdit {
 8515            range: lsp::Range {
 8516                start: lsp::Position {
 8517                    line: 0,
 8518                    character: 20,
 8519                },
 8520                end: lsp::Position {
 8521                    line: 0,
 8522                    character: 22,
 8523                },
 8524            },
 8525            new_text: "".to_string(),
 8526        }]),
 8527        ..Default::default()
 8528    };
 8529
 8530    let closure_completion_item = completion_item.clone();
 8531    let counter = Arc::new(AtomicUsize::new(0));
 8532    let counter_clone = counter.clone();
 8533    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8534        let task_completion_item = closure_completion_item.clone();
 8535        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8536        async move {
 8537            Ok(Some(lsp::CompletionResponse::Array(vec![
 8538                task_completion_item,
 8539            ])))
 8540        }
 8541    });
 8542
 8543    cx.condition(|editor, _| editor.context_menu_visible())
 8544        .await;
 8545    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8546    assert!(request.next().await.is_some());
 8547    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8548
 8549    cx.simulate_keystroke("S");
 8550    cx.simulate_keystroke("o");
 8551    cx.simulate_keystroke("m");
 8552    cx.condition(|editor, _| editor.context_menu_visible())
 8553        .await;
 8554    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8555    assert!(request.next().await.is_some());
 8556    assert!(request.next().await.is_some());
 8557    assert!(request.next().await.is_some());
 8558    request.close();
 8559    assert!(request.next().await.is_none());
 8560    assert_eq!(
 8561        counter.load(atomic::Ordering::Acquire),
 8562        4,
 8563        "With the completions menu open, only one LSP request should happen per input"
 8564    );
 8565}
 8566
 8567#[gpui::test]
 8568async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8569    init_test(cx, |_| {});
 8570    let mut cx = EditorTestContext::new(cx).await;
 8571    let language = Arc::new(Language::new(
 8572        LanguageConfig {
 8573            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8574            ..Default::default()
 8575        },
 8576        Some(tree_sitter_rust::LANGUAGE.into()),
 8577    ));
 8578    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8579
 8580    // If multiple selections intersect a line, the line is only toggled once.
 8581    cx.set_state(indoc! {"
 8582        fn a() {
 8583            «//b();
 8584            ˇ»// «c();
 8585            //ˇ»  d();
 8586        }
 8587    "});
 8588
 8589    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8590
 8591    cx.assert_editor_state(indoc! {"
 8592        fn a() {
 8593            «b();
 8594            c();
 8595            ˇ» d();
 8596        }
 8597    "});
 8598
 8599    // The comment prefix is inserted at the same column for every line in a
 8600    // selection.
 8601    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8602
 8603    cx.assert_editor_state(indoc! {"
 8604        fn a() {
 8605            // «b();
 8606            // c();
 8607            ˇ»//  d();
 8608        }
 8609    "});
 8610
 8611    // If a selection ends at the beginning of a line, that line is not toggled.
 8612    cx.set_selections_state(indoc! {"
 8613        fn a() {
 8614            // b();
 8615            «// c();
 8616        ˇ»    //  d();
 8617        }
 8618    "});
 8619
 8620    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8621
 8622    cx.assert_editor_state(indoc! {"
 8623        fn a() {
 8624            // b();
 8625            «c();
 8626        ˇ»    //  d();
 8627        }
 8628    "});
 8629
 8630    // If a selection span a single line and is empty, the line is toggled.
 8631    cx.set_state(indoc! {"
 8632        fn a() {
 8633            a();
 8634            b();
 8635        ˇ
 8636        }
 8637    "});
 8638
 8639    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8640
 8641    cx.assert_editor_state(indoc! {"
 8642        fn a() {
 8643            a();
 8644            b();
 8645        //•ˇ
 8646        }
 8647    "});
 8648
 8649    // If a selection span multiple lines, empty lines are not toggled.
 8650    cx.set_state(indoc! {"
 8651        fn a() {
 8652            «a();
 8653
 8654            c();ˇ»
 8655        }
 8656    "});
 8657
 8658    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8659
 8660    cx.assert_editor_state(indoc! {"
 8661        fn a() {
 8662            // «a();
 8663
 8664            // c();ˇ»
 8665        }
 8666    "});
 8667
 8668    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8669    cx.set_state(indoc! {"
 8670        fn a() {
 8671            «// a();
 8672            /// b();
 8673            //! c();ˇ»
 8674        }
 8675    "});
 8676
 8677    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8678
 8679    cx.assert_editor_state(indoc! {"
 8680        fn a() {
 8681            «a();
 8682            b();
 8683            c();ˇ»
 8684        }
 8685    "});
 8686}
 8687
 8688#[gpui::test]
 8689async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8690    init_test(cx, |_| {});
 8691    let mut cx = EditorTestContext::new(cx).await;
 8692    let language = Arc::new(Language::new(
 8693        LanguageConfig {
 8694            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8695            ..Default::default()
 8696        },
 8697        Some(tree_sitter_rust::LANGUAGE.into()),
 8698    ));
 8699    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8700
 8701    let toggle_comments = &ToggleComments {
 8702        advance_downwards: false,
 8703        ignore_indent: true,
 8704    };
 8705
 8706    // If multiple selections intersect a line, the line is only toggled once.
 8707    cx.set_state(indoc! {"
 8708        fn a() {
 8709        //    «b();
 8710        //    c();
 8711        //    ˇ» d();
 8712        }
 8713    "});
 8714
 8715    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8716
 8717    cx.assert_editor_state(indoc! {"
 8718        fn a() {
 8719            «b();
 8720            c();
 8721            ˇ» d();
 8722        }
 8723    "});
 8724
 8725    // The comment prefix is inserted at the beginning of each line
 8726    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8727
 8728    cx.assert_editor_state(indoc! {"
 8729        fn a() {
 8730        //    «b();
 8731        //    c();
 8732        //    ˇ» d();
 8733        }
 8734    "});
 8735
 8736    // If a selection ends at the beginning of a line, that line is not toggled.
 8737    cx.set_selections_state(indoc! {"
 8738        fn a() {
 8739        //    b();
 8740        //    «c();
 8741        ˇ»//     d();
 8742        }
 8743    "});
 8744
 8745    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8746
 8747    cx.assert_editor_state(indoc! {"
 8748        fn a() {
 8749        //    b();
 8750            «c();
 8751        ˇ»//     d();
 8752        }
 8753    "});
 8754
 8755    // If a selection span a single line and is empty, the line is toggled.
 8756    cx.set_state(indoc! {"
 8757        fn a() {
 8758            a();
 8759            b();
 8760        ˇ
 8761        }
 8762    "});
 8763
 8764    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8765
 8766    cx.assert_editor_state(indoc! {"
 8767        fn a() {
 8768            a();
 8769            b();
 8770        //ˇ
 8771        }
 8772    "});
 8773
 8774    // If a selection span multiple lines, empty lines are not toggled.
 8775    cx.set_state(indoc! {"
 8776        fn a() {
 8777            «a();
 8778
 8779            c();ˇ»
 8780        }
 8781    "});
 8782
 8783    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8784
 8785    cx.assert_editor_state(indoc! {"
 8786        fn a() {
 8787        //    «a();
 8788
 8789        //    c();ˇ»
 8790        }
 8791    "});
 8792
 8793    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8794    cx.set_state(indoc! {"
 8795        fn a() {
 8796        //    «a();
 8797        ///    b();
 8798        //!    c();ˇ»
 8799        }
 8800    "});
 8801
 8802    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8803
 8804    cx.assert_editor_state(indoc! {"
 8805        fn a() {
 8806            «a();
 8807            b();
 8808            c();ˇ»
 8809        }
 8810    "});
 8811}
 8812
 8813#[gpui::test]
 8814async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8815    init_test(cx, |_| {});
 8816
 8817    let language = Arc::new(Language::new(
 8818        LanguageConfig {
 8819            line_comments: vec!["// ".into()],
 8820            ..Default::default()
 8821        },
 8822        Some(tree_sitter_rust::LANGUAGE.into()),
 8823    ));
 8824
 8825    let mut cx = EditorTestContext::new(cx).await;
 8826
 8827    cx.language_registry().add(language.clone());
 8828    cx.update_buffer(|buffer, cx| {
 8829        buffer.set_language(Some(language), cx);
 8830    });
 8831
 8832    let toggle_comments = &ToggleComments {
 8833        advance_downwards: true,
 8834        ignore_indent: false,
 8835    };
 8836
 8837    // Single cursor on one line -> advance
 8838    // Cursor moves horizontally 3 characters as well on non-blank line
 8839    cx.set_state(indoc!(
 8840        "fn a() {
 8841             ˇdog();
 8842             cat();
 8843        }"
 8844    ));
 8845    cx.update_editor(|editor, cx| {
 8846        editor.toggle_comments(toggle_comments, cx);
 8847    });
 8848    cx.assert_editor_state(indoc!(
 8849        "fn a() {
 8850             // dog();
 8851             catˇ();
 8852        }"
 8853    ));
 8854
 8855    // Single selection on one line -> don't advance
 8856    cx.set_state(indoc!(
 8857        "fn a() {
 8858             «dog()ˇ»;
 8859             cat();
 8860        }"
 8861    ));
 8862    cx.update_editor(|editor, cx| {
 8863        editor.toggle_comments(toggle_comments, cx);
 8864    });
 8865    cx.assert_editor_state(indoc!(
 8866        "fn a() {
 8867             // «dog()ˇ»;
 8868             cat();
 8869        }"
 8870    ));
 8871
 8872    // Multiple cursors on one line -> advance
 8873    cx.set_state(indoc!(
 8874        "fn a() {
 8875             ˇdˇog();
 8876             cat();
 8877        }"
 8878    ));
 8879    cx.update_editor(|editor, cx| {
 8880        editor.toggle_comments(toggle_comments, cx);
 8881    });
 8882    cx.assert_editor_state(indoc!(
 8883        "fn a() {
 8884             // dog();
 8885             catˇ(ˇ);
 8886        }"
 8887    ));
 8888
 8889    // Multiple cursors on one line, with selection -> don't advance
 8890    cx.set_state(indoc!(
 8891        "fn a() {
 8892             ˇdˇog«()ˇ»;
 8893             cat();
 8894        }"
 8895    ));
 8896    cx.update_editor(|editor, cx| {
 8897        editor.toggle_comments(toggle_comments, cx);
 8898    });
 8899    cx.assert_editor_state(indoc!(
 8900        "fn a() {
 8901             // ˇdˇog«()ˇ»;
 8902             cat();
 8903        }"
 8904    ));
 8905
 8906    // Single cursor on one line -> advance
 8907    // Cursor moves to column 0 on blank line
 8908    cx.set_state(indoc!(
 8909        "fn a() {
 8910             ˇdog();
 8911
 8912             cat();
 8913        }"
 8914    ));
 8915    cx.update_editor(|editor, cx| {
 8916        editor.toggle_comments(toggle_comments, cx);
 8917    });
 8918    cx.assert_editor_state(indoc!(
 8919        "fn a() {
 8920             // dog();
 8921        ˇ
 8922             cat();
 8923        }"
 8924    ));
 8925
 8926    // Single cursor on one line -> advance
 8927    // Cursor starts and ends at column 0
 8928    cx.set_state(indoc!(
 8929        "fn a() {
 8930         ˇ    dog();
 8931             cat();
 8932        }"
 8933    ));
 8934    cx.update_editor(|editor, cx| {
 8935        editor.toggle_comments(toggle_comments, cx);
 8936    });
 8937    cx.assert_editor_state(indoc!(
 8938        "fn a() {
 8939             // dog();
 8940         ˇ    cat();
 8941        }"
 8942    ));
 8943}
 8944
 8945#[gpui::test]
 8946async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8947    init_test(cx, |_| {});
 8948
 8949    let mut cx = EditorTestContext::new(cx).await;
 8950
 8951    let html_language = Arc::new(
 8952        Language::new(
 8953            LanguageConfig {
 8954                name: "HTML".into(),
 8955                block_comment: Some(("<!-- ".into(), " -->".into())),
 8956                ..Default::default()
 8957            },
 8958            Some(tree_sitter_html::language()),
 8959        )
 8960        .with_injection_query(
 8961            r#"
 8962            (script_element
 8963                (raw_text) @content
 8964                (#set! "language" "javascript"))
 8965            "#,
 8966        )
 8967        .unwrap(),
 8968    );
 8969
 8970    let javascript_language = Arc::new(Language::new(
 8971        LanguageConfig {
 8972            name: "JavaScript".into(),
 8973            line_comments: vec!["// ".into()],
 8974            ..Default::default()
 8975        },
 8976        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8977    ));
 8978
 8979    cx.language_registry().add(html_language.clone());
 8980    cx.language_registry().add(javascript_language.clone());
 8981    cx.update_buffer(|buffer, cx| {
 8982        buffer.set_language(Some(html_language), cx);
 8983    });
 8984
 8985    // Toggle comments for empty selections
 8986    cx.set_state(
 8987        &r#"
 8988            <p>A</p>ˇ
 8989            <p>B</p>ˇ
 8990            <p>C</p>ˇ
 8991        "#
 8992        .unindent(),
 8993    );
 8994    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8995    cx.assert_editor_state(
 8996        &r#"
 8997            <!-- <p>A</p>ˇ -->
 8998            <!-- <p>B</p>ˇ -->
 8999            <!-- <p>C</p>ˇ -->
 9000        "#
 9001        .unindent(),
 9002    );
 9003    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9004    cx.assert_editor_state(
 9005        &r#"
 9006            <p>A</p>ˇ
 9007            <p>B</p>ˇ
 9008            <p>C</p>ˇ
 9009        "#
 9010        .unindent(),
 9011    );
 9012
 9013    // Toggle comments for mixture of empty and non-empty selections, where
 9014    // multiple selections occupy a given line.
 9015    cx.set_state(
 9016        &r#"
 9017            <p>A«</p>
 9018            <p>ˇ»B</p>ˇ
 9019            <p>C«</p>
 9020            <p>ˇ»D</p>ˇ
 9021        "#
 9022        .unindent(),
 9023    );
 9024
 9025    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9026    cx.assert_editor_state(
 9027        &r#"
 9028            <!-- <p>A«</p>
 9029            <p>ˇ»B</p>ˇ -->
 9030            <!-- <p>C«</p>
 9031            <p>ˇ»D</p>ˇ -->
 9032        "#
 9033        .unindent(),
 9034    );
 9035    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9036    cx.assert_editor_state(
 9037        &r#"
 9038            <p>A«</p>
 9039            <p>ˇ»B</p>ˇ
 9040            <p>C«</p>
 9041            <p>ˇ»D</p>ˇ
 9042        "#
 9043        .unindent(),
 9044    );
 9045
 9046    // Toggle comments when different languages are active for different
 9047    // selections.
 9048    cx.set_state(
 9049        &r#"
 9050            ˇ<script>
 9051                ˇvar x = new Y();
 9052            ˇ</script>
 9053        "#
 9054        .unindent(),
 9055    );
 9056    cx.executor().run_until_parked();
 9057    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9058    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9059    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9060    cx.assert_editor_state(
 9061        &r#"
 9062            <!-- ˇ<script> -->
 9063                // ˇvar x = new Y();
 9064            // ˇ</script>
 9065        "#
 9066        .unindent(),
 9067    );
 9068}
 9069
 9070#[gpui::test]
 9071fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9072    init_test(cx, |_| {});
 9073
 9074    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9075    let multibuffer = cx.new_model(|cx| {
 9076        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9077        multibuffer.push_excerpts(
 9078            buffer.clone(),
 9079            [
 9080                ExcerptRange {
 9081                    context: Point::new(0, 0)..Point::new(0, 4),
 9082                    primary: None,
 9083                },
 9084                ExcerptRange {
 9085                    context: Point::new(1, 0)..Point::new(1, 4),
 9086                    primary: None,
 9087                },
 9088            ],
 9089            cx,
 9090        );
 9091        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9092        multibuffer
 9093    });
 9094
 9095    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9096    view.update(cx, |view, cx| {
 9097        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9098        view.change_selections(None, cx, |s| {
 9099            s.select_ranges([
 9100                Point::new(0, 0)..Point::new(0, 0),
 9101                Point::new(1, 0)..Point::new(1, 0),
 9102            ])
 9103        });
 9104
 9105        view.handle_input("X", cx);
 9106        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9107        assert_eq!(
 9108            view.selections.ranges(cx),
 9109            [
 9110                Point::new(0, 1)..Point::new(0, 1),
 9111                Point::new(1, 1)..Point::new(1, 1),
 9112            ]
 9113        );
 9114
 9115        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9116        view.change_selections(None, cx, |s| {
 9117            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9118        });
 9119        view.backspace(&Default::default(), cx);
 9120        assert_eq!(view.text(cx), "Xa\nbbb");
 9121        assert_eq!(
 9122            view.selections.ranges(cx),
 9123            [Point::new(1, 0)..Point::new(1, 0)]
 9124        );
 9125
 9126        view.change_selections(None, cx, |s| {
 9127            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9128        });
 9129        view.backspace(&Default::default(), cx);
 9130        assert_eq!(view.text(cx), "X\nbb");
 9131        assert_eq!(
 9132            view.selections.ranges(cx),
 9133            [Point::new(0, 1)..Point::new(0, 1)]
 9134        );
 9135    });
 9136}
 9137
 9138#[gpui::test]
 9139fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9140    init_test(cx, |_| {});
 9141
 9142    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9143    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9144        indoc! {"
 9145            [aaaa
 9146            (bbbb]
 9147            cccc)",
 9148        },
 9149        markers.clone(),
 9150    );
 9151    let excerpt_ranges = markers.into_iter().map(|marker| {
 9152        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9153        ExcerptRange {
 9154            context,
 9155            primary: None,
 9156        }
 9157    });
 9158    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9159    let multibuffer = cx.new_model(|cx| {
 9160        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9161        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9162        multibuffer
 9163    });
 9164
 9165    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9166    view.update(cx, |view, cx| {
 9167        let (expected_text, selection_ranges) = marked_text_ranges(
 9168            indoc! {"
 9169                aaaa
 9170                bˇbbb
 9171                bˇbbˇb
 9172                cccc"
 9173            },
 9174            true,
 9175        );
 9176        assert_eq!(view.text(cx), expected_text);
 9177        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9178
 9179        view.handle_input("X", cx);
 9180
 9181        let (expected_text, expected_selections) = marked_text_ranges(
 9182            indoc! {"
 9183                aaaa
 9184                bXˇbbXb
 9185                bXˇbbXˇb
 9186                cccc"
 9187            },
 9188            false,
 9189        );
 9190        assert_eq!(view.text(cx), expected_text);
 9191        assert_eq!(view.selections.ranges(cx), expected_selections);
 9192
 9193        view.newline(&Newline, cx);
 9194        let (expected_text, expected_selections) = marked_text_ranges(
 9195            indoc! {"
 9196                aaaa
 9197                bX
 9198                ˇbbX
 9199                b
 9200                bX
 9201                ˇbbX
 9202                ˇb
 9203                cccc"
 9204            },
 9205            false,
 9206        );
 9207        assert_eq!(view.text(cx), expected_text);
 9208        assert_eq!(view.selections.ranges(cx), expected_selections);
 9209    });
 9210}
 9211
 9212#[gpui::test]
 9213fn test_refresh_selections(cx: &mut TestAppContext) {
 9214    init_test(cx, |_| {});
 9215
 9216    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9217    let mut excerpt1_id = None;
 9218    let multibuffer = cx.new_model(|cx| {
 9219        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9220        excerpt1_id = multibuffer
 9221            .push_excerpts(
 9222                buffer.clone(),
 9223                [
 9224                    ExcerptRange {
 9225                        context: Point::new(0, 0)..Point::new(1, 4),
 9226                        primary: None,
 9227                    },
 9228                    ExcerptRange {
 9229                        context: Point::new(1, 0)..Point::new(2, 4),
 9230                        primary: None,
 9231                    },
 9232                ],
 9233                cx,
 9234            )
 9235            .into_iter()
 9236            .next();
 9237        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9238        multibuffer
 9239    });
 9240
 9241    let editor = cx.add_window(|cx| {
 9242        let mut editor = build_editor(multibuffer.clone(), cx);
 9243        let snapshot = editor.snapshot(cx);
 9244        editor.change_selections(None, cx, |s| {
 9245            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9246        });
 9247        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9248        assert_eq!(
 9249            editor.selections.ranges(cx),
 9250            [
 9251                Point::new(1, 3)..Point::new(1, 3),
 9252                Point::new(2, 1)..Point::new(2, 1),
 9253            ]
 9254        );
 9255        editor
 9256    });
 9257
 9258    // Refreshing selections is a no-op when excerpts haven't changed.
 9259    _ = editor.update(cx, |editor, cx| {
 9260        editor.change_selections(None, cx, |s| s.refresh());
 9261        assert_eq!(
 9262            editor.selections.ranges(cx),
 9263            [
 9264                Point::new(1, 3)..Point::new(1, 3),
 9265                Point::new(2, 1)..Point::new(2, 1),
 9266            ]
 9267        );
 9268    });
 9269
 9270    multibuffer.update(cx, |multibuffer, cx| {
 9271        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9272    });
 9273    _ = editor.update(cx, |editor, cx| {
 9274        // Removing an excerpt causes the first selection to become degenerate.
 9275        assert_eq!(
 9276            editor.selections.ranges(cx),
 9277            [
 9278                Point::new(0, 0)..Point::new(0, 0),
 9279                Point::new(0, 1)..Point::new(0, 1)
 9280            ]
 9281        );
 9282
 9283        // Refreshing selections will relocate the first selection to the original buffer
 9284        // location.
 9285        editor.change_selections(None, cx, |s| s.refresh());
 9286        assert_eq!(
 9287            editor.selections.ranges(cx),
 9288            [
 9289                Point::new(0, 1)..Point::new(0, 1),
 9290                Point::new(0, 3)..Point::new(0, 3)
 9291            ]
 9292        );
 9293        assert!(editor.selections.pending_anchor().is_some());
 9294    });
 9295}
 9296
 9297#[gpui::test]
 9298fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9299    init_test(cx, |_| {});
 9300
 9301    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9302    let mut excerpt1_id = None;
 9303    let multibuffer = cx.new_model(|cx| {
 9304        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9305        excerpt1_id = multibuffer
 9306            .push_excerpts(
 9307                buffer.clone(),
 9308                [
 9309                    ExcerptRange {
 9310                        context: Point::new(0, 0)..Point::new(1, 4),
 9311                        primary: None,
 9312                    },
 9313                    ExcerptRange {
 9314                        context: Point::new(1, 0)..Point::new(2, 4),
 9315                        primary: None,
 9316                    },
 9317                ],
 9318                cx,
 9319            )
 9320            .into_iter()
 9321            .next();
 9322        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9323        multibuffer
 9324    });
 9325
 9326    let editor = cx.add_window(|cx| {
 9327        let mut editor = build_editor(multibuffer.clone(), cx);
 9328        let snapshot = editor.snapshot(cx);
 9329        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9330        assert_eq!(
 9331            editor.selections.ranges(cx),
 9332            [Point::new(1, 3)..Point::new(1, 3)]
 9333        );
 9334        editor
 9335    });
 9336
 9337    multibuffer.update(cx, |multibuffer, cx| {
 9338        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9339    });
 9340    _ = editor.update(cx, |editor, cx| {
 9341        assert_eq!(
 9342            editor.selections.ranges(cx),
 9343            [Point::new(0, 0)..Point::new(0, 0)]
 9344        );
 9345
 9346        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9347        editor.change_selections(None, cx, |s| s.refresh());
 9348        assert_eq!(
 9349            editor.selections.ranges(cx),
 9350            [Point::new(0, 3)..Point::new(0, 3)]
 9351        );
 9352        assert!(editor.selections.pending_anchor().is_some());
 9353    });
 9354}
 9355
 9356#[gpui::test]
 9357async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9358    init_test(cx, |_| {});
 9359
 9360    let language = Arc::new(
 9361        Language::new(
 9362            LanguageConfig {
 9363                brackets: BracketPairConfig {
 9364                    pairs: vec![
 9365                        BracketPair {
 9366                            start: "{".to_string(),
 9367                            end: "}".to_string(),
 9368                            close: true,
 9369                            surround: true,
 9370                            newline: true,
 9371                        },
 9372                        BracketPair {
 9373                            start: "/* ".to_string(),
 9374                            end: " */".to_string(),
 9375                            close: true,
 9376                            surround: true,
 9377                            newline: true,
 9378                        },
 9379                    ],
 9380                    ..Default::default()
 9381                },
 9382                ..Default::default()
 9383            },
 9384            Some(tree_sitter_rust::LANGUAGE.into()),
 9385        )
 9386        .with_indents_query("")
 9387        .unwrap(),
 9388    );
 9389
 9390    let text = concat!(
 9391        "{   }\n",     //
 9392        "  x\n",       //
 9393        "  /*   */\n", //
 9394        "x\n",         //
 9395        "{{} }\n",     //
 9396    );
 9397
 9398    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9399    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9400    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9401    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9402        .await;
 9403
 9404    view.update(cx, |view, cx| {
 9405        view.change_selections(None, cx, |s| {
 9406            s.select_display_ranges([
 9407                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9408                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9409                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9410            ])
 9411        });
 9412        view.newline(&Newline, cx);
 9413
 9414        assert_eq!(
 9415            view.buffer().read(cx).read(cx).text(),
 9416            concat!(
 9417                "{ \n",    // Suppress rustfmt
 9418                "\n",      //
 9419                "}\n",     //
 9420                "  x\n",   //
 9421                "  /* \n", //
 9422                "  \n",    //
 9423                "  */\n",  //
 9424                "x\n",     //
 9425                "{{} \n",  //
 9426                "}\n",     //
 9427            )
 9428        );
 9429    });
 9430}
 9431
 9432#[gpui::test]
 9433fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9434    init_test(cx, |_| {});
 9435
 9436    let editor = cx.add_window(|cx| {
 9437        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9438        build_editor(buffer.clone(), cx)
 9439    });
 9440
 9441    _ = editor.update(cx, |editor, cx| {
 9442        struct Type1;
 9443        struct Type2;
 9444
 9445        let buffer = editor.buffer.read(cx).snapshot(cx);
 9446
 9447        let anchor_range =
 9448            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9449
 9450        editor.highlight_background::<Type1>(
 9451            &[
 9452                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9453                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9454                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9455                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9456            ],
 9457            |_| Hsla::red(),
 9458            cx,
 9459        );
 9460        editor.highlight_background::<Type2>(
 9461            &[
 9462                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9463                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9464                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9465                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9466            ],
 9467            |_| Hsla::green(),
 9468            cx,
 9469        );
 9470
 9471        let snapshot = editor.snapshot(cx);
 9472        let mut highlighted_ranges = editor.background_highlights_in_range(
 9473            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9474            &snapshot,
 9475            cx.theme().colors(),
 9476        );
 9477        // Enforce a consistent ordering based on color without relying on the ordering of the
 9478        // highlight's `TypeId` which is non-executor.
 9479        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9480        assert_eq!(
 9481            highlighted_ranges,
 9482            &[
 9483                (
 9484                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9485                    Hsla::red(),
 9486                ),
 9487                (
 9488                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9489                    Hsla::red(),
 9490                ),
 9491                (
 9492                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9493                    Hsla::green(),
 9494                ),
 9495                (
 9496                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9497                    Hsla::green(),
 9498                ),
 9499            ]
 9500        );
 9501        assert_eq!(
 9502            editor.background_highlights_in_range(
 9503                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9504                &snapshot,
 9505                cx.theme().colors(),
 9506            ),
 9507            &[(
 9508                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9509                Hsla::red(),
 9510            )]
 9511        );
 9512    });
 9513}
 9514
 9515#[gpui::test]
 9516async fn test_following(cx: &mut gpui::TestAppContext) {
 9517    init_test(cx, |_| {});
 9518
 9519    let fs = FakeFs::new(cx.executor());
 9520    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9521
 9522    let buffer = project.update(cx, |project, cx| {
 9523        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9524        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9525    });
 9526    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9527    let follower = cx.update(|cx| {
 9528        cx.open_window(
 9529            WindowOptions {
 9530                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9531                    gpui::Point::new(px(0.), px(0.)),
 9532                    gpui::Point::new(px(10.), px(80.)),
 9533                ))),
 9534                ..Default::default()
 9535            },
 9536            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9537        )
 9538        .unwrap()
 9539    });
 9540
 9541    let is_still_following = Rc::new(RefCell::new(true));
 9542    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9543    let pending_update = Rc::new(RefCell::new(None));
 9544    _ = follower.update(cx, {
 9545        let update = pending_update.clone();
 9546        let is_still_following = is_still_following.clone();
 9547        let follower_edit_event_count = follower_edit_event_count.clone();
 9548        |_, cx| {
 9549            cx.subscribe(
 9550                &leader.root_view(cx).unwrap(),
 9551                move |_, leader, event, cx| {
 9552                    leader
 9553                        .read(cx)
 9554                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9555                },
 9556            )
 9557            .detach();
 9558
 9559            cx.subscribe(
 9560                &follower.root_view(cx).unwrap(),
 9561                move |_, _, event: &EditorEvent, _cx| {
 9562                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9563                        *is_still_following.borrow_mut() = false;
 9564                    }
 9565
 9566                    if let EditorEvent::BufferEdited = event {
 9567                        *follower_edit_event_count.borrow_mut() += 1;
 9568                    }
 9569                },
 9570            )
 9571            .detach();
 9572        }
 9573    });
 9574
 9575    // Update the selections only
 9576    _ = leader.update(cx, |leader, cx| {
 9577        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9578    });
 9579    follower
 9580        .update(cx, |follower, cx| {
 9581            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9582        })
 9583        .unwrap()
 9584        .await
 9585        .unwrap();
 9586    _ = follower.update(cx, |follower, cx| {
 9587        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9588    });
 9589    assert!(*is_still_following.borrow());
 9590    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9591
 9592    // Update the scroll position only
 9593    _ = leader.update(cx, |leader, cx| {
 9594        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9595    });
 9596    follower
 9597        .update(cx, |follower, cx| {
 9598            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9599        })
 9600        .unwrap()
 9601        .await
 9602        .unwrap();
 9603    assert_eq!(
 9604        follower
 9605            .update(cx, |follower, cx| follower.scroll_position(cx))
 9606            .unwrap(),
 9607        gpui::Point::new(1.5, 3.5)
 9608    );
 9609    assert!(*is_still_following.borrow());
 9610    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9611
 9612    // Update the selections and scroll position. The follower's scroll position is updated
 9613    // via autoscroll, not via the leader's exact scroll position.
 9614    _ = leader.update(cx, |leader, cx| {
 9615        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9616        leader.request_autoscroll(Autoscroll::newest(), cx);
 9617        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9618    });
 9619    follower
 9620        .update(cx, |follower, cx| {
 9621            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9622        })
 9623        .unwrap()
 9624        .await
 9625        .unwrap();
 9626    _ = follower.update(cx, |follower, cx| {
 9627        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9628        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9629    });
 9630    assert!(*is_still_following.borrow());
 9631
 9632    // Creating a pending selection that precedes another selection
 9633    _ = leader.update(cx, |leader, cx| {
 9634        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9635        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9636    });
 9637    follower
 9638        .update(cx, |follower, cx| {
 9639            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9640        })
 9641        .unwrap()
 9642        .await
 9643        .unwrap();
 9644    _ = follower.update(cx, |follower, cx| {
 9645        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9646    });
 9647    assert!(*is_still_following.borrow());
 9648
 9649    // Extend the pending selection so that it surrounds another selection
 9650    _ = leader.update(cx, |leader, cx| {
 9651        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9652    });
 9653    follower
 9654        .update(cx, |follower, cx| {
 9655            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9656        })
 9657        .unwrap()
 9658        .await
 9659        .unwrap();
 9660    _ = follower.update(cx, |follower, cx| {
 9661        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9662    });
 9663
 9664    // Scrolling locally breaks the follow
 9665    _ = follower.update(cx, |follower, cx| {
 9666        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9667        follower.set_scroll_anchor(
 9668            ScrollAnchor {
 9669                anchor: top_anchor,
 9670                offset: gpui::Point::new(0.0, 0.5),
 9671            },
 9672            cx,
 9673        );
 9674    });
 9675    assert!(!(*is_still_following.borrow()));
 9676}
 9677
 9678#[gpui::test]
 9679async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9680    init_test(cx, |_| {});
 9681
 9682    let fs = FakeFs::new(cx.executor());
 9683    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9684    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9685    let pane = workspace
 9686        .update(cx, |workspace, _| workspace.active_pane().clone())
 9687        .unwrap();
 9688
 9689    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9690
 9691    let leader = pane.update(cx, |_, cx| {
 9692        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9693        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9694    });
 9695
 9696    // Start following the editor when it has no excerpts.
 9697    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9698    let follower_1 = cx
 9699        .update_window(*workspace.deref(), |_, cx| {
 9700            Editor::from_state_proto(
 9701                workspace.root_view(cx).unwrap(),
 9702                ViewId {
 9703                    creator: Default::default(),
 9704                    id: 0,
 9705                },
 9706                &mut state_message,
 9707                cx,
 9708            )
 9709        })
 9710        .unwrap()
 9711        .unwrap()
 9712        .await
 9713        .unwrap();
 9714
 9715    let update_message = Rc::new(RefCell::new(None));
 9716    follower_1.update(cx, {
 9717        let update = update_message.clone();
 9718        |_, cx| {
 9719            cx.subscribe(&leader, move |_, leader, event, cx| {
 9720                leader
 9721                    .read(cx)
 9722                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9723            })
 9724            .detach();
 9725        }
 9726    });
 9727
 9728    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9729        (
 9730            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9731            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9732        )
 9733    });
 9734
 9735    // Insert some excerpts.
 9736    leader.update(cx, |leader, cx| {
 9737        leader.buffer.update(cx, |multibuffer, cx| {
 9738            let excerpt_ids = multibuffer.push_excerpts(
 9739                buffer_1.clone(),
 9740                [
 9741                    ExcerptRange {
 9742                        context: 1..6,
 9743                        primary: None,
 9744                    },
 9745                    ExcerptRange {
 9746                        context: 12..15,
 9747                        primary: None,
 9748                    },
 9749                    ExcerptRange {
 9750                        context: 0..3,
 9751                        primary: None,
 9752                    },
 9753                ],
 9754                cx,
 9755            );
 9756            multibuffer.insert_excerpts_after(
 9757                excerpt_ids[0],
 9758                buffer_2.clone(),
 9759                [
 9760                    ExcerptRange {
 9761                        context: 8..12,
 9762                        primary: None,
 9763                    },
 9764                    ExcerptRange {
 9765                        context: 0..6,
 9766                        primary: None,
 9767                    },
 9768                ],
 9769                cx,
 9770            );
 9771        });
 9772    });
 9773
 9774    // Apply the update of adding the excerpts.
 9775    follower_1
 9776        .update(cx, |follower, cx| {
 9777            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9778        })
 9779        .await
 9780        .unwrap();
 9781    assert_eq!(
 9782        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9783        leader.update(cx, |editor, cx| editor.text(cx))
 9784    );
 9785    update_message.borrow_mut().take();
 9786
 9787    // Start following separately after it already has excerpts.
 9788    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9789    let follower_2 = cx
 9790        .update_window(*workspace.deref(), |_, cx| {
 9791            Editor::from_state_proto(
 9792                workspace.root_view(cx).unwrap().clone(),
 9793                ViewId {
 9794                    creator: Default::default(),
 9795                    id: 0,
 9796                },
 9797                &mut state_message,
 9798                cx,
 9799            )
 9800        })
 9801        .unwrap()
 9802        .unwrap()
 9803        .await
 9804        .unwrap();
 9805    assert_eq!(
 9806        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9807        leader.update(cx, |editor, cx| editor.text(cx))
 9808    );
 9809
 9810    // Remove some excerpts.
 9811    leader.update(cx, |leader, cx| {
 9812        leader.buffer.update(cx, |multibuffer, cx| {
 9813            let excerpt_ids = multibuffer.excerpt_ids();
 9814            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9815            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9816        });
 9817    });
 9818
 9819    // Apply the update of removing the excerpts.
 9820    follower_1
 9821        .update(cx, |follower, cx| {
 9822            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9823        })
 9824        .await
 9825        .unwrap();
 9826    follower_2
 9827        .update(cx, |follower, cx| {
 9828            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9829        })
 9830        .await
 9831        .unwrap();
 9832    update_message.borrow_mut().take();
 9833    assert_eq!(
 9834        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9835        leader.update(cx, |editor, cx| editor.text(cx))
 9836    );
 9837}
 9838
 9839#[gpui::test]
 9840async fn go_to_prev_overlapping_diagnostic(
 9841    executor: BackgroundExecutor,
 9842    cx: &mut gpui::TestAppContext,
 9843) {
 9844    init_test(cx, |_| {});
 9845
 9846    let mut cx = EditorTestContext::new(cx).await;
 9847    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9848
 9849    cx.set_state(indoc! {"
 9850        ˇfn func(abc def: i32) -> u32 {
 9851        }
 9852    "});
 9853
 9854    cx.update(|cx| {
 9855        project.update(cx, |project, cx| {
 9856            project
 9857                .update_diagnostics(
 9858                    LanguageServerId(0),
 9859                    lsp::PublishDiagnosticsParams {
 9860                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9861                        version: None,
 9862                        diagnostics: vec![
 9863                            lsp::Diagnostic {
 9864                                range: lsp::Range::new(
 9865                                    lsp::Position::new(0, 11),
 9866                                    lsp::Position::new(0, 12),
 9867                                ),
 9868                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9869                                ..Default::default()
 9870                            },
 9871                            lsp::Diagnostic {
 9872                                range: lsp::Range::new(
 9873                                    lsp::Position::new(0, 12),
 9874                                    lsp::Position::new(0, 15),
 9875                                ),
 9876                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9877                                ..Default::default()
 9878                            },
 9879                            lsp::Diagnostic {
 9880                                range: lsp::Range::new(
 9881                                    lsp::Position::new(0, 25),
 9882                                    lsp::Position::new(0, 28),
 9883                                ),
 9884                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9885                                ..Default::default()
 9886                            },
 9887                        ],
 9888                    },
 9889                    &[],
 9890                    cx,
 9891                )
 9892                .unwrap()
 9893        });
 9894    });
 9895
 9896    executor.run_until_parked();
 9897
 9898    cx.update_editor(|editor, cx| {
 9899        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9900    });
 9901
 9902    cx.assert_editor_state(indoc! {"
 9903        fn func(abc def: i32) -> ˇu32 {
 9904        }
 9905    "});
 9906
 9907    cx.update_editor(|editor, cx| {
 9908        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9909    });
 9910
 9911    cx.assert_editor_state(indoc! {"
 9912        fn func(abc ˇdef: i32) -> u32 {
 9913        }
 9914    "});
 9915
 9916    cx.update_editor(|editor, cx| {
 9917        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9918    });
 9919
 9920    cx.assert_editor_state(indoc! {"
 9921        fn func(abcˇ def: i32) -> u32 {
 9922        }
 9923    "});
 9924
 9925    cx.update_editor(|editor, cx| {
 9926        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9927    });
 9928
 9929    cx.assert_editor_state(indoc! {"
 9930        fn func(abc def: i32) -> ˇu32 {
 9931        }
 9932    "});
 9933}
 9934
 9935#[gpui::test]
 9936async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9937    init_test(cx, |_| {});
 9938
 9939    let mut cx = EditorTestContext::new(cx).await;
 9940
 9941    cx.set_state(indoc! {"
 9942        fn func(abˇc def: i32) -> u32 {
 9943        }
 9944    "});
 9945    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9946
 9947    cx.update(|cx| {
 9948        project.update(cx, |project, cx| {
 9949            project.update_diagnostics(
 9950                LanguageServerId(0),
 9951                lsp::PublishDiagnosticsParams {
 9952                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9953                    version: None,
 9954                    diagnostics: vec![lsp::Diagnostic {
 9955                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9956                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9957                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9958                        ..Default::default()
 9959                    }],
 9960                },
 9961                &[],
 9962                cx,
 9963            )
 9964        })
 9965    }).unwrap();
 9966    cx.run_until_parked();
 9967    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9968    cx.run_until_parked();
 9969    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9970}
 9971
 9972#[gpui::test]
 9973async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9974    init_test(cx, |_| {});
 9975
 9976    let mut cx = EditorTestContext::new(cx).await;
 9977
 9978    let diff_base = r#"
 9979        use some::mod;
 9980
 9981        const A: u32 = 42;
 9982
 9983        fn main() {
 9984            println!("hello");
 9985
 9986            println!("world");
 9987        }
 9988        "#
 9989    .unindent();
 9990
 9991    // Edits are modified, removed, modified, added
 9992    cx.set_state(
 9993        &r#"
 9994        use some::modified;
 9995
 9996        ˇ
 9997        fn main() {
 9998            println!("hello there");
 9999
10000            println!("around the");
10001            println!("world");
10002        }
10003        "#
10004        .unindent(),
10005    );
10006
10007    cx.set_diff_base(Some(&diff_base));
10008    executor.run_until_parked();
10009
10010    cx.update_editor(|editor, cx| {
10011        //Wrap around the bottom of the buffer
10012        for _ in 0..3 {
10013            editor.go_to_next_hunk(&GoToHunk, cx);
10014        }
10015    });
10016
10017    cx.assert_editor_state(
10018        &r#"
10019        ˇuse some::modified;
10020
10021
10022        fn main() {
10023            println!("hello there");
10024
10025            println!("around the");
10026            println!("world");
10027        }
10028        "#
10029        .unindent(),
10030    );
10031
10032    cx.update_editor(|editor, cx| {
10033        //Wrap around the top of the buffer
10034        for _ in 0..2 {
10035            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10036        }
10037    });
10038
10039    cx.assert_editor_state(
10040        &r#"
10041        use some::modified;
10042
10043
10044        fn main() {
10045        ˇ    println!("hello there");
10046
10047            println!("around the");
10048            println!("world");
10049        }
10050        "#
10051        .unindent(),
10052    );
10053
10054    cx.update_editor(|editor, cx| {
10055        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10056    });
10057
10058    cx.assert_editor_state(
10059        &r#"
10060        use some::modified;
10061
10062        ˇ
10063        fn main() {
10064            println!("hello there");
10065
10066            println!("around the");
10067            println!("world");
10068        }
10069        "#
10070        .unindent(),
10071    );
10072
10073    cx.update_editor(|editor, cx| {
10074        for _ in 0..3 {
10075            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10076        }
10077    });
10078
10079    cx.assert_editor_state(
10080        &r#"
10081        use some::modified;
10082
10083
10084        fn main() {
10085        ˇ    println!("hello there");
10086
10087            println!("around the");
10088            println!("world");
10089        }
10090        "#
10091        .unindent(),
10092    );
10093
10094    cx.update_editor(|editor, cx| {
10095        editor.fold(&Fold, cx);
10096
10097        //Make sure that the fold only gets one hunk
10098        for _ in 0..4 {
10099            editor.go_to_next_hunk(&GoToHunk, cx);
10100        }
10101    });
10102
10103    cx.assert_editor_state(
10104        &r#"
10105        ˇuse some::modified;
10106
10107
10108        fn main() {
10109            println!("hello there");
10110
10111            println!("around the");
10112            println!("world");
10113        }
10114        "#
10115        .unindent(),
10116    );
10117}
10118
10119#[test]
10120fn test_split_words() {
10121    fn split(text: &str) -> Vec<&str> {
10122        split_words(text).collect()
10123    }
10124
10125    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10126    assert_eq!(split("hello_world"), &["hello_", "world"]);
10127    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10128    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10129    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10130    assert_eq!(split("helloworld"), &["helloworld"]);
10131
10132    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10133}
10134
10135#[gpui::test]
10136async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10137    init_test(cx, |_| {});
10138
10139    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10140    let mut assert = |before, after| {
10141        let _state_context = cx.set_state(before);
10142        cx.update_editor(|editor, cx| {
10143            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10144        });
10145        cx.assert_editor_state(after);
10146    };
10147
10148    // Outside bracket jumps to outside of matching bracket
10149    assert("console.logˇ(var);", "console.log(var)ˇ;");
10150    assert("console.log(var)ˇ;", "console.logˇ(var);");
10151
10152    // Inside bracket jumps to inside of matching bracket
10153    assert("console.log(ˇvar);", "console.log(varˇ);");
10154    assert("console.log(varˇ);", "console.log(ˇvar);");
10155
10156    // When outside a bracket and inside, favor jumping to the inside bracket
10157    assert(
10158        "console.log('foo', [1, 2, 3]ˇ);",
10159        "console.log(ˇ'foo', [1, 2, 3]);",
10160    );
10161    assert(
10162        "console.log(ˇ'foo', [1, 2, 3]);",
10163        "console.log('foo', [1, 2, 3]ˇ);",
10164    );
10165
10166    // Bias forward if two options are equally likely
10167    assert(
10168        "let result = curried_fun()ˇ();",
10169        "let result = curried_fun()()ˇ;",
10170    );
10171
10172    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10173    assert(
10174        indoc! {"
10175            function test() {
10176                console.log('test')ˇ
10177            }"},
10178        indoc! {"
10179            function test() {
10180                console.logˇ('test')
10181            }"},
10182    );
10183}
10184
10185#[gpui::test]
10186async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10187    init_test(cx, |_| {});
10188
10189    let fs = FakeFs::new(cx.executor());
10190    fs.insert_tree(
10191        "/a",
10192        json!({
10193            "main.rs": "fn main() { let a = 5; }",
10194            "other.rs": "// Test file",
10195        }),
10196    )
10197    .await;
10198    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10199
10200    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10201    language_registry.add(Arc::new(Language::new(
10202        LanguageConfig {
10203            name: "Rust".into(),
10204            matcher: LanguageMatcher {
10205                path_suffixes: vec!["rs".to_string()],
10206                ..Default::default()
10207            },
10208            brackets: BracketPairConfig {
10209                pairs: vec![BracketPair {
10210                    start: "{".to_string(),
10211                    end: "}".to_string(),
10212                    close: true,
10213                    surround: true,
10214                    newline: true,
10215                }],
10216                disabled_scopes_by_bracket_ix: Vec::new(),
10217            },
10218            ..Default::default()
10219        },
10220        Some(tree_sitter_rust::LANGUAGE.into()),
10221    )));
10222    let mut fake_servers = language_registry.register_fake_lsp(
10223        "Rust",
10224        FakeLspAdapter {
10225            capabilities: lsp::ServerCapabilities {
10226                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10227                    first_trigger_character: "{".to_string(),
10228                    more_trigger_character: None,
10229                }),
10230                ..Default::default()
10231            },
10232            ..Default::default()
10233        },
10234    );
10235
10236    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10237
10238    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10239
10240    let worktree_id = workspace
10241        .update(cx, |workspace, cx| {
10242            workspace.project().update(cx, |project, cx| {
10243                project.worktrees(cx).next().unwrap().read(cx).id()
10244            })
10245        })
10246        .unwrap();
10247
10248    let buffer = project
10249        .update(cx, |project, cx| {
10250            project.open_local_buffer("/a/main.rs", cx)
10251        })
10252        .await
10253        .unwrap();
10254    cx.executor().run_until_parked();
10255    cx.executor().start_waiting();
10256    let fake_server = fake_servers.next().await.unwrap();
10257    let editor_handle = workspace
10258        .update(cx, |workspace, cx| {
10259            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10260        })
10261        .unwrap()
10262        .await
10263        .unwrap()
10264        .downcast::<Editor>()
10265        .unwrap();
10266
10267    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10268        assert_eq!(
10269            params.text_document_position.text_document.uri,
10270            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10271        );
10272        assert_eq!(
10273            params.text_document_position.position,
10274            lsp::Position::new(0, 21),
10275        );
10276
10277        Ok(Some(vec![lsp::TextEdit {
10278            new_text: "]".to_string(),
10279            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10280        }]))
10281    });
10282
10283    editor_handle.update(cx, |editor, cx| {
10284        editor.focus(cx);
10285        editor.change_selections(None, cx, |s| {
10286            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10287        });
10288        editor.handle_input("{", cx);
10289    });
10290
10291    cx.executor().run_until_parked();
10292
10293    buffer.update(cx, |buffer, _| {
10294        assert_eq!(
10295            buffer.text(),
10296            "fn main() { let a = {5}; }",
10297            "No extra braces from on type formatting should appear in the buffer"
10298        )
10299    });
10300}
10301
10302#[gpui::test]
10303async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10304    init_test(cx, |_| {});
10305
10306    let fs = FakeFs::new(cx.executor());
10307    fs.insert_tree(
10308        "/a",
10309        json!({
10310            "main.rs": "fn main() { let a = 5; }",
10311            "other.rs": "// Test file",
10312        }),
10313    )
10314    .await;
10315
10316    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10317
10318    let server_restarts = Arc::new(AtomicUsize::new(0));
10319    let closure_restarts = Arc::clone(&server_restarts);
10320    let language_server_name = "test language server";
10321    let language_name: LanguageName = "Rust".into();
10322
10323    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10324    language_registry.add(Arc::new(Language::new(
10325        LanguageConfig {
10326            name: language_name.clone(),
10327            matcher: LanguageMatcher {
10328                path_suffixes: vec!["rs".to_string()],
10329                ..Default::default()
10330            },
10331            ..Default::default()
10332        },
10333        Some(tree_sitter_rust::LANGUAGE.into()),
10334    )));
10335    let mut fake_servers = language_registry.register_fake_lsp(
10336        "Rust",
10337        FakeLspAdapter {
10338            name: language_server_name,
10339            initialization_options: Some(json!({
10340                "testOptionValue": true
10341            })),
10342            initializer: Some(Box::new(move |fake_server| {
10343                let task_restarts = Arc::clone(&closure_restarts);
10344                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10345                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10346                    futures::future::ready(Ok(()))
10347                });
10348            })),
10349            ..Default::default()
10350        },
10351    );
10352
10353    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10354    let _buffer = project
10355        .update(cx, |project, cx| {
10356            project.open_local_buffer("/a/main.rs", cx)
10357        })
10358        .await
10359        .unwrap();
10360    let _fake_server = fake_servers.next().await.unwrap();
10361    update_test_language_settings(cx, |language_settings| {
10362        language_settings.languages.insert(
10363            language_name.clone(),
10364            LanguageSettingsContent {
10365                tab_size: NonZeroU32::new(8),
10366                ..Default::default()
10367            },
10368        );
10369    });
10370    cx.executor().run_until_parked();
10371    assert_eq!(
10372        server_restarts.load(atomic::Ordering::Acquire),
10373        0,
10374        "Should not restart LSP server on an unrelated change"
10375    );
10376
10377    update_test_project_settings(cx, |project_settings| {
10378        project_settings.lsp.insert(
10379            "Some other server name".into(),
10380            LspSettings {
10381                binary: None,
10382                settings: None,
10383                initialization_options: Some(json!({
10384                    "some other init value": false
10385                })),
10386            },
10387        );
10388    });
10389    cx.executor().run_until_parked();
10390    assert_eq!(
10391        server_restarts.load(atomic::Ordering::Acquire),
10392        0,
10393        "Should not restart LSP server on an unrelated LSP settings change"
10394    );
10395
10396    update_test_project_settings(cx, |project_settings| {
10397        project_settings.lsp.insert(
10398            language_server_name.into(),
10399            LspSettings {
10400                binary: None,
10401                settings: None,
10402                initialization_options: Some(json!({
10403                    "anotherInitValue": false
10404                })),
10405            },
10406        );
10407    });
10408    cx.executor().run_until_parked();
10409    assert_eq!(
10410        server_restarts.load(atomic::Ordering::Acquire),
10411        1,
10412        "Should restart LSP server on a related LSP settings change"
10413    );
10414
10415    update_test_project_settings(cx, |project_settings| {
10416        project_settings.lsp.insert(
10417            language_server_name.into(),
10418            LspSettings {
10419                binary: None,
10420                settings: None,
10421                initialization_options: Some(json!({
10422                    "anotherInitValue": false
10423                })),
10424            },
10425        );
10426    });
10427    cx.executor().run_until_parked();
10428    assert_eq!(
10429        server_restarts.load(atomic::Ordering::Acquire),
10430        1,
10431        "Should not restart LSP server on a related LSP settings change that is the same"
10432    );
10433
10434    update_test_project_settings(cx, |project_settings| {
10435        project_settings.lsp.insert(
10436            language_server_name.into(),
10437            LspSettings {
10438                binary: None,
10439                settings: None,
10440                initialization_options: None,
10441            },
10442        );
10443    });
10444    cx.executor().run_until_parked();
10445    assert_eq!(
10446        server_restarts.load(atomic::Ordering::Acquire),
10447        2,
10448        "Should restart LSP server on another related LSP settings change"
10449    );
10450}
10451
10452#[gpui::test]
10453async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10454    init_test(cx, |_| {});
10455
10456    let mut cx = EditorLspTestContext::new_rust(
10457        lsp::ServerCapabilities {
10458            completion_provider: Some(lsp::CompletionOptions {
10459                trigger_characters: Some(vec![".".to_string()]),
10460                resolve_provider: Some(true),
10461                ..Default::default()
10462            }),
10463            ..Default::default()
10464        },
10465        cx,
10466    )
10467    .await;
10468
10469    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10470    cx.simulate_keystroke(".");
10471    let completion_item = lsp::CompletionItem {
10472        label: "some".into(),
10473        kind: Some(lsp::CompletionItemKind::SNIPPET),
10474        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10475        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10476            kind: lsp::MarkupKind::Markdown,
10477            value: "```rust\nSome(2)\n```".to_string(),
10478        })),
10479        deprecated: Some(false),
10480        sort_text: Some("fffffff2".to_string()),
10481        filter_text: Some("some".to_string()),
10482        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10483        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10484            range: lsp::Range {
10485                start: lsp::Position {
10486                    line: 0,
10487                    character: 22,
10488                },
10489                end: lsp::Position {
10490                    line: 0,
10491                    character: 22,
10492                },
10493            },
10494            new_text: "Some(2)".to_string(),
10495        })),
10496        additional_text_edits: Some(vec![lsp::TextEdit {
10497            range: lsp::Range {
10498                start: lsp::Position {
10499                    line: 0,
10500                    character: 20,
10501                },
10502                end: lsp::Position {
10503                    line: 0,
10504                    character: 22,
10505                },
10506            },
10507            new_text: "".to_string(),
10508        }]),
10509        ..Default::default()
10510    };
10511
10512    let closure_completion_item = completion_item.clone();
10513    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10514        let task_completion_item = closure_completion_item.clone();
10515        async move {
10516            Ok(Some(lsp::CompletionResponse::Array(vec![
10517                task_completion_item,
10518            ])))
10519        }
10520    });
10521
10522    request.next().await;
10523
10524    cx.condition(|editor, _| editor.context_menu_visible())
10525        .await;
10526    let apply_additional_edits = cx.update_editor(|editor, cx| {
10527        editor
10528            .confirm_completion(&ConfirmCompletion::default(), cx)
10529            .unwrap()
10530    });
10531    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10532
10533    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10534        let task_completion_item = completion_item.clone();
10535        async move { Ok(task_completion_item) }
10536    })
10537    .next()
10538    .await
10539    .unwrap();
10540    apply_additional_edits.await.unwrap();
10541    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10542}
10543
10544#[gpui::test]
10545async fn test_completions_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 completion_data = default_data.clone();
10580    let completion_characters = default_commit_characters.clone();
10581    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10582        let default_data = completion_data.clone();
10583        let default_commit_characters = completion_characters.clone();
10584        async move {
10585            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10586                items: vec![
10587                    lsp::CompletionItem {
10588                        label: "Some(2)".into(),
10589                        insert_text: Some("Some(2)".into()),
10590                        data: Some(json!({ "very": "special"})),
10591                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10592                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10593                            lsp::InsertReplaceEdit {
10594                                new_text: "Some(2)".to_string(),
10595                                insert: lsp::Range::default(),
10596                                replace: lsp::Range::default(),
10597                            },
10598                        )),
10599                        ..lsp::CompletionItem::default()
10600                    },
10601                    lsp::CompletionItem {
10602                        label: "vec![2]".into(),
10603                        insert_text: Some("vec![2]".into()),
10604                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10605                        ..lsp::CompletionItem::default()
10606                    },
10607                ],
10608                item_defaults: Some(lsp::CompletionListItemDefaults {
10609                    data: Some(default_data.clone()),
10610                    commit_characters: Some(default_commit_characters.clone()),
10611                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10612                        default_edit_range,
10613                    )),
10614                    insert_text_format: Some(default_insert_text_format),
10615                    insert_text_mode: Some(default_insert_text_mode),
10616                }),
10617                ..lsp::CompletionList::default()
10618            })))
10619        }
10620    })
10621    .next()
10622    .await;
10623
10624    cx.condition(|editor, _| editor.context_menu_visible())
10625        .await;
10626
10627    cx.update_editor(|editor, _| {
10628        let menu = editor.context_menu.read();
10629        match menu.as_ref().expect("should have the completions menu") {
10630            ContextMenu::Completions(completions_menu) => {
10631                assert_eq!(
10632                    completions_menu
10633                        .matches
10634                        .iter()
10635                        .map(|c| c.string.as_str())
10636                        .collect::<Vec<_>>(),
10637                    vec!["Some(2)", "vec![2]"]
10638                );
10639            }
10640            ContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10641        }
10642    });
10643
10644    cx.update_editor(|editor, cx| {
10645        editor.context_menu_first(&ContextMenuFirst, cx);
10646    });
10647    let first_item_resolve_characters = default_commit_characters.clone();
10648    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, item_to_resolve, _| {
10649        let default_commit_characters = first_item_resolve_characters.clone();
10650
10651        async move {
10652            assert_eq!(
10653                item_to_resolve.label, "Some(2)",
10654                "Should have selected the first item"
10655            );
10656            assert_eq!(
10657                item_to_resolve.data,
10658                Some(json!({ "very": "special"})),
10659                "First item should bring its own data for resolving"
10660            );
10661            assert_eq!(
10662                item_to_resolve.commit_characters,
10663                Some(default_commit_characters),
10664                "First item had no own commit characters and should inherit the default ones"
10665            );
10666            assert!(
10667                matches!(
10668                    item_to_resolve.text_edit,
10669                    Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10670                ),
10671                "First item should bring its own edit range for resolving"
10672            );
10673            assert_eq!(
10674                item_to_resolve.insert_text_format,
10675                Some(default_insert_text_format),
10676                "First item had no own insert text format and should inherit the default one"
10677            );
10678            assert_eq!(
10679                item_to_resolve.insert_text_mode,
10680                Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10681                "First item should bring its own insert text mode for resolving"
10682            );
10683            Ok(item_to_resolve)
10684        }
10685    })
10686    .next()
10687    .await
10688    .unwrap();
10689
10690    cx.update_editor(|editor, cx| {
10691        editor.context_menu_last(&ContextMenuLast, cx);
10692    });
10693    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, item_to_resolve, _| {
10694        let default_data = default_data.clone();
10695        let default_commit_characters = default_commit_characters.clone();
10696        async move {
10697            assert_eq!(
10698                item_to_resolve.label, "vec![2]",
10699                "Should have selected the last item"
10700            );
10701            assert_eq!(
10702                item_to_resolve.data,
10703                Some(default_data),
10704                "Last item has no own resolve data and should inherit the default one"
10705            );
10706            assert_eq!(
10707                item_to_resolve.commit_characters,
10708                Some(default_commit_characters),
10709                "Last item had no own commit characters and should inherit the default ones"
10710            );
10711            assert_eq!(
10712                item_to_resolve.text_edit,
10713                Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10714                    range: default_edit_range,
10715                    new_text: "vec![2]".to_string()
10716                })),
10717                "Last item had no own edit range and should inherit the default one"
10718            );
10719            assert_eq!(
10720                item_to_resolve.insert_text_format,
10721                Some(lsp::InsertTextFormat::PLAIN_TEXT),
10722                "Last item should bring its own insert text format for resolving"
10723            );
10724            assert_eq!(
10725                item_to_resolve.insert_text_mode,
10726                Some(default_insert_text_mode),
10727                "Last item had no own insert text mode and should inherit the default one"
10728            );
10729
10730            Ok(item_to_resolve)
10731        }
10732    })
10733    .next()
10734    .await
10735    .unwrap();
10736}
10737
10738#[gpui::test]
10739async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10740    init_test(cx, |_| {});
10741
10742    let mut cx = EditorLspTestContext::new(
10743        Language::new(
10744            LanguageConfig {
10745                matcher: LanguageMatcher {
10746                    path_suffixes: vec!["jsx".into()],
10747                    ..Default::default()
10748                },
10749                overrides: [(
10750                    "element".into(),
10751                    LanguageConfigOverride {
10752                        word_characters: Override::Set(['-'].into_iter().collect()),
10753                        ..Default::default()
10754                    },
10755                )]
10756                .into_iter()
10757                .collect(),
10758                ..Default::default()
10759            },
10760            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10761        )
10762        .with_override_query("(jsx_self_closing_element) @element")
10763        .unwrap(),
10764        lsp::ServerCapabilities {
10765            completion_provider: Some(lsp::CompletionOptions {
10766                trigger_characters: Some(vec![":".to_string()]),
10767                ..Default::default()
10768            }),
10769            ..Default::default()
10770        },
10771        cx,
10772    )
10773    .await;
10774
10775    cx.lsp
10776        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10777            Ok(Some(lsp::CompletionResponse::Array(vec![
10778                lsp::CompletionItem {
10779                    label: "bg-blue".into(),
10780                    ..Default::default()
10781                },
10782                lsp::CompletionItem {
10783                    label: "bg-red".into(),
10784                    ..Default::default()
10785                },
10786                lsp::CompletionItem {
10787                    label: "bg-yellow".into(),
10788                    ..Default::default()
10789                },
10790            ])))
10791        });
10792
10793    cx.set_state(r#"<p class="bgˇ" />"#);
10794
10795    // Trigger completion when typing a dash, because the dash is an extra
10796    // word character in the 'element' scope, which contains the cursor.
10797    cx.simulate_keystroke("-");
10798    cx.executor().run_until_parked();
10799    cx.update_editor(|editor, _| {
10800        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10801            assert_eq!(
10802                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10803                &["bg-red", "bg-blue", "bg-yellow"]
10804            );
10805        } else {
10806            panic!("expected completion menu to be open");
10807        }
10808    });
10809
10810    cx.simulate_keystroke("l");
10811    cx.executor().run_until_parked();
10812    cx.update_editor(|editor, _| {
10813        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10814            assert_eq!(
10815                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10816                &["bg-blue", "bg-yellow"]
10817            );
10818        } else {
10819            panic!("expected completion menu to be open");
10820        }
10821    });
10822
10823    // When filtering completions, consider the character after the '-' to
10824    // be the start of a subword.
10825    cx.set_state(r#"<p class="yelˇ" />"#);
10826    cx.simulate_keystroke("l");
10827    cx.executor().run_until_parked();
10828    cx.update_editor(|editor, _| {
10829        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10830            assert_eq!(
10831                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10832                &["bg-yellow"]
10833            );
10834        } else {
10835            panic!("expected completion menu to be open");
10836        }
10837    });
10838}
10839
10840#[gpui::test]
10841async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10842    init_test(cx, |settings| {
10843        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10844            FormatterList(vec![Formatter::Prettier].into()),
10845        ))
10846    });
10847
10848    let fs = FakeFs::new(cx.executor());
10849    fs.insert_file("/file.ts", Default::default()).await;
10850
10851    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10852    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10853
10854    language_registry.add(Arc::new(Language::new(
10855        LanguageConfig {
10856            name: "TypeScript".into(),
10857            matcher: LanguageMatcher {
10858                path_suffixes: vec!["ts".to_string()],
10859                ..Default::default()
10860            },
10861            ..Default::default()
10862        },
10863        Some(tree_sitter_rust::LANGUAGE.into()),
10864    )));
10865    update_test_language_settings(cx, |settings| {
10866        settings.defaults.prettier = Some(PrettierSettings {
10867            allowed: true,
10868            ..PrettierSettings::default()
10869        });
10870    });
10871
10872    let test_plugin = "test_plugin";
10873    let _ = language_registry.register_fake_lsp(
10874        "TypeScript",
10875        FakeLspAdapter {
10876            prettier_plugins: vec![test_plugin],
10877            ..Default::default()
10878        },
10879    );
10880
10881    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10882    let buffer = project
10883        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10884        .await
10885        .unwrap();
10886
10887    let buffer_text = "one\ntwo\nthree\n";
10888    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10889    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10890    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10891
10892    editor
10893        .update(cx, |editor, cx| {
10894            editor.perform_format(
10895                project.clone(),
10896                FormatTrigger::Manual,
10897                FormatTarget::Buffer,
10898                cx,
10899            )
10900        })
10901        .unwrap()
10902        .await;
10903    assert_eq!(
10904        editor.update(cx, |editor, cx| editor.text(cx)),
10905        buffer_text.to_string() + prettier_format_suffix,
10906        "Test prettier formatting was not applied to the original buffer text",
10907    );
10908
10909    update_test_language_settings(cx, |settings| {
10910        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10911    });
10912    let format = editor.update(cx, |editor, cx| {
10913        editor.perform_format(
10914            project.clone(),
10915            FormatTrigger::Manual,
10916            FormatTarget::Buffer,
10917            cx,
10918        )
10919    });
10920    format.await.unwrap();
10921    assert_eq!(
10922        editor.update(cx, |editor, cx| editor.text(cx)),
10923        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10924        "Autoformatting (via test prettier) was not applied to the original buffer text",
10925    );
10926}
10927
10928#[gpui::test]
10929async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10930    init_test(cx, |_| {});
10931    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10932    let base_text = indoc! {r#"struct Row;
10933struct Row1;
10934struct Row2;
10935
10936struct Row4;
10937struct Row5;
10938struct Row6;
10939
10940struct Row8;
10941struct Row9;
10942struct Row10;"#};
10943
10944    // When addition hunks are not adjacent to carets, no hunk revert is performed
10945    assert_hunk_revert(
10946        indoc! {r#"struct Row;
10947                   struct Row1;
10948                   struct Row1.1;
10949                   struct Row1.2;
10950                   struct Row2;ˇ
10951
10952                   struct Row4;
10953                   struct Row5;
10954                   struct Row6;
10955
10956                   struct Row8;
10957                   ˇstruct Row9;
10958                   struct Row9.1;
10959                   struct Row9.2;
10960                   struct Row9.3;
10961                   struct Row10;"#},
10962        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10963        indoc! {r#"struct Row;
10964                   struct Row1;
10965                   struct Row1.1;
10966                   struct Row1.2;
10967                   struct Row2;ˇ
10968
10969                   struct Row4;
10970                   struct Row5;
10971                   struct Row6;
10972
10973                   struct Row8;
10974                   ˇstruct Row9;
10975                   struct Row9.1;
10976                   struct Row9.2;
10977                   struct Row9.3;
10978                   struct Row10;"#},
10979        base_text,
10980        &mut cx,
10981    );
10982    // Same for selections
10983    assert_hunk_revert(
10984        indoc! {r#"struct Row;
10985                   struct Row1;
10986                   struct Row2;
10987                   struct Row2.1;
10988                   struct Row2.2;
10989                   «ˇ
10990                   struct Row4;
10991                   struct» Row5;
10992                   «struct Row6;
10993                   ˇ»
10994                   struct Row9.1;
10995                   struct Row9.2;
10996                   struct Row9.3;
10997                   struct Row8;
10998                   struct Row9;
10999                   struct Row10;"#},
11000        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11001        indoc! {r#"struct Row;
11002                   struct Row1;
11003                   struct Row2;
11004                   struct Row2.1;
11005                   struct Row2.2;
11006                   «ˇ
11007                   struct Row4;
11008                   struct» Row5;
11009                   «struct Row6;
11010                   ˇ»
11011                   struct Row9.1;
11012                   struct Row9.2;
11013                   struct Row9.3;
11014                   struct Row8;
11015                   struct Row9;
11016                   struct Row10;"#},
11017        base_text,
11018        &mut cx,
11019    );
11020
11021    // When carets and selections intersect the addition hunks, those are reverted.
11022    // Adjacent carets got merged.
11023    assert_hunk_revert(
11024        indoc! {r#"struct Row;
11025                   ˇ// something on the top
11026                   struct Row1;
11027                   struct Row2;
11028                   struct Roˇw3.1;
11029                   struct Row2.2;
11030                   struct Row2.3;ˇ
11031
11032                   struct Row4;
11033                   struct ˇRow5.1;
11034                   struct Row5.2;
11035                   struct «Rowˇ»5.3;
11036                   struct Row5;
11037                   struct Row6;
11038                   ˇ
11039                   struct Row9.1;
11040                   struct «Rowˇ»9.2;
11041                   struct «ˇRow»9.3;
11042                   struct Row8;
11043                   struct Row9;
11044                   «ˇ// something on bottom»
11045                   struct Row10;"#},
11046        vec![
11047            DiffHunkStatus::Added,
11048            DiffHunkStatus::Added,
11049            DiffHunkStatus::Added,
11050            DiffHunkStatus::Added,
11051            DiffHunkStatus::Added,
11052        ],
11053        indoc! {r#"struct Row;
11054                   ˇstruct Row1;
11055                   struct Row2;
11056                   ˇ
11057                   struct Row4;
11058                   ˇstruct Row5;
11059                   struct Row6;
11060                   ˇ
11061                   ˇstruct Row8;
11062                   struct Row9;
11063                   ˇstruct Row10;"#},
11064        base_text,
11065        &mut cx,
11066    );
11067}
11068
11069#[gpui::test]
11070async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11071    init_test(cx, |_| {});
11072    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11073    let base_text = indoc! {r#"struct Row;
11074struct Row1;
11075struct Row2;
11076
11077struct Row4;
11078struct Row5;
11079struct Row6;
11080
11081struct Row8;
11082struct Row9;
11083struct Row10;"#};
11084
11085    // Modification hunks behave the same as the addition ones.
11086    assert_hunk_revert(
11087        indoc! {r#"struct Row;
11088                   struct Row1;
11089                   struct Row33;
11090                   ˇ
11091                   struct Row4;
11092                   struct Row5;
11093                   struct Row6;
11094                   ˇ
11095                   struct Row99;
11096                   struct Row9;
11097                   struct Row10;"#},
11098        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11099        indoc! {r#"struct Row;
11100                   struct Row1;
11101                   struct Row33;
11102                   ˇ
11103                   struct Row4;
11104                   struct Row5;
11105                   struct Row6;
11106                   ˇ
11107                   struct Row99;
11108                   struct Row9;
11109                   struct Row10;"#},
11110        base_text,
11111        &mut cx,
11112    );
11113    assert_hunk_revert(
11114        indoc! {r#"struct Row;
11115                   struct Row1;
11116                   struct Row33;
11117                   «ˇ
11118                   struct Row4;
11119                   struct» Row5;
11120                   «struct Row6;
11121                   ˇ»
11122                   struct Row99;
11123                   struct Row9;
11124                   struct Row10;"#},
11125        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11126        indoc! {r#"struct Row;
11127                   struct Row1;
11128                   struct Row33;
11129                   «ˇ
11130                   struct Row4;
11131                   struct» Row5;
11132                   «struct Row6;
11133                   ˇ»
11134                   struct Row99;
11135                   struct Row9;
11136                   struct Row10;"#},
11137        base_text,
11138        &mut cx,
11139    );
11140
11141    assert_hunk_revert(
11142        indoc! {r#"ˇstruct Row1.1;
11143                   struct Row1;
11144                   «ˇstr»uct Row22;
11145
11146                   struct ˇRow44;
11147                   struct Row5;
11148                   struct «Rˇ»ow66;ˇ
11149
11150                   «struˇ»ct Row88;
11151                   struct Row9;
11152                   struct Row1011;ˇ"#},
11153        vec![
11154            DiffHunkStatus::Modified,
11155            DiffHunkStatus::Modified,
11156            DiffHunkStatus::Modified,
11157            DiffHunkStatus::Modified,
11158            DiffHunkStatus::Modified,
11159            DiffHunkStatus::Modified,
11160        ],
11161        indoc! {r#"struct Row;
11162                   ˇstruct Row1;
11163                   struct Row2;
11164                   ˇ
11165                   struct Row4;
11166                   ˇstruct Row5;
11167                   struct Row6;
11168                   ˇ
11169                   struct Row8;
11170                   ˇstruct Row9;
11171                   struct Row10;ˇ"#},
11172        base_text,
11173        &mut cx,
11174    );
11175}
11176
11177#[gpui::test]
11178async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11179    init_test(cx, |_| {});
11180    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11181    let base_text = indoc! {r#"struct Row;
11182struct Row1;
11183struct Row2;
11184
11185struct Row4;
11186struct Row5;
11187struct Row6;
11188
11189struct Row8;
11190struct Row9;
11191struct Row10;"#};
11192
11193    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11194    assert_hunk_revert(
11195        indoc! {r#"struct Row;
11196                   struct Row2;
11197
11198                   ˇstruct Row4;
11199                   struct Row5;
11200                   struct Row6;
11201                   ˇ
11202                   struct Row8;
11203                   struct Row10;"#},
11204        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11205        indoc! {r#"struct Row;
11206                   struct Row2;
11207
11208                   ˇstruct Row4;
11209                   struct Row5;
11210                   struct Row6;
11211                   ˇ
11212                   struct Row8;
11213                   struct Row10;"#},
11214        base_text,
11215        &mut cx,
11216    );
11217    assert_hunk_revert(
11218        indoc! {r#"struct Row;
11219                   struct Row2;
11220
11221                   «ˇstruct Row4;
11222                   struct» Row5;
11223                   «struct Row6;
11224                   ˇ»
11225                   struct Row8;
11226                   struct Row10;"#},
11227        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11228        indoc! {r#"struct Row;
11229                   struct Row2;
11230
11231                   «ˇstruct Row4;
11232                   struct» Row5;
11233                   «struct Row6;
11234                   ˇ»
11235                   struct Row8;
11236                   struct Row10;"#},
11237        base_text,
11238        &mut cx,
11239    );
11240
11241    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11242    assert_hunk_revert(
11243        indoc! {r#"struct Row;
11244                   ˇstruct Row2;
11245
11246                   struct Row4;
11247                   struct Row5;
11248                   struct Row6;
11249
11250                   struct Row8;ˇ
11251                   struct Row10;"#},
11252        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11253        indoc! {r#"struct Row;
11254                   struct Row1;
11255                   ˇstruct Row2;
11256
11257                   struct Row4;
11258                   struct Row5;
11259                   struct Row6;
11260
11261                   struct Row8;ˇ
11262                   struct Row9;
11263                   struct Row10;"#},
11264        base_text,
11265        &mut cx,
11266    );
11267    assert_hunk_revert(
11268        indoc! {r#"struct Row;
11269                   struct Row2«ˇ;
11270                   struct Row4;
11271                   struct» Row5;
11272                   «struct Row6;
11273
11274                   struct Row8;ˇ»
11275                   struct Row10;"#},
11276        vec![
11277            DiffHunkStatus::Removed,
11278            DiffHunkStatus::Removed,
11279            DiffHunkStatus::Removed,
11280        ],
11281        indoc! {r#"struct Row;
11282                   struct Row1;
11283                   struct Row2«ˇ;
11284
11285                   struct Row4;
11286                   struct» Row5;
11287                   «struct Row6;
11288
11289                   struct Row8;ˇ»
11290                   struct Row9;
11291                   struct Row10;"#},
11292        base_text,
11293        &mut cx,
11294    );
11295}
11296
11297#[gpui::test]
11298async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11299    init_test(cx, |_| {});
11300
11301    let cols = 4;
11302    let rows = 10;
11303    let sample_text_1 = sample_text(rows, cols, 'a');
11304    assert_eq!(
11305        sample_text_1,
11306        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11307    );
11308    let sample_text_2 = sample_text(rows, cols, 'l');
11309    assert_eq!(
11310        sample_text_2,
11311        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11312    );
11313    let sample_text_3 = sample_text(rows, cols, 'v');
11314    assert_eq!(
11315        sample_text_3,
11316        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11317    );
11318
11319    fn diff_every_buffer_row(
11320        buffer: &Model<Buffer>,
11321        sample_text: String,
11322        cols: usize,
11323        cx: &mut gpui::TestAppContext,
11324    ) {
11325        // revert first character in each row, creating one large diff hunk per buffer
11326        let is_first_char = |offset: usize| offset % cols == 0;
11327        buffer.update(cx, |buffer, cx| {
11328            buffer.set_text(
11329                sample_text
11330                    .chars()
11331                    .enumerate()
11332                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11333                    .collect::<String>(),
11334                cx,
11335            );
11336            buffer.set_diff_base(Some(sample_text), cx);
11337        });
11338        cx.executor().run_until_parked();
11339    }
11340
11341    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11342    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11343
11344    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11345    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11346
11347    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11348    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11349
11350    let multibuffer = cx.new_model(|cx| {
11351        let mut multibuffer = MultiBuffer::new(ReadWrite);
11352        multibuffer.push_excerpts(
11353            buffer_1.clone(),
11354            [
11355                ExcerptRange {
11356                    context: Point::new(0, 0)..Point::new(3, 0),
11357                    primary: None,
11358                },
11359                ExcerptRange {
11360                    context: Point::new(5, 0)..Point::new(7, 0),
11361                    primary: None,
11362                },
11363                ExcerptRange {
11364                    context: Point::new(9, 0)..Point::new(10, 4),
11365                    primary: None,
11366                },
11367            ],
11368            cx,
11369        );
11370        multibuffer.push_excerpts(
11371            buffer_2.clone(),
11372            [
11373                ExcerptRange {
11374                    context: Point::new(0, 0)..Point::new(3, 0),
11375                    primary: None,
11376                },
11377                ExcerptRange {
11378                    context: Point::new(5, 0)..Point::new(7, 0),
11379                    primary: None,
11380                },
11381                ExcerptRange {
11382                    context: Point::new(9, 0)..Point::new(10, 4),
11383                    primary: None,
11384                },
11385            ],
11386            cx,
11387        );
11388        multibuffer.push_excerpts(
11389            buffer_3.clone(),
11390            [
11391                ExcerptRange {
11392                    context: Point::new(0, 0)..Point::new(3, 0),
11393                    primary: None,
11394                },
11395                ExcerptRange {
11396                    context: Point::new(5, 0)..Point::new(7, 0),
11397                    primary: None,
11398                },
11399                ExcerptRange {
11400                    context: Point::new(9, 0)..Point::new(10, 4),
11401                    primary: None,
11402                },
11403            ],
11404            cx,
11405        );
11406        multibuffer
11407    });
11408
11409    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11410    editor.update(cx, |editor, cx| {
11411        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");
11412        editor.select_all(&SelectAll, cx);
11413        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11414    });
11415    cx.executor().run_until_parked();
11416    // When all ranges are selected, all buffer hunks are reverted.
11417    editor.update(cx, |editor, cx| {
11418        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");
11419    });
11420    buffer_1.update(cx, |buffer, _| {
11421        assert_eq!(buffer.text(), sample_text_1);
11422    });
11423    buffer_2.update(cx, |buffer, _| {
11424        assert_eq!(buffer.text(), sample_text_2);
11425    });
11426    buffer_3.update(cx, |buffer, _| {
11427        assert_eq!(buffer.text(), sample_text_3);
11428    });
11429
11430    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11431    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11432    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11433    editor.update(cx, |editor, cx| {
11434        editor.change_selections(None, cx, |s| {
11435            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11436        });
11437        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11438    });
11439    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11440    // but not affect buffer_2 and its related excerpts.
11441    editor.update(cx, |editor, cx| {
11442        assert_eq!(
11443            editor.text(cx),
11444            "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"
11445        );
11446    });
11447    buffer_1.update(cx, |buffer, _| {
11448        assert_eq!(buffer.text(), sample_text_1);
11449    });
11450    buffer_2.update(cx, |buffer, _| {
11451        assert_eq!(
11452            buffer.text(),
11453            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11454        );
11455    });
11456    buffer_3.update(cx, |buffer, _| {
11457        assert_eq!(
11458            buffer.text(),
11459            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11460        );
11461    });
11462}
11463
11464#[gpui::test]
11465async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11466    init_test(cx, |_| {});
11467
11468    let cols = 4;
11469    let rows = 10;
11470    let sample_text_1 = sample_text(rows, cols, 'a');
11471    assert_eq!(
11472        sample_text_1,
11473        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11474    );
11475    let sample_text_2 = sample_text(rows, cols, 'l');
11476    assert_eq!(
11477        sample_text_2,
11478        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11479    );
11480    let sample_text_3 = sample_text(rows, cols, 'v');
11481    assert_eq!(
11482        sample_text_3,
11483        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11484    );
11485
11486    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11487    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11488    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11489
11490    let multi_buffer = cx.new_model(|cx| {
11491        let mut multibuffer = MultiBuffer::new(ReadWrite);
11492        multibuffer.push_excerpts(
11493            buffer_1.clone(),
11494            [
11495                ExcerptRange {
11496                    context: Point::new(0, 0)..Point::new(3, 0),
11497                    primary: None,
11498                },
11499                ExcerptRange {
11500                    context: Point::new(5, 0)..Point::new(7, 0),
11501                    primary: None,
11502                },
11503                ExcerptRange {
11504                    context: Point::new(9, 0)..Point::new(10, 4),
11505                    primary: None,
11506                },
11507            ],
11508            cx,
11509        );
11510        multibuffer.push_excerpts(
11511            buffer_2.clone(),
11512            [
11513                ExcerptRange {
11514                    context: Point::new(0, 0)..Point::new(3, 0),
11515                    primary: None,
11516                },
11517                ExcerptRange {
11518                    context: Point::new(5, 0)..Point::new(7, 0),
11519                    primary: None,
11520                },
11521                ExcerptRange {
11522                    context: Point::new(9, 0)..Point::new(10, 4),
11523                    primary: None,
11524                },
11525            ],
11526            cx,
11527        );
11528        multibuffer.push_excerpts(
11529            buffer_3.clone(),
11530            [
11531                ExcerptRange {
11532                    context: Point::new(0, 0)..Point::new(3, 0),
11533                    primary: None,
11534                },
11535                ExcerptRange {
11536                    context: Point::new(5, 0)..Point::new(7, 0),
11537                    primary: None,
11538                },
11539                ExcerptRange {
11540                    context: Point::new(9, 0)..Point::new(10, 4),
11541                    primary: None,
11542                },
11543            ],
11544            cx,
11545        );
11546        multibuffer
11547    });
11548
11549    let fs = FakeFs::new(cx.executor());
11550    fs.insert_tree(
11551        "/a",
11552        json!({
11553            "main.rs": sample_text_1,
11554            "other.rs": sample_text_2,
11555            "lib.rs": sample_text_3,
11556        }),
11557    )
11558    .await;
11559    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11560    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11561    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11562    let multi_buffer_editor = cx.new_view(|cx| {
11563        Editor::new(
11564            EditorMode::Full,
11565            multi_buffer,
11566            Some(project.clone()),
11567            true,
11568            cx,
11569        )
11570    });
11571    let multibuffer_item_id = workspace
11572        .update(cx, |workspace, cx| {
11573            assert!(
11574                workspace.active_item(cx).is_none(),
11575                "active item should be None before the first item is added"
11576            );
11577            workspace.add_item_to_active_pane(
11578                Box::new(multi_buffer_editor.clone()),
11579                None,
11580                true,
11581                cx,
11582            );
11583            let active_item = workspace
11584                .active_item(cx)
11585                .expect("should have an active item after adding the multi buffer");
11586            assert!(
11587                !active_item.is_singleton(cx),
11588                "A multi buffer was expected to active after adding"
11589            );
11590            active_item.item_id()
11591        })
11592        .unwrap();
11593    cx.executor().run_until_parked();
11594
11595    multi_buffer_editor.update(cx, |editor, cx| {
11596        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11597        editor.open_excerpts(&OpenExcerpts, cx);
11598    });
11599    cx.executor().run_until_parked();
11600    let first_item_id = workspace
11601        .update(cx, |workspace, cx| {
11602            let active_item = workspace
11603                .active_item(cx)
11604                .expect("should have an active item after navigating into the 1st buffer");
11605            let first_item_id = active_item.item_id();
11606            assert_ne!(
11607                first_item_id, multibuffer_item_id,
11608                "Should navigate into the 1st buffer and activate it"
11609            );
11610            assert!(
11611                active_item.is_singleton(cx),
11612                "New active item should be a singleton buffer"
11613            );
11614            assert_eq!(
11615                active_item
11616                    .act_as::<Editor>(cx)
11617                    .expect("should have navigated into an editor for the 1st buffer")
11618                    .read(cx)
11619                    .text(cx),
11620                sample_text_1
11621            );
11622
11623            workspace
11624                .go_back(workspace.active_pane().downgrade(), cx)
11625                .detach_and_log_err(cx);
11626
11627            first_item_id
11628        })
11629        .unwrap();
11630    cx.executor().run_until_parked();
11631    workspace
11632        .update(cx, |workspace, cx| {
11633            let active_item = workspace
11634                .active_item(cx)
11635                .expect("should have an active item after navigating back");
11636            assert_eq!(
11637                active_item.item_id(),
11638                multibuffer_item_id,
11639                "Should navigate back to the multi buffer"
11640            );
11641            assert!(!active_item.is_singleton(cx));
11642        })
11643        .unwrap();
11644
11645    multi_buffer_editor.update(cx, |editor, cx| {
11646        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11647            s.select_ranges(Some(39..40))
11648        });
11649        editor.open_excerpts(&OpenExcerpts, cx);
11650    });
11651    cx.executor().run_until_parked();
11652    let second_item_id = workspace
11653        .update(cx, |workspace, cx| {
11654            let active_item = workspace
11655                .active_item(cx)
11656                .expect("should have an active item after navigating into the 2nd buffer");
11657            let second_item_id = active_item.item_id();
11658            assert_ne!(
11659                second_item_id, multibuffer_item_id,
11660                "Should navigate away from the multibuffer"
11661            );
11662            assert_ne!(
11663                second_item_id, first_item_id,
11664                "Should navigate into the 2nd buffer and activate it"
11665            );
11666            assert!(
11667                active_item.is_singleton(cx),
11668                "New active item should be a singleton buffer"
11669            );
11670            assert_eq!(
11671                active_item
11672                    .act_as::<Editor>(cx)
11673                    .expect("should have navigated into an editor")
11674                    .read(cx)
11675                    .text(cx),
11676                sample_text_2
11677            );
11678
11679            workspace
11680                .go_back(workspace.active_pane().downgrade(), cx)
11681                .detach_and_log_err(cx);
11682
11683            second_item_id
11684        })
11685        .unwrap();
11686    cx.executor().run_until_parked();
11687    workspace
11688        .update(cx, |workspace, cx| {
11689            let active_item = workspace
11690                .active_item(cx)
11691                .expect("should have an active item after navigating back from the 2nd buffer");
11692            assert_eq!(
11693                active_item.item_id(),
11694                multibuffer_item_id,
11695                "Should navigate back from the 2nd buffer to the multi buffer"
11696            );
11697            assert!(!active_item.is_singleton(cx));
11698        })
11699        .unwrap();
11700
11701    multi_buffer_editor.update(cx, |editor, cx| {
11702        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11703            s.select_ranges(Some(60..70))
11704        });
11705        editor.open_excerpts(&OpenExcerpts, cx);
11706    });
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 into the 3rd buffer");
11713            let third_item_id = active_item.item_id();
11714            assert_ne!(
11715                third_item_id, multibuffer_item_id,
11716                "Should navigate into the 3rd buffer and activate it"
11717            );
11718            assert_ne!(third_item_id, first_item_id);
11719            assert_ne!(third_item_id, second_item_id);
11720            assert!(
11721                active_item.is_singleton(cx),
11722                "New active item should be a singleton buffer"
11723            );
11724            assert_eq!(
11725                active_item
11726                    .act_as::<Editor>(cx)
11727                    .expect("should have navigated into an editor")
11728                    .read(cx)
11729                    .text(cx),
11730                sample_text_3
11731            );
11732
11733            workspace
11734                .go_back(workspace.active_pane().downgrade(), cx)
11735                .detach_and_log_err(cx);
11736        })
11737        .unwrap();
11738    cx.executor().run_until_parked();
11739    workspace
11740        .update(cx, |workspace, cx| {
11741            let active_item = workspace
11742                .active_item(cx)
11743                .expect("should have an active item after navigating back from the 3rd buffer");
11744            assert_eq!(
11745                active_item.item_id(),
11746                multibuffer_item_id,
11747                "Should navigate back from the 3rd buffer to the multi buffer"
11748            );
11749            assert!(!active_item.is_singleton(cx));
11750        })
11751        .unwrap();
11752}
11753
11754#[gpui::test]
11755async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11756    init_test(cx, |_| {});
11757
11758    let mut cx = EditorTestContext::new(cx).await;
11759
11760    let diff_base = r#"
11761        use some::mod;
11762
11763        const A: u32 = 42;
11764
11765        fn main() {
11766            println!("hello");
11767
11768            println!("world");
11769        }
11770        "#
11771    .unindent();
11772
11773    cx.set_state(
11774        &r#"
11775        use some::modified;
11776
11777        ˇ
11778        fn main() {
11779            println!("hello there");
11780
11781            println!("around the");
11782            println!("world");
11783        }
11784        "#
11785        .unindent(),
11786    );
11787
11788    cx.set_diff_base(Some(&diff_base));
11789    executor.run_until_parked();
11790
11791    cx.update_editor(|editor, cx| {
11792        editor.go_to_next_hunk(&GoToHunk, cx);
11793        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11794    });
11795    executor.run_until_parked();
11796    cx.assert_diff_hunks(
11797        r#"
11798          use some::modified;
11799
11800
11801          fn main() {
11802        -     println!("hello");
11803        +     println!("hello there");
11804
11805              println!("around the");
11806              println!("world");
11807          }
11808        "#
11809        .unindent(),
11810    );
11811
11812    cx.update_editor(|editor, cx| {
11813        for _ in 0..3 {
11814            editor.go_to_next_hunk(&GoToHunk, cx);
11815            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11816        }
11817    });
11818    executor.run_until_parked();
11819    cx.assert_editor_state(
11820        &r#"
11821        use some::modified;
11822
11823        ˇ
11824        fn main() {
11825            println!("hello there");
11826
11827            println!("around the");
11828            println!("world");
11829        }
11830        "#
11831        .unindent(),
11832    );
11833
11834    cx.assert_diff_hunks(
11835        r#"
11836        - use some::mod;
11837        + use some::modified;
11838
11839        - const A: u32 = 42;
11840
11841          fn main() {
11842        -     println!("hello");
11843        +     println!("hello there");
11844
11845        +     println!("around the");
11846              println!("world");
11847          }
11848        "#
11849        .unindent(),
11850    );
11851
11852    cx.update_editor(|editor, cx| {
11853        editor.cancel(&Cancel, cx);
11854    });
11855
11856    cx.assert_diff_hunks(
11857        r#"
11858          use some::modified;
11859
11860
11861          fn main() {
11862              println!("hello there");
11863
11864              println!("around the");
11865              println!("world");
11866          }
11867        "#
11868        .unindent(),
11869    );
11870}
11871
11872#[gpui::test]
11873async fn test_diff_base_change_with_expanded_diff_hunks(
11874    executor: BackgroundExecutor,
11875    cx: &mut gpui::TestAppContext,
11876) {
11877    init_test(cx, |_| {});
11878
11879    let mut cx = EditorTestContext::new(cx).await;
11880
11881    let diff_base = r#"
11882        use some::mod1;
11883        use some::mod2;
11884
11885        const A: u32 = 42;
11886        const B: u32 = 42;
11887        const C: u32 = 42;
11888
11889        fn main() {
11890            println!("hello");
11891
11892            println!("world");
11893        }
11894        "#
11895    .unindent();
11896
11897    cx.set_state(
11898        &r#"
11899        use some::mod2;
11900
11901        const A: u32 = 42;
11902        const C: u32 = 42;
11903
11904        fn main(ˇ) {
11905            //println!("hello");
11906
11907            println!("world");
11908            //
11909            //
11910        }
11911        "#
11912        .unindent(),
11913    );
11914
11915    cx.set_diff_base(Some(&diff_base));
11916    executor.run_until_parked();
11917
11918    cx.update_editor(|editor, cx| {
11919        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11920    });
11921    executor.run_until_parked();
11922    cx.assert_diff_hunks(
11923        r#"
11924        - use some::mod1;
11925          use some::mod2;
11926
11927          const A: u32 = 42;
11928        - const B: u32 = 42;
11929          const C: u32 = 42;
11930
11931          fn main() {
11932        -     println!("hello");
11933        +     //println!("hello");
11934
11935              println!("world");
11936        +     //
11937        +     //
11938          }
11939        "#
11940        .unindent(),
11941    );
11942
11943    cx.set_diff_base(Some("new diff base!"));
11944    executor.run_until_parked();
11945    cx.assert_diff_hunks(
11946        r#"
11947          use some::mod2;
11948
11949          const A: u32 = 42;
11950          const C: u32 = 42;
11951
11952          fn main() {
11953              //println!("hello");
11954
11955              println!("world");
11956              //
11957              //
11958          }
11959        "#
11960        .unindent(),
11961    );
11962
11963    cx.update_editor(|editor, cx| {
11964        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11965    });
11966    executor.run_until_parked();
11967    cx.assert_diff_hunks(
11968        r#"
11969        - new diff base!
11970        + use some::mod2;
11971        +
11972        + const A: u32 = 42;
11973        + const C: u32 = 42;
11974        +
11975        + fn main() {
11976        +     //println!("hello");
11977        +
11978        +     println!("world");
11979        +     //
11980        +     //
11981        + }
11982        "#
11983        .unindent(),
11984    );
11985}
11986
11987#[gpui::test]
11988async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11989    init_test(cx, |_| {});
11990
11991    let mut cx = EditorTestContext::new(cx).await;
11992
11993    let diff_base = r#"
11994        use some::mod1;
11995        use some::mod2;
11996
11997        const A: u32 = 42;
11998        const B: u32 = 42;
11999        const C: u32 = 42;
12000
12001        fn main() {
12002            println!("hello");
12003
12004            println!("world");
12005        }
12006
12007        fn another() {
12008            println!("another");
12009        }
12010
12011        fn another2() {
12012            println!("another2");
12013        }
12014        "#
12015    .unindent();
12016
12017    cx.set_state(
12018        &r#"
12019        «use some::mod2;
12020
12021        const A: u32 = 42;
12022        const C: u32 = 42;
12023
12024        fn main() {
12025            //println!("hello");
12026
12027            println!("world");
12028            //
12029            //ˇ»
12030        }
12031
12032        fn another() {
12033            println!("another");
12034            println!("another");
12035        }
12036
12037            println!("another2");
12038        }
12039        "#
12040        .unindent(),
12041    );
12042
12043    cx.set_diff_base(Some(&diff_base));
12044    executor.run_until_parked();
12045
12046    cx.update_editor(|editor, cx| {
12047        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12048    });
12049    executor.run_until_parked();
12050
12051    cx.assert_diff_hunks(
12052        r#"
12053        - use some::mod1;
12054          use some::mod2;
12055
12056          const A: u32 = 42;
12057        - const B: u32 = 42;
12058          const C: u32 = 42;
12059
12060          fn main() {
12061        -     println!("hello");
12062        +     //println!("hello");
12063
12064              println!("world");
12065        +     //
12066        +     //
12067          }
12068
12069          fn another() {
12070              println!("another");
12071        +     println!("another");
12072          }
12073
12074        - fn another2() {
12075              println!("another2");
12076          }
12077        "#
12078        .unindent(),
12079    );
12080
12081    // Fold across some of the diff hunks. They should no longer appear expanded.
12082    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12083    cx.executor().run_until_parked();
12084
12085    // Hunks are not shown if their position is within a fold
12086    cx.assert_diff_hunks(
12087        r#"
12088          use some::mod2;
12089
12090          const A: u32 = 42;
12091          const C: u32 = 42;
12092
12093          fn main() {
12094              //println!("hello");
12095
12096              println!("world");
12097              //
12098              //
12099          }
12100
12101          fn another() {
12102              println!("another");
12103        +     println!("another");
12104          }
12105
12106        - fn another2() {
12107              println!("another2");
12108          }
12109        "#
12110        .unindent(),
12111    );
12112
12113    cx.update_editor(|editor, cx| {
12114        editor.select_all(&SelectAll, cx);
12115        editor.unfold_lines(&UnfoldLines, cx);
12116    });
12117    cx.executor().run_until_parked();
12118
12119    // The deletions reappear when unfolding.
12120    cx.assert_diff_hunks(
12121        r#"
12122        - use some::mod1;
12123          use some::mod2;
12124
12125          const A: u32 = 42;
12126        - const B: u32 = 42;
12127          const C: u32 = 42;
12128
12129          fn main() {
12130        -     println!("hello");
12131        +     //println!("hello");
12132
12133              println!("world");
12134        +     //
12135        +     //
12136          }
12137
12138          fn another() {
12139              println!("another");
12140        +     println!("another");
12141          }
12142
12143        - fn another2() {
12144              println!("another2");
12145          }
12146        "#
12147        .unindent(),
12148    );
12149}
12150
12151#[gpui::test]
12152async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12153    init_test(cx, |_| {});
12154
12155    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12156    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12157    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12158    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12159    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12160    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12161
12162    let buffer_1 = cx.new_model(|cx| {
12163        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
12164        buffer.set_diff_base(Some(file_1_old.into()), cx);
12165        buffer
12166    });
12167    let buffer_2 = cx.new_model(|cx| {
12168        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
12169        buffer.set_diff_base(Some(file_2_old.into()), cx);
12170        buffer
12171    });
12172    let buffer_3 = cx.new_model(|cx| {
12173        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
12174        buffer.set_diff_base(Some(file_3_old.into()), cx);
12175        buffer
12176    });
12177
12178    let multi_buffer = cx.new_model(|cx| {
12179        let mut multibuffer = MultiBuffer::new(ReadWrite);
12180        multibuffer.push_excerpts(
12181            buffer_1.clone(),
12182            [
12183                ExcerptRange {
12184                    context: Point::new(0, 0)..Point::new(3, 0),
12185                    primary: None,
12186                },
12187                ExcerptRange {
12188                    context: Point::new(5, 0)..Point::new(7, 0),
12189                    primary: None,
12190                },
12191                ExcerptRange {
12192                    context: Point::new(9, 0)..Point::new(10, 3),
12193                    primary: None,
12194                },
12195            ],
12196            cx,
12197        );
12198        multibuffer.push_excerpts(
12199            buffer_2.clone(),
12200            [
12201                ExcerptRange {
12202                    context: Point::new(0, 0)..Point::new(3, 0),
12203                    primary: None,
12204                },
12205                ExcerptRange {
12206                    context: Point::new(5, 0)..Point::new(7, 0),
12207                    primary: None,
12208                },
12209                ExcerptRange {
12210                    context: Point::new(9, 0)..Point::new(10, 3),
12211                    primary: None,
12212                },
12213            ],
12214            cx,
12215        );
12216        multibuffer.push_excerpts(
12217            buffer_3.clone(),
12218            [
12219                ExcerptRange {
12220                    context: Point::new(0, 0)..Point::new(3, 0),
12221                    primary: None,
12222                },
12223                ExcerptRange {
12224                    context: Point::new(5, 0)..Point::new(7, 0),
12225                    primary: None,
12226                },
12227                ExcerptRange {
12228                    context: Point::new(9, 0)..Point::new(10, 3),
12229                    primary: None,
12230                },
12231            ],
12232            cx,
12233        );
12234        multibuffer
12235    });
12236
12237    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12238    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12239    cx.run_until_parked();
12240
12241    cx.assert_editor_state(
12242        &"
12243            ˇaaa
12244            ccc
12245            ddd
12246
12247            ggg
12248            hhh
12249
12250
12251            lll
12252            mmm
12253            NNN
12254
12255            qqq
12256            rrr
12257
12258            uuu
12259            111
12260            222
12261            333
12262
12263            666
12264            777
12265
12266            000
12267            !!!"
12268        .unindent(),
12269    );
12270
12271    cx.update_editor(|editor, cx| {
12272        editor.select_all(&SelectAll, cx);
12273        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12274    });
12275    cx.executor().run_until_parked();
12276
12277    cx.assert_diff_hunks(
12278        "
12279            aaa
12280          - bbb
12281            ccc
12282            ddd
12283
12284            ggg
12285            hhh
12286
12287
12288            lll
12289            mmm
12290          - nnn
12291          + NNN
12292
12293            qqq
12294            rrr
12295
12296            uuu
12297            111
12298            222
12299            333
12300
12301          + 666
12302            777
12303
12304            000
12305            !!!"
12306        .unindent(),
12307    );
12308}
12309
12310#[gpui::test]
12311async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12312    init_test(cx, |_| {});
12313
12314    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12315    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12316
12317    let buffer = cx.new_model(|cx| {
12318        let mut buffer = Buffer::local(text.to_string(), cx);
12319        buffer.set_diff_base(Some(base.into()), cx);
12320        buffer
12321    });
12322
12323    let multi_buffer = cx.new_model(|cx| {
12324        let mut multibuffer = MultiBuffer::new(ReadWrite);
12325        multibuffer.push_excerpts(
12326            buffer.clone(),
12327            [
12328                ExcerptRange {
12329                    context: Point::new(0, 0)..Point::new(2, 0),
12330                    primary: None,
12331                },
12332                ExcerptRange {
12333                    context: Point::new(5, 0)..Point::new(7, 0),
12334                    primary: None,
12335                },
12336            ],
12337            cx,
12338        );
12339        multibuffer
12340    });
12341
12342    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12343    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12344    cx.run_until_parked();
12345
12346    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12347    cx.executor().run_until_parked();
12348
12349    cx.assert_diff_hunks(
12350        "
12351            aaa
12352          - bbb
12353          + BBB
12354
12355          - ddd
12356          - eee
12357          + EEE
12358            fff
12359        "
12360        .unindent(),
12361    );
12362}
12363
12364#[gpui::test]
12365async fn test_edits_around_expanded_insertion_hunks(
12366    executor: BackgroundExecutor,
12367    cx: &mut gpui::TestAppContext,
12368) {
12369    init_test(cx, |_| {});
12370
12371    let mut cx = EditorTestContext::new(cx).await;
12372
12373    let diff_base = r#"
12374        use some::mod1;
12375        use some::mod2;
12376
12377        const A: u32 = 42;
12378
12379        fn main() {
12380            println!("hello");
12381
12382            println!("world");
12383        }
12384        "#
12385    .unindent();
12386    executor.run_until_parked();
12387    cx.set_state(
12388        &r#"
12389        use some::mod1;
12390        use some::mod2;
12391
12392        const A: u32 = 42;
12393        const B: u32 = 42;
12394        const C: u32 = 42;
12395        ˇ
12396
12397        fn main() {
12398            println!("hello");
12399
12400            println!("world");
12401        }
12402        "#
12403        .unindent(),
12404    );
12405
12406    cx.set_diff_base(Some(&diff_base));
12407    executor.run_until_parked();
12408
12409    cx.update_editor(|editor, cx| {
12410        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12411    });
12412    executor.run_until_parked();
12413
12414    cx.assert_diff_hunks(
12415        r#"
12416        use some::mod1;
12417        use some::mod2;
12418
12419        const A: u32 = 42;
12420      + const B: u32 = 42;
12421      + const C: u32 = 42;
12422      +
12423
12424        fn main() {
12425            println!("hello");
12426
12427            println!("world");
12428        }
12429        "#
12430        .unindent(),
12431    );
12432
12433    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12434    executor.run_until_parked();
12435
12436    cx.assert_diff_hunks(
12437        r#"
12438        use some::mod1;
12439        use some::mod2;
12440
12441        const A: u32 = 42;
12442      + const B: u32 = 42;
12443      + const C: u32 = 42;
12444      + const D: u32 = 42;
12445      +
12446
12447        fn main() {
12448            println!("hello");
12449
12450            println!("world");
12451        }
12452        "#
12453        .unindent(),
12454    );
12455
12456    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12457    executor.run_until_parked();
12458
12459    cx.assert_diff_hunks(
12460        r#"
12461        use some::mod1;
12462        use some::mod2;
12463
12464        const A: u32 = 42;
12465      + const B: u32 = 42;
12466      + const C: u32 = 42;
12467      + const D: u32 = 42;
12468      + const E: u32 = 42;
12469      +
12470
12471        fn main() {
12472            println!("hello");
12473
12474            println!("world");
12475        }
12476        "#
12477        .unindent(),
12478    );
12479
12480    cx.update_editor(|editor, cx| {
12481        editor.delete_line(&DeleteLine, cx);
12482    });
12483    executor.run_until_parked();
12484
12485    cx.assert_diff_hunks(
12486        r#"
12487        use some::mod1;
12488        use some::mod2;
12489
12490        const A: u32 = 42;
12491      + const B: u32 = 42;
12492      + const C: u32 = 42;
12493      + const D: u32 = 42;
12494      + const E: u32 = 42;
12495
12496        fn main() {
12497            println!("hello");
12498
12499            println!("world");
12500        }
12501        "#
12502        .unindent(),
12503    );
12504
12505    cx.update_editor(|editor, cx| {
12506        editor.move_up(&MoveUp, cx);
12507        editor.delete_line(&DeleteLine, cx);
12508        editor.move_up(&MoveUp, cx);
12509        editor.delete_line(&DeleteLine, cx);
12510        editor.move_up(&MoveUp, cx);
12511        editor.delete_line(&DeleteLine, cx);
12512    });
12513    executor.run_until_parked();
12514    cx.assert_editor_state(
12515        &r#"
12516        use some::mod1;
12517        use some::mod2;
12518
12519        const A: u32 = 42;
12520        const B: u32 = 42;
12521        ˇ
12522        fn main() {
12523            println!("hello");
12524
12525            println!("world");
12526        }
12527        "#
12528        .unindent(),
12529    );
12530
12531    cx.assert_diff_hunks(
12532        r#"
12533        use some::mod1;
12534        use some::mod2;
12535
12536        const A: u32 = 42;
12537      + const B: u32 = 42;
12538
12539        fn main() {
12540            println!("hello");
12541
12542            println!("world");
12543        }
12544        "#
12545        .unindent(),
12546    );
12547
12548    cx.update_editor(|editor, cx| {
12549        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12550        editor.delete_line(&DeleteLine, cx);
12551    });
12552    executor.run_until_parked();
12553    cx.assert_diff_hunks(
12554        r#"
12555        use some::mod1;
12556      - use some::mod2;
12557      -
12558      - const A: u32 = 42;
12559
12560        fn main() {
12561            println!("hello");
12562
12563            println!("world");
12564        }
12565        "#
12566        .unindent(),
12567    );
12568}
12569
12570#[gpui::test]
12571async fn test_edits_around_expanded_deletion_hunks(
12572    executor: BackgroundExecutor,
12573    cx: &mut gpui::TestAppContext,
12574) {
12575    init_test(cx, |_| {});
12576
12577    let mut cx = EditorTestContext::new(cx).await;
12578
12579    let diff_base = r#"
12580        use some::mod1;
12581        use some::mod2;
12582
12583        const A: u32 = 42;
12584        const B: u32 = 42;
12585        const C: u32 = 42;
12586
12587
12588        fn main() {
12589            println!("hello");
12590
12591            println!("world");
12592        }
12593    "#
12594    .unindent();
12595    executor.run_until_parked();
12596    cx.set_state(
12597        &r#"
12598        use some::mod1;
12599        use some::mod2;
12600
12601        ˇconst B: u32 = 42;
12602        const C: u32 = 42;
12603
12604
12605        fn main() {
12606            println!("hello");
12607
12608            println!("world");
12609        }
12610        "#
12611        .unindent(),
12612    );
12613
12614    cx.set_diff_base(Some(&diff_base));
12615    executor.run_until_parked();
12616
12617    cx.update_editor(|editor, cx| {
12618        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12619    });
12620    executor.run_until_parked();
12621
12622    cx.assert_diff_hunks(
12623        r#"
12624        use some::mod1;
12625        use some::mod2;
12626
12627      - const A: u32 = 42;
12628        const B: u32 = 42;
12629        const C: u32 = 42;
12630
12631
12632        fn main() {
12633            println!("hello");
12634
12635            println!("world");
12636        }
12637        "#
12638        .unindent(),
12639    );
12640
12641    cx.update_editor(|editor, cx| {
12642        editor.delete_line(&DeleteLine, cx);
12643    });
12644    executor.run_until_parked();
12645    cx.assert_editor_state(
12646        &r#"
12647        use some::mod1;
12648        use some::mod2;
12649
12650        ˇconst C: u32 = 42;
12651
12652
12653        fn main() {
12654            println!("hello");
12655
12656            println!("world");
12657        }
12658        "#
12659        .unindent(),
12660    );
12661    cx.assert_diff_hunks(
12662        r#"
12663        use some::mod1;
12664        use some::mod2;
12665
12666      - const A: u32 = 42;
12667      - const B: u32 = 42;
12668        const C: u32 = 42;
12669
12670
12671        fn main() {
12672            println!("hello");
12673
12674            println!("world");
12675        }
12676        "#
12677        .unindent(),
12678    );
12679
12680    cx.update_editor(|editor, cx| {
12681        editor.delete_line(&DeleteLine, cx);
12682    });
12683    executor.run_until_parked();
12684    cx.assert_editor_state(
12685        &r#"
12686        use some::mod1;
12687        use some::mod2;
12688
12689        ˇ
12690
12691        fn main() {
12692            println!("hello");
12693
12694            println!("world");
12695        }
12696        "#
12697        .unindent(),
12698    );
12699    cx.assert_diff_hunks(
12700        r#"
12701        use some::mod1;
12702        use some::mod2;
12703
12704      - const A: u32 = 42;
12705      - const B: u32 = 42;
12706      - const C: u32 = 42;
12707
12708
12709        fn main() {
12710            println!("hello");
12711
12712            println!("world");
12713        }
12714        "#
12715        .unindent(),
12716    );
12717
12718    cx.update_editor(|editor, cx| {
12719        editor.handle_input("replacement", cx);
12720    });
12721    executor.run_until_parked();
12722    cx.assert_editor_state(
12723        &r#"
12724        use some::mod1;
12725        use some::mod2;
12726
12727        replacementˇ
12728
12729        fn main() {
12730            println!("hello");
12731
12732            println!("world");
12733        }
12734        "#
12735        .unindent(),
12736    );
12737    cx.assert_diff_hunks(
12738        r#"
12739        use some::mod1;
12740        use some::mod2;
12741
12742      - const A: u32 = 42;
12743      - const B: u32 = 42;
12744      - const C: u32 = 42;
12745      -
12746      + replacement
12747
12748        fn main() {
12749            println!("hello");
12750
12751            println!("world");
12752        }
12753        "#
12754        .unindent(),
12755    );
12756}
12757
12758#[gpui::test]
12759async fn test_edit_after_expanded_modification_hunk(
12760    executor: BackgroundExecutor,
12761    cx: &mut gpui::TestAppContext,
12762) {
12763    init_test(cx, |_| {});
12764
12765    let mut cx = EditorTestContext::new(cx).await;
12766
12767    let diff_base = r#"
12768        use some::mod1;
12769        use some::mod2;
12770
12771        const A: u32 = 42;
12772        const B: u32 = 42;
12773        const C: u32 = 42;
12774        const D: u32 = 42;
12775
12776
12777        fn main() {
12778            println!("hello");
12779
12780            println!("world");
12781        }"#
12782    .unindent();
12783
12784    cx.set_state(
12785        &r#"
12786        use some::mod1;
12787        use some::mod2;
12788
12789        const A: u32 = 42;
12790        const B: u32 = 42;
12791        const C: u32 = 43ˇ
12792        const D: u32 = 42;
12793
12794
12795        fn main() {
12796            println!("hello");
12797
12798            println!("world");
12799        }"#
12800        .unindent(),
12801    );
12802
12803    cx.set_diff_base(Some(&diff_base));
12804    executor.run_until_parked();
12805    cx.update_editor(|editor, cx| {
12806        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12807    });
12808    executor.run_until_parked();
12809
12810    cx.assert_diff_hunks(
12811        r#"
12812        use some::mod1;
12813        use some::mod2;
12814
12815        const A: u32 = 42;
12816        const B: u32 = 42;
12817      - const C: u32 = 42;
12818      + const C: u32 = 43
12819        const D: u32 = 42;
12820
12821
12822        fn main() {
12823            println!("hello");
12824
12825            println!("world");
12826        }"#
12827        .unindent(),
12828    );
12829
12830    cx.update_editor(|editor, cx| {
12831        editor.handle_input("\nnew_line\n", cx);
12832    });
12833    executor.run_until_parked();
12834
12835    cx.assert_diff_hunks(
12836        r#"
12837        use some::mod1;
12838        use some::mod2;
12839
12840        const A: u32 = 42;
12841        const B: u32 = 42;
12842      - const C: u32 = 42;
12843      + const C: u32 = 43
12844      + new_line
12845      +
12846        const D: u32 = 42;
12847
12848
12849        fn main() {
12850            println!("hello");
12851
12852            println!("world");
12853        }"#
12854        .unindent(),
12855    );
12856}
12857
12858async fn setup_indent_guides_editor(
12859    text: &str,
12860    cx: &mut gpui::TestAppContext,
12861) -> (BufferId, EditorTestContext) {
12862    init_test(cx, |_| {});
12863
12864    let mut cx = EditorTestContext::new(cx).await;
12865
12866    let buffer_id = cx.update_editor(|editor, cx| {
12867        editor.set_text(text, cx);
12868        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12869
12870        buffer_ids[0]
12871    });
12872
12873    (buffer_id, cx)
12874}
12875
12876fn assert_indent_guides(
12877    range: Range<u32>,
12878    expected: Vec<IndentGuide>,
12879    active_indices: Option<Vec<usize>>,
12880    cx: &mut EditorTestContext,
12881) {
12882    let indent_guides = cx.update_editor(|editor, cx| {
12883        let snapshot = editor.snapshot(cx).display_snapshot;
12884        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12885            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12886            true,
12887            &snapshot,
12888            cx,
12889        );
12890
12891        indent_guides.sort_by(|a, b| {
12892            a.depth.cmp(&b.depth).then(
12893                a.start_row
12894                    .cmp(&b.start_row)
12895                    .then(a.end_row.cmp(&b.end_row)),
12896            )
12897        });
12898        indent_guides
12899    });
12900
12901    if let Some(expected) = active_indices {
12902        let active_indices = cx.update_editor(|editor, cx| {
12903            let snapshot = editor.snapshot(cx).display_snapshot;
12904            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12905        });
12906
12907        assert_eq!(
12908            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12909            expected,
12910            "Active indent guide indices do not match"
12911        );
12912    }
12913
12914    let expected: Vec<_> = expected
12915        .into_iter()
12916        .map(|guide| MultiBufferIndentGuide {
12917            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12918            buffer: guide,
12919        })
12920        .collect();
12921
12922    assert_eq!(indent_guides, expected, "Indent guides do not match");
12923}
12924
12925fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12926    IndentGuide {
12927        buffer_id,
12928        start_row,
12929        end_row,
12930        depth,
12931        tab_size: 4,
12932        settings: IndentGuideSettings {
12933            enabled: true,
12934            line_width: 1,
12935            active_line_width: 1,
12936            ..Default::default()
12937        },
12938    }
12939}
12940
12941#[gpui::test]
12942async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12943    let (buffer_id, mut cx) = setup_indent_guides_editor(
12944        &"
12945    fn main() {
12946        let a = 1;
12947    }"
12948        .unindent(),
12949        cx,
12950    )
12951    .await;
12952
12953    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12954}
12955
12956#[gpui::test]
12957async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12958    let (buffer_id, mut cx) = setup_indent_guides_editor(
12959        &"
12960    fn main() {
12961        let a = 1;
12962        let b = 2;
12963    }"
12964        .unindent(),
12965        cx,
12966    )
12967    .await;
12968
12969    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12970}
12971
12972#[gpui::test]
12973async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12974    let (buffer_id, mut cx) = setup_indent_guides_editor(
12975        &"
12976    fn main() {
12977        let a = 1;
12978        if a == 3 {
12979            let b = 2;
12980        } else {
12981            let c = 3;
12982        }
12983    }"
12984        .unindent(),
12985        cx,
12986    )
12987    .await;
12988
12989    assert_indent_guides(
12990        0..8,
12991        vec![
12992            indent_guide(buffer_id, 1, 6, 0),
12993            indent_guide(buffer_id, 3, 3, 1),
12994            indent_guide(buffer_id, 5, 5, 1),
12995        ],
12996        None,
12997        &mut cx,
12998    );
12999}
13000
13001#[gpui::test]
13002async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13003    let (buffer_id, mut cx) = setup_indent_guides_editor(
13004        &"
13005    fn main() {
13006        let a = 1;
13007            let b = 2;
13008        let c = 3;
13009    }"
13010        .unindent(),
13011        cx,
13012    )
13013    .await;
13014
13015    assert_indent_guides(
13016        0..5,
13017        vec![
13018            indent_guide(buffer_id, 1, 3, 0),
13019            indent_guide(buffer_id, 2, 2, 1),
13020        ],
13021        None,
13022        &mut cx,
13023    );
13024}
13025
13026#[gpui::test]
13027async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13028    let (buffer_id, mut cx) = setup_indent_guides_editor(
13029        &"
13030        fn main() {
13031            let a = 1;
13032
13033            let c = 3;
13034        }"
13035        .unindent(),
13036        cx,
13037    )
13038    .await;
13039
13040    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13041}
13042
13043#[gpui::test]
13044async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13045    let (buffer_id, mut cx) = setup_indent_guides_editor(
13046        &"
13047        fn main() {
13048            let a = 1;
13049
13050            let c = 3;
13051
13052            if a == 3 {
13053                let b = 2;
13054            } else {
13055                let c = 3;
13056            }
13057        }"
13058        .unindent(),
13059        cx,
13060    )
13061    .await;
13062
13063    assert_indent_guides(
13064        0..11,
13065        vec![
13066            indent_guide(buffer_id, 1, 9, 0),
13067            indent_guide(buffer_id, 6, 6, 1),
13068            indent_guide(buffer_id, 8, 8, 1),
13069        ],
13070        None,
13071        &mut cx,
13072    );
13073}
13074
13075#[gpui::test]
13076async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13077    let (buffer_id, mut cx) = setup_indent_guides_editor(
13078        &"
13079        fn main() {
13080            let a = 1;
13081
13082            let c = 3;
13083
13084            if a == 3 {
13085                let b = 2;
13086            } else {
13087                let c = 3;
13088            }
13089        }"
13090        .unindent(),
13091        cx,
13092    )
13093    .await;
13094
13095    assert_indent_guides(
13096        1..11,
13097        vec![
13098            indent_guide(buffer_id, 1, 9, 0),
13099            indent_guide(buffer_id, 6, 6, 1),
13100            indent_guide(buffer_id, 8, 8, 1),
13101        ],
13102        None,
13103        &mut cx,
13104    );
13105}
13106
13107#[gpui::test]
13108async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13109    let (buffer_id, mut cx) = setup_indent_guides_editor(
13110        &"
13111        fn main() {
13112            let a = 1;
13113
13114            let c = 3;
13115
13116            if a == 3 {
13117                let b = 2;
13118            } else {
13119                let c = 3;
13120            }
13121        }"
13122        .unindent(),
13123        cx,
13124    )
13125    .await;
13126
13127    assert_indent_guides(
13128        1..10,
13129        vec![
13130            indent_guide(buffer_id, 1, 9, 0),
13131            indent_guide(buffer_id, 6, 6, 1),
13132            indent_guide(buffer_id, 8, 8, 1),
13133        ],
13134        None,
13135        &mut cx,
13136    );
13137}
13138
13139#[gpui::test]
13140async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13141    let (buffer_id, mut cx) = setup_indent_guides_editor(
13142        &"
13143        block1
13144            block2
13145                block3
13146                    block4
13147            block2
13148        block1
13149        block1"
13150            .unindent(),
13151        cx,
13152    )
13153    .await;
13154
13155    assert_indent_guides(
13156        1..10,
13157        vec![
13158            indent_guide(buffer_id, 1, 4, 0),
13159            indent_guide(buffer_id, 2, 3, 1),
13160            indent_guide(buffer_id, 3, 3, 2),
13161        ],
13162        None,
13163        &mut cx,
13164    );
13165}
13166
13167#[gpui::test]
13168async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13169    let (buffer_id, mut cx) = setup_indent_guides_editor(
13170        &"
13171        block1
13172            block2
13173                block3
13174
13175        block1
13176        block1"
13177            .unindent(),
13178        cx,
13179    )
13180    .await;
13181
13182    assert_indent_guides(
13183        0..6,
13184        vec![
13185            indent_guide(buffer_id, 1, 2, 0),
13186            indent_guide(buffer_id, 2, 2, 1),
13187        ],
13188        None,
13189        &mut cx,
13190    );
13191}
13192
13193#[gpui::test]
13194async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13195    let (buffer_id, mut cx) = setup_indent_guides_editor(
13196        &"
13197        block1
13198
13199
13200
13201            block2
13202        "
13203        .unindent(),
13204        cx,
13205    )
13206    .await;
13207
13208    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13209}
13210
13211#[gpui::test]
13212async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13213    let (buffer_id, mut cx) = setup_indent_guides_editor(
13214        &"
13215        def a:
13216        \tb = 3
13217        \tif True:
13218        \t\tc = 4
13219        \t\td = 5
13220        \tprint(b)
13221        "
13222        .unindent(),
13223        cx,
13224    )
13225    .await;
13226
13227    assert_indent_guides(
13228        0..6,
13229        vec![
13230            indent_guide(buffer_id, 1, 6, 0),
13231            indent_guide(buffer_id, 3, 4, 1),
13232        ],
13233        None,
13234        &mut cx,
13235    );
13236}
13237
13238#[gpui::test]
13239async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13240    let (buffer_id, mut cx) = setup_indent_guides_editor(
13241        &"
13242    fn main() {
13243        let a = 1;
13244    }"
13245        .unindent(),
13246        cx,
13247    )
13248    .await;
13249
13250    cx.update_editor(|editor, cx| {
13251        editor.change_selections(None, cx, |s| {
13252            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13253        });
13254    });
13255
13256    assert_indent_guides(
13257        0..3,
13258        vec![indent_guide(buffer_id, 1, 1, 0)],
13259        Some(vec![0]),
13260        &mut cx,
13261    );
13262}
13263
13264#[gpui::test]
13265async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13266    let (buffer_id, mut cx) = setup_indent_guides_editor(
13267        &"
13268    fn main() {
13269        if 1 == 2 {
13270            let a = 1;
13271        }
13272    }"
13273        .unindent(),
13274        cx,
13275    )
13276    .await;
13277
13278    cx.update_editor(|editor, cx| {
13279        editor.change_selections(None, cx, |s| {
13280            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13281        });
13282    });
13283
13284    assert_indent_guides(
13285        0..4,
13286        vec![
13287            indent_guide(buffer_id, 1, 3, 0),
13288            indent_guide(buffer_id, 2, 2, 1),
13289        ],
13290        Some(vec![1]),
13291        &mut cx,
13292    );
13293
13294    cx.update_editor(|editor, cx| {
13295        editor.change_selections(None, cx, |s| {
13296            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13297        });
13298    });
13299
13300    assert_indent_guides(
13301        0..4,
13302        vec![
13303            indent_guide(buffer_id, 1, 3, 0),
13304            indent_guide(buffer_id, 2, 2, 1),
13305        ],
13306        Some(vec![1]),
13307        &mut cx,
13308    );
13309
13310    cx.update_editor(|editor, cx| {
13311        editor.change_selections(None, cx, |s| {
13312            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13313        });
13314    });
13315
13316    assert_indent_guides(
13317        0..4,
13318        vec![
13319            indent_guide(buffer_id, 1, 3, 0),
13320            indent_guide(buffer_id, 2, 2, 1),
13321        ],
13322        Some(vec![0]),
13323        &mut cx,
13324    );
13325}
13326
13327#[gpui::test]
13328async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13329    let (buffer_id, mut cx) = setup_indent_guides_editor(
13330        &"
13331    fn main() {
13332        let a = 1;
13333
13334        let b = 2;
13335    }"
13336        .unindent(),
13337        cx,
13338    )
13339    .await;
13340
13341    cx.update_editor(|editor, cx| {
13342        editor.change_selections(None, cx, |s| {
13343            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13344        });
13345    });
13346
13347    assert_indent_guides(
13348        0..5,
13349        vec![indent_guide(buffer_id, 1, 3, 0)],
13350        Some(vec![0]),
13351        &mut cx,
13352    );
13353}
13354
13355#[gpui::test]
13356async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13357    let (buffer_id, mut cx) = setup_indent_guides_editor(
13358        &"
13359    def m:
13360        a = 1
13361        pass"
13362            .unindent(),
13363        cx,
13364    )
13365    .await;
13366
13367    cx.update_editor(|editor, cx| {
13368        editor.change_selections(None, cx, |s| {
13369            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13370        });
13371    });
13372
13373    assert_indent_guides(
13374        0..3,
13375        vec![indent_guide(buffer_id, 1, 2, 0)],
13376        Some(vec![0]),
13377        &mut cx,
13378    );
13379}
13380
13381#[gpui::test]
13382fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13383    init_test(cx, |_| {});
13384
13385    let editor = cx.add_window(|cx| {
13386        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13387        build_editor(buffer, cx)
13388    });
13389
13390    let render_args = Arc::new(Mutex::new(None));
13391    let snapshot = editor
13392        .update(cx, |editor, cx| {
13393            let snapshot = editor.buffer().read(cx).snapshot(cx);
13394            let range =
13395                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13396
13397            struct RenderArgs {
13398                row: MultiBufferRow,
13399                folded: bool,
13400                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13401            }
13402
13403            let crease = Crease::inline(
13404                range,
13405                FoldPlaceholder::test(),
13406                {
13407                    let toggle_callback = render_args.clone();
13408                    move |row, folded, callback, _cx| {
13409                        *toggle_callback.lock() = Some(RenderArgs {
13410                            row,
13411                            folded,
13412                            callback,
13413                        });
13414                        div()
13415                    }
13416                },
13417                |_row, _folded, _cx| div(),
13418            );
13419
13420            editor.insert_creases(Some(crease), cx);
13421            let snapshot = editor.snapshot(cx);
13422            let _div =
13423                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13424            snapshot
13425        })
13426        .unwrap();
13427
13428    let render_args = render_args.lock().take().unwrap();
13429    assert_eq!(render_args.row, MultiBufferRow(1));
13430    assert!(!render_args.folded);
13431    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13432
13433    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13434        .unwrap();
13435    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13436    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13437
13438    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13439        .unwrap();
13440    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13441    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13442}
13443
13444#[gpui::test]
13445async fn test_input_text(cx: &mut gpui::TestAppContext) {
13446    init_test(cx, |_| {});
13447    let mut cx = EditorTestContext::new(cx).await;
13448
13449    cx.set_state(
13450        &r#"ˇone
13451        two
13452
13453        three
13454        fourˇ
13455        five
13456
13457        siˇx"#
13458            .unindent(),
13459    );
13460
13461    cx.dispatch_action(HandleInput(String::new()));
13462    cx.assert_editor_state(
13463        &r#"ˇone
13464        two
13465
13466        three
13467        fourˇ
13468        five
13469
13470        siˇx"#
13471            .unindent(),
13472    );
13473
13474    cx.dispatch_action(HandleInput("AAAA".to_string()));
13475    cx.assert_editor_state(
13476        &r#"AAAAˇone
13477        two
13478
13479        three
13480        fourAAAAˇ
13481        five
13482
13483        siAAAAˇx"#
13484            .unindent(),
13485    );
13486}
13487
13488#[gpui::test]
13489async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13490    init_test(cx, |_| {});
13491
13492    let mut cx = EditorTestContext::new(cx).await;
13493    cx.set_state(
13494        r#"let foo = 1;
13495let foo = 2;
13496let foo = 3;
13497let fooˇ = 4;
13498let foo = 5;
13499let foo = 6;
13500let foo = 7;
13501let foo = 8;
13502let foo = 9;
13503let foo = 10;
13504let foo = 11;
13505let foo = 12;
13506let foo = 13;
13507let foo = 14;
13508let foo = 15;"#,
13509    );
13510
13511    cx.update_editor(|e, cx| {
13512        assert_eq!(
13513            e.next_scroll_position,
13514            NextScrollCursorCenterTopBottom::Center,
13515            "Default next scroll direction is center",
13516        );
13517
13518        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13519        assert_eq!(
13520            e.next_scroll_position,
13521            NextScrollCursorCenterTopBottom::Top,
13522            "After center, next scroll direction should be top",
13523        );
13524
13525        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13526        assert_eq!(
13527            e.next_scroll_position,
13528            NextScrollCursorCenterTopBottom::Bottom,
13529            "After top, next scroll direction should be bottom",
13530        );
13531
13532        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13533        assert_eq!(
13534            e.next_scroll_position,
13535            NextScrollCursorCenterTopBottom::Center,
13536            "After bottom, scrolling should start over",
13537        );
13538
13539        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13540        assert_eq!(
13541            e.next_scroll_position,
13542            NextScrollCursorCenterTopBottom::Top,
13543            "Scrolling continues if retriggered fast enough"
13544        );
13545    });
13546
13547    cx.executor()
13548        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13549    cx.executor().run_until_parked();
13550    cx.update_editor(|e, _| {
13551        assert_eq!(
13552            e.next_scroll_position,
13553            NextScrollCursorCenterTopBottom::Center,
13554            "If scrolling is not triggered fast enough, it should reset"
13555        );
13556    });
13557}
13558
13559#[gpui::test]
13560async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13561    init_test(cx, |_| {});
13562    let mut cx = EditorLspTestContext::new_rust(
13563        lsp::ServerCapabilities {
13564            definition_provider: Some(lsp::OneOf::Left(true)),
13565            references_provider: Some(lsp::OneOf::Left(true)),
13566            ..lsp::ServerCapabilities::default()
13567        },
13568        cx,
13569    )
13570    .await;
13571
13572    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13573        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13574            move |params, _| async move {
13575                if empty_go_to_definition {
13576                    Ok(None)
13577                } else {
13578                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13579                        uri: params.text_document_position_params.text_document.uri,
13580                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13581                    })))
13582                }
13583            },
13584        );
13585        let references =
13586            cx.lsp
13587                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13588                    Ok(Some(vec![lsp::Location {
13589                        uri: params.text_document_position.text_document.uri,
13590                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13591                    }]))
13592                });
13593        (go_to_definition, references)
13594    };
13595
13596    cx.set_state(
13597        &r#"fn one() {
13598            let mut a = ˇtwo();
13599        }
13600
13601        fn two() {}"#
13602            .unindent(),
13603    );
13604    set_up_lsp_handlers(false, &mut cx);
13605    let navigated = cx
13606        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13607        .await
13608        .expect("Failed to navigate to definition");
13609    assert_eq!(
13610        navigated,
13611        Navigated::Yes,
13612        "Should have navigated to definition from the GetDefinition response"
13613    );
13614    cx.assert_editor_state(
13615        &r#"fn one() {
13616            let mut a = two();
13617        }
13618
13619        fn «twoˇ»() {}"#
13620            .unindent(),
13621    );
13622
13623    let editors = cx.update_workspace(|workspace, cx| {
13624        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13625    });
13626    cx.update_editor(|_, test_editor_cx| {
13627        assert_eq!(
13628            editors.len(),
13629            1,
13630            "Initially, only one, test, editor should be open in the workspace"
13631        );
13632        assert_eq!(
13633            test_editor_cx.view(),
13634            editors.last().expect("Asserted len is 1")
13635        );
13636    });
13637
13638    set_up_lsp_handlers(true, &mut cx);
13639    let navigated = cx
13640        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13641        .await
13642        .expect("Failed to navigate to lookup references");
13643    assert_eq!(
13644        navigated,
13645        Navigated::Yes,
13646        "Should have navigated to references as a fallback after empty GoToDefinition response"
13647    );
13648    // We should not change the selections in the existing file,
13649    // if opening another milti buffer with the references
13650    cx.assert_editor_state(
13651        &r#"fn one() {
13652            let mut a = two();
13653        }
13654
13655        fn «twoˇ»() {}"#
13656            .unindent(),
13657    );
13658    let editors = cx.update_workspace(|workspace, cx| {
13659        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13660    });
13661    cx.update_editor(|_, test_editor_cx| {
13662        assert_eq!(
13663            editors.len(),
13664            2,
13665            "After falling back to references search, we open a new editor with the results"
13666        );
13667        let references_fallback_text = editors
13668            .into_iter()
13669            .find(|new_editor| new_editor != test_editor_cx.view())
13670            .expect("Should have one non-test editor now")
13671            .read(test_editor_cx)
13672            .text(test_editor_cx);
13673        assert_eq!(
13674            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13675            "Should use the range from the references response and not the GoToDefinition one"
13676        );
13677    });
13678}
13679
13680#[gpui::test]
13681async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13682    init_test(cx, |_| {});
13683
13684    let language = Arc::new(Language::new(
13685        LanguageConfig::default(),
13686        Some(tree_sitter_rust::LANGUAGE.into()),
13687    ));
13688
13689    let text = r#"
13690        #[cfg(test)]
13691        mod tests() {
13692            #[test]
13693            fn runnable_1() {
13694                let a = 1;
13695            }
13696
13697            #[test]
13698            fn runnable_2() {
13699                let a = 1;
13700                let b = 2;
13701            }
13702        }
13703    "#
13704    .unindent();
13705
13706    let fs = FakeFs::new(cx.executor());
13707    fs.insert_file("/file.rs", Default::default()).await;
13708
13709    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13710    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13711    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13712    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13713    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13714
13715    let editor = cx.new_view(|cx| {
13716        Editor::new(
13717            EditorMode::Full,
13718            multi_buffer,
13719            Some(project.clone()),
13720            true,
13721            cx,
13722        )
13723    });
13724
13725    editor.update(cx, |editor, cx| {
13726        editor.tasks.insert(
13727            (buffer.read(cx).remote_id(), 3),
13728            RunnableTasks {
13729                templates: vec![],
13730                offset: MultiBufferOffset(43),
13731                column: 0,
13732                extra_variables: HashMap::default(),
13733                context_range: BufferOffset(43)..BufferOffset(85),
13734            },
13735        );
13736        editor.tasks.insert(
13737            (buffer.read(cx).remote_id(), 8),
13738            RunnableTasks {
13739                templates: vec![],
13740                offset: MultiBufferOffset(86),
13741                column: 0,
13742                extra_variables: HashMap::default(),
13743                context_range: BufferOffset(86)..BufferOffset(191),
13744            },
13745        );
13746
13747        // Test finding task when cursor is inside function body
13748        editor.change_selections(None, cx, |s| {
13749            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13750        });
13751        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13752        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13753
13754        // Test finding task when cursor is on function name
13755        editor.change_selections(None, cx, |s| {
13756            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13757        });
13758        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13759        assert_eq!(row, 8, "Should find task when cursor is on function name");
13760    });
13761}
13762
13763fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13764    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13765    point..point
13766}
13767
13768fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13769    let (text, ranges) = marked_text_ranges(marked_text, true);
13770    assert_eq!(view.text(cx), text);
13771    assert_eq!(
13772        view.selections.ranges(cx),
13773        ranges,
13774        "Assert selections are {}",
13775        marked_text
13776    );
13777}
13778
13779pub fn handle_signature_help_request(
13780    cx: &mut EditorLspTestContext,
13781    mocked_response: lsp::SignatureHelp,
13782) -> impl Future<Output = ()> {
13783    let mut request =
13784        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13785            let mocked_response = mocked_response.clone();
13786            async move { Ok(Some(mocked_response)) }
13787        });
13788
13789    async move {
13790        request.next().await;
13791    }
13792}
13793
13794/// Handle completion request passing a marked string specifying where the completion
13795/// should be triggered from using '|' character, what range should be replaced, and what completions
13796/// should be returned using '<' and '>' to delimit the range
13797pub fn handle_completion_request(
13798    cx: &mut EditorLspTestContext,
13799    marked_string: &str,
13800    completions: Vec<&'static str>,
13801    counter: Arc<AtomicUsize>,
13802) -> impl Future<Output = ()> {
13803    let complete_from_marker: TextRangeMarker = '|'.into();
13804    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13805    let (_, mut marked_ranges) = marked_text_ranges_by(
13806        marked_string,
13807        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13808    );
13809
13810    let complete_from_position =
13811        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13812    let replace_range =
13813        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13814
13815    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13816        let completions = completions.clone();
13817        counter.fetch_add(1, atomic::Ordering::Release);
13818        async move {
13819            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13820            assert_eq!(
13821                params.text_document_position.position,
13822                complete_from_position
13823            );
13824            Ok(Some(lsp::CompletionResponse::Array(
13825                completions
13826                    .iter()
13827                    .map(|completion_text| lsp::CompletionItem {
13828                        label: completion_text.to_string(),
13829                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13830                            range: replace_range,
13831                            new_text: completion_text.to_string(),
13832                        })),
13833                        ..Default::default()
13834                    })
13835                    .collect(),
13836            )))
13837        }
13838    });
13839
13840    async move {
13841        request.next().await;
13842    }
13843}
13844
13845fn handle_resolve_completion_request(
13846    cx: &mut EditorLspTestContext,
13847    edits: Option<Vec<(&'static str, &'static str)>>,
13848) -> impl Future<Output = ()> {
13849    let edits = edits.map(|edits| {
13850        edits
13851            .iter()
13852            .map(|(marked_string, new_text)| {
13853                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13854                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13855                lsp::TextEdit::new(replace_range, new_text.to_string())
13856            })
13857            .collect::<Vec<_>>()
13858    });
13859
13860    let mut request =
13861        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13862            let edits = edits.clone();
13863            async move {
13864                Ok(lsp::CompletionItem {
13865                    additional_text_edits: edits,
13866                    ..Default::default()
13867                })
13868            }
13869        });
13870
13871    async move {
13872        request.next().await;
13873    }
13874}
13875
13876pub(crate) fn update_test_language_settings(
13877    cx: &mut TestAppContext,
13878    f: impl Fn(&mut AllLanguageSettingsContent),
13879) {
13880    cx.update(|cx| {
13881        SettingsStore::update_global(cx, |store, cx| {
13882            store.update_user_settings::<AllLanguageSettings>(cx, f);
13883        });
13884    });
13885}
13886
13887pub(crate) fn update_test_project_settings(
13888    cx: &mut TestAppContext,
13889    f: impl Fn(&mut ProjectSettings),
13890) {
13891    cx.update(|cx| {
13892        SettingsStore::update_global(cx, |store, cx| {
13893            store.update_user_settings::<ProjectSettings>(cx, f);
13894        });
13895    });
13896}
13897
13898pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13899    cx.update(|cx| {
13900        assets::Assets.load_test_fonts(cx);
13901        let store = SettingsStore::test(cx);
13902        cx.set_global(store);
13903        theme::init(theme::LoadThemes::JustBase, cx);
13904        release_channel::init(SemanticVersion::default(), cx);
13905        client::init_settings(cx);
13906        language::init(cx);
13907        Project::init_settings(cx);
13908        workspace::init_settings(cx);
13909        crate::init(cx);
13910    });
13911
13912    update_test_language_settings(cx, f);
13913}
13914
13915pub(crate) fn rust_lang() -> Arc<Language> {
13916    Arc::new(Language::new(
13917        LanguageConfig {
13918            name: "Rust".into(),
13919            matcher: LanguageMatcher {
13920                path_suffixes: vec!["rs".to_string()],
13921                ..Default::default()
13922            },
13923            ..Default::default()
13924        },
13925        Some(tree_sitter_rust::LANGUAGE.into()),
13926    ))
13927}
13928
13929#[track_caller]
13930fn assert_hunk_revert(
13931    not_reverted_text_with_selections: &str,
13932    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13933    expected_reverted_text_with_selections: &str,
13934    base_text: &str,
13935    cx: &mut EditorLspTestContext,
13936) {
13937    cx.set_state(not_reverted_text_with_selections);
13938    cx.update_editor(|editor, cx| {
13939        editor
13940            .buffer()
13941            .read(cx)
13942            .as_singleton()
13943            .unwrap()
13944            .update(cx, |buffer, cx| {
13945                buffer.set_diff_base(Some(base_text.into()), cx);
13946            });
13947    });
13948    cx.executor().run_until_parked();
13949
13950    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13951        let snapshot = editor.buffer().read(cx).snapshot(cx);
13952        let reverted_hunk_statuses = snapshot
13953            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13954            .map(|hunk| hunk_status(&hunk))
13955            .collect::<Vec<_>>();
13956
13957        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13958        reverted_hunk_statuses
13959    });
13960    cx.executor().run_until_parked();
13961    cx.assert_editor_state(expected_reverted_text_with_selections);
13962    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13963}