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