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