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.borrow_mut().is_none()));
 8346    cx.simulate_keystroke("s");
 8347    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().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.borrow_mut().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.borrow_mut().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.borrow_mut().as_ref()
 8472        {
 8473            assert_eq!(completion_menu_entries(&menu.entries), &["first", "last"]);
 8474        } else {
 8475            panic!("expected completion menu to be open");
 8476        }
 8477    });
 8478
 8479    cx.update_editor(|editor, cx| {
 8480        editor.move_page_down(&MovePageDown::default(), cx);
 8481        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8482        {
 8483            assert!(
 8484                menu.selected_item == 1,
 8485                "expected PageDown to select the last item from the context menu"
 8486            );
 8487        } else {
 8488            panic!("expected completion menu to stay open after PageDown");
 8489        }
 8490    });
 8491
 8492    cx.update_editor(|editor, cx| {
 8493        editor.move_page_up(&MovePageUp::default(), cx);
 8494        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8495        {
 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.borrow_mut().as_ref()
 8564        {
 8565            assert_eq!(
 8566                completion_menu_entries(&menu.entries),
 8567                &["r", "ret", "Range", "return"]
 8568            );
 8569        } else {
 8570            panic!("expected completion menu to be open");
 8571        }
 8572    });
 8573}
 8574
 8575#[gpui::test]
 8576async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8577    init_test(cx, |_| {});
 8578
 8579    let mut cx = EditorLspTestContext::new_rust(
 8580        lsp::ServerCapabilities {
 8581            completion_provider: Some(lsp::CompletionOptions {
 8582                trigger_characters: Some(vec![".".to_string()]),
 8583                resolve_provider: Some(true),
 8584                ..Default::default()
 8585            }),
 8586            ..Default::default()
 8587        },
 8588        cx,
 8589    )
 8590    .await;
 8591
 8592    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8593    cx.simulate_keystroke(".");
 8594    let completion_item = lsp::CompletionItem {
 8595        label: "Some".into(),
 8596        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8597        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8598        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8599            kind: lsp::MarkupKind::Markdown,
 8600            value: "```rust\nSome(2)\n```".to_string(),
 8601        })),
 8602        deprecated: Some(false),
 8603        sort_text: Some("Some".to_string()),
 8604        filter_text: Some("Some".to_string()),
 8605        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8606        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8607            range: lsp::Range {
 8608                start: lsp::Position {
 8609                    line: 0,
 8610                    character: 22,
 8611                },
 8612                end: lsp::Position {
 8613                    line: 0,
 8614                    character: 22,
 8615                },
 8616            },
 8617            new_text: "Some(2)".to_string(),
 8618        })),
 8619        additional_text_edits: Some(vec![lsp::TextEdit {
 8620            range: lsp::Range {
 8621                start: lsp::Position {
 8622                    line: 0,
 8623                    character: 20,
 8624                },
 8625                end: lsp::Position {
 8626                    line: 0,
 8627                    character: 22,
 8628                },
 8629            },
 8630            new_text: "".to_string(),
 8631        }]),
 8632        ..Default::default()
 8633    };
 8634
 8635    let closure_completion_item = completion_item.clone();
 8636    let counter = Arc::new(AtomicUsize::new(0));
 8637    let counter_clone = counter.clone();
 8638    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8639        let task_completion_item = closure_completion_item.clone();
 8640        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8641        async move {
 8642            Ok(Some(lsp::CompletionResponse::Array(vec![
 8643                task_completion_item,
 8644            ])))
 8645        }
 8646    });
 8647
 8648    cx.condition(|editor, _| editor.context_menu_visible())
 8649        .await;
 8650    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8651    assert!(request.next().await.is_some());
 8652    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8653
 8654    cx.simulate_keystroke("S");
 8655    cx.simulate_keystroke("o");
 8656    cx.simulate_keystroke("m");
 8657    cx.condition(|editor, _| editor.context_menu_visible())
 8658        .await;
 8659    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8660    assert!(request.next().await.is_some());
 8661    assert!(request.next().await.is_some());
 8662    assert!(request.next().await.is_some());
 8663    request.close();
 8664    assert!(request.next().await.is_none());
 8665    assert_eq!(
 8666        counter.load(atomic::Ordering::Acquire),
 8667        4,
 8668        "With the completions menu open, only one LSP request should happen per input"
 8669    );
 8670}
 8671
 8672#[gpui::test]
 8673async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8674    init_test(cx, |_| {});
 8675    let mut cx = EditorTestContext::new(cx).await;
 8676    let language = Arc::new(Language::new(
 8677        LanguageConfig {
 8678            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8679            ..Default::default()
 8680        },
 8681        Some(tree_sitter_rust::LANGUAGE.into()),
 8682    ));
 8683    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8684
 8685    // If multiple selections intersect a line, the line is only toggled once.
 8686    cx.set_state(indoc! {"
 8687        fn a() {
 8688            «//b();
 8689            ˇ»// «c();
 8690            //ˇ»  d();
 8691        }
 8692    "});
 8693
 8694    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8695
 8696    cx.assert_editor_state(indoc! {"
 8697        fn a() {
 8698            «b();
 8699            c();
 8700            ˇ» d();
 8701        }
 8702    "});
 8703
 8704    // The comment prefix is inserted at the same column for every line in a
 8705    // selection.
 8706    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8707
 8708    cx.assert_editor_state(indoc! {"
 8709        fn a() {
 8710            // «b();
 8711            // c();
 8712            ˇ»//  d();
 8713        }
 8714    "});
 8715
 8716    // If a selection ends at the beginning of a line, that line is not toggled.
 8717    cx.set_selections_state(indoc! {"
 8718        fn a() {
 8719            // b();
 8720            «// c();
 8721        ˇ»    //  d();
 8722        }
 8723    "});
 8724
 8725    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8726
 8727    cx.assert_editor_state(indoc! {"
 8728        fn a() {
 8729            // b();
 8730            «c();
 8731        ˇ»    //  d();
 8732        }
 8733    "});
 8734
 8735    // If a selection span a single line and is empty, the line is toggled.
 8736    cx.set_state(indoc! {"
 8737        fn a() {
 8738            a();
 8739            b();
 8740        ˇ
 8741        }
 8742    "});
 8743
 8744    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8745
 8746    cx.assert_editor_state(indoc! {"
 8747        fn a() {
 8748            a();
 8749            b();
 8750        //•ˇ
 8751        }
 8752    "});
 8753
 8754    // If a selection span multiple lines, empty lines are not toggled.
 8755    cx.set_state(indoc! {"
 8756        fn a() {
 8757            «a();
 8758
 8759            c();ˇ»
 8760        }
 8761    "});
 8762
 8763    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8764
 8765    cx.assert_editor_state(indoc! {"
 8766        fn a() {
 8767            // «a();
 8768
 8769            // c();ˇ»
 8770        }
 8771    "});
 8772
 8773    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8774    cx.set_state(indoc! {"
 8775        fn a() {
 8776            «// a();
 8777            /// b();
 8778            //! c();ˇ»
 8779        }
 8780    "});
 8781
 8782    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8783
 8784    cx.assert_editor_state(indoc! {"
 8785        fn a() {
 8786            «a();
 8787            b();
 8788            c();ˇ»
 8789        }
 8790    "});
 8791}
 8792
 8793#[gpui::test]
 8794async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8795    init_test(cx, |_| {});
 8796    let mut cx = EditorTestContext::new(cx).await;
 8797    let language = Arc::new(Language::new(
 8798        LanguageConfig {
 8799            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8800            ..Default::default()
 8801        },
 8802        Some(tree_sitter_rust::LANGUAGE.into()),
 8803    ));
 8804    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8805
 8806    let toggle_comments = &ToggleComments {
 8807        advance_downwards: false,
 8808        ignore_indent: true,
 8809    };
 8810
 8811    // If multiple selections intersect a line, the line is only toggled once.
 8812    cx.set_state(indoc! {"
 8813        fn a() {
 8814        //    «b();
 8815        //    c();
 8816        //    ˇ» d();
 8817        }
 8818    "});
 8819
 8820    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8821
 8822    cx.assert_editor_state(indoc! {"
 8823        fn a() {
 8824            «b();
 8825            c();
 8826            ˇ» d();
 8827        }
 8828    "});
 8829
 8830    // The comment prefix is inserted at the beginning of each line
 8831    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8832
 8833    cx.assert_editor_state(indoc! {"
 8834        fn a() {
 8835        //    «b();
 8836        //    c();
 8837        //    ˇ» d();
 8838        }
 8839    "});
 8840
 8841    // If a selection ends at the beginning of a line, that line is not toggled.
 8842    cx.set_selections_state(indoc! {"
 8843        fn a() {
 8844        //    b();
 8845        //    «c();
 8846        ˇ»//     d();
 8847        }
 8848    "});
 8849
 8850    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8851
 8852    cx.assert_editor_state(indoc! {"
 8853        fn a() {
 8854        //    b();
 8855            «c();
 8856        ˇ»//     d();
 8857        }
 8858    "});
 8859
 8860    // If a selection span a single line and is empty, the line is toggled.
 8861    cx.set_state(indoc! {"
 8862        fn a() {
 8863            a();
 8864            b();
 8865        ˇ
 8866        }
 8867    "});
 8868
 8869    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8870
 8871    cx.assert_editor_state(indoc! {"
 8872        fn a() {
 8873            a();
 8874            b();
 8875        //ˇ
 8876        }
 8877    "});
 8878
 8879    // If a selection span multiple lines, empty lines are not toggled.
 8880    cx.set_state(indoc! {"
 8881        fn a() {
 8882            «a();
 8883
 8884            c();ˇ»
 8885        }
 8886    "});
 8887
 8888    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8889
 8890    cx.assert_editor_state(indoc! {"
 8891        fn a() {
 8892        //    «a();
 8893
 8894        //    c();ˇ»
 8895        }
 8896    "});
 8897
 8898    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8899    cx.set_state(indoc! {"
 8900        fn a() {
 8901        //    «a();
 8902        ///    b();
 8903        //!    c();ˇ»
 8904        }
 8905    "});
 8906
 8907    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8908
 8909    cx.assert_editor_state(indoc! {"
 8910        fn a() {
 8911            «a();
 8912            b();
 8913            c();ˇ»
 8914        }
 8915    "});
 8916}
 8917
 8918#[gpui::test]
 8919async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8920    init_test(cx, |_| {});
 8921
 8922    let language = Arc::new(Language::new(
 8923        LanguageConfig {
 8924            line_comments: vec!["// ".into()],
 8925            ..Default::default()
 8926        },
 8927        Some(tree_sitter_rust::LANGUAGE.into()),
 8928    ));
 8929
 8930    let mut cx = EditorTestContext::new(cx).await;
 8931
 8932    cx.language_registry().add(language.clone());
 8933    cx.update_buffer(|buffer, cx| {
 8934        buffer.set_language(Some(language), cx);
 8935    });
 8936
 8937    let toggle_comments = &ToggleComments {
 8938        advance_downwards: true,
 8939        ignore_indent: false,
 8940    };
 8941
 8942    // Single cursor on one line -> advance
 8943    // Cursor moves horizontally 3 characters as well on non-blank line
 8944    cx.set_state(indoc!(
 8945        "fn a() {
 8946             ˇdog();
 8947             cat();
 8948        }"
 8949    ));
 8950    cx.update_editor(|editor, cx| {
 8951        editor.toggle_comments(toggle_comments, cx);
 8952    });
 8953    cx.assert_editor_state(indoc!(
 8954        "fn a() {
 8955             // dog();
 8956             catˇ();
 8957        }"
 8958    ));
 8959
 8960    // Single selection on one line -> don't advance
 8961    cx.set_state(indoc!(
 8962        "fn a() {
 8963             «dog()ˇ»;
 8964             cat();
 8965        }"
 8966    ));
 8967    cx.update_editor(|editor, cx| {
 8968        editor.toggle_comments(toggle_comments, cx);
 8969    });
 8970    cx.assert_editor_state(indoc!(
 8971        "fn a() {
 8972             // «dog()ˇ»;
 8973             cat();
 8974        }"
 8975    ));
 8976
 8977    // Multiple cursors on one line -> advance
 8978    cx.set_state(indoc!(
 8979        "fn a() {
 8980             ˇdˇog();
 8981             cat();
 8982        }"
 8983    ));
 8984    cx.update_editor(|editor, cx| {
 8985        editor.toggle_comments(toggle_comments, cx);
 8986    });
 8987    cx.assert_editor_state(indoc!(
 8988        "fn a() {
 8989             // dog();
 8990             catˇ(ˇ);
 8991        }"
 8992    ));
 8993
 8994    // Multiple cursors on one line, with selection -> don't advance
 8995    cx.set_state(indoc!(
 8996        "fn a() {
 8997             ˇdˇog«()ˇ»;
 8998             cat();
 8999        }"
 9000    ));
 9001    cx.update_editor(|editor, cx| {
 9002        editor.toggle_comments(toggle_comments, cx);
 9003    });
 9004    cx.assert_editor_state(indoc!(
 9005        "fn a() {
 9006             // ˇdˇog«()ˇ»;
 9007             cat();
 9008        }"
 9009    ));
 9010
 9011    // Single cursor on one line -> advance
 9012    // Cursor moves to column 0 on blank line
 9013    cx.set_state(indoc!(
 9014        "fn a() {
 9015             ˇdog();
 9016
 9017             cat();
 9018        }"
 9019    ));
 9020    cx.update_editor(|editor, cx| {
 9021        editor.toggle_comments(toggle_comments, cx);
 9022    });
 9023    cx.assert_editor_state(indoc!(
 9024        "fn a() {
 9025             // dog();
 9026        ˇ
 9027             cat();
 9028        }"
 9029    ));
 9030
 9031    // Single cursor on one line -> advance
 9032    // Cursor starts and ends at column 0
 9033    cx.set_state(indoc!(
 9034        "fn a() {
 9035         ˇ    dog();
 9036             cat();
 9037        }"
 9038    ));
 9039    cx.update_editor(|editor, cx| {
 9040        editor.toggle_comments(toggle_comments, cx);
 9041    });
 9042    cx.assert_editor_state(indoc!(
 9043        "fn a() {
 9044             // dog();
 9045         ˇ    cat();
 9046        }"
 9047    ));
 9048}
 9049
 9050#[gpui::test]
 9051async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9052    init_test(cx, |_| {});
 9053
 9054    let mut cx = EditorTestContext::new(cx).await;
 9055
 9056    let html_language = Arc::new(
 9057        Language::new(
 9058            LanguageConfig {
 9059                name: "HTML".into(),
 9060                block_comment: Some(("<!-- ".into(), " -->".into())),
 9061                ..Default::default()
 9062            },
 9063            Some(tree_sitter_html::language()),
 9064        )
 9065        .with_injection_query(
 9066            r#"
 9067            (script_element
 9068                (raw_text) @content
 9069                (#set! "language" "javascript"))
 9070            "#,
 9071        )
 9072        .unwrap(),
 9073    );
 9074
 9075    let javascript_language = Arc::new(Language::new(
 9076        LanguageConfig {
 9077            name: "JavaScript".into(),
 9078            line_comments: vec!["// ".into()],
 9079            ..Default::default()
 9080        },
 9081        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9082    ));
 9083
 9084    cx.language_registry().add(html_language.clone());
 9085    cx.language_registry().add(javascript_language.clone());
 9086    cx.update_buffer(|buffer, cx| {
 9087        buffer.set_language(Some(html_language), cx);
 9088    });
 9089
 9090    // Toggle comments for empty selections
 9091    cx.set_state(
 9092        &r#"
 9093            <p>A</p>ˇ
 9094            <p>B</p>ˇ
 9095            <p>C</p>ˇ
 9096        "#
 9097        .unindent(),
 9098    );
 9099    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9100    cx.assert_editor_state(
 9101        &r#"
 9102            <!-- <p>A</p>ˇ -->
 9103            <!-- <p>B</p>ˇ -->
 9104            <!-- <p>C</p>ˇ -->
 9105        "#
 9106        .unindent(),
 9107    );
 9108    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9109    cx.assert_editor_state(
 9110        &r#"
 9111            <p>A</p>ˇ
 9112            <p>B</p>ˇ
 9113            <p>C</p>ˇ
 9114        "#
 9115        .unindent(),
 9116    );
 9117
 9118    // Toggle comments for mixture of empty and non-empty selections, where
 9119    // multiple selections occupy a given line.
 9120    cx.set_state(
 9121        &r#"
 9122            <p>A«</p>
 9123            <p>ˇ»B</p>ˇ
 9124            <p>C«</p>
 9125            <p>ˇ»D</p>ˇ
 9126        "#
 9127        .unindent(),
 9128    );
 9129
 9130    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9131    cx.assert_editor_state(
 9132        &r#"
 9133            <!-- <p>A«</p>
 9134            <p>ˇ»B</p>ˇ -->
 9135            <!-- <p>C«</p>
 9136            <p>ˇ»D</p>ˇ -->
 9137        "#
 9138        .unindent(),
 9139    );
 9140    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9141    cx.assert_editor_state(
 9142        &r#"
 9143            <p>A«</p>
 9144            <p>ˇ»B</p>ˇ
 9145            <p>C«</p>
 9146            <p>ˇ»D</p>ˇ
 9147        "#
 9148        .unindent(),
 9149    );
 9150
 9151    // Toggle comments when different languages are active for different
 9152    // selections.
 9153    cx.set_state(
 9154        &r#"
 9155            ˇ<script>
 9156                ˇvar x = new Y();
 9157            ˇ</script>
 9158        "#
 9159        .unindent(),
 9160    );
 9161    cx.executor().run_until_parked();
 9162    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9163    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9164    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9165    cx.assert_editor_state(
 9166        &r#"
 9167            <!-- ˇ<script> -->
 9168                // ˇvar x = new Y();
 9169            // ˇ</script>
 9170        "#
 9171        .unindent(),
 9172    );
 9173}
 9174
 9175#[gpui::test]
 9176fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9177    init_test(cx, |_| {});
 9178
 9179    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9180    let multibuffer = cx.new_model(|cx| {
 9181        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9182        multibuffer.push_excerpts(
 9183            buffer.clone(),
 9184            [
 9185                ExcerptRange {
 9186                    context: Point::new(0, 0)..Point::new(0, 4),
 9187                    primary: None,
 9188                },
 9189                ExcerptRange {
 9190                    context: Point::new(1, 0)..Point::new(1, 4),
 9191                    primary: None,
 9192                },
 9193            ],
 9194            cx,
 9195        );
 9196        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9197        multibuffer
 9198    });
 9199
 9200    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9201    view.update(cx, |view, cx| {
 9202        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9203        view.change_selections(None, cx, |s| {
 9204            s.select_ranges([
 9205                Point::new(0, 0)..Point::new(0, 0),
 9206                Point::new(1, 0)..Point::new(1, 0),
 9207            ])
 9208        });
 9209
 9210        view.handle_input("X", cx);
 9211        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9212        assert_eq!(
 9213            view.selections.ranges(cx),
 9214            [
 9215                Point::new(0, 1)..Point::new(0, 1),
 9216                Point::new(1, 1)..Point::new(1, 1),
 9217            ]
 9218        );
 9219
 9220        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9221        view.change_selections(None, cx, |s| {
 9222            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9223        });
 9224        view.backspace(&Default::default(), cx);
 9225        assert_eq!(view.text(cx), "Xa\nbbb");
 9226        assert_eq!(
 9227            view.selections.ranges(cx),
 9228            [Point::new(1, 0)..Point::new(1, 0)]
 9229        );
 9230
 9231        view.change_selections(None, cx, |s| {
 9232            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9233        });
 9234        view.backspace(&Default::default(), cx);
 9235        assert_eq!(view.text(cx), "X\nbb");
 9236        assert_eq!(
 9237            view.selections.ranges(cx),
 9238            [Point::new(0, 1)..Point::new(0, 1)]
 9239        );
 9240    });
 9241}
 9242
 9243#[gpui::test]
 9244fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9245    init_test(cx, |_| {});
 9246
 9247    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9248    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9249        indoc! {"
 9250            [aaaa
 9251            (bbbb]
 9252            cccc)",
 9253        },
 9254        markers.clone(),
 9255    );
 9256    let excerpt_ranges = markers.into_iter().map(|marker| {
 9257        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9258        ExcerptRange {
 9259            context,
 9260            primary: None,
 9261        }
 9262    });
 9263    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9264    let multibuffer = cx.new_model(|cx| {
 9265        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9266        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9267        multibuffer
 9268    });
 9269
 9270    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9271    view.update(cx, |view, cx| {
 9272        let (expected_text, selection_ranges) = marked_text_ranges(
 9273            indoc! {"
 9274                aaaa
 9275                bˇbbb
 9276                bˇbbˇb
 9277                cccc"
 9278            },
 9279            true,
 9280        );
 9281        assert_eq!(view.text(cx), expected_text);
 9282        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9283
 9284        view.handle_input("X", cx);
 9285
 9286        let (expected_text, expected_selections) = marked_text_ranges(
 9287            indoc! {"
 9288                aaaa
 9289                bXˇbbXb
 9290                bXˇbbXˇb
 9291                cccc"
 9292            },
 9293            false,
 9294        );
 9295        assert_eq!(view.text(cx), expected_text);
 9296        assert_eq!(view.selections.ranges(cx), expected_selections);
 9297
 9298        view.newline(&Newline, cx);
 9299        let (expected_text, expected_selections) = marked_text_ranges(
 9300            indoc! {"
 9301                aaaa
 9302                bX
 9303                ˇbbX
 9304                b
 9305                bX
 9306                ˇbbX
 9307                ˇb
 9308                cccc"
 9309            },
 9310            false,
 9311        );
 9312        assert_eq!(view.text(cx), expected_text);
 9313        assert_eq!(view.selections.ranges(cx), expected_selections);
 9314    });
 9315}
 9316
 9317#[gpui::test]
 9318fn test_refresh_selections(cx: &mut TestAppContext) {
 9319    init_test(cx, |_| {});
 9320
 9321    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9322    let mut excerpt1_id = None;
 9323    let multibuffer = cx.new_model(|cx| {
 9324        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9325        excerpt1_id = multibuffer
 9326            .push_excerpts(
 9327                buffer.clone(),
 9328                [
 9329                    ExcerptRange {
 9330                        context: Point::new(0, 0)..Point::new(1, 4),
 9331                        primary: None,
 9332                    },
 9333                    ExcerptRange {
 9334                        context: Point::new(1, 0)..Point::new(2, 4),
 9335                        primary: None,
 9336                    },
 9337                ],
 9338                cx,
 9339            )
 9340            .into_iter()
 9341            .next();
 9342        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9343        multibuffer
 9344    });
 9345
 9346    let editor = cx.add_window(|cx| {
 9347        let mut editor = build_editor(multibuffer.clone(), cx);
 9348        let snapshot = editor.snapshot(cx);
 9349        editor.change_selections(None, cx, |s| {
 9350            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9351        });
 9352        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9353        assert_eq!(
 9354            editor.selections.ranges(cx),
 9355            [
 9356                Point::new(1, 3)..Point::new(1, 3),
 9357                Point::new(2, 1)..Point::new(2, 1),
 9358            ]
 9359        );
 9360        editor
 9361    });
 9362
 9363    // Refreshing selections is a no-op when excerpts haven't changed.
 9364    _ = editor.update(cx, |editor, cx| {
 9365        editor.change_selections(None, cx, |s| s.refresh());
 9366        assert_eq!(
 9367            editor.selections.ranges(cx),
 9368            [
 9369                Point::new(1, 3)..Point::new(1, 3),
 9370                Point::new(2, 1)..Point::new(2, 1),
 9371            ]
 9372        );
 9373    });
 9374
 9375    multibuffer.update(cx, |multibuffer, cx| {
 9376        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9377    });
 9378    _ = editor.update(cx, |editor, cx| {
 9379        // Removing an excerpt causes the first selection to become degenerate.
 9380        assert_eq!(
 9381            editor.selections.ranges(cx),
 9382            [
 9383                Point::new(0, 0)..Point::new(0, 0),
 9384                Point::new(0, 1)..Point::new(0, 1)
 9385            ]
 9386        );
 9387
 9388        // Refreshing selections will relocate the first selection to the original buffer
 9389        // location.
 9390        editor.change_selections(None, cx, |s| s.refresh());
 9391        assert_eq!(
 9392            editor.selections.ranges(cx),
 9393            [
 9394                Point::new(0, 1)..Point::new(0, 1),
 9395                Point::new(0, 3)..Point::new(0, 3)
 9396            ]
 9397        );
 9398        assert!(editor.selections.pending_anchor().is_some());
 9399    });
 9400}
 9401
 9402#[gpui::test]
 9403fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9404    init_test(cx, |_| {});
 9405
 9406    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9407    let mut excerpt1_id = None;
 9408    let multibuffer = cx.new_model(|cx| {
 9409        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9410        excerpt1_id = multibuffer
 9411            .push_excerpts(
 9412                buffer.clone(),
 9413                [
 9414                    ExcerptRange {
 9415                        context: Point::new(0, 0)..Point::new(1, 4),
 9416                        primary: None,
 9417                    },
 9418                    ExcerptRange {
 9419                        context: Point::new(1, 0)..Point::new(2, 4),
 9420                        primary: None,
 9421                    },
 9422                ],
 9423                cx,
 9424            )
 9425            .into_iter()
 9426            .next();
 9427        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9428        multibuffer
 9429    });
 9430
 9431    let editor = cx.add_window(|cx| {
 9432        let mut editor = build_editor(multibuffer.clone(), cx);
 9433        let snapshot = editor.snapshot(cx);
 9434        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9435        assert_eq!(
 9436            editor.selections.ranges(cx),
 9437            [Point::new(1, 3)..Point::new(1, 3)]
 9438        );
 9439        editor
 9440    });
 9441
 9442    multibuffer.update(cx, |multibuffer, cx| {
 9443        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9444    });
 9445    _ = editor.update(cx, |editor, cx| {
 9446        assert_eq!(
 9447            editor.selections.ranges(cx),
 9448            [Point::new(0, 0)..Point::new(0, 0)]
 9449        );
 9450
 9451        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9452        editor.change_selections(None, cx, |s| s.refresh());
 9453        assert_eq!(
 9454            editor.selections.ranges(cx),
 9455            [Point::new(0, 3)..Point::new(0, 3)]
 9456        );
 9457        assert!(editor.selections.pending_anchor().is_some());
 9458    });
 9459}
 9460
 9461#[gpui::test]
 9462async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9463    init_test(cx, |_| {});
 9464
 9465    let language = Arc::new(
 9466        Language::new(
 9467            LanguageConfig {
 9468                brackets: BracketPairConfig {
 9469                    pairs: vec![
 9470                        BracketPair {
 9471                            start: "{".to_string(),
 9472                            end: "}".to_string(),
 9473                            close: true,
 9474                            surround: true,
 9475                            newline: true,
 9476                        },
 9477                        BracketPair {
 9478                            start: "/* ".to_string(),
 9479                            end: " */".to_string(),
 9480                            close: true,
 9481                            surround: true,
 9482                            newline: true,
 9483                        },
 9484                    ],
 9485                    ..Default::default()
 9486                },
 9487                ..Default::default()
 9488            },
 9489            Some(tree_sitter_rust::LANGUAGE.into()),
 9490        )
 9491        .with_indents_query("")
 9492        .unwrap(),
 9493    );
 9494
 9495    let text = concat!(
 9496        "{   }\n",     //
 9497        "  x\n",       //
 9498        "  /*   */\n", //
 9499        "x\n",         //
 9500        "{{} }\n",     //
 9501    );
 9502
 9503    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9504    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9505    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9506    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9507        .await;
 9508
 9509    view.update(cx, |view, cx| {
 9510        view.change_selections(None, cx, |s| {
 9511            s.select_display_ranges([
 9512                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9513                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9514                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9515            ])
 9516        });
 9517        view.newline(&Newline, cx);
 9518
 9519        assert_eq!(
 9520            view.buffer().read(cx).read(cx).text(),
 9521            concat!(
 9522                "{ \n",    // Suppress rustfmt
 9523                "\n",      //
 9524                "}\n",     //
 9525                "  x\n",   //
 9526                "  /* \n", //
 9527                "  \n",    //
 9528                "  */\n",  //
 9529                "x\n",     //
 9530                "{{} \n",  //
 9531                "}\n",     //
 9532            )
 9533        );
 9534    });
 9535}
 9536
 9537#[gpui::test]
 9538fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9539    init_test(cx, |_| {});
 9540
 9541    let editor = cx.add_window(|cx| {
 9542        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9543        build_editor(buffer.clone(), cx)
 9544    });
 9545
 9546    _ = editor.update(cx, |editor, cx| {
 9547        struct Type1;
 9548        struct Type2;
 9549
 9550        let buffer = editor.buffer.read(cx).snapshot(cx);
 9551
 9552        let anchor_range =
 9553            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9554
 9555        editor.highlight_background::<Type1>(
 9556            &[
 9557                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9558                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9559                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9560                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9561            ],
 9562            |_| Hsla::red(),
 9563            cx,
 9564        );
 9565        editor.highlight_background::<Type2>(
 9566            &[
 9567                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9568                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9569                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9570                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9571            ],
 9572            |_| Hsla::green(),
 9573            cx,
 9574        );
 9575
 9576        let snapshot = editor.snapshot(cx);
 9577        let mut highlighted_ranges = editor.background_highlights_in_range(
 9578            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9579            &snapshot,
 9580            cx.theme().colors(),
 9581        );
 9582        // Enforce a consistent ordering based on color without relying on the ordering of the
 9583        // highlight's `TypeId` which is non-executor.
 9584        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9585        assert_eq!(
 9586            highlighted_ranges,
 9587            &[
 9588                (
 9589                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9590                    Hsla::red(),
 9591                ),
 9592                (
 9593                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9594                    Hsla::red(),
 9595                ),
 9596                (
 9597                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9598                    Hsla::green(),
 9599                ),
 9600                (
 9601                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9602                    Hsla::green(),
 9603                ),
 9604            ]
 9605        );
 9606        assert_eq!(
 9607            editor.background_highlights_in_range(
 9608                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9609                &snapshot,
 9610                cx.theme().colors(),
 9611            ),
 9612            &[(
 9613                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9614                Hsla::red(),
 9615            )]
 9616        );
 9617    });
 9618}
 9619
 9620#[gpui::test]
 9621async fn test_following(cx: &mut gpui::TestAppContext) {
 9622    init_test(cx, |_| {});
 9623
 9624    let fs = FakeFs::new(cx.executor());
 9625    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9626
 9627    let buffer = project.update(cx, |project, cx| {
 9628        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9629        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9630    });
 9631    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9632    let follower = cx.update(|cx| {
 9633        cx.open_window(
 9634            WindowOptions {
 9635                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9636                    gpui::Point::new(px(0.), px(0.)),
 9637                    gpui::Point::new(px(10.), px(80.)),
 9638                ))),
 9639                ..Default::default()
 9640            },
 9641            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9642        )
 9643        .unwrap()
 9644    });
 9645
 9646    let is_still_following = Rc::new(RefCell::new(true));
 9647    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9648    let pending_update = Rc::new(RefCell::new(None));
 9649    _ = follower.update(cx, {
 9650        let update = pending_update.clone();
 9651        let is_still_following = is_still_following.clone();
 9652        let follower_edit_event_count = follower_edit_event_count.clone();
 9653        |_, cx| {
 9654            cx.subscribe(
 9655                &leader.root_view(cx).unwrap(),
 9656                move |_, leader, event, cx| {
 9657                    leader
 9658                        .read(cx)
 9659                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9660                },
 9661            )
 9662            .detach();
 9663
 9664            cx.subscribe(
 9665                &follower.root_view(cx).unwrap(),
 9666                move |_, _, event: &EditorEvent, _cx| {
 9667                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9668                        *is_still_following.borrow_mut() = false;
 9669                    }
 9670
 9671                    if let EditorEvent::BufferEdited = event {
 9672                        *follower_edit_event_count.borrow_mut() += 1;
 9673                    }
 9674                },
 9675            )
 9676            .detach();
 9677        }
 9678    });
 9679
 9680    // Update the selections only
 9681    _ = leader.update(cx, |leader, cx| {
 9682        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9683    });
 9684    follower
 9685        .update(cx, |follower, cx| {
 9686            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9687        })
 9688        .unwrap()
 9689        .await
 9690        .unwrap();
 9691    _ = follower.update(cx, |follower, cx| {
 9692        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9693    });
 9694    assert!(*is_still_following.borrow());
 9695    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9696
 9697    // Update the scroll position only
 9698    _ = leader.update(cx, |leader, cx| {
 9699        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9700    });
 9701    follower
 9702        .update(cx, |follower, cx| {
 9703            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9704        })
 9705        .unwrap()
 9706        .await
 9707        .unwrap();
 9708    assert_eq!(
 9709        follower
 9710            .update(cx, |follower, cx| follower.scroll_position(cx))
 9711            .unwrap(),
 9712        gpui::Point::new(1.5, 3.5)
 9713    );
 9714    assert!(*is_still_following.borrow());
 9715    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9716
 9717    // Update the selections and scroll position. The follower's scroll position is updated
 9718    // via autoscroll, not via the leader's exact scroll position.
 9719    _ = leader.update(cx, |leader, cx| {
 9720        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9721        leader.request_autoscroll(Autoscroll::newest(), cx);
 9722        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9723    });
 9724    follower
 9725        .update(cx, |follower, cx| {
 9726            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9727        })
 9728        .unwrap()
 9729        .await
 9730        .unwrap();
 9731    _ = follower.update(cx, |follower, cx| {
 9732        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9733        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9734    });
 9735    assert!(*is_still_following.borrow());
 9736
 9737    // Creating a pending selection that precedes another selection
 9738    _ = leader.update(cx, |leader, cx| {
 9739        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9740        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9741    });
 9742    follower
 9743        .update(cx, |follower, cx| {
 9744            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9745        })
 9746        .unwrap()
 9747        .await
 9748        .unwrap();
 9749    _ = follower.update(cx, |follower, cx| {
 9750        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9751    });
 9752    assert!(*is_still_following.borrow());
 9753
 9754    // Extend the pending selection so that it surrounds another selection
 9755    _ = leader.update(cx, |leader, cx| {
 9756        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9757    });
 9758    follower
 9759        .update(cx, |follower, cx| {
 9760            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9761        })
 9762        .unwrap()
 9763        .await
 9764        .unwrap();
 9765    _ = follower.update(cx, |follower, cx| {
 9766        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9767    });
 9768
 9769    // Scrolling locally breaks the follow
 9770    _ = follower.update(cx, |follower, cx| {
 9771        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9772        follower.set_scroll_anchor(
 9773            ScrollAnchor {
 9774                anchor: top_anchor,
 9775                offset: gpui::Point::new(0.0, 0.5),
 9776            },
 9777            cx,
 9778        );
 9779    });
 9780    assert!(!(*is_still_following.borrow()));
 9781}
 9782
 9783#[gpui::test]
 9784async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9785    init_test(cx, |_| {});
 9786
 9787    let fs = FakeFs::new(cx.executor());
 9788    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9789    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9790    let pane = workspace
 9791        .update(cx, |workspace, _| workspace.active_pane().clone())
 9792        .unwrap();
 9793
 9794    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9795
 9796    let leader = pane.update(cx, |_, cx| {
 9797        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9798        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9799    });
 9800
 9801    // Start following the editor when it has no excerpts.
 9802    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9803    let follower_1 = cx
 9804        .update_window(*workspace.deref(), |_, cx| {
 9805            Editor::from_state_proto(
 9806                workspace.root_view(cx).unwrap(),
 9807                ViewId {
 9808                    creator: Default::default(),
 9809                    id: 0,
 9810                },
 9811                &mut state_message,
 9812                cx,
 9813            )
 9814        })
 9815        .unwrap()
 9816        .unwrap()
 9817        .await
 9818        .unwrap();
 9819
 9820    let update_message = Rc::new(RefCell::new(None));
 9821    follower_1.update(cx, {
 9822        let update = update_message.clone();
 9823        |_, cx| {
 9824            cx.subscribe(&leader, move |_, leader, event, cx| {
 9825                leader
 9826                    .read(cx)
 9827                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9828            })
 9829            .detach();
 9830        }
 9831    });
 9832
 9833    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9834        (
 9835            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9836            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9837        )
 9838    });
 9839
 9840    // Insert some excerpts.
 9841    leader.update(cx, |leader, cx| {
 9842        leader.buffer.update(cx, |multibuffer, cx| {
 9843            let excerpt_ids = multibuffer.push_excerpts(
 9844                buffer_1.clone(),
 9845                [
 9846                    ExcerptRange {
 9847                        context: 1..6,
 9848                        primary: None,
 9849                    },
 9850                    ExcerptRange {
 9851                        context: 12..15,
 9852                        primary: None,
 9853                    },
 9854                    ExcerptRange {
 9855                        context: 0..3,
 9856                        primary: None,
 9857                    },
 9858                ],
 9859                cx,
 9860            );
 9861            multibuffer.insert_excerpts_after(
 9862                excerpt_ids[0],
 9863                buffer_2.clone(),
 9864                [
 9865                    ExcerptRange {
 9866                        context: 8..12,
 9867                        primary: None,
 9868                    },
 9869                    ExcerptRange {
 9870                        context: 0..6,
 9871                        primary: None,
 9872                    },
 9873                ],
 9874                cx,
 9875            );
 9876        });
 9877    });
 9878
 9879    // Apply the update of adding the excerpts.
 9880    follower_1
 9881        .update(cx, |follower, cx| {
 9882            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9883        })
 9884        .await
 9885        .unwrap();
 9886    assert_eq!(
 9887        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9888        leader.update(cx, |editor, cx| editor.text(cx))
 9889    );
 9890    update_message.borrow_mut().take();
 9891
 9892    // Start following separately after it already has excerpts.
 9893    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9894    let follower_2 = cx
 9895        .update_window(*workspace.deref(), |_, cx| {
 9896            Editor::from_state_proto(
 9897                workspace.root_view(cx).unwrap().clone(),
 9898                ViewId {
 9899                    creator: Default::default(),
 9900                    id: 0,
 9901                },
 9902                &mut state_message,
 9903                cx,
 9904            )
 9905        })
 9906        .unwrap()
 9907        .unwrap()
 9908        .await
 9909        .unwrap();
 9910    assert_eq!(
 9911        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9912        leader.update(cx, |editor, cx| editor.text(cx))
 9913    );
 9914
 9915    // Remove some excerpts.
 9916    leader.update(cx, |leader, cx| {
 9917        leader.buffer.update(cx, |multibuffer, cx| {
 9918            let excerpt_ids = multibuffer.excerpt_ids();
 9919            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9920            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9921        });
 9922    });
 9923
 9924    // Apply the update of removing the excerpts.
 9925    follower_1
 9926        .update(cx, |follower, cx| {
 9927            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9928        })
 9929        .await
 9930        .unwrap();
 9931    follower_2
 9932        .update(cx, |follower, cx| {
 9933            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9934        })
 9935        .await
 9936        .unwrap();
 9937    update_message.borrow_mut().take();
 9938    assert_eq!(
 9939        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9940        leader.update(cx, |editor, cx| editor.text(cx))
 9941    );
 9942}
 9943
 9944#[gpui::test]
 9945async fn go_to_prev_overlapping_diagnostic(
 9946    executor: BackgroundExecutor,
 9947    cx: &mut gpui::TestAppContext,
 9948) {
 9949    init_test(cx, |_| {});
 9950
 9951    let mut cx = EditorTestContext::new(cx).await;
 9952    let lsp_store =
 9953        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
 9954
 9955    cx.set_state(indoc! {"
 9956        ˇfn func(abc def: i32) -> u32 {
 9957        }
 9958    "});
 9959
 9960    cx.update(|cx| {
 9961        lsp_store.update(cx, |lsp_store, cx| {
 9962            lsp_store
 9963                .update_diagnostics(
 9964                    LanguageServerId(0),
 9965                    lsp::PublishDiagnosticsParams {
 9966                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9967                        version: None,
 9968                        diagnostics: vec![
 9969                            lsp::Diagnostic {
 9970                                range: lsp::Range::new(
 9971                                    lsp::Position::new(0, 11),
 9972                                    lsp::Position::new(0, 12),
 9973                                ),
 9974                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9975                                ..Default::default()
 9976                            },
 9977                            lsp::Diagnostic {
 9978                                range: lsp::Range::new(
 9979                                    lsp::Position::new(0, 12),
 9980                                    lsp::Position::new(0, 15),
 9981                                ),
 9982                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9983                                ..Default::default()
 9984                            },
 9985                            lsp::Diagnostic {
 9986                                range: lsp::Range::new(
 9987                                    lsp::Position::new(0, 25),
 9988                                    lsp::Position::new(0, 28),
 9989                                ),
 9990                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9991                                ..Default::default()
 9992                            },
 9993                        ],
 9994                    },
 9995                    &[],
 9996                    cx,
 9997                )
 9998                .unwrap()
 9999        });
10000    });
10001
10002    executor.run_until_parked();
10003
10004    cx.update_editor(|editor, cx| {
10005        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10006    });
10007
10008    cx.assert_editor_state(indoc! {"
10009        fn func(abc def: i32) -> ˇu32 {
10010        }
10011    "});
10012
10013    cx.update_editor(|editor, cx| {
10014        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10015    });
10016
10017    cx.assert_editor_state(indoc! {"
10018        fn func(abc ˇdef: i32) -> u32 {
10019        }
10020    "});
10021
10022    cx.update_editor(|editor, cx| {
10023        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10024    });
10025
10026    cx.assert_editor_state(indoc! {"
10027        fn func(abcˇ def: i32) -> u32 {
10028        }
10029    "});
10030
10031    cx.update_editor(|editor, cx| {
10032        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10033    });
10034
10035    cx.assert_editor_state(indoc! {"
10036        fn func(abc def: i32) -> ˇu32 {
10037        }
10038    "});
10039}
10040
10041#[gpui::test]
10042async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10043    init_test(cx, |_| {});
10044
10045    let mut cx = EditorTestContext::new(cx).await;
10046
10047    cx.set_state(indoc! {"
10048        fn func(abˇc def: i32) -> u32 {
10049        }
10050    "});
10051    let lsp_store =
10052        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10053
10054    cx.update(|cx| {
10055        lsp_store.update(cx, |lsp_store, cx| {
10056            lsp_store.update_diagnostics(
10057                LanguageServerId(0),
10058                lsp::PublishDiagnosticsParams {
10059                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10060                    version: None,
10061                    diagnostics: vec![lsp::Diagnostic {
10062                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10063                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10064                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10065                        ..Default::default()
10066                    }],
10067                },
10068                &[],
10069                cx,
10070            )
10071        })
10072    }).unwrap();
10073    cx.run_until_parked();
10074    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10075    cx.run_until_parked();
10076    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10077}
10078
10079#[gpui::test]
10080async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10081    init_test(cx, |_| {});
10082
10083    let mut cx = EditorTestContext::new(cx).await;
10084
10085    let diff_base = r#"
10086        use some::mod;
10087
10088        const A: u32 = 42;
10089
10090        fn main() {
10091            println!("hello");
10092
10093            println!("world");
10094        }
10095        "#
10096    .unindent();
10097
10098    // Edits are modified, removed, modified, added
10099    cx.set_state(
10100        &r#"
10101        use some::modified;
10102
10103        ˇ
10104        fn main() {
10105            println!("hello there");
10106
10107            println!("around the");
10108            println!("world");
10109        }
10110        "#
10111        .unindent(),
10112    );
10113
10114    cx.set_diff_base(&diff_base);
10115    executor.run_until_parked();
10116
10117    cx.update_editor(|editor, cx| {
10118        //Wrap around the bottom of the buffer
10119        for _ in 0..3 {
10120            editor.go_to_next_hunk(&GoToHunk, cx);
10121        }
10122    });
10123
10124    cx.assert_editor_state(
10125        &r#"
10126        ˇuse some::modified;
10127
10128
10129        fn main() {
10130            println!("hello there");
10131
10132            println!("around the");
10133            println!("world");
10134        }
10135        "#
10136        .unindent(),
10137    );
10138
10139    cx.update_editor(|editor, cx| {
10140        //Wrap around the top of the buffer
10141        for _ in 0..2 {
10142            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10143        }
10144    });
10145
10146    cx.assert_editor_state(
10147        &r#"
10148        use some::modified;
10149
10150
10151        fn main() {
10152        ˇ    println!("hello there");
10153
10154            println!("around the");
10155            println!("world");
10156        }
10157        "#
10158        .unindent(),
10159    );
10160
10161    cx.update_editor(|editor, cx| {
10162        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10163    });
10164
10165    cx.assert_editor_state(
10166        &r#"
10167        use some::modified;
10168
10169        ˇ
10170        fn main() {
10171            println!("hello there");
10172
10173            println!("around the");
10174            println!("world");
10175        }
10176        "#
10177        .unindent(),
10178    );
10179
10180    cx.update_editor(|editor, cx| {
10181        for _ in 0..3 {
10182            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10183        }
10184    });
10185
10186    cx.assert_editor_state(
10187        &r#"
10188        use some::modified;
10189
10190
10191        fn main() {
10192        ˇ    println!("hello there");
10193
10194            println!("around the");
10195            println!("world");
10196        }
10197        "#
10198        .unindent(),
10199    );
10200
10201    cx.update_editor(|editor, cx| {
10202        editor.fold(&Fold, cx);
10203
10204        //Make sure that the fold only gets one hunk
10205        for _ in 0..4 {
10206            editor.go_to_next_hunk(&GoToHunk, cx);
10207        }
10208    });
10209
10210    cx.assert_editor_state(
10211        &r#"
10212        ˇuse some::modified;
10213
10214
10215        fn main() {
10216            println!("hello there");
10217
10218            println!("around the");
10219            println!("world");
10220        }
10221        "#
10222        .unindent(),
10223    );
10224}
10225
10226#[test]
10227fn test_split_words() {
10228    fn split(text: &str) -> Vec<&str> {
10229        split_words(text).collect()
10230    }
10231
10232    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10233    assert_eq!(split("hello_world"), &["hello_", "world"]);
10234    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10235    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10236    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10237    assert_eq!(split("helloworld"), &["helloworld"]);
10238
10239    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10240}
10241
10242#[gpui::test]
10243async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10244    init_test(cx, |_| {});
10245
10246    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10247    let mut assert = |before, after| {
10248        let _state_context = cx.set_state(before);
10249        cx.update_editor(|editor, cx| {
10250            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10251        });
10252        cx.assert_editor_state(after);
10253    };
10254
10255    // Outside bracket jumps to outside of matching bracket
10256    assert("console.logˇ(var);", "console.log(var)ˇ;");
10257    assert("console.log(var)ˇ;", "console.logˇ(var);");
10258
10259    // Inside bracket jumps to inside of matching bracket
10260    assert("console.log(ˇvar);", "console.log(varˇ);");
10261    assert("console.log(varˇ);", "console.log(ˇvar);");
10262
10263    // When outside a bracket and inside, favor jumping to the inside bracket
10264    assert(
10265        "console.log('foo', [1, 2, 3]ˇ);",
10266        "console.log(ˇ'foo', [1, 2, 3]);",
10267    );
10268    assert(
10269        "console.log(ˇ'foo', [1, 2, 3]);",
10270        "console.log('foo', [1, 2, 3]ˇ);",
10271    );
10272
10273    // Bias forward if two options are equally likely
10274    assert(
10275        "let result = curried_fun()ˇ();",
10276        "let result = curried_fun()()ˇ;",
10277    );
10278
10279    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10280    assert(
10281        indoc! {"
10282            function test() {
10283                console.log('test')ˇ
10284            }"},
10285        indoc! {"
10286            function test() {
10287                console.logˇ('test')
10288            }"},
10289    );
10290}
10291
10292#[gpui::test]
10293async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10294    init_test(cx, |_| {});
10295
10296    let fs = FakeFs::new(cx.executor());
10297    fs.insert_tree(
10298        "/a",
10299        json!({
10300            "main.rs": "fn main() { let a = 5; }",
10301            "other.rs": "// Test file",
10302        }),
10303    )
10304    .await;
10305    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10306
10307    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10308    language_registry.add(Arc::new(Language::new(
10309        LanguageConfig {
10310            name: "Rust".into(),
10311            matcher: LanguageMatcher {
10312                path_suffixes: vec!["rs".to_string()],
10313                ..Default::default()
10314            },
10315            brackets: BracketPairConfig {
10316                pairs: vec![BracketPair {
10317                    start: "{".to_string(),
10318                    end: "}".to_string(),
10319                    close: true,
10320                    surround: true,
10321                    newline: true,
10322                }],
10323                disabled_scopes_by_bracket_ix: Vec::new(),
10324            },
10325            ..Default::default()
10326        },
10327        Some(tree_sitter_rust::LANGUAGE.into()),
10328    )));
10329    let mut fake_servers = language_registry.register_fake_lsp(
10330        "Rust",
10331        FakeLspAdapter {
10332            capabilities: lsp::ServerCapabilities {
10333                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10334                    first_trigger_character: "{".to_string(),
10335                    more_trigger_character: None,
10336                }),
10337                ..Default::default()
10338            },
10339            ..Default::default()
10340        },
10341    );
10342
10343    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10344
10345    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10346
10347    let worktree_id = workspace
10348        .update(cx, |workspace, cx| {
10349            workspace.project().update(cx, |project, cx| {
10350                project.worktrees(cx).next().unwrap().read(cx).id()
10351            })
10352        })
10353        .unwrap();
10354
10355    let buffer = project
10356        .update(cx, |project, cx| {
10357            project.open_local_buffer("/a/main.rs", cx)
10358        })
10359        .await
10360        .unwrap();
10361    let editor_handle = workspace
10362        .update(cx, |workspace, cx| {
10363            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10364        })
10365        .unwrap()
10366        .await
10367        .unwrap()
10368        .downcast::<Editor>()
10369        .unwrap();
10370
10371    cx.executor().start_waiting();
10372    let fake_server = fake_servers.next().await.unwrap();
10373
10374    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10375        assert_eq!(
10376            params.text_document_position.text_document.uri,
10377            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10378        );
10379        assert_eq!(
10380            params.text_document_position.position,
10381            lsp::Position::new(0, 21),
10382        );
10383
10384        Ok(Some(vec![lsp::TextEdit {
10385            new_text: "]".to_string(),
10386            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10387        }]))
10388    });
10389
10390    editor_handle.update(cx, |editor, cx| {
10391        editor.focus(cx);
10392        editor.change_selections(None, cx, |s| {
10393            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10394        });
10395        editor.handle_input("{", cx);
10396    });
10397
10398    cx.executor().run_until_parked();
10399
10400    buffer.update(cx, |buffer, _| {
10401        assert_eq!(
10402            buffer.text(),
10403            "fn main() { let a = {5}; }",
10404            "No extra braces from on type formatting should appear in the buffer"
10405        )
10406    });
10407}
10408
10409#[gpui::test]
10410async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10411    init_test(cx, |_| {});
10412
10413    let fs = FakeFs::new(cx.executor());
10414    fs.insert_tree(
10415        "/a",
10416        json!({
10417            "main.rs": "fn main() { let a = 5; }",
10418            "other.rs": "// Test file",
10419        }),
10420    )
10421    .await;
10422
10423    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10424
10425    let server_restarts = Arc::new(AtomicUsize::new(0));
10426    let closure_restarts = Arc::clone(&server_restarts);
10427    let language_server_name = "test language server";
10428    let language_name: LanguageName = "Rust".into();
10429
10430    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10431    language_registry.add(Arc::new(Language::new(
10432        LanguageConfig {
10433            name: language_name.clone(),
10434            matcher: LanguageMatcher {
10435                path_suffixes: vec!["rs".to_string()],
10436                ..Default::default()
10437            },
10438            ..Default::default()
10439        },
10440        Some(tree_sitter_rust::LANGUAGE.into()),
10441    )));
10442    let mut fake_servers = language_registry.register_fake_lsp(
10443        "Rust",
10444        FakeLspAdapter {
10445            name: language_server_name,
10446            initialization_options: Some(json!({
10447                "testOptionValue": true
10448            })),
10449            initializer: Some(Box::new(move |fake_server| {
10450                let task_restarts = Arc::clone(&closure_restarts);
10451                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10452                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10453                    futures::future::ready(Ok(()))
10454                });
10455            })),
10456            ..Default::default()
10457        },
10458    );
10459
10460    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10461    let _buffer = project
10462        .update(cx, |project, cx| {
10463            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10464        })
10465        .await
10466        .unwrap();
10467    let _fake_server = fake_servers.next().await.unwrap();
10468    update_test_language_settings(cx, |language_settings| {
10469        language_settings.languages.insert(
10470            language_name.clone(),
10471            LanguageSettingsContent {
10472                tab_size: NonZeroU32::new(8),
10473                ..Default::default()
10474            },
10475        );
10476    });
10477    cx.executor().run_until_parked();
10478    assert_eq!(
10479        server_restarts.load(atomic::Ordering::Acquire),
10480        0,
10481        "Should not restart LSP server on an unrelated change"
10482    );
10483
10484    update_test_project_settings(cx, |project_settings| {
10485        project_settings.lsp.insert(
10486            "Some other server name".into(),
10487            LspSettings {
10488                binary: None,
10489                settings: None,
10490                initialization_options: Some(json!({
10491                    "some other init value": false
10492                })),
10493            },
10494        );
10495    });
10496    cx.executor().run_until_parked();
10497    assert_eq!(
10498        server_restarts.load(atomic::Ordering::Acquire),
10499        0,
10500        "Should not restart LSP server on an unrelated LSP settings change"
10501    );
10502
10503    update_test_project_settings(cx, |project_settings| {
10504        project_settings.lsp.insert(
10505            language_server_name.into(),
10506            LspSettings {
10507                binary: None,
10508                settings: None,
10509                initialization_options: Some(json!({
10510                    "anotherInitValue": false
10511                })),
10512            },
10513        );
10514    });
10515    cx.executor().run_until_parked();
10516    assert_eq!(
10517        server_restarts.load(atomic::Ordering::Acquire),
10518        1,
10519        "Should restart LSP server on a related LSP settings change"
10520    );
10521
10522    update_test_project_settings(cx, |project_settings| {
10523        project_settings.lsp.insert(
10524            language_server_name.into(),
10525            LspSettings {
10526                binary: None,
10527                settings: None,
10528                initialization_options: Some(json!({
10529                    "anotherInitValue": false
10530                })),
10531            },
10532        );
10533    });
10534    cx.executor().run_until_parked();
10535    assert_eq!(
10536        server_restarts.load(atomic::Ordering::Acquire),
10537        1,
10538        "Should not restart LSP server on a related LSP settings change that is the same"
10539    );
10540
10541    update_test_project_settings(cx, |project_settings| {
10542        project_settings.lsp.insert(
10543            language_server_name.into(),
10544            LspSettings {
10545                binary: None,
10546                settings: None,
10547                initialization_options: None,
10548            },
10549        );
10550    });
10551    cx.executor().run_until_parked();
10552    assert_eq!(
10553        server_restarts.load(atomic::Ordering::Acquire),
10554        2,
10555        "Should restart LSP server on another related LSP settings change"
10556    );
10557}
10558
10559#[gpui::test]
10560async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10561    init_test(cx, |_| {});
10562
10563    let mut cx = EditorLspTestContext::new_rust(
10564        lsp::ServerCapabilities {
10565            completion_provider: Some(lsp::CompletionOptions {
10566                trigger_characters: Some(vec![".".to_string()]),
10567                resolve_provider: Some(true),
10568                ..Default::default()
10569            }),
10570            ..Default::default()
10571        },
10572        cx,
10573    )
10574    .await;
10575
10576    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10577    cx.simulate_keystroke(".");
10578    let completion_item = lsp::CompletionItem {
10579        label: "some".into(),
10580        kind: Some(lsp::CompletionItemKind::SNIPPET),
10581        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10582        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10583            kind: lsp::MarkupKind::Markdown,
10584            value: "```rust\nSome(2)\n```".to_string(),
10585        })),
10586        deprecated: Some(false),
10587        sort_text: Some("fffffff2".to_string()),
10588        filter_text: Some("some".to_string()),
10589        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10590        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10591            range: lsp::Range {
10592                start: lsp::Position {
10593                    line: 0,
10594                    character: 22,
10595                },
10596                end: lsp::Position {
10597                    line: 0,
10598                    character: 22,
10599                },
10600            },
10601            new_text: "Some(2)".to_string(),
10602        })),
10603        additional_text_edits: Some(vec![lsp::TextEdit {
10604            range: lsp::Range {
10605                start: lsp::Position {
10606                    line: 0,
10607                    character: 20,
10608                },
10609                end: lsp::Position {
10610                    line: 0,
10611                    character: 22,
10612                },
10613            },
10614            new_text: "".to_string(),
10615        }]),
10616        ..Default::default()
10617    };
10618
10619    let closure_completion_item = completion_item.clone();
10620    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10621        let task_completion_item = closure_completion_item.clone();
10622        async move {
10623            Ok(Some(lsp::CompletionResponse::Array(vec![
10624                task_completion_item,
10625            ])))
10626        }
10627    });
10628
10629    request.next().await;
10630
10631    cx.condition(|editor, _| editor.context_menu_visible())
10632        .await;
10633    let apply_additional_edits = cx.update_editor(|editor, cx| {
10634        editor
10635            .confirm_completion(&ConfirmCompletion::default(), cx)
10636            .unwrap()
10637    });
10638    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10639
10640    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10641        let task_completion_item = completion_item.clone();
10642        async move { Ok(task_completion_item) }
10643    })
10644    .next()
10645    .await
10646    .unwrap();
10647    apply_additional_edits.await.unwrap();
10648    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10649}
10650
10651#[gpui::test]
10652async fn test_completions_resolve_updates_labels_if_filter_text_matches(
10653    cx: &mut gpui::TestAppContext,
10654) {
10655    init_test(cx, |_| {});
10656
10657    let mut cx = EditorLspTestContext::new_rust(
10658        lsp::ServerCapabilities {
10659            completion_provider: Some(lsp::CompletionOptions {
10660                trigger_characters: Some(vec![".".to_string()]),
10661                resolve_provider: Some(true),
10662                ..Default::default()
10663            }),
10664            ..Default::default()
10665        },
10666        cx,
10667    )
10668    .await;
10669
10670    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10671    cx.simulate_keystroke(".");
10672
10673    let item1 = lsp::CompletionItem {
10674        label: "id".to_string(),
10675        filter_text: Some("id".to_string()),
10676        detail: None,
10677        documentation: None,
10678        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10679            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10680            new_text: ".id".to_string(),
10681        })),
10682        ..lsp::CompletionItem::default()
10683    };
10684
10685    let item2 = lsp::CompletionItem {
10686        label: "other".to_string(),
10687        filter_text: Some("other".to_string()),
10688        detail: None,
10689        documentation: None,
10690        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10691            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10692            new_text: ".other".to_string(),
10693        })),
10694        ..lsp::CompletionItem::default()
10695    };
10696
10697    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10698        let item1 = item1.clone();
10699        let item2 = item2.clone();
10700        async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
10701    })
10702    .next()
10703    .await;
10704
10705    cx.condition(|editor, _| editor.context_menu_visible())
10706        .await;
10707    cx.update_editor(|editor, _| {
10708        let context_menu = editor.context_menu.borrow_mut();
10709        let context_menu = context_menu
10710            .as_ref()
10711            .expect("Should have the context menu deployed");
10712        match context_menu {
10713            CodeContextMenu::Completions(completions_menu) => {
10714                let completions = completions_menu.completions.borrow_mut();
10715                assert_eq!(
10716                    completions
10717                        .iter()
10718                        .map(|completion| &completion.label.text)
10719                        .collect::<Vec<_>>(),
10720                    vec!["id", "other"]
10721                )
10722            }
10723            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10724        }
10725    });
10726
10727    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10728        Ok(lsp::CompletionItem {
10729            label: "method id()".to_string(),
10730            filter_text: Some("id".to_string()),
10731            detail: Some("Now resolved!".to_string()),
10732            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10733            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10734                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10735                new_text: ".id".to_string(),
10736            })),
10737            ..lsp::CompletionItem::default()
10738        })
10739    })
10740    .next()
10741    .await;
10742    cx.run_until_parked();
10743
10744    cx.update_editor(|editor, cx| {
10745        editor.context_menu_next(&Default::default(), cx);
10746    });
10747
10748    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
10749        Ok(lsp::CompletionItem {
10750            label: "invalid changed label".to_string(),
10751            detail: Some("Now resolved!".to_string()),
10752            documentation: Some(lsp::Documentation::String("Docs".to_string())),
10753            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10754                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10755                new_text: ".id".to_string(),
10756            })),
10757            ..lsp::CompletionItem::default()
10758        })
10759    })
10760    .next()
10761    .await;
10762    cx.run_until_parked();
10763
10764    cx.update_editor(|editor, _| {
10765        let context_menu = editor.context_menu.borrow_mut();
10766        let context_menu = context_menu
10767            .as_ref()
10768            .expect("Should have the context menu deployed");
10769        match context_menu {
10770            CodeContextMenu::Completions(completions_menu) => {
10771                let completions = completions_menu.completions.borrow_mut();
10772                assert_eq!(
10773                    completions
10774                        .iter()
10775                        .map(|completion| &completion.label.text)
10776                        .collect::<Vec<_>>(),
10777                    vec!["method id()", "other"],
10778                    "Should update first completion label, but not second as the filter text did not match."
10779                );
10780            }
10781            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10782        }
10783    });
10784}
10785
10786#[gpui::test]
10787async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
10788    init_test(cx, |_| {});
10789
10790    let mut cx = EditorLspTestContext::new_rust(
10791        lsp::ServerCapabilities {
10792            completion_provider: Some(lsp::CompletionOptions {
10793                trigger_characters: Some(vec![".".to_string()]),
10794                resolve_provider: Some(true),
10795                ..Default::default()
10796            }),
10797            ..Default::default()
10798        },
10799        cx,
10800    )
10801    .await;
10802
10803    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10804    cx.simulate_keystroke(".");
10805
10806    let default_commit_characters = vec!["?".to_string()];
10807    let default_data = json!({ "very": "special"});
10808    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
10809    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
10810    let default_edit_range = lsp::Range {
10811        start: lsp::Position {
10812            line: 0,
10813            character: 5,
10814        },
10815        end: lsp::Position {
10816            line: 0,
10817            character: 5,
10818        },
10819    };
10820
10821    let resolve_requests_number = Arc::new(AtomicUsize::new(0));
10822    let expect_first_item = Arc::new(AtomicBool::new(true));
10823    cx.lsp
10824        .server
10825        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
10826            let closure_default_data = default_data.clone();
10827            let closure_resolve_requests_number = resolve_requests_number.clone();
10828            let closure_expect_first_item = expect_first_item.clone();
10829            let closure_default_commit_characters = default_commit_characters.clone();
10830            move |item_to_resolve, _| {
10831                closure_resolve_requests_number.fetch_add(1, atomic::Ordering::Release);
10832                let default_data = closure_default_data.clone();
10833                let default_commit_characters = closure_default_commit_characters.clone();
10834                let expect_first_item = closure_expect_first_item.clone();
10835                async move {
10836                    if expect_first_item.load(atomic::Ordering::Acquire) {
10837                        assert_eq!(
10838                            item_to_resolve.label, "Some(2)",
10839                            "Should have selected the first item"
10840                        );
10841                        assert_eq!(
10842                            item_to_resolve.data,
10843                            Some(json!({ "very": "special"})),
10844                            "First item should bring its own data for resolving"
10845                        );
10846                        assert_eq!(
10847                            item_to_resolve.commit_characters,
10848                            Some(default_commit_characters),
10849                            "First item had no own commit characters and should inherit the default ones"
10850                        );
10851                        assert!(
10852                            matches!(
10853                                item_to_resolve.text_edit,
10854                                Some(lsp::CompletionTextEdit::InsertAndReplace { .. })
10855                            ),
10856                            "First item should bring its own edit range for resolving"
10857                        );
10858                        assert_eq!(
10859                            item_to_resolve.insert_text_format,
10860                            Some(default_insert_text_format),
10861                            "First item had no own insert text format and should inherit the default one"
10862                        );
10863                        assert_eq!(
10864                            item_to_resolve.insert_text_mode,
10865                            Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10866                            "First item should bring its own insert text mode for resolving"
10867                        );
10868                        Ok(item_to_resolve)
10869                    } else {
10870                        assert_eq!(
10871                            item_to_resolve.label, "vec![2]",
10872                            "Should have selected the last item"
10873                        );
10874                        assert_eq!(
10875                            item_to_resolve.data,
10876                            Some(default_data),
10877                            "Last item has no own resolve data and should inherit the default one"
10878                        );
10879                        assert_eq!(
10880                            item_to_resolve.commit_characters,
10881                            Some(default_commit_characters),
10882                            "Last item had no own commit characters and should inherit the default ones"
10883                        );
10884                        assert_eq!(
10885                            item_to_resolve.text_edit,
10886                            Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10887                                range: default_edit_range,
10888                                new_text: "vec![2]".to_string()
10889                            })),
10890                            "Last item had no own edit range and should inherit the default one"
10891                        );
10892                        assert_eq!(
10893                            item_to_resolve.insert_text_format,
10894                            Some(lsp::InsertTextFormat::PLAIN_TEXT),
10895                            "Last item should bring its own insert text format for resolving"
10896                        );
10897                        assert_eq!(
10898                            item_to_resolve.insert_text_mode,
10899                            Some(default_insert_text_mode),
10900                            "Last item had no own insert text mode and should inherit the default one"
10901                        );
10902
10903                        Ok(item_to_resolve)
10904                    }
10905                }
10906            }
10907        }).detach();
10908
10909    let completion_data = default_data.clone();
10910    let completion_characters = default_commit_characters.clone();
10911    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10912        let default_data = completion_data.clone();
10913        let default_commit_characters = completion_characters.clone();
10914        async move {
10915            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
10916                items: vec![
10917                    lsp::CompletionItem {
10918                        label: "Some(2)".into(),
10919                        insert_text: Some("Some(2)".into()),
10920                        data: Some(json!({ "very": "special"})),
10921                        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
10922                        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10923                            lsp::InsertReplaceEdit {
10924                                new_text: "Some(2)".to_string(),
10925                                insert: lsp::Range::default(),
10926                                replace: lsp::Range::default(),
10927                            },
10928                        )),
10929                        ..lsp::CompletionItem::default()
10930                    },
10931                    lsp::CompletionItem {
10932                        label: "vec![2]".into(),
10933                        insert_text: Some("vec![2]".into()),
10934                        insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
10935                        ..lsp::CompletionItem::default()
10936                    },
10937                ],
10938                item_defaults: Some(lsp::CompletionListItemDefaults {
10939                    data: Some(default_data.clone()),
10940                    commit_characters: Some(default_commit_characters.clone()),
10941                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
10942                        default_edit_range,
10943                    )),
10944                    insert_text_format: Some(default_insert_text_format),
10945                    insert_text_mode: Some(default_insert_text_mode),
10946                }),
10947                ..lsp::CompletionList::default()
10948            })))
10949        }
10950    })
10951    .next()
10952    .await;
10953
10954    cx.condition(|editor, _| editor.context_menu_visible())
10955        .await;
10956    cx.run_until_parked();
10957    cx.update_editor(|editor, _| {
10958        let menu = editor.context_menu.borrow_mut();
10959        match menu.as_ref().expect("should have the completions menu") {
10960            CodeContextMenu::Completions(completions_menu) => {
10961                assert_eq!(
10962                    completion_menu_entries(&completions_menu.entries),
10963                    vec!["Some(2)", "vec![2]"]
10964                );
10965            }
10966            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
10967        }
10968    });
10969    assert_eq!(
10970        resolve_requests_number.load(atomic::Ordering::Acquire),
10971        1,
10972        "While there are 2 items in the completion list, only 1 resolve request should have been sent, for the selected item"
10973    );
10974
10975    cx.update_editor(|editor, cx| {
10976        editor.context_menu_first(&ContextMenuFirst, cx);
10977    });
10978    cx.run_until_parked();
10979    assert_eq!(
10980        resolve_requests_number.load(atomic::Ordering::Acquire),
10981        2,
10982        "After re-selecting the first item, another resolve request should have been sent"
10983    );
10984
10985    expect_first_item.store(false, atomic::Ordering::Release);
10986    cx.update_editor(|editor, cx| {
10987        editor.context_menu_last(&ContextMenuLast, cx);
10988    });
10989    cx.run_until_parked();
10990    assert_eq!(
10991        resolve_requests_number.load(atomic::Ordering::Acquire),
10992        3,
10993        "After selecting the other item, another resolve request should have been sent"
10994    );
10995}
10996
10997#[gpui::test]
10998async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10999    init_test(cx, |_| {});
11000
11001    let mut cx = EditorLspTestContext::new(
11002        Language::new(
11003            LanguageConfig {
11004                matcher: LanguageMatcher {
11005                    path_suffixes: vec!["jsx".into()],
11006                    ..Default::default()
11007                },
11008                overrides: [(
11009                    "element".into(),
11010                    LanguageConfigOverride {
11011                        word_characters: Override::Set(['-'].into_iter().collect()),
11012                        ..Default::default()
11013                    },
11014                )]
11015                .into_iter()
11016                .collect(),
11017                ..Default::default()
11018            },
11019            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11020        )
11021        .with_override_query("(jsx_self_closing_element) @element")
11022        .unwrap(),
11023        lsp::ServerCapabilities {
11024            completion_provider: Some(lsp::CompletionOptions {
11025                trigger_characters: Some(vec![":".to_string()]),
11026                ..Default::default()
11027            }),
11028            ..Default::default()
11029        },
11030        cx,
11031    )
11032    .await;
11033
11034    cx.lsp
11035        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11036            Ok(Some(lsp::CompletionResponse::Array(vec![
11037                lsp::CompletionItem {
11038                    label: "bg-blue".into(),
11039                    ..Default::default()
11040                },
11041                lsp::CompletionItem {
11042                    label: "bg-red".into(),
11043                    ..Default::default()
11044                },
11045                lsp::CompletionItem {
11046                    label: "bg-yellow".into(),
11047                    ..Default::default()
11048                },
11049            ])))
11050        });
11051
11052    cx.set_state(r#"<p class="bgˇ" />"#);
11053
11054    // Trigger completion when typing a dash, because the dash is an extra
11055    // word character in the 'element' scope, which contains the cursor.
11056    cx.simulate_keystroke("-");
11057    cx.executor().run_until_parked();
11058    cx.update_editor(|editor, _| {
11059        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11060        {
11061            assert_eq!(
11062                completion_menu_entries(&menu.entries),
11063                &["bg-red", "bg-blue", "bg-yellow"]
11064            );
11065        } else {
11066            panic!("expected completion menu to be open");
11067        }
11068    });
11069
11070    cx.simulate_keystroke("l");
11071    cx.executor().run_until_parked();
11072    cx.update_editor(|editor, _| {
11073        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11074        {
11075            assert_eq!(
11076                completion_menu_entries(&menu.entries),
11077                &["bg-blue", "bg-yellow"]
11078            );
11079        } else {
11080            panic!("expected completion menu to be open");
11081        }
11082    });
11083
11084    // When filtering completions, consider the character after the '-' to
11085    // be the start of a subword.
11086    cx.set_state(r#"<p class="yelˇ" />"#);
11087    cx.simulate_keystroke("l");
11088    cx.executor().run_until_parked();
11089    cx.update_editor(|editor, _| {
11090        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11091        {
11092            assert_eq!(completion_menu_entries(&menu.entries), &["bg-yellow"]);
11093        } else {
11094            panic!("expected completion menu to be open");
11095        }
11096    });
11097}
11098
11099fn completion_menu_entries(entries: &[CompletionEntry]) -> Vec<&str> {
11100    entries
11101        .iter()
11102        .flat_map(|e| match e {
11103            CompletionEntry::Match(mat) => Some(mat.string.as_str()),
11104            _ => None,
11105        })
11106        .collect()
11107}
11108
11109#[gpui::test]
11110async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11111    init_test(cx, |settings| {
11112        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11113            FormatterList(vec![Formatter::Prettier].into()),
11114        ))
11115    });
11116
11117    let fs = FakeFs::new(cx.executor());
11118    fs.insert_file("/file.ts", Default::default()).await;
11119
11120    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11121    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11122
11123    language_registry.add(Arc::new(Language::new(
11124        LanguageConfig {
11125            name: "TypeScript".into(),
11126            matcher: LanguageMatcher {
11127                path_suffixes: vec!["ts".to_string()],
11128                ..Default::default()
11129            },
11130            ..Default::default()
11131        },
11132        Some(tree_sitter_rust::LANGUAGE.into()),
11133    )));
11134    update_test_language_settings(cx, |settings| {
11135        settings.defaults.prettier = Some(PrettierSettings {
11136            allowed: true,
11137            ..PrettierSettings::default()
11138        });
11139    });
11140
11141    let test_plugin = "test_plugin";
11142    let _ = language_registry.register_fake_lsp(
11143        "TypeScript",
11144        FakeLspAdapter {
11145            prettier_plugins: vec![test_plugin],
11146            ..Default::default()
11147        },
11148    );
11149
11150    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11151    let buffer = project
11152        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11153        .await
11154        .unwrap();
11155
11156    let buffer_text = "one\ntwo\nthree\n";
11157    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11158    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11159    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11160
11161    editor
11162        .update(cx, |editor, cx| {
11163            editor.perform_format(
11164                project.clone(),
11165                FormatTrigger::Manual,
11166                FormatTarget::Buffer,
11167                cx,
11168            )
11169        })
11170        .unwrap()
11171        .await;
11172    assert_eq!(
11173        editor.update(cx, |editor, cx| editor.text(cx)),
11174        buffer_text.to_string() + prettier_format_suffix,
11175        "Test prettier formatting was not applied to the original buffer text",
11176    );
11177
11178    update_test_language_settings(cx, |settings| {
11179        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11180    });
11181    let format = editor.update(cx, |editor, cx| {
11182        editor.perform_format(
11183            project.clone(),
11184            FormatTrigger::Manual,
11185            FormatTarget::Buffer,
11186            cx,
11187        )
11188    });
11189    format.await.unwrap();
11190    assert_eq!(
11191        editor.update(cx, |editor, cx| editor.text(cx)),
11192        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11193        "Autoformatting (via test prettier) was not applied to the original buffer text",
11194    );
11195}
11196
11197#[gpui::test]
11198async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11199    init_test(cx, |_| {});
11200    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11201    let base_text = indoc! {r#"
11202        struct Row;
11203        struct Row1;
11204        struct Row2;
11205
11206        struct Row4;
11207        struct Row5;
11208        struct Row6;
11209
11210        struct Row8;
11211        struct Row9;
11212        struct Row10;"#};
11213
11214    // When addition hunks are not adjacent to carets, no hunk revert is performed
11215    assert_hunk_revert(
11216        indoc! {r#"struct Row;
11217                   struct Row1;
11218                   struct Row1.1;
11219                   struct Row1.2;
11220                   struct Row2;ˇ
11221
11222                   struct Row4;
11223                   struct Row5;
11224                   struct Row6;
11225
11226                   struct Row8;
11227                   ˇstruct Row9;
11228                   struct Row9.1;
11229                   struct Row9.2;
11230                   struct Row9.3;
11231                   struct Row10;"#},
11232        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11233        indoc! {r#"struct Row;
11234                   struct Row1;
11235                   struct Row1.1;
11236                   struct Row1.2;
11237                   struct Row2;ˇ
11238
11239                   struct Row4;
11240                   struct Row5;
11241                   struct Row6;
11242
11243                   struct Row8;
11244                   ˇstruct Row9;
11245                   struct Row9.1;
11246                   struct Row9.2;
11247                   struct Row9.3;
11248                   struct Row10;"#},
11249        base_text,
11250        &mut cx,
11251    );
11252    // Same for selections
11253    assert_hunk_revert(
11254        indoc! {r#"struct Row;
11255                   struct Row1;
11256                   struct Row2;
11257                   struct Row2.1;
11258                   struct Row2.2;
11259                   «ˇ
11260                   struct Row4;
11261                   struct» Row5;
11262                   «struct Row6;
11263                   ˇ»
11264                   struct Row9.1;
11265                   struct Row9.2;
11266                   struct Row9.3;
11267                   struct Row8;
11268                   struct Row9;
11269                   struct Row10;"#},
11270        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11271        indoc! {r#"struct Row;
11272                   struct Row1;
11273                   struct Row2;
11274                   struct Row2.1;
11275                   struct Row2.2;
11276                   «ˇ
11277                   struct Row4;
11278                   struct» Row5;
11279                   «struct Row6;
11280                   ˇ»
11281                   struct Row9.1;
11282                   struct Row9.2;
11283                   struct Row9.3;
11284                   struct Row8;
11285                   struct Row9;
11286                   struct Row10;"#},
11287        base_text,
11288        &mut cx,
11289    );
11290
11291    // When carets and selections intersect the addition hunks, those are reverted.
11292    // Adjacent carets got merged.
11293    assert_hunk_revert(
11294        indoc! {r#"struct Row;
11295                   ˇ// something on the top
11296                   struct Row1;
11297                   struct Row2;
11298                   struct Roˇw3.1;
11299                   struct Row2.2;
11300                   struct Row2.3;ˇ
11301
11302                   struct Row4;
11303                   struct ˇRow5.1;
11304                   struct Row5.2;
11305                   struct «Rowˇ»5.3;
11306                   struct Row5;
11307                   struct Row6;
11308                   ˇ
11309                   struct Row9.1;
11310                   struct «Rowˇ»9.2;
11311                   struct «ˇRow»9.3;
11312                   struct Row8;
11313                   struct Row9;
11314                   «ˇ// something on bottom»
11315                   struct Row10;"#},
11316        vec![
11317            DiffHunkStatus::Added,
11318            DiffHunkStatus::Added,
11319            DiffHunkStatus::Added,
11320            DiffHunkStatus::Added,
11321            DiffHunkStatus::Added,
11322        ],
11323        indoc! {r#"struct Row;
11324                   ˇstruct Row1;
11325                   struct Row2;
11326                   ˇ
11327                   struct Row4;
11328                   ˇstruct Row5;
11329                   struct Row6;
11330                   ˇ
11331                   ˇstruct Row8;
11332                   struct Row9;
11333                   ˇstruct Row10;"#},
11334        base_text,
11335        &mut cx,
11336    );
11337}
11338
11339#[gpui::test]
11340async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11341    init_test(cx, |_| {});
11342    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11343    let base_text = indoc! {r#"
11344        struct Row;
11345        struct Row1;
11346        struct Row2;
11347
11348        struct Row4;
11349        struct Row5;
11350        struct Row6;
11351
11352        struct Row8;
11353        struct Row9;
11354        struct Row10;"#};
11355
11356    // Modification hunks behave the same as the addition ones.
11357    assert_hunk_revert(
11358        indoc! {r#"struct Row;
11359                   struct Row1;
11360                   struct Row33;
11361                   ˇ
11362                   struct Row4;
11363                   struct Row5;
11364                   struct Row6;
11365                   ˇ
11366                   struct Row99;
11367                   struct Row9;
11368                   struct Row10;"#},
11369        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11370        indoc! {r#"struct Row;
11371                   struct Row1;
11372                   struct Row33;
11373                   ˇ
11374                   struct Row4;
11375                   struct Row5;
11376                   struct Row6;
11377                   ˇ
11378                   struct Row99;
11379                   struct Row9;
11380                   struct Row10;"#},
11381        base_text,
11382        &mut cx,
11383    );
11384    assert_hunk_revert(
11385        indoc! {r#"struct Row;
11386                   struct Row1;
11387                   struct Row33;
11388                   «ˇ
11389                   struct Row4;
11390                   struct» Row5;
11391                   «struct Row6;
11392                   ˇ»
11393                   struct Row99;
11394                   struct Row9;
11395                   struct Row10;"#},
11396        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11397        indoc! {r#"struct Row;
11398                   struct Row1;
11399                   struct Row33;
11400                   «ˇ
11401                   struct Row4;
11402                   struct» Row5;
11403                   «struct Row6;
11404                   ˇ»
11405                   struct Row99;
11406                   struct Row9;
11407                   struct Row10;"#},
11408        base_text,
11409        &mut cx,
11410    );
11411
11412    assert_hunk_revert(
11413        indoc! {r#"ˇstruct Row1.1;
11414                   struct Row1;
11415                   «ˇstr»uct Row22;
11416
11417                   struct ˇRow44;
11418                   struct Row5;
11419                   struct «Rˇ»ow66;ˇ
11420
11421                   «struˇ»ct Row88;
11422                   struct Row9;
11423                   struct Row1011;ˇ"#},
11424        vec![
11425            DiffHunkStatus::Modified,
11426            DiffHunkStatus::Modified,
11427            DiffHunkStatus::Modified,
11428            DiffHunkStatus::Modified,
11429            DiffHunkStatus::Modified,
11430            DiffHunkStatus::Modified,
11431        ],
11432        indoc! {r#"struct Row;
11433                   ˇstruct Row1;
11434                   struct Row2;
11435                   ˇ
11436                   struct Row4;
11437                   ˇstruct Row5;
11438                   struct Row6;
11439                   ˇ
11440                   struct Row8;
11441                   ˇstruct Row9;
11442                   struct Row10;ˇ"#},
11443        base_text,
11444        &mut cx,
11445    );
11446}
11447
11448#[gpui::test]
11449async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11450    init_test(cx, |_| {});
11451    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11452    let base_text = indoc! {r#"struct Row;
11453struct Row1;
11454struct Row2;
11455
11456struct Row4;
11457struct Row5;
11458struct Row6;
11459
11460struct Row8;
11461struct Row9;
11462struct Row10;"#};
11463
11464    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11465    assert_hunk_revert(
11466        indoc! {r#"struct Row;
11467                   struct Row2;
11468
11469                   ˇstruct Row4;
11470                   struct Row5;
11471                   struct Row6;
11472                   ˇ
11473                   struct Row8;
11474                   struct Row10;"#},
11475        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11476        indoc! {r#"struct Row;
11477                   struct Row2;
11478
11479                   ˇstruct Row4;
11480                   struct Row5;
11481                   struct Row6;
11482                   ˇ
11483                   struct Row8;
11484                   struct Row10;"#},
11485        base_text,
11486        &mut cx,
11487    );
11488    assert_hunk_revert(
11489        indoc! {r#"struct Row;
11490                   struct Row2;
11491
11492                   «ˇstruct Row4;
11493                   struct» Row5;
11494                   «struct Row6;
11495                   ˇ»
11496                   struct Row8;
11497                   struct Row10;"#},
11498        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11499        indoc! {r#"struct Row;
11500                   struct Row2;
11501
11502                   «ˇstruct Row4;
11503                   struct» Row5;
11504                   «struct Row6;
11505                   ˇ»
11506                   struct Row8;
11507                   struct Row10;"#},
11508        base_text,
11509        &mut cx,
11510    );
11511
11512    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11513    assert_hunk_revert(
11514        indoc! {r#"struct Row;
11515                   ˇstruct Row2;
11516
11517                   struct Row4;
11518                   struct Row5;
11519                   struct Row6;
11520
11521                   struct Row8;ˇ
11522                   struct Row10;"#},
11523        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11524        indoc! {r#"struct Row;
11525                   struct Row1;
11526                   ˇstruct Row2;
11527
11528                   struct Row4;
11529                   struct Row5;
11530                   struct Row6;
11531
11532                   struct Row8;ˇ
11533                   struct Row9;
11534                   struct Row10;"#},
11535        base_text,
11536        &mut cx,
11537    );
11538    assert_hunk_revert(
11539        indoc! {r#"struct Row;
11540                   struct Row2«ˇ;
11541                   struct Row4;
11542                   struct» Row5;
11543                   «struct Row6;
11544
11545                   struct Row8;ˇ»
11546                   struct Row10;"#},
11547        vec![
11548            DiffHunkStatus::Removed,
11549            DiffHunkStatus::Removed,
11550            DiffHunkStatus::Removed,
11551        ],
11552        indoc! {r#"struct Row;
11553                   struct Row1;
11554                   struct Row2«ˇ;
11555
11556                   struct Row4;
11557                   struct» Row5;
11558                   «struct Row6;
11559
11560                   struct Row8;ˇ»
11561                   struct Row9;
11562                   struct Row10;"#},
11563        base_text,
11564        &mut cx,
11565    );
11566}
11567
11568#[gpui::test]
11569async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11570    init_test(cx, |_| {});
11571
11572    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11573    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11574    let base_text_3 =
11575        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11576
11577    let text_1 = edit_first_char_of_every_line(base_text_1);
11578    let text_2 = edit_first_char_of_every_line(base_text_2);
11579    let text_3 = edit_first_char_of_every_line(base_text_3);
11580
11581    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11582    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11583    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11584
11585    let multibuffer = cx.new_model(|cx| {
11586        let mut multibuffer = MultiBuffer::new(ReadWrite);
11587        multibuffer.push_excerpts(
11588            buffer_1.clone(),
11589            [
11590                ExcerptRange {
11591                    context: Point::new(0, 0)..Point::new(3, 0),
11592                    primary: None,
11593                },
11594                ExcerptRange {
11595                    context: Point::new(5, 0)..Point::new(7, 0),
11596                    primary: None,
11597                },
11598                ExcerptRange {
11599                    context: Point::new(9, 0)..Point::new(10, 4),
11600                    primary: None,
11601                },
11602            ],
11603            cx,
11604        );
11605        multibuffer.push_excerpts(
11606            buffer_2.clone(),
11607            [
11608                ExcerptRange {
11609                    context: Point::new(0, 0)..Point::new(3, 0),
11610                    primary: None,
11611                },
11612                ExcerptRange {
11613                    context: Point::new(5, 0)..Point::new(7, 0),
11614                    primary: None,
11615                },
11616                ExcerptRange {
11617                    context: Point::new(9, 0)..Point::new(10, 4),
11618                    primary: None,
11619                },
11620            ],
11621            cx,
11622        );
11623        multibuffer.push_excerpts(
11624            buffer_3.clone(),
11625            [
11626                ExcerptRange {
11627                    context: Point::new(0, 0)..Point::new(3, 0),
11628                    primary: None,
11629                },
11630                ExcerptRange {
11631                    context: Point::new(5, 0)..Point::new(7, 0),
11632                    primary: None,
11633                },
11634                ExcerptRange {
11635                    context: Point::new(9, 0)..Point::new(10, 4),
11636                    primary: None,
11637                },
11638            ],
11639            cx,
11640        );
11641        multibuffer
11642    });
11643
11644    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11645    editor.update(cx, |editor, cx| {
11646        for (buffer, diff_base) in [
11647            (buffer_1.clone(), base_text_1),
11648            (buffer_2.clone(), base_text_2),
11649            (buffer_3.clone(), base_text_3),
11650        ] {
11651            let change_set = cx.new_model(|cx| {
11652                BufferChangeSet::new_with_base_text(
11653                    diff_base.to_string(),
11654                    buffer.read(cx).text_snapshot(),
11655                    cx,
11656                )
11657            });
11658            editor.diff_map.add_change_set(change_set, cx)
11659        }
11660    });
11661    cx.executor().run_until_parked();
11662
11663    editor.update(cx, |editor, cx| {
11664        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}");
11665        editor.select_all(&SelectAll, cx);
11666        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11667    });
11668    cx.executor().run_until_parked();
11669
11670    // When all ranges are selected, all buffer hunks are reverted.
11671    editor.update(cx, |editor, cx| {
11672        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");
11673    });
11674    buffer_1.update(cx, |buffer, _| {
11675        assert_eq!(buffer.text(), base_text_1);
11676    });
11677    buffer_2.update(cx, |buffer, _| {
11678        assert_eq!(buffer.text(), base_text_2);
11679    });
11680    buffer_3.update(cx, |buffer, _| {
11681        assert_eq!(buffer.text(), base_text_3);
11682    });
11683
11684    editor.update(cx, |editor, cx| {
11685        editor.undo(&Default::default(), cx);
11686    });
11687
11688    editor.update(cx, |editor, cx| {
11689        editor.change_selections(None, cx, |s| {
11690            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11691        });
11692        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11693    });
11694
11695    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11696    // but not affect buffer_2 and its related excerpts.
11697    editor.update(cx, |editor, cx| {
11698        assert_eq!(
11699            editor.text(cx),
11700            "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}"
11701        );
11702    });
11703    buffer_1.update(cx, |buffer, _| {
11704        assert_eq!(buffer.text(), base_text_1);
11705    });
11706    buffer_2.update(cx, |buffer, _| {
11707        assert_eq!(
11708            buffer.text(),
11709            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
11710        );
11711    });
11712    buffer_3.update(cx, |buffer, _| {
11713        assert_eq!(
11714            buffer.text(),
11715            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
11716        );
11717    });
11718
11719    fn edit_first_char_of_every_line(text: &str) -> String {
11720        text.split('\n')
11721            .map(|line| format!("X{}", &line[1..]))
11722            .collect::<Vec<_>>()
11723            .join("\n")
11724    }
11725}
11726
11727#[gpui::test]
11728async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11729    init_test(cx, |_| {});
11730
11731    let cols = 4;
11732    let rows = 10;
11733    let sample_text_1 = sample_text(rows, cols, 'a');
11734    assert_eq!(
11735        sample_text_1,
11736        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11737    );
11738    let sample_text_2 = sample_text(rows, cols, 'l');
11739    assert_eq!(
11740        sample_text_2,
11741        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11742    );
11743    let sample_text_3 = sample_text(rows, cols, 'v');
11744    assert_eq!(
11745        sample_text_3,
11746        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11747    );
11748
11749    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11750    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11751    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11752
11753    let multi_buffer = cx.new_model(|cx| {
11754        let mut multibuffer = MultiBuffer::new(ReadWrite);
11755        multibuffer.push_excerpts(
11756            buffer_1.clone(),
11757            [
11758                ExcerptRange {
11759                    context: Point::new(0, 0)..Point::new(3, 0),
11760                    primary: None,
11761                },
11762                ExcerptRange {
11763                    context: Point::new(5, 0)..Point::new(7, 0),
11764                    primary: None,
11765                },
11766                ExcerptRange {
11767                    context: Point::new(9, 0)..Point::new(10, 4),
11768                    primary: None,
11769                },
11770            ],
11771            cx,
11772        );
11773        multibuffer.push_excerpts(
11774            buffer_2.clone(),
11775            [
11776                ExcerptRange {
11777                    context: Point::new(0, 0)..Point::new(3, 0),
11778                    primary: None,
11779                },
11780                ExcerptRange {
11781                    context: Point::new(5, 0)..Point::new(7, 0),
11782                    primary: None,
11783                },
11784                ExcerptRange {
11785                    context: Point::new(9, 0)..Point::new(10, 4),
11786                    primary: None,
11787                },
11788            ],
11789            cx,
11790        );
11791        multibuffer.push_excerpts(
11792            buffer_3.clone(),
11793            [
11794                ExcerptRange {
11795                    context: Point::new(0, 0)..Point::new(3, 0),
11796                    primary: None,
11797                },
11798                ExcerptRange {
11799                    context: Point::new(5, 0)..Point::new(7, 0),
11800                    primary: None,
11801                },
11802                ExcerptRange {
11803                    context: Point::new(9, 0)..Point::new(10, 4),
11804                    primary: None,
11805                },
11806            ],
11807            cx,
11808        );
11809        multibuffer
11810    });
11811
11812    let fs = FakeFs::new(cx.executor());
11813    fs.insert_tree(
11814        "/a",
11815        json!({
11816            "main.rs": sample_text_1,
11817            "other.rs": sample_text_2,
11818            "lib.rs": sample_text_3,
11819        }),
11820    )
11821    .await;
11822    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11823    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11824    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11825    let multi_buffer_editor = cx.new_view(|cx| {
11826        Editor::new(
11827            EditorMode::Full,
11828            multi_buffer,
11829            Some(project.clone()),
11830            true,
11831            cx,
11832        )
11833    });
11834    let multibuffer_item_id = workspace
11835        .update(cx, |workspace, cx| {
11836            assert!(
11837                workspace.active_item(cx).is_none(),
11838                "active item should be None before the first item is added"
11839            );
11840            workspace.add_item_to_active_pane(
11841                Box::new(multi_buffer_editor.clone()),
11842                None,
11843                true,
11844                cx,
11845            );
11846            let active_item = workspace
11847                .active_item(cx)
11848                .expect("should have an active item after adding the multi buffer");
11849            assert!(
11850                !active_item.is_singleton(cx),
11851                "A multi buffer was expected to active after adding"
11852            );
11853            active_item.item_id()
11854        })
11855        .unwrap();
11856    cx.executor().run_until_parked();
11857
11858    multi_buffer_editor.update(cx, |editor, cx| {
11859        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11860        editor.open_excerpts(&OpenExcerpts, cx);
11861    });
11862    cx.executor().run_until_parked();
11863    let first_item_id = workspace
11864        .update(cx, |workspace, cx| {
11865            let active_item = workspace
11866                .active_item(cx)
11867                .expect("should have an active item after navigating into the 1st buffer");
11868            let first_item_id = active_item.item_id();
11869            assert_ne!(
11870                first_item_id, multibuffer_item_id,
11871                "Should navigate into the 1st buffer and activate it"
11872            );
11873            assert!(
11874                active_item.is_singleton(cx),
11875                "New active item should be a singleton buffer"
11876            );
11877            assert_eq!(
11878                active_item
11879                    .act_as::<Editor>(cx)
11880                    .expect("should have navigated into an editor for the 1st buffer")
11881                    .read(cx)
11882                    .text(cx),
11883                sample_text_1
11884            );
11885
11886            workspace
11887                .go_back(workspace.active_pane().downgrade(), cx)
11888                .detach_and_log_err(cx);
11889
11890            first_item_id
11891        })
11892        .unwrap();
11893    cx.executor().run_until_parked();
11894    workspace
11895        .update(cx, |workspace, cx| {
11896            let active_item = workspace
11897                .active_item(cx)
11898                .expect("should have an active item after navigating back");
11899            assert_eq!(
11900                active_item.item_id(),
11901                multibuffer_item_id,
11902                "Should navigate back to the multi buffer"
11903            );
11904            assert!(!active_item.is_singleton(cx));
11905        })
11906        .unwrap();
11907
11908    multi_buffer_editor.update(cx, |editor, cx| {
11909        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11910            s.select_ranges(Some(39..40))
11911        });
11912        editor.open_excerpts(&OpenExcerpts, cx);
11913    });
11914    cx.executor().run_until_parked();
11915    let second_item_id = workspace
11916        .update(cx, |workspace, cx| {
11917            let active_item = workspace
11918                .active_item(cx)
11919                .expect("should have an active item after navigating into the 2nd buffer");
11920            let second_item_id = active_item.item_id();
11921            assert_ne!(
11922                second_item_id, multibuffer_item_id,
11923                "Should navigate away from the multibuffer"
11924            );
11925            assert_ne!(
11926                second_item_id, first_item_id,
11927                "Should navigate into the 2nd buffer and activate it"
11928            );
11929            assert!(
11930                active_item.is_singleton(cx),
11931                "New active item should be a singleton buffer"
11932            );
11933            assert_eq!(
11934                active_item
11935                    .act_as::<Editor>(cx)
11936                    .expect("should have navigated into an editor")
11937                    .read(cx)
11938                    .text(cx),
11939                sample_text_2
11940            );
11941
11942            workspace
11943                .go_back(workspace.active_pane().downgrade(), cx)
11944                .detach_and_log_err(cx);
11945
11946            second_item_id
11947        })
11948        .unwrap();
11949    cx.executor().run_until_parked();
11950    workspace
11951        .update(cx, |workspace, cx| {
11952            let active_item = workspace
11953                .active_item(cx)
11954                .expect("should have an active item after navigating back from the 2nd buffer");
11955            assert_eq!(
11956                active_item.item_id(),
11957                multibuffer_item_id,
11958                "Should navigate back from the 2nd buffer to the multi buffer"
11959            );
11960            assert!(!active_item.is_singleton(cx));
11961        })
11962        .unwrap();
11963
11964    multi_buffer_editor.update(cx, |editor, cx| {
11965        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11966            s.select_ranges(Some(70..70))
11967        });
11968        editor.open_excerpts(&OpenExcerpts, cx);
11969    });
11970    cx.executor().run_until_parked();
11971    workspace
11972        .update(cx, |workspace, cx| {
11973            let active_item = workspace
11974                .active_item(cx)
11975                .expect("should have an active item after navigating into the 3rd buffer");
11976            let third_item_id = active_item.item_id();
11977            assert_ne!(
11978                third_item_id, multibuffer_item_id,
11979                "Should navigate into the 3rd buffer and activate it"
11980            );
11981            assert_ne!(third_item_id, first_item_id);
11982            assert_ne!(third_item_id, second_item_id);
11983            assert!(
11984                active_item.is_singleton(cx),
11985                "New active item should be a singleton buffer"
11986            );
11987            assert_eq!(
11988                active_item
11989                    .act_as::<Editor>(cx)
11990                    .expect("should have navigated into an editor")
11991                    .read(cx)
11992                    .text(cx),
11993                sample_text_3
11994            );
11995
11996            workspace
11997                .go_back(workspace.active_pane().downgrade(), cx)
11998                .detach_and_log_err(cx);
11999        })
12000        .unwrap();
12001    cx.executor().run_until_parked();
12002    workspace
12003        .update(cx, |workspace, cx| {
12004            let active_item = workspace
12005                .active_item(cx)
12006                .expect("should have an active item after navigating back from the 3rd buffer");
12007            assert_eq!(
12008                active_item.item_id(),
12009                multibuffer_item_id,
12010                "Should navigate back from the 3rd buffer to the multi buffer"
12011            );
12012            assert!(!active_item.is_singleton(cx));
12013        })
12014        .unwrap();
12015}
12016
12017#[gpui::test]
12018async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12019    init_test(cx, |_| {});
12020
12021    let mut cx = EditorTestContext::new(cx).await;
12022
12023    let diff_base = r#"
12024        use some::mod;
12025
12026        const A: u32 = 42;
12027
12028        fn main() {
12029            println!("hello");
12030
12031            println!("world");
12032        }
12033        "#
12034    .unindent();
12035
12036    cx.set_state(
12037        &r#"
12038        use some::modified;
12039
12040        ˇ
12041        fn main() {
12042            println!("hello there");
12043
12044            println!("around the");
12045            println!("world");
12046        }
12047        "#
12048        .unindent(),
12049    );
12050
12051    cx.set_diff_base(&diff_base);
12052    executor.run_until_parked();
12053
12054    cx.update_editor(|editor, cx| {
12055        editor.go_to_next_hunk(&GoToHunk, cx);
12056        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12057    });
12058    executor.run_until_parked();
12059    cx.assert_state_with_diff(
12060        r#"
12061          use some::modified;
12062
12063
12064          fn main() {
12065        -     println!("hello");
12066        + ˇ    println!("hello there");
12067
12068              println!("around the");
12069              println!("world");
12070          }
12071        "#
12072        .unindent(),
12073    );
12074
12075    cx.update_editor(|editor, cx| {
12076        for _ in 0..3 {
12077            editor.go_to_next_hunk(&GoToHunk, cx);
12078            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12079        }
12080    });
12081    executor.run_until_parked();
12082    cx.assert_state_with_diff(
12083        r#"
12084        - use some::mod;
12085        + use some::modified;
12086
12087        - const A: u32 = 42;
12088          ˇ
12089          fn main() {
12090        -     println!("hello");
12091        +     println!("hello there");
12092
12093        +     println!("around the");
12094              println!("world");
12095          }
12096        "#
12097        .unindent(),
12098    );
12099
12100    cx.update_editor(|editor, cx| {
12101        editor.cancel(&Cancel, cx);
12102    });
12103
12104    cx.assert_state_with_diff(
12105        r#"
12106          use some::modified;
12107
12108          ˇ
12109          fn main() {
12110              println!("hello there");
12111
12112              println!("around the");
12113              println!("world");
12114          }
12115        "#
12116        .unindent(),
12117    );
12118}
12119
12120#[gpui::test]
12121async fn test_diff_base_change_with_expanded_diff_hunks(
12122    executor: BackgroundExecutor,
12123    cx: &mut gpui::TestAppContext,
12124) {
12125    init_test(cx, |_| {});
12126
12127    let mut cx = EditorTestContext::new(cx).await;
12128
12129    let diff_base = r#"
12130        use some::mod1;
12131        use some::mod2;
12132
12133        const A: u32 = 42;
12134        const B: u32 = 42;
12135        const C: u32 = 42;
12136
12137        fn main() {
12138            println!("hello");
12139
12140            println!("world");
12141        }
12142        "#
12143    .unindent();
12144
12145    cx.set_state(
12146        &r#"
12147        use some::mod2;
12148
12149        const A: u32 = 42;
12150        const C: u32 = 42;
12151
12152        fn main(ˇ) {
12153            //println!("hello");
12154
12155            println!("world");
12156            //
12157            //
12158        }
12159        "#
12160        .unindent(),
12161    );
12162
12163    cx.set_diff_base(&diff_base);
12164    executor.run_until_parked();
12165
12166    cx.update_editor(|editor, cx| {
12167        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12168    });
12169    executor.run_until_parked();
12170    cx.assert_state_with_diff(
12171        r#"
12172        - use some::mod1;
12173          use some::mod2;
12174
12175          const A: u32 = 42;
12176        - const B: u32 = 42;
12177          const C: u32 = 42;
12178
12179          fn main(ˇ) {
12180        -     println!("hello");
12181        +     //println!("hello");
12182
12183              println!("world");
12184        +     //
12185        +     //
12186          }
12187        "#
12188        .unindent(),
12189    );
12190
12191    cx.set_diff_base("new diff base!");
12192    executor.run_until_parked();
12193    cx.assert_state_with_diff(
12194        r#"
12195          use some::mod2;
12196
12197          const A: u32 = 42;
12198          const C: u32 = 42;
12199
12200          fn main(ˇ) {
12201              //println!("hello");
12202
12203              println!("world");
12204              //
12205              //
12206          }
12207        "#
12208        .unindent(),
12209    );
12210
12211    cx.update_editor(|editor, cx| {
12212        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12213    });
12214    executor.run_until_parked();
12215    cx.assert_state_with_diff(
12216        r#"
12217        - new diff base!
12218        + use some::mod2;
12219        +
12220        + const A: u32 = 42;
12221        + const C: u32 = 42;
12222        +
12223        + fn main(ˇ) {
12224        +     //println!("hello");
12225        +
12226        +     println!("world");
12227        +     //
12228        +     //
12229        + }
12230        "#
12231        .unindent(),
12232    );
12233}
12234
12235#[gpui::test]
12236async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12237    init_test(cx, |_| {});
12238
12239    let mut cx = EditorTestContext::new(cx).await;
12240
12241    let diff_base = r#"
12242        use some::mod1;
12243        use some::mod2;
12244
12245        const A: u32 = 42;
12246        const B: u32 = 42;
12247        const C: u32 = 42;
12248
12249        fn main() {
12250            println!("hello");
12251
12252            println!("world");
12253        }
12254
12255        fn another() {
12256            println!("another");
12257        }
12258
12259        fn another2() {
12260            println!("another2");
12261        }
12262        "#
12263    .unindent();
12264
12265    cx.set_state(
12266        &r#"
12267        «use some::mod2;
12268
12269        const A: u32 = 42;
12270        const C: u32 = 42;
12271
12272        fn main() {
12273            //println!("hello");
12274
12275            println!("world");
12276            //
12277            //ˇ»
12278        }
12279
12280        fn another() {
12281            println!("another");
12282            println!("another");
12283        }
12284
12285            println!("another2");
12286        }
12287        "#
12288        .unindent(),
12289    );
12290
12291    cx.set_diff_base(&diff_base);
12292    executor.run_until_parked();
12293
12294    cx.update_editor(|editor, cx| {
12295        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12296    });
12297    executor.run_until_parked();
12298
12299    cx.assert_state_with_diff(
12300        r#"
12301        - use some::mod1;
12302          «use some::mod2;
12303
12304          const A: u32 = 42;
12305        - const B: u32 = 42;
12306          const C: u32 = 42;
12307
12308          fn main() {
12309        -     println!("hello");
12310        +     //println!("hello");
12311
12312              println!("world");
12313        +     //
12314        +     //ˇ»
12315          }
12316
12317          fn another() {
12318              println!("another");
12319        +     println!("another");
12320          }
12321
12322        - fn another2() {
12323              println!("another2");
12324          }
12325        "#
12326        .unindent(),
12327    );
12328
12329    // Fold across some of the diff hunks. They should no longer appear expanded.
12330    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12331    cx.executor().run_until_parked();
12332
12333    // Hunks are not shown if their position is within a fold
12334    cx.assert_state_with_diff(
12335        r#"
12336          «use some::mod2;
12337
12338          const A: u32 = 42;
12339          const C: u32 = 42;
12340
12341          fn main() {
12342              //println!("hello");
12343
12344              println!("world");
12345              //
12346              //ˇ»
12347          }
12348
12349          fn another() {
12350              println!("another");
12351        +     println!("another");
12352          }
12353
12354        - fn another2() {
12355              println!("another2");
12356          }
12357        "#
12358        .unindent(),
12359    );
12360
12361    cx.update_editor(|editor, cx| {
12362        editor.select_all(&SelectAll, cx);
12363        editor.unfold_lines(&UnfoldLines, cx);
12364    });
12365    cx.executor().run_until_parked();
12366
12367    // The deletions reappear when unfolding.
12368    cx.assert_state_with_diff(
12369        r#"
12370        - use some::mod1;
12371          «use some::mod2;
12372
12373          const A: u32 = 42;
12374        - const B: u32 = 42;
12375          const C: u32 = 42;
12376
12377          fn main() {
12378        -     println!("hello");
12379        +     //println!("hello");
12380
12381              println!("world");
12382        +     //
12383        +     //
12384          }
12385
12386          fn another() {
12387              println!("another");
12388        +     println!("another");
12389          }
12390
12391        - fn another2() {
12392              println!("another2");
12393          }
12394          ˇ»"#
12395        .unindent(),
12396    );
12397}
12398
12399#[gpui::test]
12400async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12401    init_test(cx, |_| {});
12402
12403    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12404    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12405    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12406    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12407    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12408    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12409
12410    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12411    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12412    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12413
12414    let multi_buffer = cx.new_model(|cx| {
12415        let mut multibuffer = MultiBuffer::new(ReadWrite);
12416        multibuffer.push_excerpts(
12417            buffer_1.clone(),
12418            [
12419                ExcerptRange {
12420                    context: Point::new(0, 0)..Point::new(3, 0),
12421                    primary: None,
12422                },
12423                ExcerptRange {
12424                    context: Point::new(5, 0)..Point::new(7, 0),
12425                    primary: None,
12426                },
12427                ExcerptRange {
12428                    context: Point::new(9, 0)..Point::new(10, 3),
12429                    primary: None,
12430                },
12431            ],
12432            cx,
12433        );
12434        multibuffer.push_excerpts(
12435            buffer_2.clone(),
12436            [
12437                ExcerptRange {
12438                    context: Point::new(0, 0)..Point::new(3, 0),
12439                    primary: None,
12440                },
12441                ExcerptRange {
12442                    context: Point::new(5, 0)..Point::new(7, 0),
12443                    primary: None,
12444                },
12445                ExcerptRange {
12446                    context: Point::new(9, 0)..Point::new(10, 3),
12447                    primary: None,
12448                },
12449            ],
12450            cx,
12451        );
12452        multibuffer.push_excerpts(
12453            buffer_3.clone(),
12454            [
12455                ExcerptRange {
12456                    context: Point::new(0, 0)..Point::new(3, 0),
12457                    primary: None,
12458                },
12459                ExcerptRange {
12460                    context: Point::new(5, 0)..Point::new(7, 0),
12461                    primary: None,
12462                },
12463                ExcerptRange {
12464                    context: Point::new(9, 0)..Point::new(10, 3),
12465                    primary: None,
12466                },
12467            ],
12468            cx,
12469        );
12470        multibuffer
12471    });
12472
12473    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12474    editor
12475        .update(cx, |editor, cx| {
12476            for (buffer, diff_base) in [
12477                (buffer_1.clone(), file_1_old),
12478                (buffer_2.clone(), file_2_old),
12479                (buffer_3.clone(), file_3_old),
12480            ] {
12481                let change_set = cx.new_model(|cx| {
12482                    BufferChangeSet::new_with_base_text(
12483                        diff_base.to_string(),
12484                        buffer.read(cx).text_snapshot(),
12485                        cx,
12486                    )
12487                });
12488                editor.diff_map.add_change_set(change_set, cx)
12489            }
12490        })
12491        .unwrap();
12492
12493    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12494    cx.run_until_parked();
12495
12496    cx.assert_editor_state(
12497        &"
12498            ˇaaa
12499            ccc
12500            ddd
12501
12502            ggg
12503            hhh
12504
12505
12506            lll
12507            mmm
12508            NNN
12509
12510            qqq
12511            rrr
12512
12513            uuu
12514            111
12515            222
12516            333
12517
12518            666
12519            777
12520
12521            000
12522            !!!"
12523        .unindent(),
12524    );
12525
12526    cx.update_editor(|editor, cx| {
12527        editor.select_all(&SelectAll, cx);
12528        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12529    });
12530    cx.executor().run_until_parked();
12531
12532    cx.assert_state_with_diff(
12533        "
12534            «aaa
12535          - bbb
12536            ccc
12537            ddd
12538
12539            ggg
12540            hhh
12541
12542
12543            lll
12544            mmm
12545          - nnn
12546          + NNN
12547
12548            qqq
12549            rrr
12550
12551            uuu
12552            111
12553            222
12554            333
12555
12556          + 666
12557            777
12558
12559            000
12560            !!!ˇ»"
12561            .unindent(),
12562    );
12563}
12564
12565#[gpui::test]
12566async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12567    init_test(cx, |_| {});
12568
12569    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12570    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12571
12572    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12573    let multi_buffer = cx.new_model(|cx| {
12574        let mut multibuffer = MultiBuffer::new(ReadWrite);
12575        multibuffer.push_excerpts(
12576            buffer.clone(),
12577            [
12578                ExcerptRange {
12579                    context: Point::new(0, 0)..Point::new(2, 0),
12580                    primary: None,
12581                },
12582                ExcerptRange {
12583                    context: Point::new(5, 0)..Point::new(7, 0),
12584                    primary: None,
12585                },
12586            ],
12587            cx,
12588        );
12589        multibuffer
12590    });
12591
12592    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12593    editor
12594        .update(cx, |editor, cx| {
12595            let buffer = buffer.read(cx).text_snapshot();
12596            let change_set = cx
12597                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12598            editor.diff_map.add_change_set(change_set, cx)
12599        })
12600        .unwrap();
12601
12602    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12603    cx.run_until_parked();
12604
12605    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12606    cx.executor().run_until_parked();
12607
12608    cx.assert_state_with_diff(
12609        "
12610            ˇaaa
12611          - bbb
12612          + BBB
12613
12614          - ddd
12615          - eee
12616          + EEE
12617            fff
12618        "
12619        .unindent(),
12620    );
12621}
12622
12623#[gpui::test]
12624async fn test_edits_around_expanded_insertion_hunks(
12625    executor: BackgroundExecutor,
12626    cx: &mut gpui::TestAppContext,
12627) {
12628    init_test(cx, |_| {});
12629
12630    let mut cx = EditorTestContext::new(cx).await;
12631
12632    let diff_base = r#"
12633        use some::mod1;
12634        use some::mod2;
12635
12636        const A: u32 = 42;
12637
12638        fn main() {
12639            println!("hello");
12640
12641            println!("world");
12642        }
12643        "#
12644    .unindent();
12645    executor.run_until_parked();
12646    cx.set_state(
12647        &r#"
12648        use some::mod1;
12649        use some::mod2;
12650
12651        const A: u32 = 42;
12652        const B: u32 = 42;
12653        const C: u32 = 42;
12654        ˇ
12655
12656        fn main() {
12657            println!("hello");
12658
12659            println!("world");
12660        }
12661        "#
12662        .unindent(),
12663    );
12664
12665    cx.set_diff_base(&diff_base);
12666    executor.run_until_parked();
12667
12668    cx.update_editor(|editor, cx| {
12669        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12670    });
12671    executor.run_until_parked();
12672
12673    cx.assert_state_with_diff(
12674        r#"
12675        use some::mod1;
12676        use some::mod2;
12677
12678        const A: u32 = 42;
12679      + const B: u32 = 42;
12680      + const C: u32 = 42;
12681      + ˇ
12682
12683        fn main() {
12684            println!("hello");
12685
12686            println!("world");
12687        }
12688        "#
12689        .unindent(),
12690    );
12691
12692    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12693    executor.run_until_parked();
12694
12695    cx.assert_state_with_diff(
12696        r#"
12697        use some::mod1;
12698        use some::mod2;
12699
12700        const A: u32 = 42;
12701      + const B: u32 = 42;
12702      + const C: u32 = 42;
12703      + const D: u32 = 42;
12704      + ˇ
12705
12706        fn main() {
12707            println!("hello");
12708
12709            println!("world");
12710        }
12711        "#
12712        .unindent(),
12713    );
12714
12715    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12716    executor.run_until_parked();
12717
12718    cx.assert_state_with_diff(
12719        r#"
12720        use some::mod1;
12721        use some::mod2;
12722
12723        const A: u32 = 42;
12724      + const B: u32 = 42;
12725      + const C: u32 = 42;
12726      + const D: u32 = 42;
12727      + const E: u32 = 42;
12728      + ˇ
12729
12730        fn main() {
12731            println!("hello");
12732
12733            println!("world");
12734        }
12735        "#
12736        .unindent(),
12737    );
12738
12739    cx.update_editor(|editor, cx| {
12740        editor.delete_line(&DeleteLine, cx);
12741    });
12742    executor.run_until_parked();
12743
12744    cx.assert_state_with_diff(
12745        r#"
12746        use some::mod1;
12747        use some::mod2;
12748
12749        const A: u32 = 42;
12750      + const B: u32 = 42;
12751      + const C: u32 = 42;
12752      + const D: u32 = 42;
12753      + const E: u32 = 42;
12754        ˇ
12755        fn main() {
12756            println!("hello");
12757
12758            println!("world");
12759        }
12760        "#
12761        .unindent(),
12762    );
12763
12764    cx.update_editor(|editor, cx| {
12765        editor.move_up(&MoveUp, cx);
12766        editor.delete_line(&DeleteLine, cx);
12767        editor.move_up(&MoveUp, cx);
12768        editor.delete_line(&DeleteLine, cx);
12769        editor.move_up(&MoveUp, cx);
12770        editor.delete_line(&DeleteLine, cx);
12771    });
12772    executor.run_until_parked();
12773    cx.assert_state_with_diff(
12774        r#"
12775        use some::mod1;
12776        use some::mod2;
12777
12778        const A: u32 = 42;
12779      + const B: u32 = 42;
12780        ˇ
12781        fn main() {
12782            println!("hello");
12783
12784            println!("world");
12785        }
12786        "#
12787        .unindent(),
12788    );
12789
12790    cx.update_editor(|editor, cx| {
12791        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12792        editor.delete_line(&DeleteLine, cx);
12793    });
12794    executor.run_until_parked();
12795    cx.assert_state_with_diff(
12796        r#"
12797        use some::mod1;
12798      - use some::mod2;
12799      -
12800      - const A: u32 = 42;
12801        ˇ
12802        fn main() {
12803            println!("hello");
12804
12805            println!("world");
12806        }
12807        "#
12808        .unindent(),
12809    );
12810}
12811
12812#[gpui::test]
12813async fn test_edits_around_expanded_deletion_hunks(
12814    executor: BackgroundExecutor,
12815    cx: &mut gpui::TestAppContext,
12816) {
12817    init_test(cx, |_| {});
12818
12819    let mut cx = EditorTestContext::new(cx).await;
12820
12821    let diff_base = r#"
12822        use some::mod1;
12823        use some::mod2;
12824
12825        const A: u32 = 42;
12826        const B: u32 = 42;
12827        const C: u32 = 42;
12828
12829
12830        fn main() {
12831            println!("hello");
12832
12833            println!("world");
12834        }
12835    "#
12836    .unindent();
12837    executor.run_until_parked();
12838    cx.set_state(
12839        &r#"
12840        use some::mod1;
12841        use some::mod2;
12842
12843        ˇconst B: u32 = 42;
12844        const C: u32 = 42;
12845
12846
12847        fn main() {
12848            println!("hello");
12849
12850            println!("world");
12851        }
12852        "#
12853        .unindent(),
12854    );
12855
12856    cx.set_diff_base(&diff_base);
12857    executor.run_until_parked();
12858
12859    cx.update_editor(|editor, cx| {
12860        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12861    });
12862    executor.run_until_parked();
12863
12864    cx.assert_state_with_diff(
12865        r#"
12866        use some::mod1;
12867        use some::mod2;
12868
12869      - const A: u32 = 42;
12870        ˇconst B: u32 = 42;
12871        const C: u32 = 42;
12872
12873
12874        fn main() {
12875            println!("hello");
12876
12877            println!("world");
12878        }
12879        "#
12880        .unindent(),
12881    );
12882
12883    cx.update_editor(|editor, cx| {
12884        editor.delete_line(&DeleteLine, cx);
12885    });
12886    executor.run_until_parked();
12887    cx.assert_state_with_diff(
12888        r#"
12889        use some::mod1;
12890        use some::mod2;
12891
12892      - const A: u32 = 42;
12893      - const B: u32 = 42;
12894        ˇconst C: u32 = 42;
12895
12896
12897        fn main() {
12898            println!("hello");
12899
12900            println!("world");
12901        }
12902        "#
12903        .unindent(),
12904    );
12905
12906    cx.update_editor(|editor, cx| {
12907        editor.delete_line(&DeleteLine, cx);
12908    });
12909    executor.run_until_parked();
12910    cx.assert_state_with_diff(
12911        r#"
12912        use some::mod1;
12913        use some::mod2;
12914
12915      - const A: u32 = 42;
12916      - const B: u32 = 42;
12917      - const C: u32 = 42;
12918        ˇ
12919
12920        fn main() {
12921            println!("hello");
12922
12923            println!("world");
12924        }
12925        "#
12926        .unindent(),
12927    );
12928
12929    cx.update_editor(|editor, cx| {
12930        editor.handle_input("replacement", cx);
12931    });
12932    executor.run_until_parked();
12933    cx.assert_state_with_diff(
12934        r#"
12935        use some::mod1;
12936        use some::mod2;
12937
12938      - const A: u32 = 42;
12939      - const B: u32 = 42;
12940      - const C: u32 = 42;
12941      -
12942      + replacementˇ
12943
12944        fn main() {
12945            println!("hello");
12946
12947            println!("world");
12948        }
12949        "#
12950        .unindent(),
12951    );
12952}
12953
12954#[gpui::test]
12955async fn test_edit_after_expanded_modification_hunk(
12956    executor: BackgroundExecutor,
12957    cx: &mut gpui::TestAppContext,
12958) {
12959    init_test(cx, |_| {});
12960
12961    let mut cx = EditorTestContext::new(cx).await;
12962
12963    let diff_base = r#"
12964        use some::mod1;
12965        use some::mod2;
12966
12967        const A: u32 = 42;
12968        const B: u32 = 42;
12969        const C: u32 = 42;
12970        const D: u32 = 42;
12971
12972
12973        fn main() {
12974            println!("hello");
12975
12976            println!("world");
12977        }"#
12978    .unindent();
12979
12980    cx.set_state(
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 = 43ˇ
12988        const D: u32 = 42;
12989
12990
12991        fn main() {
12992            println!("hello");
12993
12994            println!("world");
12995        }"#
12996        .unindent(),
12997    );
12998
12999    cx.set_diff_base(&diff_base);
13000    executor.run_until_parked();
13001    cx.update_editor(|editor, cx| {
13002        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13003    });
13004    executor.run_until_parked();
13005
13006    cx.assert_state_with_diff(
13007        r#"
13008        use some::mod1;
13009        use some::mod2;
13010
13011        const A: u32 = 42;
13012        const B: u32 = 42;
13013      - const C: u32 = 42;
13014      + const C: u32 = 43ˇ
13015        const D: u32 = 42;
13016
13017
13018        fn main() {
13019            println!("hello");
13020
13021            println!("world");
13022        }"#
13023        .unindent(),
13024    );
13025
13026    cx.update_editor(|editor, cx| {
13027        editor.handle_input("\nnew_line\n", cx);
13028    });
13029    executor.run_until_parked();
13030
13031    cx.assert_state_with_diff(
13032        r#"
13033        use some::mod1;
13034        use some::mod2;
13035
13036        const A: u32 = 42;
13037        const B: u32 = 42;
13038      - const C: u32 = 42;
13039      + const C: u32 = 43
13040      + new_line
13041      + ˇ
13042        const D: u32 = 42;
13043
13044
13045        fn main() {
13046            println!("hello");
13047
13048            println!("world");
13049        }"#
13050        .unindent(),
13051    );
13052}
13053
13054async fn setup_indent_guides_editor(
13055    text: &str,
13056    cx: &mut gpui::TestAppContext,
13057) -> (BufferId, EditorTestContext) {
13058    init_test(cx, |_| {});
13059
13060    let mut cx = EditorTestContext::new(cx).await;
13061
13062    let buffer_id = cx.update_editor(|editor, cx| {
13063        editor.set_text(text, cx);
13064        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13065
13066        buffer_ids[0]
13067    });
13068
13069    (buffer_id, cx)
13070}
13071
13072fn assert_indent_guides(
13073    range: Range<u32>,
13074    expected: Vec<IndentGuide>,
13075    active_indices: Option<Vec<usize>>,
13076    cx: &mut EditorTestContext,
13077) {
13078    let indent_guides = cx.update_editor(|editor, cx| {
13079        let snapshot = editor.snapshot(cx).display_snapshot;
13080        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13081            editor,
13082            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13083            true,
13084            &snapshot,
13085            cx,
13086        );
13087
13088        indent_guides.sort_by(|a, b| {
13089            a.depth.cmp(&b.depth).then(
13090                a.start_row
13091                    .cmp(&b.start_row)
13092                    .then(a.end_row.cmp(&b.end_row)),
13093            )
13094        });
13095        indent_guides
13096    });
13097
13098    if let Some(expected) = active_indices {
13099        let active_indices = cx.update_editor(|editor, cx| {
13100            let snapshot = editor.snapshot(cx).display_snapshot;
13101            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13102        });
13103
13104        assert_eq!(
13105            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13106            expected,
13107            "Active indent guide indices do not match"
13108        );
13109    }
13110
13111    let expected: Vec<_> = expected
13112        .into_iter()
13113        .map(|guide| MultiBufferIndentGuide {
13114            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13115            buffer: guide,
13116        })
13117        .collect();
13118
13119    assert_eq!(indent_guides, expected, "Indent guides do not match");
13120}
13121
13122fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13123    IndentGuide {
13124        buffer_id,
13125        start_row,
13126        end_row,
13127        depth,
13128        tab_size: 4,
13129        settings: IndentGuideSettings {
13130            enabled: true,
13131            line_width: 1,
13132            active_line_width: 1,
13133            ..Default::default()
13134        },
13135    }
13136}
13137
13138#[gpui::test]
13139async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13140    let (buffer_id, mut cx) = setup_indent_guides_editor(
13141        &"
13142    fn main() {
13143        let a = 1;
13144    }"
13145        .unindent(),
13146        cx,
13147    )
13148    .await;
13149
13150    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13151}
13152
13153#[gpui::test]
13154async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13155    let (buffer_id, mut cx) = setup_indent_guides_editor(
13156        &"
13157    fn main() {
13158        let a = 1;
13159        let b = 2;
13160    }"
13161        .unindent(),
13162        cx,
13163    )
13164    .await;
13165
13166    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13167}
13168
13169#[gpui::test]
13170async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13171    let (buffer_id, mut cx) = setup_indent_guides_editor(
13172        &"
13173    fn main() {
13174        let a = 1;
13175        if a == 3 {
13176            let b = 2;
13177        } else {
13178            let c = 3;
13179        }
13180    }"
13181        .unindent(),
13182        cx,
13183    )
13184    .await;
13185
13186    assert_indent_guides(
13187        0..8,
13188        vec![
13189            indent_guide(buffer_id, 1, 6, 0),
13190            indent_guide(buffer_id, 3, 3, 1),
13191            indent_guide(buffer_id, 5, 5, 1),
13192        ],
13193        None,
13194        &mut cx,
13195    );
13196}
13197
13198#[gpui::test]
13199async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13200    let (buffer_id, mut cx) = setup_indent_guides_editor(
13201        &"
13202    fn main() {
13203        let a = 1;
13204            let b = 2;
13205        let c = 3;
13206    }"
13207        .unindent(),
13208        cx,
13209    )
13210    .await;
13211
13212    assert_indent_guides(
13213        0..5,
13214        vec![
13215            indent_guide(buffer_id, 1, 3, 0),
13216            indent_guide(buffer_id, 2, 2, 1),
13217        ],
13218        None,
13219        &mut cx,
13220    );
13221}
13222
13223#[gpui::test]
13224async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13225    let (buffer_id, mut cx) = setup_indent_guides_editor(
13226        &"
13227        fn main() {
13228            let a = 1;
13229
13230            let c = 3;
13231        }"
13232        .unindent(),
13233        cx,
13234    )
13235    .await;
13236
13237    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13238}
13239
13240#[gpui::test]
13241async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13242    let (buffer_id, mut cx) = setup_indent_guides_editor(
13243        &"
13244        fn main() {
13245            let a = 1;
13246
13247            let c = 3;
13248
13249            if a == 3 {
13250                let b = 2;
13251            } else {
13252                let c = 3;
13253            }
13254        }"
13255        .unindent(),
13256        cx,
13257    )
13258    .await;
13259
13260    assert_indent_guides(
13261        0..11,
13262        vec![
13263            indent_guide(buffer_id, 1, 9, 0),
13264            indent_guide(buffer_id, 6, 6, 1),
13265            indent_guide(buffer_id, 8, 8, 1),
13266        ],
13267        None,
13268        &mut cx,
13269    );
13270}
13271
13272#[gpui::test]
13273async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13274    let (buffer_id, mut cx) = setup_indent_guides_editor(
13275        &"
13276        fn main() {
13277            let a = 1;
13278
13279            let c = 3;
13280
13281            if a == 3 {
13282                let b = 2;
13283            } else {
13284                let c = 3;
13285            }
13286        }"
13287        .unindent(),
13288        cx,
13289    )
13290    .await;
13291
13292    assert_indent_guides(
13293        1..11,
13294        vec![
13295            indent_guide(buffer_id, 1, 9, 0),
13296            indent_guide(buffer_id, 6, 6, 1),
13297            indent_guide(buffer_id, 8, 8, 1),
13298        ],
13299        None,
13300        &mut cx,
13301    );
13302}
13303
13304#[gpui::test]
13305async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13306    let (buffer_id, mut cx) = setup_indent_guides_editor(
13307        &"
13308        fn main() {
13309            let a = 1;
13310
13311            let c = 3;
13312
13313            if a == 3 {
13314                let b = 2;
13315            } else {
13316                let c = 3;
13317            }
13318        }"
13319        .unindent(),
13320        cx,
13321    )
13322    .await;
13323
13324    assert_indent_guides(
13325        1..10,
13326        vec![
13327            indent_guide(buffer_id, 1, 9, 0),
13328            indent_guide(buffer_id, 6, 6, 1),
13329            indent_guide(buffer_id, 8, 8, 1),
13330        ],
13331        None,
13332        &mut cx,
13333    );
13334}
13335
13336#[gpui::test]
13337async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13338    let (buffer_id, mut cx) = setup_indent_guides_editor(
13339        &"
13340        block1
13341            block2
13342                block3
13343                    block4
13344            block2
13345        block1
13346        block1"
13347            .unindent(),
13348        cx,
13349    )
13350    .await;
13351
13352    assert_indent_guides(
13353        1..10,
13354        vec![
13355            indent_guide(buffer_id, 1, 4, 0),
13356            indent_guide(buffer_id, 2, 3, 1),
13357            indent_guide(buffer_id, 3, 3, 2),
13358        ],
13359        None,
13360        &mut cx,
13361    );
13362}
13363
13364#[gpui::test]
13365async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13366    let (buffer_id, mut cx) = setup_indent_guides_editor(
13367        &"
13368        block1
13369            block2
13370                block3
13371
13372        block1
13373        block1"
13374            .unindent(),
13375        cx,
13376    )
13377    .await;
13378
13379    assert_indent_guides(
13380        0..6,
13381        vec![
13382            indent_guide(buffer_id, 1, 2, 0),
13383            indent_guide(buffer_id, 2, 2, 1),
13384        ],
13385        None,
13386        &mut cx,
13387    );
13388}
13389
13390#[gpui::test]
13391async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13392    let (buffer_id, mut cx) = setup_indent_guides_editor(
13393        &"
13394        block1
13395
13396
13397
13398            block2
13399        "
13400        .unindent(),
13401        cx,
13402    )
13403    .await;
13404
13405    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13406}
13407
13408#[gpui::test]
13409async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13410    let (buffer_id, mut cx) = setup_indent_guides_editor(
13411        &"
13412        def a:
13413        \tb = 3
13414        \tif True:
13415        \t\tc = 4
13416        \t\td = 5
13417        \tprint(b)
13418        "
13419        .unindent(),
13420        cx,
13421    )
13422    .await;
13423
13424    assert_indent_guides(
13425        0..6,
13426        vec![
13427            indent_guide(buffer_id, 1, 6, 0),
13428            indent_guide(buffer_id, 3, 4, 1),
13429        ],
13430        None,
13431        &mut cx,
13432    );
13433}
13434
13435#[gpui::test]
13436async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13437    let (buffer_id, mut cx) = setup_indent_guides_editor(
13438        &"
13439    fn main() {
13440        let a = 1;
13441    }"
13442        .unindent(),
13443        cx,
13444    )
13445    .await;
13446
13447    cx.update_editor(|editor, cx| {
13448        editor.change_selections(None, cx, |s| {
13449            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13450        });
13451    });
13452
13453    assert_indent_guides(
13454        0..3,
13455        vec![indent_guide(buffer_id, 1, 1, 0)],
13456        Some(vec![0]),
13457        &mut cx,
13458    );
13459}
13460
13461#[gpui::test]
13462async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13463    let (buffer_id, mut cx) = setup_indent_guides_editor(
13464        &"
13465    fn main() {
13466        if 1 == 2 {
13467            let a = 1;
13468        }
13469    }"
13470        .unindent(),
13471        cx,
13472    )
13473    .await;
13474
13475    cx.update_editor(|editor, cx| {
13476        editor.change_selections(None, cx, |s| {
13477            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13478        });
13479    });
13480
13481    assert_indent_guides(
13482        0..4,
13483        vec![
13484            indent_guide(buffer_id, 1, 3, 0),
13485            indent_guide(buffer_id, 2, 2, 1),
13486        ],
13487        Some(vec![1]),
13488        &mut cx,
13489    );
13490
13491    cx.update_editor(|editor, cx| {
13492        editor.change_selections(None, cx, |s| {
13493            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13494        });
13495    });
13496
13497    assert_indent_guides(
13498        0..4,
13499        vec![
13500            indent_guide(buffer_id, 1, 3, 0),
13501            indent_guide(buffer_id, 2, 2, 1),
13502        ],
13503        Some(vec![1]),
13504        &mut cx,
13505    );
13506
13507    cx.update_editor(|editor, cx| {
13508        editor.change_selections(None, cx, |s| {
13509            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13510        });
13511    });
13512
13513    assert_indent_guides(
13514        0..4,
13515        vec![
13516            indent_guide(buffer_id, 1, 3, 0),
13517            indent_guide(buffer_id, 2, 2, 1),
13518        ],
13519        Some(vec![0]),
13520        &mut cx,
13521    );
13522}
13523
13524#[gpui::test]
13525async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13526    let (buffer_id, mut cx) = setup_indent_guides_editor(
13527        &"
13528    fn main() {
13529        let a = 1;
13530
13531        let b = 2;
13532    }"
13533        .unindent(),
13534        cx,
13535    )
13536    .await;
13537
13538    cx.update_editor(|editor, cx| {
13539        editor.change_selections(None, cx, |s| {
13540            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13541        });
13542    });
13543
13544    assert_indent_guides(
13545        0..5,
13546        vec![indent_guide(buffer_id, 1, 3, 0)],
13547        Some(vec![0]),
13548        &mut cx,
13549    );
13550}
13551
13552#[gpui::test]
13553async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13554    let (buffer_id, mut cx) = setup_indent_guides_editor(
13555        &"
13556    def m:
13557        a = 1
13558        pass"
13559            .unindent(),
13560        cx,
13561    )
13562    .await;
13563
13564    cx.update_editor(|editor, cx| {
13565        editor.change_selections(None, cx, |s| {
13566            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13567        });
13568    });
13569
13570    assert_indent_guides(
13571        0..3,
13572        vec![indent_guide(buffer_id, 1, 2, 0)],
13573        Some(vec![0]),
13574        &mut cx,
13575    );
13576}
13577
13578#[gpui::test]
13579fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13580    init_test(cx, |_| {});
13581
13582    let editor = cx.add_window(|cx| {
13583        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13584        build_editor(buffer, cx)
13585    });
13586
13587    let render_args = Arc::new(Mutex::new(None));
13588    let snapshot = editor
13589        .update(cx, |editor, cx| {
13590            let snapshot = editor.buffer().read(cx).snapshot(cx);
13591            let range =
13592                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13593
13594            struct RenderArgs {
13595                row: MultiBufferRow,
13596                folded: bool,
13597                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13598            }
13599
13600            let crease = Crease::inline(
13601                range,
13602                FoldPlaceholder::test(),
13603                {
13604                    let toggle_callback = render_args.clone();
13605                    move |row, folded, callback, _cx| {
13606                        *toggle_callback.lock() = Some(RenderArgs {
13607                            row,
13608                            folded,
13609                            callback,
13610                        });
13611                        div()
13612                    }
13613                },
13614                |_row, _folded, _cx| div(),
13615            );
13616
13617            editor.insert_creases(Some(crease), cx);
13618            let snapshot = editor.snapshot(cx);
13619            let _div =
13620                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13621            snapshot
13622        })
13623        .unwrap();
13624
13625    let render_args = render_args.lock().take().unwrap();
13626    assert_eq!(render_args.row, MultiBufferRow(1));
13627    assert!(!render_args.folded);
13628    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13629
13630    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13631        .unwrap();
13632    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13633    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13634
13635    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13636        .unwrap();
13637    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13638    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13639}
13640
13641#[gpui::test]
13642async fn test_input_text(cx: &mut gpui::TestAppContext) {
13643    init_test(cx, |_| {});
13644    let mut cx = EditorTestContext::new(cx).await;
13645
13646    cx.set_state(
13647        &r#"ˇone
13648        two
13649
13650        three
13651        fourˇ
13652        five
13653
13654        siˇx"#
13655            .unindent(),
13656    );
13657
13658    cx.dispatch_action(HandleInput(String::new()));
13659    cx.assert_editor_state(
13660        &r#"ˇone
13661        two
13662
13663        three
13664        fourˇ
13665        five
13666
13667        siˇx"#
13668            .unindent(),
13669    );
13670
13671    cx.dispatch_action(HandleInput("AAAA".to_string()));
13672    cx.assert_editor_state(
13673        &r#"AAAAˇone
13674        two
13675
13676        three
13677        fourAAAAˇ
13678        five
13679
13680        siAAAAˇx"#
13681            .unindent(),
13682    );
13683}
13684
13685#[gpui::test]
13686async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13687    init_test(cx, |_| {});
13688
13689    let mut cx = EditorTestContext::new(cx).await;
13690    cx.set_state(
13691        r#"let foo = 1;
13692let foo = 2;
13693let foo = 3;
13694let fooˇ = 4;
13695let foo = 5;
13696let foo = 6;
13697let foo = 7;
13698let foo = 8;
13699let foo = 9;
13700let foo = 10;
13701let foo = 11;
13702let foo = 12;
13703let foo = 13;
13704let foo = 14;
13705let foo = 15;"#,
13706    );
13707
13708    cx.update_editor(|e, cx| {
13709        assert_eq!(
13710            e.next_scroll_position,
13711            NextScrollCursorCenterTopBottom::Center,
13712            "Default next scroll direction is center",
13713        );
13714
13715        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13716        assert_eq!(
13717            e.next_scroll_position,
13718            NextScrollCursorCenterTopBottom::Top,
13719            "After center, next scroll direction should be top",
13720        );
13721
13722        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13723        assert_eq!(
13724            e.next_scroll_position,
13725            NextScrollCursorCenterTopBottom::Bottom,
13726            "After top, next scroll direction should be bottom",
13727        );
13728
13729        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13730        assert_eq!(
13731            e.next_scroll_position,
13732            NextScrollCursorCenterTopBottom::Center,
13733            "After bottom, scrolling should start over",
13734        );
13735
13736        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13737        assert_eq!(
13738            e.next_scroll_position,
13739            NextScrollCursorCenterTopBottom::Top,
13740            "Scrolling continues if retriggered fast enough"
13741        );
13742    });
13743
13744    cx.executor()
13745        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13746    cx.executor().run_until_parked();
13747    cx.update_editor(|e, _| {
13748        assert_eq!(
13749            e.next_scroll_position,
13750            NextScrollCursorCenterTopBottom::Center,
13751            "If scrolling is not triggered fast enough, it should reset"
13752        );
13753    });
13754}
13755
13756#[gpui::test]
13757async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13758    init_test(cx, |_| {});
13759    let mut cx = EditorLspTestContext::new_rust(
13760        lsp::ServerCapabilities {
13761            definition_provider: Some(lsp::OneOf::Left(true)),
13762            references_provider: Some(lsp::OneOf::Left(true)),
13763            ..lsp::ServerCapabilities::default()
13764        },
13765        cx,
13766    )
13767    .await;
13768
13769    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13770        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13771            move |params, _| async move {
13772                if empty_go_to_definition {
13773                    Ok(None)
13774                } else {
13775                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13776                        uri: params.text_document_position_params.text_document.uri,
13777                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13778                    })))
13779                }
13780            },
13781        );
13782        let references =
13783            cx.lsp
13784                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13785                    Ok(Some(vec![lsp::Location {
13786                        uri: params.text_document_position.text_document.uri,
13787                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13788                    }]))
13789                });
13790        (go_to_definition, references)
13791    };
13792
13793    cx.set_state(
13794        &r#"fn one() {
13795            let mut a = ˇtwo();
13796        }
13797
13798        fn two() {}"#
13799            .unindent(),
13800    );
13801    set_up_lsp_handlers(false, &mut cx);
13802    let navigated = cx
13803        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13804        .await
13805        .expect("Failed to navigate to definition");
13806    assert_eq!(
13807        navigated,
13808        Navigated::Yes,
13809        "Should have navigated to definition from the GetDefinition response"
13810    );
13811    cx.assert_editor_state(
13812        &r#"fn one() {
13813            let mut a = two();
13814        }
13815
13816        fn «twoˇ»() {}"#
13817            .unindent(),
13818    );
13819
13820    let editors = cx.update_workspace(|workspace, cx| {
13821        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13822    });
13823    cx.update_editor(|_, test_editor_cx| {
13824        assert_eq!(
13825            editors.len(),
13826            1,
13827            "Initially, only one, test, editor should be open in the workspace"
13828        );
13829        assert_eq!(
13830            test_editor_cx.view(),
13831            editors.last().expect("Asserted len is 1")
13832        );
13833    });
13834
13835    set_up_lsp_handlers(true, &mut cx);
13836    let navigated = cx
13837        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13838        .await
13839        .expect("Failed to navigate to lookup references");
13840    assert_eq!(
13841        navigated,
13842        Navigated::Yes,
13843        "Should have navigated to references as a fallback after empty GoToDefinition response"
13844    );
13845    // We should not change the selections in the existing file,
13846    // if opening another milti buffer with the references
13847    cx.assert_editor_state(
13848        &r#"fn one() {
13849            let mut a = two();
13850        }
13851
13852        fn «twoˇ»() {}"#
13853            .unindent(),
13854    );
13855    let editors = cx.update_workspace(|workspace, cx| {
13856        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13857    });
13858    cx.update_editor(|_, test_editor_cx| {
13859        assert_eq!(
13860            editors.len(),
13861            2,
13862            "After falling back to references search, we open a new editor with the results"
13863        );
13864        let references_fallback_text = editors
13865            .into_iter()
13866            .find(|new_editor| new_editor != test_editor_cx.view())
13867            .expect("Should have one non-test editor now")
13868            .read(test_editor_cx)
13869            .text(test_editor_cx);
13870        assert_eq!(
13871            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13872            "Should use the range from the references response and not the GoToDefinition one"
13873        );
13874    });
13875}
13876
13877#[gpui::test]
13878async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13879    init_test(cx, |_| {});
13880
13881    let language = Arc::new(Language::new(
13882        LanguageConfig::default(),
13883        Some(tree_sitter_rust::LANGUAGE.into()),
13884    ));
13885
13886    let text = r#"
13887        #[cfg(test)]
13888        mod tests() {
13889            #[test]
13890            fn runnable_1() {
13891                let a = 1;
13892            }
13893
13894            #[test]
13895            fn runnable_2() {
13896                let a = 1;
13897                let b = 2;
13898            }
13899        }
13900    "#
13901    .unindent();
13902
13903    let fs = FakeFs::new(cx.executor());
13904    fs.insert_file("/file.rs", Default::default()).await;
13905
13906    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13907    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13908    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13909    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13910    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13911
13912    let editor = cx.new_view(|cx| {
13913        Editor::new(
13914            EditorMode::Full,
13915            multi_buffer,
13916            Some(project.clone()),
13917            true,
13918            cx,
13919        )
13920    });
13921
13922    editor.update(cx, |editor, cx| {
13923        editor.tasks.insert(
13924            (buffer.read(cx).remote_id(), 3),
13925            RunnableTasks {
13926                templates: vec![],
13927                offset: MultiBufferOffset(43),
13928                column: 0,
13929                extra_variables: HashMap::default(),
13930                context_range: BufferOffset(43)..BufferOffset(85),
13931            },
13932        );
13933        editor.tasks.insert(
13934            (buffer.read(cx).remote_id(), 8),
13935            RunnableTasks {
13936                templates: vec![],
13937                offset: MultiBufferOffset(86),
13938                column: 0,
13939                extra_variables: HashMap::default(),
13940                context_range: BufferOffset(86)..BufferOffset(191),
13941            },
13942        );
13943
13944        // Test finding task when cursor is inside function body
13945        editor.change_selections(None, cx, |s| {
13946            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13947        });
13948        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13949        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13950
13951        // Test finding task when cursor is on function name
13952        editor.change_selections(None, cx, |s| {
13953            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13954        });
13955        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13956        assert_eq!(row, 8, "Should find task when cursor is on function name");
13957    });
13958}
13959
13960#[gpui::test]
13961async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
13962    init_test(cx, |_| {});
13963
13964    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
13965    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
13966    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
13967
13968    let fs = FakeFs::new(cx.executor());
13969    fs.insert_tree(
13970        "/a",
13971        json!({
13972            "first.rs": sample_text_1,
13973            "second.rs": sample_text_2,
13974            "third.rs": sample_text_3,
13975        }),
13976    )
13977    .await;
13978    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13979    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13980    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13981    let worktree = project.update(cx, |project, cx| {
13982        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
13983        assert_eq!(worktrees.len(), 1);
13984        worktrees.pop().unwrap()
13985    });
13986    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
13987
13988    let buffer_1 = project
13989        .update(cx, |project, cx| {
13990            project.open_buffer((worktree_id, "first.rs"), cx)
13991        })
13992        .await
13993        .unwrap();
13994    let buffer_2 = project
13995        .update(cx, |project, cx| {
13996            project.open_buffer((worktree_id, "second.rs"), cx)
13997        })
13998        .await
13999        .unwrap();
14000    let buffer_3 = project
14001        .update(cx, |project, cx| {
14002            project.open_buffer((worktree_id, "third.rs"), cx)
14003        })
14004        .await
14005        .unwrap();
14006
14007    let multi_buffer = cx.new_model(|cx| {
14008        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14009        multi_buffer.push_excerpts(
14010            buffer_1.clone(),
14011            [
14012                ExcerptRange {
14013                    context: Point::new(0, 0)..Point::new(3, 0),
14014                    primary: None,
14015                },
14016                ExcerptRange {
14017                    context: Point::new(5, 0)..Point::new(7, 0),
14018                    primary: None,
14019                },
14020                ExcerptRange {
14021                    context: Point::new(9, 0)..Point::new(10, 4),
14022                    primary: None,
14023                },
14024            ],
14025            cx,
14026        );
14027        multi_buffer.push_excerpts(
14028            buffer_2.clone(),
14029            [
14030                ExcerptRange {
14031                    context: Point::new(0, 0)..Point::new(3, 0),
14032                    primary: None,
14033                },
14034                ExcerptRange {
14035                    context: Point::new(5, 0)..Point::new(7, 0),
14036                    primary: None,
14037                },
14038                ExcerptRange {
14039                    context: Point::new(9, 0)..Point::new(10, 4),
14040                    primary: None,
14041                },
14042            ],
14043            cx,
14044        );
14045        multi_buffer.push_excerpts(
14046            buffer_3.clone(),
14047            [
14048                ExcerptRange {
14049                    context: Point::new(0, 0)..Point::new(3, 0),
14050                    primary: None,
14051                },
14052                ExcerptRange {
14053                    context: Point::new(5, 0)..Point::new(7, 0),
14054                    primary: None,
14055                },
14056                ExcerptRange {
14057                    context: Point::new(9, 0)..Point::new(10, 4),
14058                    primary: None,
14059                },
14060            ],
14061            cx,
14062        );
14063        multi_buffer
14064    });
14065    let multi_buffer_editor = cx.new_view(|cx| {
14066        Editor::new(
14067            EditorMode::Full,
14068            multi_buffer,
14069            Some(project.clone()),
14070            true,
14071            cx,
14072        )
14073    });
14074
14075    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
14076    assert_eq!(
14077        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14078        full_text,
14079    );
14080
14081    multi_buffer_editor.update(cx, |editor, cx| {
14082        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14083    });
14084    assert_eq!(
14085        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14086        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14087        "After folding the first buffer, its text should not be displayed"
14088    );
14089
14090    multi_buffer_editor.update(cx, |editor, cx| {
14091        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14092    });
14093    assert_eq!(
14094        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14095        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14096        "After folding the second buffer, its text should not be displayed"
14097    );
14098
14099    multi_buffer_editor.update(cx, |editor, cx| {
14100        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14101    });
14102    assert_eq!(
14103        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14104        "\n\n\n\n\n",
14105        "After folding the third buffer, its text should not be displayed"
14106    );
14107
14108    // Emulate selection inside the fold logic, that should work
14109    multi_buffer_editor.update(cx, |editor, cx| {
14110        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14111    });
14112
14113    multi_buffer_editor.update(cx, |editor, cx| {
14114        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14115    });
14116    assert_eq!(
14117        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14118        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14119        "After unfolding the second buffer, its text should be displayed"
14120    );
14121
14122    multi_buffer_editor.update(cx, |editor, cx| {
14123        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14124    });
14125    assert_eq!(
14126        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14127        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14128        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14129    );
14130
14131    multi_buffer_editor.update(cx, |editor, cx| {
14132        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14133    });
14134    assert_eq!(
14135        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14136        full_text,
14137        "After unfolding the all buffers, all original text should be displayed"
14138    );
14139}
14140
14141#[gpui::test]
14142async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14143    init_test(cx, |_| {});
14144
14145    let sample_text_1 = "1111\n2222\n3333".to_string();
14146    let sample_text_2 = "4444\n5555\n6666".to_string();
14147    let sample_text_3 = "7777\n8888\n9999".to_string();
14148
14149    let fs = FakeFs::new(cx.executor());
14150    fs.insert_tree(
14151        "/a",
14152        json!({
14153            "first.rs": sample_text_1,
14154            "second.rs": sample_text_2,
14155            "third.rs": sample_text_3,
14156        }),
14157    )
14158    .await;
14159    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14160    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14161    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14162    let worktree = project.update(cx, |project, cx| {
14163        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14164        assert_eq!(worktrees.len(), 1);
14165        worktrees.pop().unwrap()
14166    });
14167    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14168
14169    let buffer_1 = project
14170        .update(cx, |project, cx| {
14171            project.open_buffer((worktree_id, "first.rs"), cx)
14172        })
14173        .await
14174        .unwrap();
14175    let buffer_2 = project
14176        .update(cx, |project, cx| {
14177            project.open_buffer((worktree_id, "second.rs"), cx)
14178        })
14179        .await
14180        .unwrap();
14181    let buffer_3 = project
14182        .update(cx, |project, cx| {
14183            project.open_buffer((worktree_id, "third.rs"), cx)
14184        })
14185        .await
14186        .unwrap();
14187
14188    let multi_buffer = cx.new_model(|cx| {
14189        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14190        multi_buffer.push_excerpts(
14191            buffer_1.clone(),
14192            [ExcerptRange {
14193                context: Point::new(0, 0)..Point::new(3, 0),
14194                primary: None,
14195            }],
14196            cx,
14197        );
14198        multi_buffer.push_excerpts(
14199            buffer_2.clone(),
14200            [ExcerptRange {
14201                context: Point::new(0, 0)..Point::new(3, 0),
14202                primary: None,
14203            }],
14204            cx,
14205        );
14206        multi_buffer.push_excerpts(
14207            buffer_3.clone(),
14208            [ExcerptRange {
14209                context: Point::new(0, 0)..Point::new(3, 0),
14210                primary: None,
14211            }],
14212            cx,
14213        );
14214        multi_buffer
14215    });
14216
14217    let multi_buffer_editor = cx.new_view(|cx| {
14218        Editor::new(
14219            EditorMode::Full,
14220            multi_buffer,
14221            Some(project.clone()),
14222            true,
14223            cx,
14224        )
14225    });
14226
14227    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14228    assert_eq!(
14229        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14230        full_text,
14231    );
14232
14233    multi_buffer_editor.update(cx, |editor, cx| {
14234        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14235    });
14236    assert_eq!(
14237        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14238        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14239        "After folding the first buffer, its text should not be displayed"
14240    );
14241
14242    multi_buffer_editor.update(cx, |editor, cx| {
14243        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14244    });
14245
14246    assert_eq!(
14247        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14248        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14249        "After folding the second buffer, its text should not be displayed"
14250    );
14251
14252    multi_buffer_editor.update(cx, |editor, cx| {
14253        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14254    });
14255    assert_eq!(
14256        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14257        "\n\n\n\n\n",
14258        "After folding the third buffer, its text should not be displayed"
14259    );
14260
14261    multi_buffer_editor.update(cx, |editor, cx| {
14262        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14263    });
14264    assert_eq!(
14265        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14266        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14267        "After unfolding the second buffer, its text should be displayed"
14268    );
14269
14270    multi_buffer_editor.update(cx, |editor, cx| {
14271        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14272    });
14273    assert_eq!(
14274        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14275        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14276        "After unfolding the first buffer, its text should be displayed"
14277    );
14278
14279    multi_buffer_editor.update(cx, |editor, cx| {
14280        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14281    });
14282    assert_eq!(
14283        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14284        full_text,
14285        "After unfolding all buffers, all original text should be displayed"
14286    );
14287}
14288
14289#[gpui::test]
14290async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14291    init_test(cx, |_| {});
14292
14293    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14294
14295    let fs = FakeFs::new(cx.executor());
14296    fs.insert_tree(
14297        "/a",
14298        json!({
14299            "main.rs": sample_text,
14300        }),
14301    )
14302    .await;
14303    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14304    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14305    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14306    let worktree = project.update(cx, |project, cx| {
14307        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14308        assert_eq!(worktrees.len(), 1);
14309        worktrees.pop().unwrap()
14310    });
14311    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14312
14313    let buffer_1 = project
14314        .update(cx, |project, cx| {
14315            project.open_buffer((worktree_id, "main.rs"), cx)
14316        })
14317        .await
14318        .unwrap();
14319
14320    let multi_buffer = cx.new_model(|cx| {
14321        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14322        multi_buffer.push_excerpts(
14323            buffer_1.clone(),
14324            [ExcerptRange {
14325                context: Point::new(0, 0)
14326                    ..Point::new(
14327                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14328                        0,
14329                    ),
14330                primary: None,
14331            }],
14332            cx,
14333        );
14334        multi_buffer
14335    });
14336    let multi_buffer_editor = cx.new_view(|cx| {
14337        Editor::new(
14338            EditorMode::Full,
14339            multi_buffer,
14340            Some(project.clone()),
14341            true,
14342            cx,
14343        )
14344    });
14345
14346    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14347    multi_buffer_editor.update(cx, |editor, cx| {
14348        enum TestHighlight {}
14349        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14350        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14351        editor.highlight_text::<TestHighlight>(
14352            vec![highlight_range.clone()],
14353            HighlightStyle::color(Hsla::green()),
14354            cx,
14355        );
14356        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14357    });
14358
14359    let full_text = format!("\n\n\n{sample_text}\n");
14360    assert_eq!(
14361        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14362        full_text,
14363    );
14364}
14365
14366#[gpui::test]
14367fn test_inline_completion_text(cx: &mut TestAppContext) {
14368    init_test(cx, |_| {});
14369
14370    // Simple insertion
14371    {
14372        let window = cx.add_window(|cx| {
14373            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14374            Editor::new(EditorMode::Full, buffer, None, true, cx)
14375        });
14376        let cx = &mut VisualTestContext::from_window(*window, cx);
14377
14378        window
14379            .update(cx, |editor, cx| {
14380                let snapshot = editor.snapshot(cx);
14381                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14382                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14383                let edits = vec![(edit_range, " beautiful".to_string())];
14384
14385                let InlineCompletionText::Edit { text, highlights } =
14386                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14387                else {
14388                    panic!("Failed to generate inline completion text");
14389                };
14390
14391                assert_eq!(text, "Hello, beautiful world!");
14392                assert_eq!(highlights.len(), 1);
14393                assert_eq!(highlights[0].0, 6..16);
14394                assert_eq!(
14395                    highlights[0].1.background_color,
14396                    Some(cx.theme().status().created_background)
14397                );
14398            })
14399            .unwrap();
14400    }
14401
14402    // Replacement
14403    {
14404        let window = cx.add_window(|cx| {
14405            let buffer = MultiBuffer::build_simple("This is a test.", cx);
14406            Editor::new(EditorMode::Full, buffer, None, true, cx)
14407        });
14408        let cx = &mut VisualTestContext::from_window(*window, cx);
14409
14410        window
14411            .update(cx, |editor, cx| {
14412                let snapshot = editor.snapshot(cx);
14413                let edits = vec![(
14414                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14415                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
14416                    "That".to_string(),
14417                )];
14418
14419                let InlineCompletionText::Edit { text, highlights } =
14420                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14421                else {
14422                    panic!("Failed to generate inline completion text");
14423                };
14424
14425                assert_eq!(text, "That is a test.");
14426                assert_eq!(highlights.len(), 1);
14427                assert_eq!(highlights[0].0, 0..4);
14428                assert_eq!(
14429                    highlights[0].1.background_color,
14430                    Some(cx.theme().status().created_background)
14431                );
14432            })
14433            .unwrap();
14434    }
14435
14436    // Multiple edits
14437    {
14438        let window = cx.add_window(|cx| {
14439            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14440            Editor::new(EditorMode::Full, buffer, None, true, cx)
14441        });
14442        let cx = &mut VisualTestContext::from_window(*window, cx);
14443
14444        window
14445            .update(cx, |editor, cx| {
14446                let snapshot = editor.snapshot(cx);
14447                let edits = vec![
14448                    (
14449                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14450                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
14451                        "Greetings".into(),
14452                    ),
14453                    (
14454                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
14455                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
14456                        " and universe".into(),
14457                    ),
14458                ];
14459
14460                let InlineCompletionText::Edit { text, highlights } =
14461                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14462                else {
14463                    panic!("Failed to generate inline completion text");
14464                };
14465
14466                assert_eq!(text, "Greetings, world and universe!");
14467                assert_eq!(highlights.len(), 2);
14468                assert_eq!(highlights[0].0, 0..9);
14469                assert_eq!(highlights[1].0, 16..29);
14470                assert_eq!(
14471                    highlights[0].1.background_color,
14472                    Some(cx.theme().status().created_background)
14473                );
14474                assert_eq!(
14475                    highlights[1].1.background_color,
14476                    Some(cx.theme().status().created_background)
14477                );
14478            })
14479            .unwrap();
14480    }
14481
14482    // Multiple lines with edits
14483    {
14484        let window = cx.add_window(|cx| {
14485            let buffer =
14486                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
14487            Editor::new(EditorMode::Full, buffer, None, true, cx)
14488        });
14489        let cx = &mut VisualTestContext::from_window(*window, cx);
14490
14491        window
14492            .update(cx, |editor, cx| {
14493                let snapshot = editor.snapshot(cx);
14494                let edits = vec![
14495                    (
14496                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
14497                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
14498                        "modified".to_string(),
14499                    ),
14500                    (
14501                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
14502                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
14503                        "New third line".to_string(),
14504                    ),
14505                    (
14506                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
14507                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
14508                        " updated".to_string(),
14509                    ),
14510                ];
14511
14512                let InlineCompletionText::Edit { text, highlights } =
14513                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14514                else {
14515                    panic!("Failed to generate inline completion text");
14516                };
14517
14518                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
14519                assert_eq!(highlights.len(), 3);
14520                assert_eq!(highlights[0].0, 7..15); // "modified"
14521                assert_eq!(highlights[1].0, 16..30); // "New third line"
14522                assert_eq!(highlights[2].0, 37..45); // " updated"
14523
14524                for highlight in &highlights {
14525                    assert_eq!(
14526                        highlight.1.background_color,
14527                        Some(cx.theme().status().created_background)
14528                    );
14529                }
14530            })
14531            .unwrap();
14532    }
14533}
14534
14535#[gpui::test]
14536fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
14537    init_test(cx, |_| {});
14538
14539    // Deletion
14540    {
14541        let window = cx.add_window(|cx| {
14542            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14543            Editor::new(EditorMode::Full, buffer, None, true, cx)
14544        });
14545        let cx = &mut VisualTestContext::from_window(*window, cx);
14546
14547        window
14548            .update(cx, |editor, cx| {
14549                let snapshot = editor.snapshot(cx);
14550                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
14551                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
14552                let edits = vec![(edit_range, "".to_string())];
14553
14554                let InlineCompletionText::Edit { text, highlights } =
14555                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14556                else {
14557                    panic!("Failed to generate inline completion text");
14558                };
14559
14560                assert_eq!(text, "Hello, world!");
14561                assert_eq!(highlights.len(), 1);
14562                assert_eq!(highlights[0].0, 5..11);
14563                assert_eq!(
14564                    highlights[0].1.background_color,
14565                    Some(cx.theme().status().deleted_background)
14566                );
14567            })
14568            .unwrap();
14569    }
14570
14571    // Insertion
14572    {
14573        let window = cx.add_window(|cx| {
14574            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14575            Editor::new(EditorMode::Full, buffer, None, true, cx)
14576        });
14577        let cx = &mut VisualTestContext::from_window(*window, cx);
14578
14579        window
14580            .update(cx, |editor, cx| {
14581                let snapshot = editor.snapshot(cx);
14582                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14583                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14584                let edits = vec![(edit_range, " digital".to_string())];
14585
14586                let InlineCompletionText::Edit { text, highlights } =
14587                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14588                else {
14589                    panic!("Failed to generate inline completion text");
14590                };
14591
14592                assert_eq!(text, "Hello, digital world!");
14593                assert_eq!(highlights.len(), 1);
14594                assert_eq!(highlights[0].0, 6..14);
14595                assert_eq!(
14596                    highlights[0].1.background_color,
14597                    Some(cx.theme().status().created_background)
14598                );
14599            })
14600            .unwrap();
14601    }
14602}
14603
14604fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
14605    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
14606    point..point
14607}
14608
14609fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
14610    let (text, ranges) = marked_text_ranges(marked_text, true);
14611    assert_eq!(view.text(cx), text);
14612    assert_eq!(
14613        view.selections.ranges(cx),
14614        ranges,
14615        "Assert selections are {}",
14616        marked_text
14617    );
14618}
14619
14620pub fn handle_signature_help_request(
14621    cx: &mut EditorLspTestContext,
14622    mocked_response: lsp::SignatureHelp,
14623) -> impl Future<Output = ()> {
14624    let mut request =
14625        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
14626            let mocked_response = mocked_response.clone();
14627            async move { Ok(Some(mocked_response)) }
14628        });
14629
14630    async move {
14631        request.next().await;
14632    }
14633}
14634
14635/// Handle completion request passing a marked string specifying where the completion
14636/// should be triggered from using '|' character, what range should be replaced, and what completions
14637/// should be returned using '<' and '>' to delimit the range
14638pub fn handle_completion_request(
14639    cx: &mut EditorLspTestContext,
14640    marked_string: &str,
14641    completions: Vec<&'static str>,
14642    counter: Arc<AtomicUsize>,
14643) -> impl Future<Output = ()> {
14644    let complete_from_marker: TextRangeMarker = '|'.into();
14645    let replace_range_marker: TextRangeMarker = ('<', '>').into();
14646    let (_, mut marked_ranges) = marked_text_ranges_by(
14647        marked_string,
14648        vec![complete_from_marker.clone(), replace_range_marker.clone()],
14649    );
14650
14651    let complete_from_position =
14652        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
14653    let replace_range =
14654        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
14655
14656    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
14657        let completions = completions.clone();
14658        counter.fetch_add(1, atomic::Ordering::Release);
14659        async move {
14660            assert_eq!(params.text_document_position.text_document.uri, url.clone());
14661            assert_eq!(
14662                params.text_document_position.position,
14663                complete_from_position
14664            );
14665            Ok(Some(lsp::CompletionResponse::Array(
14666                completions
14667                    .iter()
14668                    .map(|completion_text| lsp::CompletionItem {
14669                        label: completion_text.to_string(),
14670                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14671                            range: replace_range,
14672                            new_text: completion_text.to_string(),
14673                        })),
14674                        ..Default::default()
14675                    })
14676                    .collect(),
14677            )))
14678        }
14679    });
14680
14681    async move {
14682        request.next().await;
14683    }
14684}
14685
14686fn handle_resolve_completion_request(
14687    cx: &mut EditorLspTestContext,
14688    edits: Option<Vec<(&'static str, &'static str)>>,
14689) -> impl Future<Output = ()> {
14690    let edits = edits.map(|edits| {
14691        edits
14692            .iter()
14693            .map(|(marked_string, new_text)| {
14694                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
14695                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
14696                lsp::TextEdit::new(replace_range, new_text.to_string())
14697            })
14698            .collect::<Vec<_>>()
14699    });
14700
14701    let mut request =
14702        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14703            let edits = edits.clone();
14704            async move {
14705                Ok(lsp::CompletionItem {
14706                    additional_text_edits: edits,
14707                    ..Default::default()
14708                })
14709            }
14710        });
14711
14712    async move {
14713        request.next().await;
14714    }
14715}
14716
14717pub(crate) fn update_test_language_settings(
14718    cx: &mut TestAppContext,
14719    f: impl Fn(&mut AllLanguageSettingsContent),
14720) {
14721    cx.update(|cx| {
14722        SettingsStore::update_global(cx, |store, cx| {
14723            store.update_user_settings::<AllLanguageSettings>(cx, f);
14724        });
14725    });
14726}
14727
14728pub(crate) fn update_test_project_settings(
14729    cx: &mut TestAppContext,
14730    f: impl Fn(&mut ProjectSettings),
14731) {
14732    cx.update(|cx| {
14733        SettingsStore::update_global(cx, |store, cx| {
14734            store.update_user_settings::<ProjectSettings>(cx, f);
14735        });
14736    });
14737}
14738
14739pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
14740    cx.update(|cx| {
14741        assets::Assets.load_test_fonts(cx);
14742        let store = SettingsStore::test(cx);
14743        cx.set_global(store);
14744        theme::init(theme::LoadThemes::JustBase, cx);
14745        release_channel::init(SemanticVersion::default(), cx);
14746        client::init_settings(cx);
14747        language::init(cx);
14748        Project::init_settings(cx);
14749        workspace::init_settings(cx);
14750        crate::init(cx);
14751    });
14752
14753    update_test_language_settings(cx, f);
14754}
14755
14756#[track_caller]
14757fn assert_hunk_revert(
14758    not_reverted_text_with_selections: &str,
14759    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
14760    expected_reverted_text_with_selections: &str,
14761    base_text: &str,
14762    cx: &mut EditorLspTestContext,
14763) {
14764    cx.set_state(not_reverted_text_with_selections);
14765    cx.set_diff_base(base_text);
14766    cx.executor().run_until_parked();
14767
14768    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
14769        let snapshot = editor.snapshot(cx);
14770        let reverted_hunk_statuses = snapshot
14771            .diff_map
14772            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
14773            .map(|hunk| hunk_status(&hunk))
14774            .collect::<Vec<_>>();
14775
14776        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
14777        reverted_hunk_statuses
14778    });
14779    cx.executor().run_until_parked();
14780    cx.assert_editor_state(expected_reverted_text_with_selections);
14781    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
14782}