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