editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use project::FakeFs;
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic;
   35use std::sync::atomic::AtomicUsize;
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use unindent::Unindent;
   38use util::{
   39    assert_set_eq,
   40    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   41};
   42use workspace::{
   43    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   44    NavigationEntry, ViewId,
   45};
   46
   47#[gpui::test]
   48fn test_edit_events(cx: &mut TestAppContext) {
   49    init_test(cx, |_| {});
   50
   51    let buffer = cx.new_model(|cx| {
   52        let mut buffer = language::Buffer::local("123456", cx);
   53        buffer.set_group_interval(Duration::from_secs(1));
   54        buffer
   55    });
   56
   57    let events = Rc::new(RefCell::new(Vec::new()));
   58    let editor1 = cx.add_window({
   59        let events = events.clone();
   60        |cx| {
   61            let view = cx.view().clone();
   62            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   63                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   64                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   65                _ => {}
   66            })
   67            .detach();
   68            Editor::for_buffer(buffer.clone(), None, cx)
   69        }
   70    });
   71
   72    let editor2 = cx.add_window({
   73        let events = events.clone();
   74        |cx| {
   75            cx.subscribe(
   76                &cx.view().clone(),
   77                move |_, _, event: &EditorEvent, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor2", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, cx)
   87        }
   88    });
   89
   90    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   91
   92    // Mutating editor 1 will emit an `Edited` event only for that editor.
   93    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   94    assert_eq!(
   95        mem::take(&mut *events.borrow_mut()),
   96        [
   97            ("editor1", "edited"),
   98            ("editor1", "buffer edited"),
   99            ("editor2", "buffer edited"),
  100        ]
  101    );
  102
  103    // Mutating editor 2 will emit an `Edited` event only for that editor.
  104    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor2", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  137    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor2", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // No event is emitted when the mutation is a no-op.
  159    _ = editor2.update(cx, |editor, cx| {
  160        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  161
  162        editor.backspace(&Backspace, cx);
  163    });
  164    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  165}
  166
  167#[gpui::test]
  168fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  169    init_test(cx, |_| {});
  170
  171    let mut now = Instant::now();
  172    let group_interval = Duration::from_millis(1);
  173    let buffer = cx.new_model(|cx| {
  174        let mut buf = language::Buffer::local("123456", cx);
  175        buf.set_group_interval(group_interval);
  176        buf
  177    });
  178    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  179    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  180
  181    _ = editor.update(cx, |editor, cx| {
  182        editor.start_transaction_at(now, cx);
  183        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  184
  185        editor.insert("cd", cx);
  186        editor.end_transaction_at(now, cx);
  187        assert_eq!(editor.text(cx), "12cd56");
  188        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  189
  190        editor.start_transaction_at(now, cx);
  191        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  192        editor.insert("e", cx);
  193        editor.end_transaction_at(now, cx);
  194        assert_eq!(editor.text(cx), "12cde6");
  195        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  196
  197        now += group_interval + Duration::from_millis(1);
  198        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  199
  200        // Simulate an edit in another editor
  201        buffer.update(cx, |buffer, cx| {
  202            buffer.start_transaction_at(now, cx);
  203            buffer.edit([(0..1, "a")], None, cx);
  204            buffer.edit([(1..1, "b")], None, cx);
  205            buffer.end_transaction_at(now, cx);
  206        });
  207
  208        assert_eq!(editor.text(cx), "ab2cde6");
  209        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  210
  211        // Last transaction happened past the group interval in a different editor.
  212        // Undo it individually and don't restore selections.
  213        editor.undo(&Undo, cx);
  214        assert_eq!(editor.text(cx), "12cde6");
  215        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  216
  217        // First two transactions happened within the group interval in this editor.
  218        // Undo them together and restore selections.
  219        editor.undo(&Undo, cx);
  220        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  221        assert_eq!(editor.text(cx), "123456");
  222        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  223
  224        // Redo the first two transactions together.
  225        editor.redo(&Redo, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  228
  229        // Redo the last transaction on its own.
  230        editor.redo(&Redo, cx);
  231        assert_eq!(editor.text(cx), "ab2cde6");
  232        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  233
  234        // Test empty transactions.
  235        editor.start_transaction_at(now, cx);
  236        editor.end_transaction_at(now, cx);
  237        editor.undo(&Undo, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239    });
  240}
  241
  242#[gpui::test]
  243fn test_ime_composition(cx: &mut TestAppContext) {
  244    init_test(cx, |_| {});
  245
  246    let buffer = cx.new_model(|cx| {
  247        let mut buffer = language::Buffer::local("abcde", cx);
  248        // Ensure automatic grouping doesn't occur.
  249        buffer.set_group_interval(Duration::ZERO);
  250        buffer
  251    });
  252
  253    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  254    cx.add_window(|cx| {
  255        let mut editor = build_editor(buffer.clone(), cx);
  256
  257        // Start a new IME composition.
  258        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  259        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  260        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  261        assert_eq!(editor.text(cx), "äbcde");
  262        assert_eq!(
  263            editor.marked_text_ranges(cx),
  264            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  265        );
  266
  267        // Finalize IME composition.
  268        editor.replace_text_in_range(None, "ā", cx);
  269        assert_eq!(editor.text(cx), "ābcde");
  270        assert_eq!(editor.marked_text_ranges(cx), None);
  271
  272        // IME composition edits are grouped and are undone/redone at once.
  273        editor.undo(&Default::default(), cx);
  274        assert_eq!(editor.text(cx), "abcde");
  275        assert_eq!(editor.marked_text_ranges(cx), None);
  276        editor.redo(&Default::default(), cx);
  277        assert_eq!(editor.text(cx), "ābcde");
  278        assert_eq!(editor.marked_text_ranges(cx), None);
  279
  280        // Start a new IME composition.
  281        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  282        assert_eq!(
  283            editor.marked_text_ranges(cx),
  284            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  285        );
  286
  287        // Undoing during an IME composition cancels it.
  288        editor.undo(&Default::default(), cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  293        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  294        assert_eq!(editor.text(cx), "ābcdè");
  295        assert_eq!(
  296            editor.marked_text_ranges(cx),
  297            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  298        );
  299
  300        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  301        editor.replace_text_in_range(Some(4..999), "ę", cx);
  302        assert_eq!(editor.text(cx), "ābcdę");
  303        assert_eq!(editor.marked_text_ranges(cx), None);
  304
  305        // Start a new IME composition with multiple cursors.
  306        editor.change_selections(None, cx, |s| {
  307            s.select_ranges([
  308                OffsetUtf16(1)..OffsetUtf16(1),
  309                OffsetUtf16(3)..OffsetUtf16(3),
  310                OffsetUtf16(5)..OffsetUtf16(5),
  311            ])
  312        });
  313        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  314        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  315        assert_eq!(
  316            editor.marked_text_ranges(cx),
  317            Some(vec![
  318                OffsetUtf16(0)..OffsetUtf16(3),
  319                OffsetUtf16(4)..OffsetUtf16(7),
  320                OffsetUtf16(8)..OffsetUtf16(11)
  321            ])
  322        );
  323
  324        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  325        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  326        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(1)..OffsetUtf16(2),
  331                OffsetUtf16(5)..OffsetUtf16(6),
  332                OffsetUtf16(9)..OffsetUtf16(10)
  333            ])
  334        );
  335
  336        // Finalize IME composition with multiple cursors.
  337        editor.replace_text_in_range(Some(9..10), "2", cx);
  338        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  339        assert_eq!(editor.marked_text_ranges(cx), None);
  340
  341        editor
  342    });
  343}
  344
  345#[gpui::test]
  346fn test_selection_with_mouse(cx: &mut TestAppContext) {
  347    init_test(cx, |_| {});
  348
  349    let editor = cx.add_window(|cx| {
  350        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  351        build_editor(buffer, cx)
  352    });
  353
  354    _ = editor.update(cx, |view, cx| {
  355        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  356    });
  357    assert_eq!(
  358        editor
  359            .update(cx, |view, cx| view.selections.display_ranges(cx))
  360            .unwrap(),
  361        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  362    );
  363
  364    _ = editor.update(cx, |view, cx| {
  365        view.update_selection(
  366            DisplayPoint::new(DisplayRow(3), 3),
  367            0,
  368            gpui::Point::<f32>::default(),
  369            cx,
  370        );
  371    });
  372
  373    assert_eq!(
  374        editor
  375            .update(cx, |view, cx| view.selections.display_ranges(cx))
  376            .unwrap(),
  377        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  378    );
  379
  380    _ = editor.update(cx, |view, cx| {
  381        view.update_selection(
  382            DisplayPoint::new(DisplayRow(1), 1),
  383            0,
  384            gpui::Point::<f32>::default(),
  385            cx,
  386        );
  387    });
  388
  389    assert_eq!(
  390        editor
  391            .update(cx, |view, cx| view.selections.display_ranges(cx))
  392            .unwrap(),
  393        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  394    );
  395
  396    _ = editor.update(cx, |view, cx| {
  397        view.end_selection(cx);
  398        view.update_selection(
  399            DisplayPoint::new(DisplayRow(3), 3),
  400            0,
  401            gpui::Point::<f32>::default(),
  402            cx,
  403        );
  404    });
  405
  406    assert_eq!(
  407        editor
  408            .update(cx, |view, cx| view.selections.display_ranges(cx))
  409            .unwrap(),
  410        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  411    );
  412
  413    _ = editor.update(cx, |view, cx| {
  414        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  415        view.update_selection(
  416            DisplayPoint::new(DisplayRow(0), 0),
  417            0,
  418            gpui::Point::<f32>::default(),
  419            cx,
  420        );
  421    });
  422
  423    assert_eq!(
  424        editor
  425            .update(cx, |view, cx| view.selections.display_ranges(cx))
  426            .unwrap(),
  427        [
  428            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  429            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  430        ]
  431    );
  432
  433    _ = editor.update(cx, |view, cx| {
  434        view.end_selection(cx);
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |view, cx| view.selections.display_ranges(cx))
  440            .unwrap(),
  441        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  442    );
  443}
  444
  445#[gpui::test]
  446fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  447    init_test(cx, |_| {});
  448
  449    let editor = cx.add_window(|cx| {
  450        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  451        build_editor(buffer, cx)
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.end_selection(cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.end_selection(cx);
  468    });
  469
  470    assert_eq!(
  471        editor
  472            .update(cx, |view, cx| view.selections.display_ranges(cx))
  473            .unwrap(),
  474        [
  475            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  476            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  477        ]
  478    );
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  482    });
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.end_selection(cx);
  486    });
  487
  488    assert_eq!(
  489        editor
  490            .update(cx, |view, cx| view.selections.display_ranges(cx))
  491            .unwrap(),
  492        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  493    );
  494}
  495
  496#[gpui::test]
  497fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  498    init_test(cx, |_| {});
  499
  500    let view = cx.add_window(|cx| {
  501        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  502        build_editor(buffer, cx)
  503    });
  504
  505    _ = view.update(cx, |view, cx| {
  506        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  507        assert_eq!(
  508            view.selections.display_ranges(cx),
  509            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  510        );
  511    });
  512
  513    _ = view.update(cx, |view, cx| {
  514        view.update_selection(
  515            DisplayPoint::new(DisplayRow(3), 3),
  516            0,
  517            gpui::Point::<f32>::default(),
  518            cx,
  519        );
  520        assert_eq!(
  521            view.selections.display_ranges(cx),
  522            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  523        );
  524    });
  525
  526    _ = view.update(cx, |view, cx| {
  527        view.cancel(&Cancel, cx);
  528        view.update_selection(
  529            DisplayPoint::new(DisplayRow(1), 1),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            cx,
  533        );
  534        assert_eq!(
  535            view.selections.display_ranges(cx),
  536            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  537        );
  538    });
  539}
  540
  541#[gpui::test]
  542fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  543    init_test(cx, |_| {});
  544
  545    let view = cx.add_window(|cx| {
  546        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  547        build_editor(buffer, cx)
  548    });
  549
  550    _ = view.update(cx, |view, cx| {
  551        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  552        assert_eq!(
  553            view.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  555        );
  556
  557        view.move_down(&Default::default(), cx);
  558        assert_eq!(
  559            view.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  561        );
  562
  563        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  564        assert_eq!(
  565            view.selections.display_ranges(cx),
  566            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  567        );
  568
  569        view.move_up(&Default::default(), cx);
  570        assert_eq!(
  571            view.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  573        );
  574    });
  575}
  576
  577#[gpui::test]
  578fn test_clone(cx: &mut TestAppContext) {
  579    init_test(cx, |_| {});
  580
  581    let (text, selection_ranges) = marked_text_ranges(
  582        indoc! {"
  583            one
  584            two
  585            threeˇ
  586            four
  587            fiveˇ
  588        "},
  589        true,
  590    );
  591
  592    let editor = cx.add_window(|cx| {
  593        let buffer = MultiBuffer::build_simple(&text, cx);
  594        build_editor(buffer, cx)
  595    });
  596
  597    _ = editor.update(cx, |editor, cx| {
  598        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  599        editor.fold_ranges(
  600            [
  601                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  602                (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_ranges(
 1287            vec![
 1288                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1289                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1290                (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        view.move_down(&MoveDown, cx);
 1402        assert_eq!(
 1403            view.selections.display_ranges(cx),
 1404            &[empty_range(1, "abcd".len())]
 1405        );
 1406
 1407        view.move_down(&MoveDown, cx);
 1408        assert_eq!(
 1409            view.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβγ".len())]
 1411        );
 1412
 1413        view.move_down(&MoveDown, cx);
 1414        assert_eq!(
 1415            view.selections.display_ranges(cx),
 1416            &[empty_range(3, "abcd".len())]
 1417        );
 1418
 1419        view.move_down(&MoveDown, cx);
 1420        assert_eq!(
 1421            view.selections.display_ranges(cx),
 1422            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1423        );
 1424
 1425        view.move_up(&MoveUp, cx);
 1426        assert_eq!(
 1427            view.selections.display_ranges(cx),
 1428            &[empty_range(3, "abcd".len())]
 1429        );
 1430
 1431        view.move_up(&MoveUp, cx);
 1432        assert_eq!(
 1433            view.selections.display_ranges(cx),
 1434            &[empty_range(2, "αβγ".len())]
 1435        );
 1436    });
 1437}
 1438
 1439#[gpui::test]
 1440fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1441    init_test(cx, |_| {});
 1442    let move_to_beg = MoveToBeginningOfLine {
 1443        stop_at_soft_wraps: true,
 1444    };
 1445
 1446    let move_to_end = MoveToEndOfLine {
 1447        stop_at_soft_wraps: true,
 1448    };
 1449
 1450    let view = cx.add_window(|cx| {
 1451        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1452        build_editor(buffer, cx)
 1453    });
 1454    _ = view.update(cx, |view, cx| {
 1455        view.change_selections(None, cx, |s| {
 1456            s.select_display_ranges([
 1457                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1458                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1459            ]);
 1460        });
 1461    });
 1462
 1463    _ = view.update(cx, |view, cx| {
 1464        view.move_to_beginning_of_line(&move_to_beg, cx);
 1465        assert_eq!(
 1466            view.selections.display_ranges(cx),
 1467            &[
 1468                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1469                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1470            ]
 1471        );
 1472    });
 1473
 1474    _ = view.update(cx, |view, cx| {
 1475        view.move_to_beginning_of_line(&move_to_beg, cx);
 1476        assert_eq!(
 1477            view.selections.display_ranges(cx),
 1478            &[
 1479                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1480                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1481            ]
 1482        );
 1483    });
 1484
 1485    _ = view.update(cx, |view, cx| {
 1486        view.move_to_beginning_of_line(&move_to_beg, cx);
 1487        assert_eq!(
 1488            view.selections.display_ranges(cx),
 1489            &[
 1490                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1491                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1492            ]
 1493        );
 1494    });
 1495
 1496    _ = view.update(cx, |view, cx| {
 1497        view.move_to_end_of_line(&move_to_end, cx);
 1498        assert_eq!(
 1499            view.selections.display_ranges(cx),
 1500            &[
 1501                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1502                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1503            ]
 1504        );
 1505    });
 1506
 1507    // Moving to the end of line again is a no-op.
 1508    _ = view.update(cx, |view, cx| {
 1509        view.move_to_end_of_line(&move_to_end, cx);
 1510        assert_eq!(
 1511            view.selections.display_ranges(cx),
 1512            &[
 1513                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1514                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1515            ]
 1516        );
 1517    });
 1518
 1519    _ = view.update(cx, |view, cx| {
 1520        view.move_left(&MoveLeft, cx);
 1521        view.select_to_beginning_of_line(
 1522            &SelectToBeginningOfLine {
 1523                stop_at_soft_wraps: true,
 1524            },
 1525            cx,
 1526        );
 1527        assert_eq!(
 1528            view.selections.display_ranges(cx),
 1529            &[
 1530                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1531                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1532            ]
 1533        );
 1534    });
 1535
 1536    _ = view.update(cx, |view, cx| {
 1537        view.select_to_beginning_of_line(
 1538            &SelectToBeginningOfLine {
 1539                stop_at_soft_wraps: true,
 1540            },
 1541            cx,
 1542        );
 1543        assert_eq!(
 1544            view.selections.display_ranges(cx),
 1545            &[
 1546                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1547                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1548            ]
 1549        );
 1550    });
 1551
 1552    _ = view.update(cx, |view, cx| {
 1553        view.select_to_beginning_of_line(
 1554            &SelectToBeginningOfLine {
 1555                stop_at_soft_wraps: true,
 1556            },
 1557            cx,
 1558        );
 1559        assert_eq!(
 1560            view.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = view.update(cx, |view, cx| {
 1569        view.select_to_end_of_line(
 1570            &SelectToEndOfLine {
 1571                stop_at_soft_wraps: true,
 1572            },
 1573            cx,
 1574        );
 1575        assert_eq!(
 1576            view.selections.display_ranges(cx),
 1577            &[
 1578                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1579                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1580            ]
 1581        );
 1582    });
 1583
 1584    _ = view.update(cx, |view, cx| {
 1585        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1586        assert_eq!(view.display_text(cx), "ab\n  de");
 1587        assert_eq!(
 1588            view.selections.display_ranges(cx),
 1589            &[
 1590                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1591                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1592            ]
 1593        );
 1594    });
 1595
 1596    _ = view.update(cx, |view, cx| {
 1597        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1598        assert_eq!(view.display_text(cx), "\n");
 1599        assert_eq!(
 1600            view.selections.display_ranges(cx),
 1601            &[
 1602                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1603                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1604            ]
 1605        );
 1606    });
 1607}
 1608
 1609#[gpui::test]
 1610fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1611    init_test(cx, |_| {});
 1612    let move_to_beg = MoveToBeginningOfLine {
 1613        stop_at_soft_wraps: false,
 1614    };
 1615
 1616    let move_to_end = MoveToEndOfLine {
 1617        stop_at_soft_wraps: false,
 1618    };
 1619
 1620    let view = cx.add_window(|cx| {
 1621        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1622        build_editor(buffer, cx)
 1623    });
 1624
 1625    _ = view.update(cx, |view, cx| {
 1626        view.set_wrap_width(Some(140.0.into()), cx);
 1627
 1628        // We expect the following lines after wrapping
 1629        // ```
 1630        // thequickbrownfox
 1631        // jumpedoverthelazydo
 1632        // gs
 1633        // ```
 1634        // The final `gs` was soft-wrapped onto a new line.
 1635        assert_eq!(
 1636            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1637            view.display_text(cx),
 1638        );
 1639
 1640        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1641        // Start the cursor at the `k` on the first line
 1642        view.change_selections(None, cx, |s| {
 1643            s.select_display_ranges([
 1644                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1645            ]);
 1646        });
 1647
 1648        // Moving to the beginning of the line should put us at the beginning of the line.
 1649        view.move_to_beginning_of_line(&move_to_beg, cx);
 1650        assert_eq!(
 1651            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1652            view.selections.display_ranges(cx)
 1653        );
 1654
 1655        // Moving to the end of the line should put us at the end of the line.
 1656        view.move_to_end_of_line(&move_to_end, cx);
 1657        assert_eq!(
 1658            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1659            view.selections.display_ranges(cx)
 1660        );
 1661
 1662        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1663        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1664        view.change_selections(None, cx, |s| {
 1665            s.select_display_ranges([
 1666                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1667            ]);
 1668        });
 1669
 1670        // Moving to the beginning of the line should put us at the start of the second line of
 1671        // display text, i.e., the `j`.
 1672        view.move_to_beginning_of_line(&move_to_beg, cx);
 1673        assert_eq!(
 1674            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1675            view.selections.display_ranges(cx)
 1676        );
 1677
 1678        // Moving to the beginning of the line again should be a no-op.
 1679        view.move_to_beginning_of_line(&move_to_beg, cx);
 1680        assert_eq!(
 1681            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1682            view.selections.display_ranges(cx)
 1683        );
 1684
 1685        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1686        // next display line.
 1687        view.move_to_end_of_line(&move_to_end, cx);
 1688        assert_eq!(
 1689            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1690            view.selections.display_ranges(cx)
 1691        );
 1692
 1693        // Moving to the end of the line again should be a no-op.
 1694        view.move_to_end_of_line(&move_to_end, cx);
 1695        assert_eq!(
 1696            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1697            view.selections.display_ranges(cx)
 1698        );
 1699    });
 1700}
 1701
 1702#[gpui::test]
 1703fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1704    init_test(cx, |_| {});
 1705
 1706    let view = cx.add_window(|cx| {
 1707        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1708        build_editor(buffer, cx)
 1709    });
 1710    _ = view.update(cx, |view, cx| {
 1711        view.change_selections(None, cx, |s| {
 1712            s.select_display_ranges([
 1713                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1714                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1715            ])
 1716        });
 1717
 1718        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1719        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1720
 1721        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1722        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1723
 1724        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1725        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1726
 1727        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1728        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1729
 1730        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1731        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1732
 1733        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1734        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1735
 1736        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1737        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1738
 1739        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1740        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1741
 1742        view.move_right(&MoveRight, cx);
 1743        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1744        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1745
 1746        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1747        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1748
 1749        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1750        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1751    });
 1752}
 1753
 1754#[gpui::test]
 1755fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1756    init_test(cx, |_| {});
 1757
 1758    let view = cx.add_window(|cx| {
 1759        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1760        build_editor(buffer, cx)
 1761    });
 1762
 1763    _ = view.update(cx, |view, cx| {
 1764        view.set_wrap_width(Some(140.0.into()), cx);
 1765        assert_eq!(
 1766            view.display_text(cx),
 1767            "use one::{\n    two::three::\n    four::five\n};"
 1768        );
 1769
 1770        view.change_selections(None, cx, |s| {
 1771            s.select_display_ranges([
 1772                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1773            ]);
 1774        });
 1775
 1776        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1777        assert_eq!(
 1778            view.selections.display_ranges(cx),
 1779            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1780        );
 1781
 1782        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1783        assert_eq!(
 1784            view.selections.display_ranges(cx),
 1785            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1786        );
 1787
 1788        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1789        assert_eq!(
 1790            view.selections.display_ranges(cx),
 1791            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1792        );
 1793
 1794        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1795        assert_eq!(
 1796            view.selections.display_ranges(cx),
 1797            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1798        );
 1799
 1800        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1801        assert_eq!(
 1802            view.selections.display_ranges(cx),
 1803            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1804        );
 1805
 1806        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1807        assert_eq!(
 1808            view.selections.display_ranges(cx),
 1809            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1810        );
 1811    });
 1812}
 1813
 1814#[gpui::test]
 1815async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1816    init_test(cx, |_| {});
 1817    let mut cx = EditorTestContext::new(cx).await;
 1818
 1819    let line_height = cx.editor(|editor, cx| {
 1820        editor
 1821            .style()
 1822            .unwrap()
 1823            .text
 1824            .line_height_in_pixels(cx.rem_size())
 1825    });
 1826    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1827
 1828    cx.set_state(
 1829        &r#"ˇone
 1830        two
 1831
 1832        three
 1833        fourˇ
 1834        five
 1835
 1836        six"#
 1837            .unindent(),
 1838    );
 1839
 1840    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1841    cx.assert_editor_state(
 1842        &r#"one
 1843        two
 1844        ˇ
 1845        three
 1846        four
 1847        five
 1848        ˇ
 1849        six"#
 1850            .unindent(),
 1851    );
 1852
 1853    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1854    cx.assert_editor_state(
 1855        &r#"one
 1856        two
 1857
 1858        three
 1859        four
 1860        five
 1861        ˇ
 1862        sixˇ"#
 1863            .unindent(),
 1864    );
 1865
 1866    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1867    cx.assert_editor_state(
 1868        &r#"one
 1869        two
 1870
 1871        three
 1872        four
 1873        five
 1874
 1875        sixˇ"#
 1876            .unindent(),
 1877    );
 1878
 1879    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1880    cx.assert_editor_state(
 1881        &r#"one
 1882        two
 1883
 1884        three
 1885        four
 1886        five
 1887        ˇ
 1888        six"#
 1889            .unindent(),
 1890    );
 1891
 1892    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1893    cx.assert_editor_state(
 1894        &r#"one
 1895        two
 1896        ˇ
 1897        three
 1898        four
 1899        five
 1900
 1901        six"#
 1902            .unindent(),
 1903    );
 1904
 1905    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1906    cx.assert_editor_state(
 1907        &r#"ˇone
 1908        two
 1909
 1910        three
 1911        four
 1912        five
 1913
 1914        six"#
 1915            .unindent(),
 1916    );
 1917}
 1918
 1919#[gpui::test]
 1920async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1921    init_test(cx, |_| {});
 1922    let mut cx = EditorTestContext::new(cx).await;
 1923    let line_height = cx.editor(|editor, cx| {
 1924        editor
 1925            .style()
 1926            .unwrap()
 1927            .text
 1928            .line_height_in_pixels(cx.rem_size())
 1929    });
 1930    let window = cx.window;
 1931    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1932
 1933    cx.set_state(
 1934        r#"ˇone
 1935        two
 1936        three
 1937        four
 1938        five
 1939        six
 1940        seven
 1941        eight
 1942        nine
 1943        ten
 1944        "#,
 1945    );
 1946
 1947    cx.update_editor(|editor, cx| {
 1948        assert_eq!(
 1949            editor.snapshot(cx).scroll_position(),
 1950            gpui::Point::new(0., 0.)
 1951        );
 1952        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1953        assert_eq!(
 1954            editor.snapshot(cx).scroll_position(),
 1955            gpui::Point::new(0., 3.)
 1956        );
 1957        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1958        assert_eq!(
 1959            editor.snapshot(cx).scroll_position(),
 1960            gpui::Point::new(0., 6.)
 1961        );
 1962        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1963        assert_eq!(
 1964            editor.snapshot(cx).scroll_position(),
 1965            gpui::Point::new(0., 3.)
 1966        );
 1967
 1968        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1969        assert_eq!(
 1970            editor.snapshot(cx).scroll_position(),
 1971            gpui::Point::new(0., 1.)
 1972        );
 1973        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1974        assert_eq!(
 1975            editor.snapshot(cx).scroll_position(),
 1976            gpui::Point::new(0., 3.)
 1977        );
 1978    });
 1979}
 1980
 1981#[gpui::test]
 1982async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1983    init_test(cx, |_| {});
 1984    let mut cx = EditorTestContext::new(cx).await;
 1985
 1986    let line_height = cx.update_editor(|editor, cx| {
 1987        editor.set_vertical_scroll_margin(2, cx);
 1988        editor
 1989            .style()
 1990            .unwrap()
 1991            .text
 1992            .line_height_in_pixels(cx.rem_size())
 1993    });
 1994    let window = cx.window;
 1995    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1996
 1997    cx.set_state(
 1998        r#"ˇone
 1999            two
 2000            three
 2001            four
 2002            five
 2003            six
 2004            seven
 2005            eight
 2006            nine
 2007            ten
 2008        "#,
 2009    );
 2010    cx.update_editor(|editor, cx| {
 2011        assert_eq!(
 2012            editor.snapshot(cx).scroll_position(),
 2013            gpui::Point::new(0., 0.0)
 2014        );
 2015    });
 2016
 2017    // Add a cursor below the visible area. Since both cursors cannot fit
 2018    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2019    // allows the vertical scroll margin below that cursor.
 2020    cx.update_editor(|editor, cx| {
 2021        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2022            selections.select_ranges([
 2023                Point::new(0, 0)..Point::new(0, 0),
 2024                Point::new(6, 0)..Point::new(6, 0),
 2025            ]);
 2026        })
 2027    });
 2028    cx.update_editor(|editor, cx| {
 2029        assert_eq!(
 2030            editor.snapshot(cx).scroll_position(),
 2031            gpui::Point::new(0., 3.0)
 2032        );
 2033    });
 2034
 2035    // Move down. The editor cursor scrolls down to track the newest cursor.
 2036    cx.update_editor(|editor, cx| {
 2037        editor.move_down(&Default::default(), cx);
 2038    });
 2039    cx.update_editor(|editor, cx| {
 2040        assert_eq!(
 2041            editor.snapshot(cx).scroll_position(),
 2042            gpui::Point::new(0., 4.0)
 2043        );
 2044    });
 2045
 2046    // Add a cursor above the visible area. Since both cursors fit on screen,
 2047    // the editor scrolls to show both.
 2048    cx.update_editor(|editor, cx| {
 2049        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2050            selections.select_ranges([
 2051                Point::new(1, 0)..Point::new(1, 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., 1.0)
 2060        );
 2061    });
 2062}
 2063
 2064#[gpui::test]
 2065async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2066    init_test(cx, |_| {});
 2067    let mut cx = EditorTestContext::new(cx).await;
 2068
 2069    let line_height = cx.editor(|editor, cx| {
 2070        editor
 2071            .style()
 2072            .unwrap()
 2073            .text
 2074            .line_height_in_pixels(cx.rem_size())
 2075    });
 2076    let window = cx.window;
 2077    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2078    cx.set_state(
 2079        &r#"
 2080        ˇone
 2081        two
 2082        threeˇ
 2083        four
 2084        five
 2085        six
 2086        seven
 2087        eight
 2088        nine
 2089        ten
 2090        "#
 2091        .unindent(),
 2092    );
 2093
 2094    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2095    cx.assert_editor_state(
 2096        &r#"
 2097        one
 2098        two
 2099        three
 2100        ˇfour
 2101        five
 2102        sixˇ
 2103        seven
 2104        eight
 2105        nine
 2106        ten
 2107        "#
 2108        .unindent(),
 2109    );
 2110
 2111    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2112    cx.assert_editor_state(
 2113        &r#"
 2114        one
 2115        two
 2116        three
 2117        four
 2118        five
 2119        six
 2120        ˇseven
 2121        eight
 2122        nineˇ
 2123        ten
 2124        "#
 2125        .unindent(),
 2126    );
 2127
 2128    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2129    cx.assert_editor_state(
 2130        &r#"
 2131        one
 2132        two
 2133        three
 2134        ˇfour
 2135        five
 2136        sixˇ
 2137        seven
 2138        eight
 2139        nine
 2140        ten
 2141        "#
 2142        .unindent(),
 2143    );
 2144
 2145    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2146    cx.assert_editor_state(
 2147        &r#"
 2148        ˇone
 2149        two
 2150        threeˇ
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#
 2159        .unindent(),
 2160    );
 2161
 2162    // Test select collapsing
 2163    cx.update_editor(|editor, cx| {
 2164        editor.move_page_down(&MovePageDown::default(), cx);
 2165        editor.move_page_down(&MovePageDown::default(), cx);
 2166        editor.move_page_down(&MovePageDown::default(), cx);
 2167    });
 2168    cx.assert_editor_state(
 2169        &r#"
 2170        one
 2171        two
 2172        three
 2173        four
 2174        five
 2175        six
 2176        seven
 2177        eight
 2178        nine
 2179        ˇten
 2180        ˇ"#
 2181        .unindent(),
 2182    );
 2183}
 2184
 2185#[gpui::test]
 2186async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2187    init_test(cx, |_| {});
 2188    let mut cx = EditorTestContext::new(cx).await;
 2189    cx.set_state("one «two threeˇ» four");
 2190    cx.update_editor(|editor, cx| {
 2191        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2192        assert_eq!(editor.text(cx), " four");
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199
 2200    let view = cx.add_window(|cx| {
 2201        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2202        build_editor(buffer.clone(), cx)
 2203    });
 2204
 2205    _ = view.update(cx, |view, cx| {
 2206        view.change_selections(None, cx, |s| {
 2207            s.select_display_ranges([
 2208                // an empty selection - the preceding word fragment is deleted
 2209                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2210                // characters selected - they are deleted
 2211                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2212            ])
 2213        });
 2214        view.delete_to_previous_word_start(
 2215            &DeleteToPreviousWordStart {
 2216                ignore_newlines: false,
 2217            },
 2218            cx,
 2219        );
 2220        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2221    });
 2222
 2223    _ = view.update(cx, |view, cx| {
 2224        view.change_selections(None, cx, |s| {
 2225            s.select_display_ranges([
 2226                // an empty selection - the following word fragment is deleted
 2227                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2228                // characters selected - they are deleted
 2229                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2230            ])
 2231        });
 2232        view.delete_to_next_word_end(
 2233            &DeleteToNextWordEnd {
 2234                ignore_newlines: false,
 2235            },
 2236            cx,
 2237        );
 2238        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2239    });
 2240}
 2241
 2242#[gpui::test]
 2243fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2244    init_test(cx, |_| {});
 2245
 2246    let view = cx.add_window(|cx| {
 2247        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2248        build_editor(buffer.clone(), cx)
 2249    });
 2250    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2251        ignore_newlines: false,
 2252    };
 2253    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2254        ignore_newlines: true,
 2255    };
 2256
 2257    _ = view.update(cx, |view, cx| {
 2258        view.change_selections(None, cx, |s| {
 2259            s.select_display_ranges([
 2260                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2261            ])
 2262        });
 2263        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2264        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2265        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2266        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2267        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2268        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2269        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2271        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2272        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2273        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2274        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281
 2282    let view = cx.add_window(|cx| {
 2283        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2284        build_editor(buffer.clone(), cx)
 2285    });
 2286    let del_to_next_word_end = DeleteToNextWordEnd {
 2287        ignore_newlines: false,
 2288    };
 2289    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2290        ignore_newlines: true,
 2291    };
 2292
 2293    _ = view.update(cx, |view, cx| {
 2294        view.change_selections(None, cx, |s| {
 2295            s.select_display_ranges([
 2296                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2297            ])
 2298        });
 2299        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2300        assert_eq!(
 2301            view.buffer.read(cx).read(cx).text(),
 2302            "one\n   two\nthree\n   four"
 2303        );
 2304        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2305        assert_eq!(
 2306            view.buffer.read(cx).read(cx).text(),
 2307            "\n   two\nthree\n   four"
 2308        );
 2309        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2310        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2311        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2312        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2313        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2314        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2315        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2316        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2317    });
 2318}
 2319
 2320#[gpui::test]
 2321fn test_newline(cx: &mut TestAppContext) {
 2322    init_test(cx, |_| {});
 2323
 2324    let view = cx.add_window(|cx| {
 2325        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2326        build_editor(buffer.clone(), cx)
 2327    });
 2328
 2329    _ = view.update(cx, |view, cx| {
 2330        view.change_selections(None, cx, |s| {
 2331            s.select_display_ranges([
 2332                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2333                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2334                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2335            ])
 2336        });
 2337
 2338        view.newline(&Newline, cx);
 2339        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2340    });
 2341}
 2342
 2343#[gpui::test]
 2344fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2345    init_test(cx, |_| {});
 2346
 2347    let editor = cx.add_window(|cx| {
 2348        let buffer = MultiBuffer::build_simple(
 2349            "
 2350                a
 2351                b(
 2352                    X
 2353                )
 2354                c(
 2355                    X
 2356                )
 2357            "
 2358            .unindent()
 2359            .as_str(),
 2360            cx,
 2361        );
 2362        let mut editor = build_editor(buffer.clone(), cx);
 2363        editor.change_selections(None, cx, |s| {
 2364            s.select_ranges([
 2365                Point::new(2, 4)..Point::new(2, 5),
 2366                Point::new(5, 4)..Point::new(5, 5),
 2367            ])
 2368        });
 2369        editor
 2370    });
 2371
 2372    _ = editor.update(cx, |editor, cx| {
 2373        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2374        editor.buffer.update(cx, |buffer, cx| {
 2375            buffer.edit(
 2376                [
 2377                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2378                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2379                ],
 2380                None,
 2381                cx,
 2382            );
 2383            assert_eq!(
 2384                buffer.read(cx).text(),
 2385                "
 2386                    a
 2387                    b()
 2388                    c()
 2389                "
 2390                .unindent()
 2391            );
 2392        });
 2393        assert_eq!(
 2394            editor.selections.ranges(cx),
 2395            &[
 2396                Point::new(1, 2)..Point::new(1, 2),
 2397                Point::new(2, 2)..Point::new(2, 2),
 2398            ],
 2399        );
 2400
 2401        editor.newline(&Newline, cx);
 2402        assert_eq!(
 2403            editor.text(cx),
 2404            "
 2405                a
 2406                b(
 2407                )
 2408                c(
 2409                )
 2410            "
 2411            .unindent()
 2412        );
 2413
 2414        // The selections are moved after the inserted newlines
 2415        assert_eq!(
 2416            editor.selections.ranges(cx),
 2417            &[
 2418                Point::new(2, 0)..Point::new(2, 0),
 2419                Point::new(4, 0)..Point::new(4, 0),
 2420            ],
 2421        );
 2422    });
 2423}
 2424
 2425#[gpui::test]
 2426async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2427    init_test(cx, |settings| {
 2428        settings.defaults.tab_size = NonZeroU32::new(4)
 2429    });
 2430
 2431    let language = Arc::new(
 2432        Language::new(
 2433            LanguageConfig::default(),
 2434            Some(tree_sitter_rust::LANGUAGE.into()),
 2435        )
 2436        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2437        .unwrap(),
 2438    );
 2439
 2440    let mut cx = EditorTestContext::new(cx).await;
 2441    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2442    cx.set_state(indoc! {"
 2443        const a: ˇA = (
 2444 2445                «const_functionˇ»(ˇ),
 2446                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2447 2448        ˇ);ˇ
 2449    "});
 2450
 2451    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2452    cx.assert_editor_state(indoc! {"
 2453        ˇ
 2454        const a: A = (
 2455            ˇ
 2456            (
 2457                ˇ
 2458                ˇ
 2459                const_function(),
 2460                ˇ
 2461                ˇ
 2462                ˇ
 2463                ˇ
 2464                something_else,
 2465                ˇ
 2466            )
 2467            ˇ
 2468            ˇ
 2469        );
 2470    "});
 2471}
 2472
 2473#[gpui::test]
 2474async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2475    init_test(cx, |settings| {
 2476        settings.defaults.tab_size = NonZeroU32::new(4)
 2477    });
 2478
 2479    let language = Arc::new(
 2480        Language::new(
 2481            LanguageConfig::default(),
 2482            Some(tree_sitter_rust::LANGUAGE.into()),
 2483        )
 2484        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2485        .unwrap(),
 2486    );
 2487
 2488    let mut cx = EditorTestContext::new(cx).await;
 2489    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2490    cx.set_state(indoc! {"
 2491        const a: ˇA = (
 2492 2493                «const_functionˇ»(ˇ),
 2494                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2495 2496        ˇ);ˇ
 2497    "});
 2498
 2499    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2500    cx.assert_editor_state(indoc! {"
 2501        const a: A = (
 2502            ˇ
 2503            (
 2504                ˇ
 2505                const_function(),
 2506                ˇ
 2507                ˇ
 2508                something_else,
 2509                ˇ
 2510                ˇ
 2511                ˇ
 2512                ˇ
 2513            )
 2514            ˇ
 2515        );
 2516        ˇ
 2517        ˇ
 2518    "});
 2519}
 2520
 2521#[gpui::test]
 2522async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2523    init_test(cx, |settings| {
 2524        settings.defaults.tab_size = NonZeroU32::new(4)
 2525    });
 2526
 2527    let language = Arc::new(Language::new(
 2528        LanguageConfig {
 2529            line_comments: vec!["//".into()],
 2530            ..LanguageConfig::default()
 2531        },
 2532        None,
 2533    ));
 2534    {
 2535        let mut cx = EditorTestContext::new(cx).await;
 2536        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2537        cx.set_state(indoc! {"
 2538        // Fooˇ
 2539    "});
 2540
 2541        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2542        cx.assert_editor_state(indoc! {"
 2543        // Foo
 2544        //ˇ
 2545    "});
 2546        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2547        cx.set_state(indoc! {"
 2548        ˇ// Foo
 2549    "});
 2550        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2551        cx.assert_editor_state(indoc! {"
 2552
 2553        ˇ// Foo
 2554    "});
 2555    }
 2556    // Ensure that comment continuations can be disabled.
 2557    update_test_language_settings(cx, |settings| {
 2558        settings.defaults.extend_comment_on_newline = Some(false);
 2559    });
 2560    let mut cx = EditorTestContext::new(cx).await;
 2561    cx.set_state(indoc! {"
 2562        // Fooˇ
 2563    "});
 2564    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2565    cx.assert_editor_state(indoc! {"
 2566        // Foo
 2567        ˇ
 2568    "});
 2569}
 2570
 2571#[gpui::test]
 2572fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|cx| {
 2576        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2577        let mut editor = build_editor(buffer.clone(), cx);
 2578        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2579        editor
 2580    });
 2581
 2582    _ = editor.update(cx, |editor, cx| {
 2583        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2584        editor.buffer.update(cx, |buffer, cx| {
 2585            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2586            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2587        });
 2588        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2589
 2590        editor.insert("Z", cx);
 2591        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2592
 2593        // The selections are moved after the inserted characters
 2594        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2595    });
 2596}
 2597
 2598#[gpui::test]
 2599async fn test_tab(cx: &mut gpui::TestAppContext) {
 2600    init_test(cx, |settings| {
 2601        settings.defaults.tab_size = NonZeroU32::new(3)
 2602    });
 2603
 2604    let mut cx = EditorTestContext::new(cx).await;
 2605    cx.set_state(indoc! {"
 2606        ˇabˇc
 2607        ˇ🏀ˇ🏀ˇefg
 2608 2609    "});
 2610    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2611    cx.assert_editor_state(indoc! {"
 2612           ˇab ˇc
 2613           ˇ🏀  ˇ🏀  ˇefg
 2614        d  ˇ
 2615    "});
 2616
 2617    cx.set_state(indoc! {"
 2618        a
 2619        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2620    "});
 2621    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2622    cx.assert_editor_state(indoc! {"
 2623        a
 2624           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2625    "});
 2626}
 2627
 2628#[gpui::test]
 2629async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2630    init_test(cx, |_| {});
 2631
 2632    let mut cx = EditorTestContext::new(cx).await;
 2633    let language = Arc::new(
 2634        Language::new(
 2635            LanguageConfig::default(),
 2636            Some(tree_sitter_rust::LANGUAGE.into()),
 2637        )
 2638        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2639        .unwrap(),
 2640    );
 2641    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2642
 2643    // cursors that are already at the suggested indent level insert
 2644    // a soft tab. cursors that are to the left of the suggested indent
 2645    // auto-indent their line.
 2646    cx.set_state(indoc! {"
 2647        ˇ
 2648        const a: B = (
 2649            c(
 2650                d(
 2651        ˇ
 2652                )
 2653        ˇ
 2654        ˇ    )
 2655        );
 2656    "});
 2657    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2658    cx.assert_editor_state(indoc! {"
 2659            ˇ
 2660        const a: B = (
 2661            c(
 2662                d(
 2663                    ˇ
 2664                )
 2665                ˇ
 2666            ˇ)
 2667        );
 2668    "});
 2669
 2670    // handle auto-indent when there are multiple cursors on the same line
 2671    cx.set_state(indoc! {"
 2672        const a: B = (
 2673            c(
 2674        ˇ    ˇ
 2675        ˇ    )
 2676        );
 2677    "});
 2678    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2679    cx.assert_editor_state(indoc! {"
 2680        const a: B = (
 2681            c(
 2682                ˇ
 2683            ˇ)
 2684        );
 2685    "});
 2686}
 2687
 2688#[gpui::test]
 2689async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2690    init_test(cx, |settings| {
 2691        settings.defaults.tab_size = NonZeroU32::new(4)
 2692    });
 2693
 2694    let language = Arc::new(
 2695        Language::new(
 2696            LanguageConfig::default(),
 2697            Some(tree_sitter_rust::LANGUAGE.into()),
 2698        )
 2699        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2700        .unwrap(),
 2701    );
 2702
 2703    let mut cx = EditorTestContext::new(cx).await;
 2704    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2705    cx.set_state(indoc! {"
 2706        fn a() {
 2707            if b {
 2708        \t ˇc
 2709            }
 2710        }
 2711    "});
 2712
 2713    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2714    cx.assert_editor_state(indoc! {"
 2715        fn a() {
 2716            if b {
 2717                ˇc
 2718            }
 2719        }
 2720    "});
 2721}
 2722
 2723#[gpui::test]
 2724async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2725    init_test(cx, |settings| {
 2726        settings.defaults.tab_size = NonZeroU32::new(4);
 2727    });
 2728
 2729    let mut cx = EditorTestContext::new(cx).await;
 2730
 2731    cx.set_state(indoc! {"
 2732          «oneˇ» «twoˇ»
 2733        three
 2734         four
 2735    "});
 2736    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2737    cx.assert_editor_state(indoc! {"
 2738            «oneˇ» «twoˇ»
 2739        three
 2740         four
 2741    "});
 2742
 2743    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2744    cx.assert_editor_state(indoc! {"
 2745        «oneˇ» «twoˇ»
 2746        three
 2747         four
 2748    "});
 2749
 2750    // select across line ending
 2751    cx.set_state(indoc! {"
 2752        one two
 2753        t«hree
 2754        ˇ» four
 2755    "});
 2756    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2757    cx.assert_editor_state(indoc! {"
 2758        one two
 2759            t«hree
 2760        ˇ» four
 2761    "});
 2762
 2763    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2764    cx.assert_editor_state(indoc! {"
 2765        one two
 2766        t«hree
 2767        ˇ» four
 2768    "});
 2769
 2770    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2771    cx.set_state(indoc! {"
 2772        one two
 2773        ˇthree
 2774            four
 2775    "});
 2776    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2777    cx.assert_editor_state(indoc! {"
 2778        one two
 2779            ˇthree
 2780            four
 2781    "});
 2782
 2783    cx.set_state(indoc! {"
 2784        one two
 2785        ˇ    three
 2786            four
 2787    "});
 2788    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        one two
 2791        ˇthree
 2792            four
 2793    "});
 2794}
 2795
 2796#[gpui::test]
 2797async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2798    init_test(cx, |settings| {
 2799        settings.defaults.hard_tabs = Some(true);
 2800    });
 2801
 2802    let mut cx = EditorTestContext::new(cx).await;
 2803
 2804    // select two ranges on one line
 2805    cx.set_state(indoc! {"
 2806        «oneˇ» «twoˇ»
 2807        three
 2808        four
 2809    "});
 2810    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2811    cx.assert_editor_state(indoc! {"
 2812        \t«oneˇ» «twoˇ»
 2813        three
 2814        four
 2815    "});
 2816    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2817    cx.assert_editor_state(indoc! {"
 2818        \t\t«oneˇ» «twoˇ»
 2819        three
 2820        four
 2821    "});
 2822    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2823    cx.assert_editor_state(indoc! {"
 2824        \t«oneˇ» «twoˇ»
 2825        three
 2826        four
 2827    "});
 2828    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2829    cx.assert_editor_state(indoc! {"
 2830        «oneˇ» «twoˇ»
 2831        three
 2832        four
 2833    "});
 2834
 2835    // select across a line ending
 2836    cx.set_state(indoc! {"
 2837        one two
 2838        t«hree
 2839        ˇ»four
 2840    "});
 2841    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843        one two
 2844        \tt«hree
 2845        ˇ»four
 2846    "});
 2847    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2848    cx.assert_editor_state(indoc! {"
 2849        one two
 2850        \t\tt«hree
 2851        ˇ»four
 2852    "});
 2853    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        one two
 2856        \tt«hree
 2857        ˇ»four
 2858    "});
 2859    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2860    cx.assert_editor_state(indoc! {"
 2861        one two
 2862        t«hree
 2863        ˇ»four
 2864    "});
 2865
 2866    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2867    cx.set_state(indoc! {"
 2868        one two
 2869        ˇthree
 2870        four
 2871    "});
 2872    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2873    cx.assert_editor_state(indoc! {"
 2874        one two
 2875        ˇthree
 2876        four
 2877    "});
 2878    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2879    cx.assert_editor_state(indoc! {"
 2880        one two
 2881        \tˇthree
 2882        four
 2883    "});
 2884    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2885    cx.assert_editor_state(indoc! {"
 2886        one two
 2887        ˇthree
 2888        four
 2889    "});
 2890}
 2891
 2892#[gpui::test]
 2893fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2894    init_test(cx, |settings| {
 2895        settings.languages.extend([
 2896            (
 2897                "TOML".into(),
 2898                LanguageSettingsContent {
 2899                    tab_size: NonZeroU32::new(2),
 2900                    ..Default::default()
 2901                },
 2902            ),
 2903            (
 2904                "Rust".into(),
 2905                LanguageSettingsContent {
 2906                    tab_size: NonZeroU32::new(4),
 2907                    ..Default::default()
 2908                },
 2909            ),
 2910        ]);
 2911    });
 2912
 2913    let toml_language = Arc::new(Language::new(
 2914        LanguageConfig {
 2915            name: "TOML".into(),
 2916            ..Default::default()
 2917        },
 2918        None,
 2919    ));
 2920    let rust_language = Arc::new(Language::new(
 2921        LanguageConfig {
 2922            name: "Rust".into(),
 2923            ..Default::default()
 2924        },
 2925        None,
 2926    ));
 2927
 2928    let toml_buffer =
 2929        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2930    let rust_buffer = cx.new_model(|cx| {
 2931        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2932    });
 2933    let multibuffer = cx.new_model(|cx| {
 2934        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2935        multibuffer.push_excerpts(
 2936            toml_buffer.clone(),
 2937            [ExcerptRange {
 2938                context: Point::new(0, 0)..Point::new(2, 0),
 2939                primary: None,
 2940            }],
 2941            cx,
 2942        );
 2943        multibuffer.push_excerpts(
 2944            rust_buffer.clone(),
 2945            [ExcerptRange {
 2946                context: Point::new(0, 0)..Point::new(1, 0),
 2947                primary: None,
 2948            }],
 2949            cx,
 2950        );
 2951        multibuffer
 2952    });
 2953
 2954    cx.add_window(|cx| {
 2955        let mut editor = build_editor(multibuffer, cx);
 2956
 2957        assert_eq!(
 2958            editor.text(cx),
 2959            indoc! {"
 2960                a = 1
 2961                b = 2
 2962
 2963                const c: usize = 3;
 2964            "}
 2965        );
 2966
 2967        select_ranges(
 2968            &mut editor,
 2969            indoc! {"
 2970                «aˇ» = 1
 2971                b = 2
 2972
 2973                «const c:ˇ» usize = 3;
 2974            "},
 2975            cx,
 2976        );
 2977
 2978        editor.tab(&Tab, cx);
 2979        assert_text_with_selections(
 2980            &mut editor,
 2981            indoc! {"
 2982                  «aˇ» = 1
 2983                b = 2
 2984
 2985                    «const c:ˇ» usize = 3;
 2986            "},
 2987            cx,
 2988        );
 2989        editor.tab_prev(&TabPrev, cx);
 2990        assert_text_with_selections(
 2991            &mut editor,
 2992            indoc! {"
 2993                «aˇ» = 1
 2994                b = 2
 2995
 2996                «const c:ˇ» usize = 3;
 2997            "},
 2998            cx,
 2999        );
 3000
 3001        editor
 3002    });
 3003}
 3004
 3005#[gpui::test]
 3006async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3007    init_test(cx, |_| {});
 3008
 3009    let mut cx = EditorTestContext::new(cx).await;
 3010
 3011    // Basic backspace
 3012    cx.set_state(indoc! {"
 3013        onˇe two three
 3014        fou«rˇ» five six
 3015        seven «ˇeight nine
 3016        »ten
 3017    "});
 3018    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        oˇe two three
 3021        fouˇ five six
 3022        seven ˇten
 3023    "});
 3024
 3025    // Test backspace inside and around indents
 3026    cx.set_state(indoc! {"
 3027        zero
 3028            ˇone
 3029                ˇtwo
 3030            ˇ ˇ ˇ  three
 3031        ˇ  ˇ  four
 3032    "});
 3033    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3034    cx.assert_editor_state(indoc! {"
 3035        zero
 3036        ˇone
 3037            ˇtwo
 3038        ˇ  threeˇ  four
 3039    "});
 3040
 3041    // Test backspace with line_mode set to true
 3042    cx.update_editor(|e, _| e.selections.line_mode = true);
 3043    cx.set_state(indoc! {"
 3044        The ˇquick ˇbrown
 3045        fox jumps over
 3046        the lazy dog
 3047        ˇThe qu«ick bˇ»rown"});
 3048    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        ˇfox jumps over
 3051        the lazy dogˇ"});
 3052}
 3053
 3054#[gpui::test]
 3055async fn test_delete(cx: &mut gpui::TestAppContext) {
 3056    init_test(cx, |_| {});
 3057
 3058    let mut cx = EditorTestContext::new(cx).await;
 3059    cx.set_state(indoc! {"
 3060        onˇe two three
 3061        fou«rˇ» five six
 3062        seven «ˇeight nine
 3063        »ten
 3064    "});
 3065    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        onˇ two three
 3068        fouˇ five six
 3069        seven ˇten
 3070    "});
 3071
 3072    // Test backspace with line_mode set to true
 3073    cx.update_editor(|e, _| e.selections.line_mode = true);
 3074    cx.set_state(indoc! {"
 3075        The ˇquick ˇbrown
 3076        fox «ˇjum»ps over
 3077        the lazy dog
 3078        ˇThe qu«ick bˇ»rown"});
 3079    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3080    cx.assert_editor_state("ˇthe lazy dogˇ");
 3081}
 3082
 3083#[gpui::test]
 3084fn test_delete_line(cx: &mut TestAppContext) {
 3085    init_test(cx, |_| {});
 3086
 3087    let view = cx.add_window(|cx| {
 3088        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3089        build_editor(buffer, cx)
 3090    });
 3091    _ = view.update(cx, |view, cx| {
 3092        view.change_selections(None, cx, |s| {
 3093            s.select_display_ranges([
 3094                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3095                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3096                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3097            ])
 3098        });
 3099        view.delete_line(&DeleteLine, cx);
 3100        assert_eq!(view.display_text(cx), "ghi");
 3101        assert_eq!(
 3102            view.selections.display_ranges(cx),
 3103            vec![
 3104                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3105                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3106            ]
 3107        );
 3108    });
 3109
 3110    let view = cx.add_window(|cx| {
 3111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3112        build_editor(buffer, cx)
 3113    });
 3114    _ = view.update(cx, |view, cx| {
 3115        view.change_selections(None, cx, |s| {
 3116            s.select_display_ranges([
 3117                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3118            ])
 3119        });
 3120        view.delete_line(&DeleteLine, cx);
 3121        assert_eq!(view.display_text(cx), "ghi\n");
 3122        assert_eq!(
 3123            view.selections.display_ranges(cx),
 3124            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3125        );
 3126    });
 3127}
 3128
 3129#[gpui::test]
 3130fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3131    init_test(cx, |_| {});
 3132
 3133    cx.add_window(|cx| {
 3134        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3135        let mut editor = build_editor(buffer.clone(), cx);
 3136        let buffer = buffer.read(cx).as_singleton().unwrap();
 3137
 3138        assert_eq!(
 3139            editor.selections.ranges::<Point>(cx),
 3140            &[Point::new(0, 0)..Point::new(0, 0)]
 3141        );
 3142
 3143        // When on single line, replace newline at end by space
 3144        editor.join_lines(&JoinLines, cx);
 3145        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3146        assert_eq!(
 3147            editor.selections.ranges::<Point>(cx),
 3148            &[Point::new(0, 3)..Point::new(0, 3)]
 3149        );
 3150
 3151        // When multiple lines are selected, remove newlines that are spanned by the selection
 3152        editor.change_selections(None, cx, |s| {
 3153            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3154        });
 3155        editor.join_lines(&JoinLines, cx);
 3156        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3157        assert_eq!(
 3158            editor.selections.ranges::<Point>(cx),
 3159            &[Point::new(0, 11)..Point::new(0, 11)]
 3160        );
 3161
 3162        // Undo should be transactional
 3163        editor.undo(&Undo, cx);
 3164        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3165        assert_eq!(
 3166            editor.selections.ranges::<Point>(cx),
 3167            &[Point::new(0, 5)..Point::new(2, 2)]
 3168        );
 3169
 3170        // When joining an empty line don't insert a space
 3171        editor.change_selections(None, cx, |s| {
 3172            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3173        });
 3174        editor.join_lines(&JoinLines, cx);
 3175        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3176        assert_eq!(
 3177            editor.selections.ranges::<Point>(cx),
 3178            [Point::new(2, 3)..Point::new(2, 3)]
 3179        );
 3180
 3181        // We can remove trailing newlines
 3182        editor.join_lines(&JoinLines, cx);
 3183        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3184        assert_eq!(
 3185            editor.selections.ranges::<Point>(cx),
 3186            [Point::new(2, 3)..Point::new(2, 3)]
 3187        );
 3188
 3189        // We don't blow up on the last line
 3190        editor.join_lines(&JoinLines, cx);
 3191        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3192        assert_eq!(
 3193            editor.selections.ranges::<Point>(cx),
 3194            [Point::new(2, 3)..Point::new(2, 3)]
 3195        );
 3196
 3197        // reset to test indentation
 3198        editor.buffer.update(cx, |buffer, cx| {
 3199            buffer.edit(
 3200                [
 3201                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3202                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3203                ],
 3204                None,
 3205                cx,
 3206            )
 3207        });
 3208
 3209        // We remove any leading spaces
 3210        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3211        editor.change_selections(None, cx, |s| {
 3212            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3213        });
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3216
 3217        // We don't insert a space for a line containing only spaces
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3220
 3221        // We ignore any leading tabs
 3222        editor.join_lines(&JoinLines, cx);
 3223        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3224
 3225        editor
 3226    });
 3227}
 3228
 3229#[gpui::test]
 3230fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3231    init_test(cx, |_| {});
 3232
 3233    cx.add_window(|cx| {
 3234        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3235        let mut editor = build_editor(buffer.clone(), cx);
 3236        let buffer = buffer.read(cx).as_singleton().unwrap();
 3237
 3238        editor.change_selections(None, cx, |s| {
 3239            s.select_ranges([
 3240                Point::new(0, 2)..Point::new(1, 1),
 3241                Point::new(1, 2)..Point::new(1, 2),
 3242                Point::new(3, 1)..Point::new(3, 2),
 3243            ])
 3244        });
 3245
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3248
 3249        assert_eq!(
 3250            editor.selections.ranges::<Point>(cx),
 3251            [
 3252                Point::new(0, 7)..Point::new(0, 7),
 3253                Point::new(1, 3)..Point::new(1, 3)
 3254            ]
 3255        );
 3256        editor
 3257    });
 3258}
 3259
 3260#[gpui::test]
 3261async fn test_join_lines_with_git_diff_base(
 3262    executor: BackgroundExecutor,
 3263    cx: &mut gpui::TestAppContext,
 3264) {
 3265    init_test(cx, |_| {});
 3266
 3267    let mut cx = EditorTestContext::new(cx).await;
 3268
 3269    let diff_base = r#"
 3270        Line 0
 3271        Line 1
 3272        Line 2
 3273        Line 3
 3274        "#
 3275    .unindent();
 3276
 3277    cx.set_state(
 3278        &r#"
 3279        ˇLine 0
 3280        Line 1
 3281        Line 2
 3282        Line 3
 3283        "#
 3284        .unindent(),
 3285    );
 3286
 3287    cx.set_diff_base(Some(&diff_base));
 3288    executor.run_until_parked();
 3289
 3290    // Join lines
 3291    cx.update_editor(|editor, cx| {
 3292        editor.join_lines(&JoinLines, cx);
 3293    });
 3294    executor.run_until_parked();
 3295
 3296    cx.assert_editor_state(
 3297        &r#"
 3298        Line 0ˇ Line 1
 3299        Line 2
 3300        Line 3
 3301        "#
 3302        .unindent(),
 3303    );
 3304    // Join again
 3305    cx.update_editor(|editor, cx| {
 3306        editor.join_lines(&JoinLines, cx);
 3307    });
 3308    executor.run_until_parked();
 3309
 3310    cx.assert_editor_state(
 3311        &r#"
 3312        Line 0 Line 1ˇ Line 2
 3313        Line 3
 3314        "#
 3315        .unindent(),
 3316    );
 3317}
 3318
 3319#[gpui::test]
 3320async fn test_custom_newlines_cause_no_false_positive_diffs(
 3321    executor: BackgroundExecutor,
 3322    cx: &mut gpui::TestAppContext,
 3323) {
 3324    init_test(cx, |_| {});
 3325    let mut cx = EditorTestContext::new(cx).await;
 3326    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3327    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3328    executor.run_until_parked();
 3329
 3330    cx.update_editor(|editor, cx| {
 3331        assert_eq!(
 3332            editor
 3333                .buffer()
 3334                .read(cx)
 3335                .snapshot(cx)
 3336                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3337                .collect::<Vec<_>>(),
 3338            Vec::new(),
 3339            "Should not have any diffs for files with custom newlines"
 3340        );
 3341    });
 3342}
 3343
 3344#[gpui::test]
 3345async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3346    init_test(cx, |_| {});
 3347
 3348    let mut cx = EditorTestContext::new(cx).await;
 3349
 3350    // Test sort_lines_case_insensitive()
 3351    cx.set_state(indoc! {"
 3352        «z
 3353        y
 3354        x
 3355        Z
 3356        Y
 3357        Xˇ»
 3358    "});
 3359    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3360    cx.assert_editor_state(indoc! {"
 3361        «x
 3362        X
 3363        y
 3364        Y
 3365        z
 3366        Zˇ»
 3367    "});
 3368
 3369    // Test reverse_lines()
 3370    cx.set_state(indoc! {"
 3371        «5
 3372        4
 3373        3
 3374        2
 3375        1ˇ»
 3376    "});
 3377    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3378    cx.assert_editor_state(indoc! {"
 3379        «1
 3380        2
 3381        3
 3382        4
 3383        5ˇ»
 3384    "});
 3385
 3386    // Skip testing shuffle_line()
 3387
 3388    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3389    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3390
 3391    // Don't manipulate when cursor is on single line, but expand the selection
 3392    cx.set_state(indoc! {"
 3393        ddˇdd
 3394        ccc
 3395        bb
 3396        a
 3397    "});
 3398    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3399    cx.assert_editor_state(indoc! {"
 3400        «ddddˇ»
 3401        ccc
 3402        bb
 3403        a
 3404    "});
 3405
 3406    // Basic manipulate case
 3407    // Start selection moves to column 0
 3408    // End of selection shrinks to fit shorter line
 3409    cx.set_state(indoc! {"
 3410        dd«d
 3411        ccc
 3412        bb
 3413        aaaaaˇ»
 3414    "});
 3415    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3416    cx.assert_editor_state(indoc! {"
 3417        «aaaaa
 3418        bb
 3419        ccc
 3420        dddˇ»
 3421    "});
 3422
 3423    // Manipulate case with newlines
 3424    cx.set_state(indoc! {"
 3425        dd«d
 3426        ccc
 3427
 3428        bb
 3429        aaaaa
 3430
 3431        ˇ»
 3432    "});
 3433    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3434    cx.assert_editor_state(indoc! {"
 3435        «
 3436
 3437        aaaaa
 3438        bb
 3439        ccc
 3440        dddˇ»
 3441
 3442    "});
 3443
 3444    // Adding new line
 3445    cx.set_state(indoc! {"
 3446        aa«a
 3447        bbˇ»b
 3448    "});
 3449    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3450    cx.assert_editor_state(indoc! {"
 3451        «aaa
 3452        bbb
 3453        added_lineˇ»
 3454    "});
 3455
 3456    // Removing line
 3457    cx.set_state(indoc! {"
 3458        aa«a
 3459        bbbˇ»
 3460    "});
 3461    cx.update_editor(|e, cx| {
 3462        e.manipulate_lines(cx, |lines| {
 3463            lines.pop();
 3464        })
 3465    });
 3466    cx.assert_editor_state(indoc! {"
 3467        «aaaˇ»
 3468    "});
 3469
 3470    // Removing all lines
 3471    cx.set_state(indoc! {"
 3472        aa«a
 3473        bbbˇ»
 3474    "});
 3475    cx.update_editor(|e, cx| {
 3476        e.manipulate_lines(cx, |lines| {
 3477            lines.drain(..);
 3478        })
 3479    });
 3480    cx.assert_editor_state(indoc! {"
 3481        ˇ
 3482    "});
 3483}
 3484
 3485#[gpui::test]
 3486async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3487    init_test(cx, |_| {});
 3488
 3489    let mut cx = EditorTestContext::new(cx).await;
 3490
 3491    // Consider continuous selection as single selection
 3492    cx.set_state(indoc! {"
 3493        Aaa«aa
 3494        cˇ»c«c
 3495        bb
 3496        aaaˇ»aa
 3497    "});
 3498    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3499    cx.assert_editor_state(indoc! {"
 3500        «Aaaaa
 3501        ccc
 3502        bb
 3503        aaaaaˇ»
 3504    "});
 3505
 3506    cx.set_state(indoc! {"
 3507        Aaa«aa
 3508        cˇ»c«c
 3509        bb
 3510        aaaˇ»aa
 3511    "});
 3512    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3513    cx.assert_editor_state(indoc! {"
 3514        «Aaaaa
 3515        ccc
 3516        bbˇ»
 3517    "});
 3518
 3519    // Consider non continuous selection as distinct dedup operations
 3520    cx.set_state(indoc! {"
 3521        «aaaaa
 3522        bb
 3523        aaaaa
 3524        aaaaaˇ»
 3525
 3526        aaa«aaˇ»
 3527    "});
 3528    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3529    cx.assert_editor_state(indoc! {"
 3530        «aaaaa
 3531        bbˇ»
 3532
 3533        «aaaaaˇ»
 3534    "});
 3535}
 3536
 3537#[gpui::test]
 3538async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3539    init_test(cx, |_| {});
 3540
 3541    let mut cx = EditorTestContext::new(cx).await;
 3542
 3543    cx.set_state(indoc! {"
 3544        «Aaa
 3545        aAa
 3546        Aaaˇ»
 3547    "});
 3548    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3549    cx.assert_editor_state(indoc! {"
 3550        «Aaa
 3551        aAaˇ»
 3552    "});
 3553
 3554    cx.set_state(indoc! {"
 3555        «Aaa
 3556        aAa
 3557        aaAˇ»
 3558    "});
 3559    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3560    cx.assert_editor_state(indoc! {"
 3561        «Aaaˇ»
 3562    "});
 3563}
 3564
 3565#[gpui::test]
 3566async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3567    init_test(cx, |_| {});
 3568
 3569    let mut cx = EditorTestContext::new(cx).await;
 3570
 3571    // Manipulate with multiple selections on a single line
 3572    cx.set_state(indoc! {"
 3573        dd«dd
 3574        cˇ»c«c
 3575        bb
 3576        aaaˇ»aa
 3577    "});
 3578    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3579    cx.assert_editor_state(indoc! {"
 3580        «aaaaa
 3581        bb
 3582        ccc
 3583        ddddˇ»
 3584    "});
 3585
 3586    // Manipulate with multiple disjoin selections
 3587    cx.set_state(indoc! {"
 3588 3589        4
 3590        3
 3591        2
 3592        1ˇ»
 3593
 3594        dd«dd
 3595        ccc
 3596        bb
 3597        aaaˇ»aa
 3598    "});
 3599    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3600    cx.assert_editor_state(indoc! {"
 3601        «1
 3602        2
 3603        3
 3604        4
 3605        5ˇ»
 3606
 3607        «aaaaa
 3608        bb
 3609        ccc
 3610        ddddˇ»
 3611    "});
 3612
 3613    // Adding lines on each selection
 3614    cx.set_state(indoc! {"
 3615 3616        1ˇ»
 3617
 3618        bb«bb
 3619        aaaˇ»aa
 3620    "});
 3621    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3622    cx.assert_editor_state(indoc! {"
 3623        «2
 3624        1
 3625        added lineˇ»
 3626
 3627        «bbbb
 3628        aaaaa
 3629        added lineˇ»
 3630    "});
 3631
 3632    // Removing lines on each selection
 3633    cx.set_state(indoc! {"
 3634 3635        1ˇ»
 3636
 3637        bb«bb
 3638        aaaˇ»aa
 3639    "});
 3640    cx.update_editor(|e, cx| {
 3641        e.manipulate_lines(cx, |lines| {
 3642            lines.pop();
 3643        })
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «2ˇ»
 3647
 3648        «bbbbˇ»
 3649    "});
 3650}
 3651
 3652#[gpui::test]
 3653async fn test_manipulate_text(cx: &mut TestAppContext) {
 3654    init_test(cx, |_| {});
 3655
 3656    let mut cx = EditorTestContext::new(cx).await;
 3657
 3658    // Test convert_to_upper_case()
 3659    cx.set_state(indoc! {"
 3660        «hello worldˇ»
 3661    "});
 3662    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3663    cx.assert_editor_state(indoc! {"
 3664        «HELLO WORLDˇ»
 3665    "});
 3666
 3667    // Test convert_to_lower_case()
 3668    cx.set_state(indoc! {"
 3669        «HELLO WORLDˇ»
 3670    "});
 3671    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3672    cx.assert_editor_state(indoc! {"
 3673        «hello worldˇ»
 3674    "});
 3675
 3676    // Test multiple line, single selection case
 3677    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3678    cx.set_state(indoc! {"
 3679        «The quick brown
 3680        fox jumps over
 3681        the lazy dogˇ»
 3682    "});
 3683    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3684    cx.assert_editor_state(indoc! {"
 3685        «The Quick Brown
 3686        Fox Jumps Over
 3687        The Lazy Dogˇ»
 3688    "});
 3689
 3690    // Test multiple line, single selection case
 3691    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3692    cx.set_state(indoc! {"
 3693        «The quick brown
 3694        fox jumps over
 3695        the lazy dogˇ»
 3696    "});
 3697    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3698    cx.assert_editor_state(indoc! {"
 3699        «TheQuickBrown
 3700        FoxJumpsOver
 3701        TheLazyDogˇ»
 3702    "});
 3703
 3704    // From here on out, test more complex cases of manipulate_text()
 3705
 3706    // Test no selection case - should affect words cursors are in
 3707    // Cursor at beginning, middle, and end of word
 3708    cx.set_state(indoc! {"
 3709        ˇhello big beauˇtiful worldˇ
 3710    "});
 3711    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3712    cx.assert_editor_state(indoc! {"
 3713        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3714    "});
 3715
 3716    // Test multiple selections on a single line and across multiple lines
 3717    cx.set_state(indoc! {"
 3718        «Theˇ» quick «brown
 3719        foxˇ» jumps «overˇ»
 3720        the «lazyˇ» dog
 3721    "});
 3722    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3723    cx.assert_editor_state(indoc! {"
 3724        «THEˇ» quick «BROWN
 3725        FOXˇ» jumps «OVERˇ»
 3726        the «LAZYˇ» dog
 3727    "});
 3728
 3729    // Test case where text length grows
 3730    cx.set_state(indoc! {"
 3731        «tschüߡ»
 3732    "});
 3733    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3734    cx.assert_editor_state(indoc! {"
 3735        «TSCHÜSSˇ»
 3736    "});
 3737
 3738    // Test to make sure we don't crash when text shrinks
 3739    cx.set_state(indoc! {"
 3740        aaa_bbbˇ
 3741    "});
 3742    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3743    cx.assert_editor_state(indoc! {"
 3744        «aaaBbbˇ»
 3745    "});
 3746
 3747    // Test to make sure we all aware of the fact that each word can grow and shrink
 3748    // Final selections should be aware of this fact
 3749    cx.set_state(indoc! {"
 3750        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3751    "});
 3752    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3753    cx.assert_editor_state(indoc! {"
 3754        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3755    "});
 3756
 3757    cx.set_state(indoc! {"
 3758        «hElLo, WoRld!ˇ»
 3759    "});
 3760    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3761    cx.assert_editor_state(indoc! {"
 3762        «HeLlO, wOrLD!ˇ»
 3763    "});
 3764}
 3765
 3766#[gpui::test]
 3767fn test_duplicate_line(cx: &mut TestAppContext) {
 3768    init_test(cx, |_| {});
 3769
 3770    let view = cx.add_window(|cx| {
 3771        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3772        build_editor(buffer, cx)
 3773    });
 3774    _ = view.update(cx, |view, cx| {
 3775        view.change_selections(None, cx, |s| {
 3776            s.select_display_ranges([
 3777                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3778                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3779                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3780                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3781            ])
 3782        });
 3783        view.duplicate_line_down(&DuplicateLineDown, cx);
 3784        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3785        assert_eq!(
 3786            view.selections.display_ranges(cx),
 3787            vec![
 3788                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3789                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3790                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3791                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3792            ]
 3793        );
 3794    });
 3795
 3796    let view = cx.add_window(|cx| {
 3797        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3798        build_editor(buffer, cx)
 3799    });
 3800    _ = view.update(cx, |view, cx| {
 3801        view.change_selections(None, cx, |s| {
 3802            s.select_display_ranges([
 3803                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3804                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3805            ])
 3806        });
 3807        view.duplicate_line_down(&DuplicateLineDown, cx);
 3808        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3809        assert_eq!(
 3810            view.selections.display_ranges(cx),
 3811            vec![
 3812                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3813                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3814            ]
 3815        );
 3816    });
 3817
 3818    // With `move_upwards` the selections stay in place, except for
 3819    // the lines inserted above them
 3820    let view = cx.add_window(|cx| {
 3821        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3822        build_editor(buffer, cx)
 3823    });
 3824    _ = view.update(cx, |view, cx| {
 3825        view.change_selections(None, cx, |s| {
 3826            s.select_display_ranges([
 3827                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3828                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3829                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3830                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3831            ])
 3832        });
 3833        view.duplicate_line_up(&DuplicateLineUp, cx);
 3834        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3835        assert_eq!(
 3836            view.selections.display_ranges(cx),
 3837            vec![
 3838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3839                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3840                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3841                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3842            ]
 3843        );
 3844    });
 3845
 3846    let view = cx.add_window(|cx| {
 3847        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3848        build_editor(buffer, cx)
 3849    });
 3850    _ = view.update(cx, |view, cx| {
 3851        view.change_selections(None, cx, |s| {
 3852            s.select_display_ranges([
 3853                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3855            ])
 3856        });
 3857        view.duplicate_line_up(&DuplicateLineUp, cx);
 3858        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3859        assert_eq!(
 3860            view.selections.display_ranges(cx),
 3861            vec![
 3862                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3863                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3864            ]
 3865        );
 3866    });
 3867}
 3868
 3869#[gpui::test]
 3870fn test_move_line_up_down(cx: &mut TestAppContext) {
 3871    init_test(cx, |_| {});
 3872
 3873    let view = cx.add_window(|cx| {
 3874        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3875        build_editor(buffer, cx)
 3876    });
 3877    _ = view.update(cx, |view, cx| {
 3878        view.fold_ranges(
 3879            vec![
 3880                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3881                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3882                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3883            ],
 3884            true,
 3885            cx,
 3886        );
 3887        view.change_selections(None, cx, |s| {
 3888            s.select_display_ranges([
 3889                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3890                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3891                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3892                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3893            ])
 3894        });
 3895        assert_eq!(
 3896            view.display_text(cx),
 3897            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3898        );
 3899
 3900        view.move_line_up(&MoveLineUp, cx);
 3901        assert_eq!(
 3902            view.display_text(cx),
 3903            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3904        );
 3905        assert_eq!(
 3906            view.selections.display_ranges(cx),
 3907            vec![
 3908                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3909                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3910                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3911                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3912            ]
 3913        );
 3914    });
 3915
 3916    _ = view.update(cx, |view, cx| {
 3917        view.move_line_down(&MoveLineDown, cx);
 3918        assert_eq!(
 3919            view.display_text(cx),
 3920            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3921        );
 3922        assert_eq!(
 3923            view.selections.display_ranges(cx),
 3924            vec![
 3925                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3926                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3927                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3928                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3929            ]
 3930        );
 3931    });
 3932
 3933    _ = view.update(cx, |view, cx| {
 3934        view.move_line_down(&MoveLineDown, cx);
 3935        assert_eq!(
 3936            view.display_text(cx),
 3937            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3938        );
 3939        assert_eq!(
 3940            view.selections.display_ranges(cx),
 3941            vec![
 3942                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3943                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3944                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3945                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3946            ]
 3947        );
 3948    });
 3949
 3950    _ = view.update(cx, |view, cx| {
 3951        view.move_line_up(&MoveLineUp, cx);
 3952        assert_eq!(
 3953            view.display_text(cx),
 3954            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3955        );
 3956        assert_eq!(
 3957            view.selections.display_ranges(cx),
 3958            vec![
 3959                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3960                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3961                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3962                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3963            ]
 3964        );
 3965    });
 3966}
 3967
 3968#[gpui::test]
 3969fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3970    init_test(cx, |_| {});
 3971
 3972    let editor = cx.add_window(|cx| {
 3973        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3974        build_editor(buffer, cx)
 3975    });
 3976    _ = editor.update(cx, |editor, cx| {
 3977        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3978        editor.insert_blocks(
 3979            [BlockProperties {
 3980                style: BlockStyle::Fixed,
 3981                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 3982                height: 1,
 3983                render: Box::new(|_| div().into_any()),
 3984                priority: 0,
 3985            }],
 3986            Some(Autoscroll::fit()),
 3987            cx,
 3988        );
 3989        editor.change_selections(None, cx, |s| {
 3990            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3991        });
 3992        editor.move_line_down(&MoveLineDown, cx);
 3993    });
 3994}
 3995
 3996#[gpui::test]
 3997async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 3998    init_test(cx, |_| {});
 3999
 4000    let mut cx = EditorTestContext::new(cx).await;
 4001    cx.set_state(
 4002        &"
 4003            ˇzero
 4004            one
 4005            two
 4006            three
 4007            four
 4008            five
 4009        "
 4010        .unindent(),
 4011    );
 4012
 4013    // Create a four-line block that replaces three lines of text.
 4014    cx.update_editor(|editor, cx| {
 4015        let snapshot = editor.snapshot(cx);
 4016        let snapshot = &snapshot.buffer_snapshot;
 4017        let placement = BlockPlacement::Replace(
 4018            snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
 4019        );
 4020        editor.insert_blocks(
 4021            [BlockProperties {
 4022                placement,
 4023                height: 4,
 4024                style: BlockStyle::Sticky,
 4025                render: Box::new(|_| gpui::div().into_any_element()),
 4026                priority: 0,
 4027            }],
 4028            None,
 4029            cx,
 4030        );
 4031    });
 4032
 4033    // Move down so that the cursor touches the block.
 4034    cx.update_editor(|editor, cx| {
 4035        editor.move_down(&Default::default(), cx);
 4036    });
 4037    cx.assert_editor_state(
 4038        &"
 4039            zero
 4040            «one
 4041            two
 4042            threeˇ»
 4043            four
 4044            five
 4045        "
 4046        .unindent(),
 4047    );
 4048
 4049    // Move down past the block.
 4050    cx.update_editor(|editor, cx| {
 4051        editor.move_down(&Default::default(), cx);
 4052    });
 4053    cx.assert_editor_state(
 4054        &"
 4055            zero
 4056            one
 4057            two
 4058            three
 4059            ˇfour
 4060            five
 4061        "
 4062        .unindent(),
 4063    );
 4064}
 4065
 4066#[gpui::test]
 4067fn test_transpose(cx: &mut TestAppContext) {
 4068    init_test(cx, |_| {});
 4069
 4070    _ = cx.add_window(|cx| {
 4071        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4072        editor.set_style(EditorStyle::default(), cx);
 4073        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "bac");
 4076        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "bca");
 4080        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4081
 4082        editor.transpose(&Default::default(), cx);
 4083        assert_eq!(editor.text(cx), "bac");
 4084        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4085
 4086        editor
 4087    });
 4088
 4089    _ = cx.add_window(|cx| {
 4090        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4091        editor.set_style(EditorStyle::default(), cx);
 4092        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4093        editor.transpose(&Default::default(), cx);
 4094        assert_eq!(editor.text(cx), "acb\nde");
 4095        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4096
 4097        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4098        editor.transpose(&Default::default(), cx);
 4099        assert_eq!(editor.text(cx), "acbd\ne");
 4100        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4101
 4102        editor.transpose(&Default::default(), cx);
 4103        assert_eq!(editor.text(cx), "acbde\n");
 4104        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4105
 4106        editor.transpose(&Default::default(), cx);
 4107        assert_eq!(editor.text(cx), "acbd\ne");
 4108        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4109
 4110        editor
 4111    });
 4112
 4113    _ = cx.add_window(|cx| {
 4114        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4115        editor.set_style(EditorStyle::default(), cx);
 4116        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4117        editor.transpose(&Default::default(), cx);
 4118        assert_eq!(editor.text(cx), "bacd\ne");
 4119        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4120
 4121        editor.transpose(&Default::default(), cx);
 4122        assert_eq!(editor.text(cx), "bcade\n");
 4123        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4124
 4125        editor.transpose(&Default::default(), cx);
 4126        assert_eq!(editor.text(cx), "bcda\ne");
 4127        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4128
 4129        editor.transpose(&Default::default(), cx);
 4130        assert_eq!(editor.text(cx), "bcade\n");
 4131        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4132
 4133        editor.transpose(&Default::default(), cx);
 4134        assert_eq!(editor.text(cx), "bcaed\n");
 4135        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4136
 4137        editor
 4138    });
 4139
 4140    _ = cx.add_window(|cx| {
 4141        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4142        editor.set_style(EditorStyle::default(), cx);
 4143        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4144        editor.transpose(&Default::default(), cx);
 4145        assert_eq!(editor.text(cx), "🏀🍐✋");
 4146        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4147
 4148        editor.transpose(&Default::default(), cx);
 4149        assert_eq!(editor.text(cx), "🏀✋🍐");
 4150        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4151
 4152        editor.transpose(&Default::default(), cx);
 4153        assert_eq!(editor.text(cx), "🏀🍐✋");
 4154        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4155
 4156        editor
 4157    });
 4158}
 4159
 4160#[gpui::test]
 4161async fn test_rewrap(cx: &mut TestAppContext) {
 4162    init_test(cx, |_| {});
 4163
 4164    let mut cx = EditorTestContext::new(cx).await;
 4165
 4166    {
 4167        let language = Arc::new(Language::new(
 4168            LanguageConfig {
 4169                line_comments: vec!["// ".into()],
 4170                ..LanguageConfig::default()
 4171            },
 4172            None,
 4173        ));
 4174        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4175
 4176        let unwrapped_text = indoc! {"
 4177            // ˇ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.
 4178        "};
 4179
 4180        let wrapped_text = indoc! {"
 4181            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4182            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4183            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4184            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4185            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4186            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4187            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4188            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4189            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4190            // porttitor id. Aliquam id accumsan eros.ˇ
 4191        "};
 4192
 4193        cx.set_state(unwrapped_text);
 4194        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4195        cx.assert_editor_state(wrapped_text);
 4196    }
 4197
 4198    // Test that rewrapping works inside of a selection
 4199    {
 4200        let language = Arc::new(Language::new(
 4201            LanguageConfig {
 4202                line_comments: vec!["// ".into()],
 4203                ..LanguageConfig::default()
 4204            },
 4205            None,
 4206        ));
 4207        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4208
 4209        let unwrapped_text = indoc! {"
 4210            «// 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.ˇ»
 4211        "};
 4212
 4213        let wrapped_text = indoc! {"
 4214            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4215            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4216            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4217            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4218            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4219            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4220            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4221            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4222            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4223            // porttitor id. Aliquam id accumsan eros.ˇ
 4224        "};
 4225
 4226        cx.set_state(unwrapped_text);
 4227        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4228        cx.assert_editor_state(wrapped_text);
 4229    }
 4230
 4231    // Test that cursors that expand to the same region are collapsed.
 4232    {
 4233        let language = Arc::new(Language::new(
 4234            LanguageConfig {
 4235                line_comments: vec!["// ".into()],
 4236                ..LanguageConfig::default()
 4237            },
 4238            None,
 4239        ));
 4240        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4241
 4242        let unwrapped_text = indoc! {"
 4243            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4244            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4245            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4246            // ˇ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.
 4247        "};
 4248
 4249        let wrapped_text = indoc! {"
 4250            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4251            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4252            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4253            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4254            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4255            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4256            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4257            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4258            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4259            // porttitor id. Aliquam id accumsan eros.ˇ
 4260        "};
 4261
 4262        cx.set_state(unwrapped_text);
 4263        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4264        cx.assert_editor_state(wrapped_text);
 4265    }
 4266
 4267    // Test that non-contiguous selections are treated separately.
 4268    {
 4269        let language = Arc::new(Language::new(
 4270            LanguageConfig {
 4271                line_comments: vec!["// ".into()],
 4272                ..LanguageConfig::default()
 4273            },
 4274            None,
 4275        ));
 4276        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4277
 4278        let unwrapped_text = indoc! {"
 4279            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4280            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4281            //
 4282            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4283            // ˇ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.
 4284        "};
 4285
 4286        let wrapped_text = indoc! {"
 4287            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4288            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4289            // auctor, eu lacinia sapien scelerisque.ˇ
 4290            //
 4291            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4292            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4293            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4294            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4295            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4296            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4297            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ
 4298        "};
 4299
 4300        cx.set_state(unwrapped_text);
 4301        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4302        cx.assert_editor_state(wrapped_text);
 4303    }
 4304
 4305    // Test that different comment prefixes are supported.
 4306    {
 4307        let language = Arc::new(Language::new(
 4308            LanguageConfig {
 4309                line_comments: vec!["# ".into()],
 4310                ..LanguageConfig::default()
 4311            },
 4312            None,
 4313        ));
 4314        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4315
 4316        let unwrapped_text = indoc! {"
 4317            # ˇ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.
 4318        "};
 4319
 4320        let wrapped_text = indoc! {"
 4321            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4322            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4323            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4324            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4325            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4326            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4327            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4328            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4329            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4330            # accumsan eros.ˇ
 4331        "};
 4332
 4333        cx.set_state(unwrapped_text);
 4334        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4335        cx.assert_editor_state(wrapped_text);
 4336    }
 4337
 4338    // Test that rewrapping is ignored outside of comments in most languages.
 4339    {
 4340        let language = Arc::new(Language::new(
 4341            LanguageConfig {
 4342                line_comments: vec!["// ".into(), "/// ".into()],
 4343                ..LanguageConfig::default()
 4344            },
 4345            Some(tree_sitter_rust::LANGUAGE.into()),
 4346        ));
 4347        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4348
 4349        let unwrapped_text = indoc! {"
 4350            /// Adds two numbers.
 4351            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4352            fn add(a: u32, b: u32) -> u32 {
 4353                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ˇ
 4354            }
 4355        "};
 4356
 4357        let wrapped_text = indoc! {"
 4358            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4359            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4360            fn add(a: u32, b: u32) -> u32 {
 4361                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ˇ
 4362            }
 4363        "};
 4364
 4365        cx.set_state(unwrapped_text);
 4366        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4367        cx.assert_editor_state(wrapped_text);
 4368    }
 4369
 4370    // Test that rewrapping works in Markdown and Plain Text languages.
 4371    {
 4372        let markdown_language = Arc::new(Language::new(
 4373            LanguageConfig {
 4374                name: "Markdown".into(),
 4375                ..LanguageConfig::default()
 4376            },
 4377            None,
 4378        ));
 4379        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4380
 4381        let unwrapped_text = indoc! {"
 4382            # Hello
 4383
 4384            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.
 4385        "};
 4386
 4387        let wrapped_text = indoc! {"
 4388            # Hello
 4389
 4390            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4391            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4392            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4393            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4394            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4395            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4396            Integer sit amet scelerisque nisi.ˇ
 4397        "};
 4398
 4399        cx.set_state(unwrapped_text);
 4400        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4401        cx.assert_editor_state(wrapped_text);
 4402
 4403        let plaintext_language = Arc::new(Language::new(
 4404            LanguageConfig {
 4405                name: "Plain Text".into(),
 4406                ..LanguageConfig::default()
 4407            },
 4408            None,
 4409        ));
 4410        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4411
 4412        let unwrapped_text = indoc! {"
 4413            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.
 4414        "};
 4415
 4416        let wrapped_text = indoc! {"
 4417            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4418            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4419            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4420            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4421            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4422            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4423            Integer sit amet scelerisque nisi.ˇ
 4424        "};
 4425
 4426        cx.set_state(unwrapped_text);
 4427        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4428        cx.assert_editor_state(wrapped_text);
 4429    }
 4430
 4431    // Test rewrapping unaligned comments in a selection.
 4432    {
 4433        let language = Arc::new(Language::new(
 4434            LanguageConfig {
 4435                line_comments: vec!["// ".into(), "/// ".into()],
 4436                ..LanguageConfig::default()
 4437            },
 4438            Some(tree_sitter_rust::LANGUAGE.into()),
 4439        ));
 4440        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4441
 4442        let unwrapped_text = indoc! {"
 4443            fn foo() {
 4444                if true {
 4445            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4446            // Praesent semper egestas tellus id dignissim.ˇ»
 4447                    do_something();
 4448                } else {
 4449                    //
 4450                }
 4451            }
 4452        "};
 4453
 4454        let wrapped_text = indoc! {"
 4455            fn foo() {
 4456                if true {
 4457                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4458                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4459                    // egestas tellus id dignissim.ˇ
 4460                    do_something();
 4461                } else {
 4462                    //
 4463                }
 4464            }
 4465        "};
 4466
 4467        cx.set_state(unwrapped_text);
 4468        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4469        cx.assert_editor_state(wrapped_text);
 4470
 4471        let unwrapped_text = indoc! {"
 4472            fn foo() {
 4473                if true {
 4474            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4475            // Praesent semper egestas tellus id dignissim.»
 4476                    do_something();
 4477                } else {
 4478                    //
 4479                }
 4480
 4481            }
 4482        "};
 4483
 4484        let wrapped_text = indoc! {"
 4485            fn foo() {
 4486                if true {
 4487                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4488                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4489                    // egestas tellus id dignissim.ˇ
 4490                    do_something();
 4491                } else {
 4492                    //
 4493                }
 4494
 4495            }
 4496        "};
 4497
 4498        cx.set_state(unwrapped_text);
 4499        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4500        cx.assert_editor_state(wrapped_text);
 4501    }
 4502}
 4503
 4504#[gpui::test]
 4505async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4506    init_test(cx, |_| {});
 4507
 4508    let mut cx = EditorTestContext::new(cx).await;
 4509
 4510    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4511    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4512    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4513
 4514    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4515    cx.set_state("two ˇfour ˇsix ˇ");
 4516    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4517    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4518
 4519    // Paste again but with only two cursors. Since the number of cursors doesn't
 4520    // match the number of slices in the clipboard, the entire clipboard text
 4521    // is pasted at each cursor.
 4522    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4523    cx.update_editor(|e, cx| {
 4524        e.handle_input("( ", cx);
 4525        e.paste(&Paste, cx);
 4526        e.handle_input(") ", cx);
 4527    });
 4528    cx.assert_editor_state(
 4529        &([
 4530            "( one✅ ",
 4531            "three ",
 4532            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4533            "three ",
 4534            "five ) ˇ",
 4535        ]
 4536        .join("\n")),
 4537    );
 4538
 4539    // Cut with three selections, one of which is full-line.
 4540    cx.set_state(indoc! {"
 4541        1«2ˇ»3
 4542        4ˇ567
 4543        «8ˇ»9"});
 4544    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4545    cx.assert_editor_state(indoc! {"
 4546        1ˇ3
 4547        ˇ9"});
 4548
 4549    // Paste with three selections, noticing how the copied selection that was full-line
 4550    // gets inserted before the second cursor.
 4551    cx.set_state(indoc! {"
 4552        1ˇ3
 4553 4554        «oˇ»ne"});
 4555    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4556    cx.assert_editor_state(indoc! {"
 4557        12ˇ3
 4558        4567
 4559 4560        8ˇne"});
 4561
 4562    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4563    cx.set_state(indoc! {"
 4564        The quick brown
 4565        fox juˇmps over
 4566        the lazy dog"});
 4567    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4568    assert_eq!(
 4569        cx.read_from_clipboard()
 4570            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4571        Some("fox jumps over\n".to_string())
 4572    );
 4573
 4574    // Paste with three selections, noticing how the copied full-line selection is inserted
 4575    // before the empty selections but replaces the selection that is non-empty.
 4576    cx.set_state(indoc! {"
 4577        Tˇhe quick brown
 4578        «foˇ»x jumps over
 4579        tˇhe lazy dog"});
 4580    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4581    cx.assert_editor_state(indoc! {"
 4582        fox jumps over
 4583        Tˇhe quick brown
 4584        fox jumps over
 4585        ˇx jumps over
 4586        fox jumps over
 4587        tˇhe lazy dog"});
 4588}
 4589
 4590#[gpui::test]
 4591async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4592    init_test(cx, |_| {});
 4593
 4594    let mut cx = EditorTestContext::new(cx).await;
 4595    let language = Arc::new(Language::new(
 4596        LanguageConfig::default(),
 4597        Some(tree_sitter_rust::LANGUAGE.into()),
 4598    ));
 4599    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4600
 4601    // Cut an indented block, without the leading whitespace.
 4602    cx.set_state(indoc! {"
 4603        const a: B = (
 4604            c(),
 4605            «d(
 4606                e,
 4607                f
 4608            )ˇ»
 4609        );
 4610    "});
 4611    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4612    cx.assert_editor_state(indoc! {"
 4613        const a: B = (
 4614            c(),
 4615            ˇ
 4616        );
 4617    "});
 4618
 4619    // Paste it at the same position.
 4620    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4621    cx.assert_editor_state(indoc! {"
 4622        const a: B = (
 4623            c(),
 4624            d(
 4625                e,
 4626                f
 4627 4628        );
 4629    "});
 4630
 4631    // Paste it at a line with a lower indent level.
 4632    cx.set_state(indoc! {"
 4633        ˇ
 4634        const a: B = (
 4635            c(),
 4636        );
 4637    "});
 4638    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4639    cx.assert_editor_state(indoc! {"
 4640        d(
 4641            e,
 4642            f
 4643 4644        const a: B = (
 4645            c(),
 4646        );
 4647    "});
 4648
 4649    // Cut an indented block, with the leading whitespace.
 4650    cx.set_state(indoc! {"
 4651        const a: B = (
 4652            c(),
 4653        «    d(
 4654                e,
 4655                f
 4656            )
 4657        ˇ»);
 4658    "});
 4659    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4660    cx.assert_editor_state(indoc! {"
 4661        const a: B = (
 4662            c(),
 4663        ˇ);
 4664    "});
 4665
 4666    // Paste it at the same position.
 4667    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4668    cx.assert_editor_state(indoc! {"
 4669        const a: B = (
 4670            c(),
 4671            d(
 4672                e,
 4673                f
 4674            )
 4675        ˇ);
 4676    "});
 4677
 4678    // Paste it at a line with a higher indent level.
 4679    cx.set_state(indoc! {"
 4680        const a: B = (
 4681            c(),
 4682            d(
 4683                e,
 4684 4685            )
 4686        );
 4687    "});
 4688    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4689    cx.assert_editor_state(indoc! {"
 4690        const a: B = (
 4691            c(),
 4692            d(
 4693                e,
 4694                f    d(
 4695                    e,
 4696                    f
 4697                )
 4698        ˇ
 4699            )
 4700        );
 4701    "});
 4702}
 4703
 4704#[gpui::test]
 4705fn test_select_all(cx: &mut TestAppContext) {
 4706    init_test(cx, |_| {});
 4707
 4708    let view = cx.add_window(|cx| {
 4709        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4710        build_editor(buffer, cx)
 4711    });
 4712    _ = view.update(cx, |view, cx| {
 4713        view.select_all(&SelectAll, cx);
 4714        assert_eq!(
 4715            view.selections.display_ranges(cx),
 4716            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4717        );
 4718    });
 4719}
 4720
 4721#[gpui::test]
 4722fn test_select_line(cx: &mut TestAppContext) {
 4723    init_test(cx, |_| {});
 4724
 4725    let view = cx.add_window(|cx| {
 4726        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4727        build_editor(buffer, cx)
 4728    });
 4729    _ = view.update(cx, |view, cx| {
 4730        view.change_selections(None, cx, |s| {
 4731            s.select_display_ranges([
 4732                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4733                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4734                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4735                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4736            ])
 4737        });
 4738        view.select_line(&SelectLine, cx);
 4739        assert_eq!(
 4740            view.selections.display_ranges(cx),
 4741            vec![
 4742                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4743                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4744            ]
 4745        );
 4746    });
 4747
 4748    _ = view.update(cx, |view, cx| {
 4749        view.select_line(&SelectLine, cx);
 4750        assert_eq!(
 4751            view.selections.display_ranges(cx),
 4752            vec![
 4753                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4754                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4755            ]
 4756        );
 4757    });
 4758
 4759    _ = view.update(cx, |view, cx| {
 4760        view.select_line(&SelectLine, cx);
 4761        assert_eq!(
 4762            view.selections.display_ranges(cx),
 4763            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4764        );
 4765    });
 4766}
 4767
 4768#[gpui::test]
 4769fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4770    init_test(cx, |_| {});
 4771
 4772    let view = cx.add_window(|cx| {
 4773        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4774        build_editor(buffer, cx)
 4775    });
 4776    _ = view.update(cx, |view, cx| {
 4777        view.fold_ranges(
 4778            vec![
 4779                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4780                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4781                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4782            ],
 4783            true,
 4784            cx,
 4785        );
 4786        view.change_selections(None, cx, |s| {
 4787            s.select_display_ranges([
 4788                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4789                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4790                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4791                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4792            ])
 4793        });
 4794        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4795    });
 4796
 4797    _ = view.update(cx, |view, cx| {
 4798        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4799        assert_eq!(
 4800            view.display_text(cx),
 4801            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4802        );
 4803        assert_eq!(
 4804            view.selections.display_ranges(cx),
 4805            [
 4806                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4807                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4808                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4809                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4810            ]
 4811        );
 4812    });
 4813
 4814    _ = view.update(cx, |view, cx| {
 4815        view.change_selections(None, cx, |s| {
 4816            s.select_display_ranges([
 4817                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4818            ])
 4819        });
 4820        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4821        assert_eq!(
 4822            view.display_text(cx),
 4823            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4824        );
 4825        assert_eq!(
 4826            view.selections.display_ranges(cx),
 4827            [
 4828                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4829                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4830                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4831                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4832                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4833                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4834                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4835                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4836            ]
 4837        );
 4838    });
 4839}
 4840
 4841#[gpui::test]
 4842async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4843    init_test(cx, |_| {});
 4844
 4845    let mut cx = EditorTestContext::new(cx).await;
 4846
 4847    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4848    cx.set_state(indoc!(
 4849        r#"abc
 4850           defˇghi
 4851
 4852           jk
 4853           nlmo
 4854           "#
 4855    ));
 4856
 4857    cx.update_editor(|editor, cx| {
 4858        editor.add_selection_above(&Default::default(), cx);
 4859    });
 4860
 4861    cx.assert_editor_state(indoc!(
 4862        r#"abcˇ
 4863           defˇghi
 4864
 4865           jk
 4866           nlmo
 4867           "#
 4868    ));
 4869
 4870    cx.update_editor(|editor, cx| {
 4871        editor.add_selection_above(&Default::default(), cx);
 4872    });
 4873
 4874    cx.assert_editor_state(indoc!(
 4875        r#"abcˇ
 4876            defˇghi
 4877
 4878            jk
 4879            nlmo
 4880            "#
 4881    ));
 4882
 4883    cx.update_editor(|view, cx| {
 4884        view.add_selection_below(&Default::default(), cx);
 4885    });
 4886
 4887    cx.assert_editor_state(indoc!(
 4888        r#"abc
 4889           defˇghi
 4890
 4891           jk
 4892           nlmo
 4893           "#
 4894    ));
 4895
 4896    cx.update_editor(|view, cx| {
 4897        view.undo_selection(&Default::default(), cx);
 4898    });
 4899
 4900    cx.assert_editor_state(indoc!(
 4901        r#"abcˇ
 4902           defˇghi
 4903
 4904           jk
 4905           nlmo
 4906           "#
 4907    ));
 4908
 4909    cx.update_editor(|view, cx| {
 4910        view.redo_selection(&Default::default(), cx);
 4911    });
 4912
 4913    cx.assert_editor_state(indoc!(
 4914        r#"abc
 4915           defˇghi
 4916
 4917           jk
 4918           nlmo
 4919           "#
 4920    ));
 4921
 4922    cx.update_editor(|view, cx| {
 4923        view.add_selection_below(&Default::default(), cx);
 4924    });
 4925
 4926    cx.assert_editor_state(indoc!(
 4927        r#"abc
 4928           defˇghi
 4929
 4930           jk
 4931           nlmˇo
 4932           "#
 4933    ));
 4934
 4935    cx.update_editor(|view, cx| {
 4936        view.add_selection_below(&Default::default(), cx);
 4937    });
 4938
 4939    cx.assert_editor_state(indoc!(
 4940        r#"abc
 4941           defˇghi
 4942
 4943           jk
 4944           nlmˇo
 4945           "#
 4946    ));
 4947
 4948    // change selections
 4949    cx.set_state(indoc!(
 4950        r#"abc
 4951           def«ˇg»hi
 4952
 4953           jk
 4954           nlmo
 4955           "#
 4956    ));
 4957
 4958    cx.update_editor(|view, cx| {
 4959        view.add_selection_below(&Default::default(), cx);
 4960    });
 4961
 4962    cx.assert_editor_state(indoc!(
 4963        r#"abc
 4964           def«ˇg»hi
 4965
 4966           jk
 4967           nlm«ˇo»
 4968           "#
 4969    ));
 4970
 4971    cx.update_editor(|view, cx| {
 4972        view.add_selection_below(&Default::default(), cx);
 4973    });
 4974
 4975    cx.assert_editor_state(indoc!(
 4976        r#"abc
 4977           def«ˇg»hi
 4978
 4979           jk
 4980           nlm«ˇo»
 4981           "#
 4982    ));
 4983
 4984    cx.update_editor(|view, cx| {
 4985        view.add_selection_above(&Default::default(), cx);
 4986    });
 4987
 4988    cx.assert_editor_state(indoc!(
 4989        r#"abc
 4990           def«ˇg»hi
 4991
 4992           jk
 4993           nlmo
 4994           "#
 4995    ));
 4996
 4997    cx.update_editor(|view, cx| {
 4998        view.add_selection_above(&Default::default(), cx);
 4999    });
 5000
 5001    cx.assert_editor_state(indoc!(
 5002        r#"abc
 5003           def«ˇg»hi
 5004
 5005           jk
 5006           nlmo
 5007           "#
 5008    ));
 5009
 5010    // Change selections again
 5011    cx.set_state(indoc!(
 5012        r#"a«bc
 5013           defgˇ»hi
 5014
 5015           jk
 5016           nlmo
 5017           "#
 5018    ));
 5019
 5020    cx.update_editor(|view, cx| {
 5021        view.add_selection_below(&Default::default(), cx);
 5022    });
 5023
 5024    cx.assert_editor_state(indoc!(
 5025        r#"a«bcˇ»
 5026           d«efgˇ»hi
 5027
 5028           j«kˇ»
 5029           nlmo
 5030           "#
 5031    ));
 5032
 5033    cx.update_editor(|view, cx| {
 5034        view.add_selection_below(&Default::default(), cx);
 5035    });
 5036    cx.assert_editor_state(indoc!(
 5037        r#"a«bcˇ»
 5038           d«efgˇ»hi
 5039
 5040           j«kˇ»
 5041           n«lmoˇ»
 5042           "#
 5043    ));
 5044    cx.update_editor(|view, cx| {
 5045        view.add_selection_above(&Default::default(), cx);
 5046    });
 5047
 5048    cx.assert_editor_state(indoc!(
 5049        r#"a«bcˇ»
 5050           d«efgˇ»hi
 5051
 5052           j«kˇ»
 5053           nlmo
 5054           "#
 5055    ));
 5056
 5057    // Change selections again
 5058    cx.set_state(indoc!(
 5059        r#"abc
 5060           d«ˇefghi
 5061
 5062           jk
 5063           nlm»o
 5064           "#
 5065    ));
 5066
 5067    cx.update_editor(|view, cx| {
 5068        view.add_selection_above(&Default::default(), cx);
 5069    });
 5070
 5071    cx.assert_editor_state(indoc!(
 5072        r#"a«ˇbc»
 5073           d«ˇef»ghi
 5074
 5075           j«ˇk»
 5076           n«ˇlm»o
 5077           "#
 5078    ));
 5079
 5080    cx.update_editor(|view, cx| {
 5081        view.add_selection_below(&Default::default(), cx);
 5082    });
 5083
 5084    cx.assert_editor_state(indoc!(
 5085        r#"abc
 5086           d«ˇef»ghi
 5087
 5088           j«ˇk»
 5089           n«ˇlm»o
 5090           "#
 5091    ));
 5092}
 5093
 5094#[gpui::test]
 5095async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5096    init_test(cx, |_| {});
 5097
 5098    let mut cx = EditorTestContext::new(cx).await;
 5099    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5100
 5101    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5102        .unwrap();
 5103    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5104
 5105    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5106        .unwrap();
 5107    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5108
 5109    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5110    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5111
 5112    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5113    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5114
 5115    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5116        .unwrap();
 5117    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5118
 5119    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5120        .unwrap();
 5121    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5122}
 5123
 5124#[gpui::test]
 5125async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5126    init_test(cx, |_| {});
 5127
 5128    let mut cx = EditorTestContext::new(cx).await;
 5129    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5130
 5131    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5132        .unwrap();
 5133    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5134}
 5135
 5136#[gpui::test]
 5137async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5138    init_test(cx, |_| {});
 5139
 5140    let mut cx = EditorTestContext::new(cx).await;
 5141    cx.set_state(
 5142        r#"let foo = 2;
 5143lˇet foo = 2;
 5144let fooˇ = 2;
 5145let foo = 2;
 5146let foo = ˇ2;"#,
 5147    );
 5148
 5149    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5150        .unwrap();
 5151    cx.assert_editor_state(
 5152        r#"let foo = 2;
 5153«letˇ» foo = 2;
 5154let «fooˇ» = 2;
 5155let foo = 2;
 5156let foo = «2ˇ»;"#,
 5157    );
 5158
 5159    // noop for multiple selections with different contents
 5160    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5161        .unwrap();
 5162    cx.assert_editor_state(
 5163        r#"let foo = 2;
 5164«letˇ» foo = 2;
 5165let «fooˇ» = 2;
 5166let foo = 2;
 5167let foo = «2ˇ»;"#,
 5168    );
 5169}
 5170
 5171#[gpui::test]
 5172async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5173    init_test(cx, |_| {});
 5174
 5175    let mut cx =
 5176        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5177
 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    cx.dispatch_action(SelectPrevious::default());
 5194    cx.assert_editor_state(indoc! {"
 5195                «bbbˇ»
 5196                ccc
 5197
 5198                «bbbˇ»
 5199                ccc
 5200                "});
 5201}
 5202
 5203#[gpui::test]
 5204async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5205    init_test(cx, |_| {});
 5206
 5207    let mut cx = EditorTestContext::new(cx).await;
 5208    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5209
 5210    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5211        .unwrap();
 5212    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5213
 5214    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5215        .unwrap();
 5216    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5217
 5218    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5219    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5220
 5221    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5222    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5223
 5224    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5225        .unwrap();
 5226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5227
 5228    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5229        .unwrap();
 5230    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5231
 5232    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5233        .unwrap();
 5234    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5235}
 5236
 5237#[gpui::test]
 5238async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5239    init_test(cx, |_| {});
 5240
 5241    let mut cx = EditorTestContext::new(cx).await;
 5242    cx.set_state(
 5243        r#"let foo = 2;
 5244lˇet foo = 2;
 5245let fooˇ = 2;
 5246let foo = 2;
 5247let foo = ˇ2;"#,
 5248    );
 5249
 5250    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5251        .unwrap();
 5252    cx.assert_editor_state(
 5253        r#"let foo = 2;
 5254«letˇ» foo = 2;
 5255let «fooˇ» = 2;
 5256let foo = 2;
 5257let foo = «2ˇ»;"#,
 5258    );
 5259
 5260    // noop for multiple selections with different contents
 5261    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5262        .unwrap();
 5263    cx.assert_editor_state(
 5264        r#"let foo = 2;
 5265«letˇ» foo = 2;
 5266let «fooˇ» = 2;
 5267let foo = 2;
 5268let foo = «2ˇ»;"#,
 5269    );
 5270}
 5271
 5272#[gpui::test]
 5273async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5274    init_test(cx, |_| {});
 5275
 5276    let mut cx = EditorTestContext::new(cx).await;
 5277    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5278
 5279    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5280        .unwrap();
 5281    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5282
 5283    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5284        .unwrap();
 5285    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5286
 5287    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5288    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5289
 5290    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5291    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5292
 5293    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5294        .unwrap();
 5295    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5296
 5297    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5298        .unwrap();
 5299    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5300}
 5301
 5302#[gpui::test]
 5303async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5304    init_test(cx, |_| {});
 5305
 5306    let language = Arc::new(Language::new(
 5307        LanguageConfig::default(),
 5308        Some(tree_sitter_rust::LANGUAGE.into()),
 5309    ));
 5310
 5311    let text = r#"
 5312        use mod1::mod2::{mod3, mod4};
 5313
 5314        fn fn_1(param1: bool, param2: &str) {
 5315            let var1 = "text";
 5316        }
 5317    "#
 5318    .unindent();
 5319
 5320    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5321    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5322    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5323
 5324    editor
 5325        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5326        .await;
 5327
 5328    editor.update(cx, |view, cx| {
 5329        view.change_selections(None, cx, |s| {
 5330            s.select_display_ranges([
 5331                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5332                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5333                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5334            ]);
 5335        });
 5336        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5337    });
 5338    editor.update(cx, |editor, cx| {
 5339        assert_text_with_selections(
 5340            editor,
 5341            indoc! {r#"
 5342                use mod1::mod2::{mod3, «mod4ˇ»};
 5343
 5344                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5345                    let var1 = "«textˇ»";
 5346                }
 5347            "#},
 5348            cx,
 5349        );
 5350    });
 5351
 5352    editor.update(cx, |view, cx| {
 5353        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5354    });
 5355    editor.update(cx, |editor, cx| {
 5356        assert_text_with_selections(
 5357            editor,
 5358            indoc! {r#"
 5359                use mod1::mod2::«{mod3, mod4}ˇ»;
 5360
 5361                «ˇfn fn_1(param1: bool, param2: &str) {
 5362                    let var1 = "text";
 5363 5364            "#},
 5365            cx,
 5366        );
 5367    });
 5368
 5369    editor.update(cx, |view, cx| {
 5370        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5371    });
 5372    assert_eq!(
 5373        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5374        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5375    );
 5376
 5377    // Trying to expand the selected syntax node one more time has no effect.
 5378    editor.update(cx, |view, cx| {
 5379        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5380    });
 5381    assert_eq!(
 5382        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5383        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5384    );
 5385
 5386    editor.update(cx, |view, cx| {
 5387        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5388    });
 5389    editor.update(cx, |editor, cx| {
 5390        assert_text_with_selections(
 5391            editor,
 5392            indoc! {r#"
 5393                use mod1::mod2::«{mod3, mod4}ˇ»;
 5394
 5395                «ˇfn fn_1(param1: bool, param2: &str) {
 5396                    let var1 = "text";
 5397 5398            "#},
 5399            cx,
 5400        );
 5401    });
 5402
 5403    editor.update(cx, |view, cx| {
 5404        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5405    });
 5406    editor.update(cx, |editor, cx| {
 5407        assert_text_with_selections(
 5408            editor,
 5409            indoc! {r#"
 5410                use mod1::mod2::{mod3, «mod4ˇ»};
 5411
 5412                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5413                    let var1 = "«textˇ»";
 5414                }
 5415            "#},
 5416            cx,
 5417        );
 5418    });
 5419
 5420    editor.update(cx, |view, cx| {
 5421        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5422    });
 5423    editor.update(cx, |editor, cx| {
 5424        assert_text_with_selections(
 5425            editor,
 5426            indoc! {r#"
 5427                use mod1::mod2::{mod3, mo«ˇ»d4};
 5428
 5429                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5430                    let var1 = "te«ˇ»xt";
 5431                }
 5432            "#},
 5433            cx,
 5434        );
 5435    });
 5436
 5437    // Trying to shrink the selected syntax node one more time has no effect.
 5438    editor.update(cx, |view, cx| {
 5439        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5440    });
 5441    editor.update(cx, |editor, cx| {
 5442        assert_text_with_selections(
 5443            editor,
 5444            indoc! {r#"
 5445                use mod1::mod2::{mod3, mo«ˇ»d4};
 5446
 5447                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5448                    let var1 = "te«ˇ»xt";
 5449                }
 5450            "#},
 5451            cx,
 5452        );
 5453    });
 5454
 5455    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5456    // a fold.
 5457    editor.update(cx, |view, cx| {
 5458        view.fold_ranges(
 5459            vec![
 5460                (
 5461                    Point::new(0, 21)..Point::new(0, 24),
 5462                    FoldPlaceholder::test(),
 5463                ),
 5464                (
 5465                    Point::new(3, 20)..Point::new(3, 22),
 5466                    FoldPlaceholder::test(),
 5467                ),
 5468            ],
 5469            true,
 5470            cx,
 5471        );
 5472        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5473    });
 5474    editor.update(cx, |editor, cx| {
 5475        assert_text_with_selections(
 5476            editor,
 5477            indoc! {r#"
 5478                use mod1::mod2::«{mod3, mod4}ˇ»;
 5479
 5480                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5481                    «let var1 = "text";ˇ»
 5482                }
 5483            "#},
 5484            cx,
 5485        );
 5486    });
 5487}
 5488
 5489#[gpui::test]
 5490async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5491    init_test(cx, |_| {});
 5492
 5493    let language = Arc::new(
 5494        Language::new(
 5495            LanguageConfig {
 5496                brackets: BracketPairConfig {
 5497                    pairs: vec![
 5498                        BracketPair {
 5499                            start: "{".to_string(),
 5500                            end: "}".to_string(),
 5501                            close: false,
 5502                            surround: false,
 5503                            newline: true,
 5504                        },
 5505                        BracketPair {
 5506                            start: "(".to_string(),
 5507                            end: ")".to_string(),
 5508                            close: false,
 5509                            surround: false,
 5510                            newline: true,
 5511                        },
 5512                    ],
 5513                    ..Default::default()
 5514                },
 5515                ..Default::default()
 5516            },
 5517            Some(tree_sitter_rust::LANGUAGE.into()),
 5518        )
 5519        .with_indents_query(
 5520            r#"
 5521                (_ "(" ")" @end) @indent
 5522                (_ "{" "}" @end) @indent
 5523            "#,
 5524        )
 5525        .unwrap(),
 5526    );
 5527
 5528    let text = "fn a() {}";
 5529
 5530    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5531    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5532    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5533    editor
 5534        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5535        .await;
 5536
 5537    editor.update(cx, |editor, cx| {
 5538        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5539        editor.newline(&Newline, cx);
 5540        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5541        assert_eq!(
 5542            editor.selections.ranges(cx),
 5543            &[
 5544                Point::new(1, 4)..Point::new(1, 4),
 5545                Point::new(3, 4)..Point::new(3, 4),
 5546                Point::new(5, 0)..Point::new(5, 0)
 5547            ]
 5548        );
 5549    });
 5550}
 5551
 5552#[gpui::test]
 5553async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5554    init_test(cx, |_| {});
 5555
 5556    let mut cx = EditorTestContext::new(cx).await;
 5557
 5558    let language = Arc::new(Language::new(
 5559        LanguageConfig {
 5560            brackets: BracketPairConfig {
 5561                pairs: vec![
 5562                    BracketPair {
 5563                        start: "{".to_string(),
 5564                        end: "}".to_string(),
 5565                        close: true,
 5566                        surround: true,
 5567                        newline: true,
 5568                    },
 5569                    BracketPair {
 5570                        start: "(".to_string(),
 5571                        end: ")".to_string(),
 5572                        close: true,
 5573                        surround: true,
 5574                        newline: true,
 5575                    },
 5576                    BracketPair {
 5577                        start: "/*".to_string(),
 5578                        end: " */".to_string(),
 5579                        close: true,
 5580                        surround: true,
 5581                        newline: true,
 5582                    },
 5583                    BracketPair {
 5584                        start: "[".to_string(),
 5585                        end: "]".to_string(),
 5586                        close: false,
 5587                        surround: false,
 5588                        newline: true,
 5589                    },
 5590                    BracketPair {
 5591                        start: "\"".to_string(),
 5592                        end: "\"".to_string(),
 5593                        close: true,
 5594                        surround: true,
 5595                        newline: false,
 5596                    },
 5597                    BracketPair {
 5598                        start: "<".to_string(),
 5599                        end: ">".to_string(),
 5600                        close: false,
 5601                        surround: true,
 5602                        newline: true,
 5603                    },
 5604                ],
 5605                ..Default::default()
 5606            },
 5607            autoclose_before: "})]".to_string(),
 5608            ..Default::default()
 5609        },
 5610        Some(tree_sitter_rust::LANGUAGE.into()),
 5611    ));
 5612
 5613    cx.language_registry().add(language.clone());
 5614    cx.update_buffer(|buffer, cx| {
 5615        buffer.set_language(Some(language), cx);
 5616    });
 5617
 5618    cx.set_state(
 5619        &r#"
 5620            🏀ˇ
 5621            εˇ
 5622            ❤️ˇ
 5623        "#
 5624        .unindent(),
 5625    );
 5626
 5627    // autoclose multiple nested brackets at multiple cursors
 5628    cx.update_editor(|view, cx| {
 5629        view.handle_input("{", cx);
 5630        view.handle_input("{", cx);
 5631        view.handle_input("{", cx);
 5632    });
 5633    cx.assert_editor_state(
 5634        &"
 5635            🏀{{{ˇ}}}
 5636            ε{{{ˇ}}}
 5637            ❤️{{{ˇ}}}
 5638        "
 5639        .unindent(),
 5640    );
 5641
 5642    // insert a different closing bracket
 5643    cx.update_editor(|view, cx| {
 5644        view.handle_input(")", cx);
 5645    });
 5646    cx.assert_editor_state(
 5647        &"
 5648            🏀{{{)ˇ}}}
 5649            ε{{{)ˇ}}}
 5650            ❤️{{{)ˇ}}}
 5651        "
 5652        .unindent(),
 5653    );
 5654
 5655    // skip over the auto-closed brackets when typing a closing bracket
 5656    cx.update_editor(|view, cx| {
 5657        view.move_right(&MoveRight, cx);
 5658        view.handle_input("}", cx);
 5659        view.handle_input("}", cx);
 5660        view.handle_input("}", cx);
 5661    });
 5662    cx.assert_editor_state(
 5663        &"
 5664            🏀{{{)}}}}ˇ
 5665            ε{{{)}}}}ˇ
 5666            ❤️{{{)}}}}ˇ
 5667        "
 5668        .unindent(),
 5669    );
 5670
 5671    // autoclose multi-character pairs
 5672    cx.set_state(
 5673        &"
 5674            ˇ
 5675            ˇ
 5676        "
 5677        .unindent(),
 5678    );
 5679    cx.update_editor(|view, cx| {
 5680        view.handle_input("/", cx);
 5681        view.handle_input("*", cx);
 5682    });
 5683    cx.assert_editor_state(
 5684        &"
 5685            /*ˇ */
 5686            /*ˇ */
 5687        "
 5688        .unindent(),
 5689    );
 5690
 5691    // one cursor autocloses a multi-character pair, one cursor
 5692    // does not autoclose.
 5693    cx.set_state(
 5694        &"
 5695 5696            ˇ
 5697        "
 5698        .unindent(),
 5699    );
 5700    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5701    cx.assert_editor_state(
 5702        &"
 5703            /*ˇ */
 5704 5705        "
 5706        .unindent(),
 5707    );
 5708
 5709    // Don't autoclose if the next character isn't whitespace and isn't
 5710    // listed in the language's "autoclose_before" section.
 5711    cx.set_state("ˇa b");
 5712    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5713    cx.assert_editor_state("{ˇa b");
 5714
 5715    // Don't autoclose if `close` is false for the bracket pair
 5716    cx.set_state("ˇ");
 5717    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5718    cx.assert_editor_state("");
 5719
 5720    // Surround with brackets if text is selected
 5721    cx.set_state("«aˇ» b");
 5722    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5723    cx.assert_editor_state("{«aˇ»} b");
 5724
 5725    // Autclose pair where the start and end characters are the same
 5726    cx.set_state("");
 5727    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5728    cx.assert_editor_state("a\"ˇ\"");
 5729    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5730    cx.assert_editor_state("a\"\"ˇ");
 5731
 5732    // Don't autoclose pair if autoclose is disabled
 5733    cx.set_state("ˇ");
 5734    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5735    cx.assert_editor_state("");
 5736
 5737    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5738    cx.set_state("«aˇ» b");
 5739    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5740    cx.assert_editor_state("<«aˇ»> b");
 5741}
 5742
 5743#[gpui::test]
 5744async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5745    init_test(cx, |settings| {
 5746        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5747    });
 5748
 5749    let mut cx = EditorTestContext::new(cx).await;
 5750
 5751    let language = Arc::new(Language::new(
 5752        LanguageConfig {
 5753            brackets: BracketPairConfig {
 5754                pairs: vec![
 5755                    BracketPair {
 5756                        start: "{".to_string(),
 5757                        end: "}".to_string(),
 5758                        close: true,
 5759                        surround: true,
 5760                        newline: true,
 5761                    },
 5762                    BracketPair {
 5763                        start: "(".to_string(),
 5764                        end: ")".to_string(),
 5765                        close: true,
 5766                        surround: true,
 5767                        newline: true,
 5768                    },
 5769                    BracketPair {
 5770                        start: "[".to_string(),
 5771                        end: "]".to_string(),
 5772                        close: false,
 5773                        surround: false,
 5774                        newline: true,
 5775                    },
 5776                ],
 5777                ..Default::default()
 5778            },
 5779            autoclose_before: "})]".to_string(),
 5780            ..Default::default()
 5781        },
 5782        Some(tree_sitter_rust::LANGUAGE.into()),
 5783    ));
 5784
 5785    cx.language_registry().add(language.clone());
 5786    cx.update_buffer(|buffer, cx| {
 5787        buffer.set_language(Some(language), cx);
 5788    });
 5789
 5790    cx.set_state(
 5791        &"
 5792            ˇ
 5793            ˇ
 5794            ˇ
 5795        "
 5796        .unindent(),
 5797    );
 5798
 5799    // ensure only matching closing brackets are skipped over
 5800    cx.update_editor(|view, cx| {
 5801        view.handle_input("}", cx);
 5802        view.move_left(&MoveLeft, cx);
 5803        view.handle_input(")", cx);
 5804        view.move_left(&MoveLeft, cx);
 5805    });
 5806    cx.assert_editor_state(
 5807        &"
 5808            ˇ)}
 5809            ˇ)}
 5810            ˇ)}
 5811        "
 5812        .unindent(),
 5813    );
 5814
 5815    // skip-over closing brackets at multiple cursors
 5816    cx.update_editor(|view, cx| {
 5817        view.handle_input(")", cx);
 5818        view.handle_input("}", cx);
 5819    });
 5820    cx.assert_editor_state(
 5821        &"
 5822            )}ˇ
 5823            )}ˇ
 5824            )}ˇ
 5825        "
 5826        .unindent(),
 5827    );
 5828
 5829    // ignore non-close brackets
 5830    cx.update_editor(|view, cx| {
 5831        view.handle_input("]", cx);
 5832        view.move_left(&MoveLeft, cx);
 5833        view.handle_input("]", cx);
 5834    });
 5835    cx.assert_editor_state(
 5836        &"
 5837            )}]ˇ]
 5838            )}]ˇ]
 5839            )}]ˇ]
 5840        "
 5841        .unindent(),
 5842    );
 5843}
 5844
 5845#[gpui::test]
 5846async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5847    init_test(cx, |_| {});
 5848
 5849    let mut cx = EditorTestContext::new(cx).await;
 5850
 5851    let html_language = Arc::new(
 5852        Language::new(
 5853            LanguageConfig {
 5854                name: "HTML".into(),
 5855                brackets: BracketPairConfig {
 5856                    pairs: vec![
 5857                        BracketPair {
 5858                            start: "<".into(),
 5859                            end: ">".into(),
 5860                            close: true,
 5861                            ..Default::default()
 5862                        },
 5863                        BracketPair {
 5864                            start: "{".into(),
 5865                            end: "}".into(),
 5866                            close: true,
 5867                            ..Default::default()
 5868                        },
 5869                        BracketPair {
 5870                            start: "(".into(),
 5871                            end: ")".into(),
 5872                            close: true,
 5873                            ..Default::default()
 5874                        },
 5875                    ],
 5876                    ..Default::default()
 5877                },
 5878                autoclose_before: "})]>".into(),
 5879                ..Default::default()
 5880            },
 5881            Some(tree_sitter_html::language()),
 5882        )
 5883        .with_injection_query(
 5884            r#"
 5885            (script_element
 5886                (raw_text) @content
 5887                (#set! "language" "javascript"))
 5888            "#,
 5889        )
 5890        .unwrap(),
 5891    );
 5892
 5893    let javascript_language = Arc::new(Language::new(
 5894        LanguageConfig {
 5895            name: "JavaScript".into(),
 5896            brackets: BracketPairConfig {
 5897                pairs: vec![
 5898                    BracketPair {
 5899                        start: "/*".into(),
 5900                        end: " */".into(),
 5901                        close: true,
 5902                        ..Default::default()
 5903                    },
 5904                    BracketPair {
 5905                        start: "{".into(),
 5906                        end: "}".into(),
 5907                        close: true,
 5908                        ..Default::default()
 5909                    },
 5910                    BracketPair {
 5911                        start: "(".into(),
 5912                        end: ")".into(),
 5913                        close: true,
 5914                        ..Default::default()
 5915                    },
 5916                ],
 5917                ..Default::default()
 5918            },
 5919            autoclose_before: "})]>".into(),
 5920            ..Default::default()
 5921        },
 5922        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5923    ));
 5924
 5925    cx.language_registry().add(html_language.clone());
 5926    cx.language_registry().add(javascript_language.clone());
 5927
 5928    cx.update_buffer(|buffer, cx| {
 5929        buffer.set_language(Some(html_language), cx);
 5930    });
 5931
 5932    cx.set_state(
 5933        &r#"
 5934            <body>ˇ
 5935                <script>
 5936                    var x = 1;ˇ
 5937                </script>
 5938            </body>ˇ
 5939        "#
 5940        .unindent(),
 5941    );
 5942
 5943    // Precondition: different languages are active at different locations.
 5944    cx.update_editor(|editor, cx| {
 5945        let snapshot = editor.snapshot(cx);
 5946        let cursors = editor.selections.ranges::<usize>(cx);
 5947        let languages = cursors
 5948            .iter()
 5949            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5950            .collect::<Vec<_>>();
 5951        assert_eq!(
 5952            languages,
 5953            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5954        );
 5955    });
 5956
 5957    // Angle brackets autoclose in HTML, but not JavaScript.
 5958    cx.update_editor(|editor, cx| {
 5959        editor.handle_input("<", cx);
 5960        editor.handle_input("a", cx);
 5961    });
 5962    cx.assert_editor_state(
 5963        &r#"
 5964            <body><aˇ>
 5965                <script>
 5966                    var x = 1;<aˇ
 5967                </script>
 5968            </body><aˇ>
 5969        "#
 5970        .unindent(),
 5971    );
 5972
 5973    // Curly braces and parens autoclose in both HTML and JavaScript.
 5974    cx.update_editor(|editor, cx| {
 5975        editor.handle_input(" b=", cx);
 5976        editor.handle_input("{", cx);
 5977        editor.handle_input("c", cx);
 5978        editor.handle_input("(", cx);
 5979    });
 5980    cx.assert_editor_state(
 5981        &r#"
 5982            <body><a b={c(ˇ)}>
 5983                <script>
 5984                    var x = 1;<a b={c(ˇ)}
 5985                </script>
 5986            </body><a b={c(ˇ)}>
 5987        "#
 5988        .unindent(),
 5989    );
 5990
 5991    // Brackets that were already autoclosed are skipped.
 5992    cx.update_editor(|editor, cx| {
 5993        editor.handle_input(")", cx);
 5994        editor.handle_input("d", cx);
 5995        editor.handle_input("}", cx);
 5996    });
 5997    cx.assert_editor_state(
 5998        &r#"
 5999            <body><a b={c()d}ˇ>
 6000                <script>
 6001                    var x = 1;<a b={c()d}ˇ
 6002                </script>
 6003            </body><a b={c()d}ˇ>
 6004        "#
 6005        .unindent(),
 6006    );
 6007    cx.update_editor(|editor, cx| {
 6008        editor.handle_input(">", cx);
 6009    });
 6010    cx.assert_editor_state(
 6011        &r#"
 6012            <body><a b={c()d}>ˇ
 6013                <script>
 6014                    var x = 1;<a b={c()d}>ˇ
 6015                </script>
 6016            </body><a b={c()d}>ˇ
 6017        "#
 6018        .unindent(),
 6019    );
 6020
 6021    // Reset
 6022    cx.set_state(
 6023        &r#"
 6024            <body>ˇ
 6025                <script>
 6026                    var x = 1;ˇ
 6027                </script>
 6028            </body>ˇ
 6029        "#
 6030        .unindent(),
 6031    );
 6032
 6033    cx.update_editor(|editor, cx| {
 6034        editor.handle_input("<", cx);
 6035    });
 6036    cx.assert_editor_state(
 6037        &r#"
 6038            <body><ˇ>
 6039                <script>
 6040                    var x = 1;<ˇ
 6041                </script>
 6042            </body><ˇ>
 6043        "#
 6044        .unindent(),
 6045    );
 6046
 6047    // When backspacing, the closing angle brackets are removed.
 6048    cx.update_editor(|editor, cx| {
 6049        editor.backspace(&Backspace, cx);
 6050    });
 6051    cx.assert_editor_state(
 6052        &r#"
 6053            <body>ˇ
 6054                <script>
 6055                    var x = 1;ˇ
 6056                </script>
 6057            </body>ˇ
 6058        "#
 6059        .unindent(),
 6060    );
 6061
 6062    // Block comments autoclose in JavaScript, but not HTML.
 6063    cx.update_editor(|editor, cx| {
 6064        editor.handle_input("/", cx);
 6065        editor.handle_input("*", cx);
 6066    });
 6067    cx.assert_editor_state(
 6068        &r#"
 6069            <body>/*ˇ
 6070                <script>
 6071                    var x = 1;/*ˇ */
 6072                </script>
 6073            </body>/*ˇ
 6074        "#
 6075        .unindent(),
 6076    );
 6077}
 6078
 6079#[gpui::test]
 6080async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6081    init_test(cx, |_| {});
 6082
 6083    let mut cx = EditorTestContext::new(cx).await;
 6084
 6085    let rust_language = Arc::new(
 6086        Language::new(
 6087            LanguageConfig {
 6088                name: "Rust".into(),
 6089                brackets: serde_json::from_value(json!([
 6090                    { "start": "{", "end": "}", "close": true, "newline": true },
 6091                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6092                ]))
 6093                .unwrap(),
 6094                autoclose_before: "})]>".into(),
 6095                ..Default::default()
 6096            },
 6097            Some(tree_sitter_rust::LANGUAGE.into()),
 6098        )
 6099        .with_override_query("(string_literal) @string")
 6100        .unwrap(),
 6101    );
 6102
 6103    cx.language_registry().add(rust_language.clone());
 6104    cx.update_buffer(|buffer, cx| {
 6105        buffer.set_language(Some(rust_language), cx);
 6106    });
 6107
 6108    cx.set_state(
 6109        &r#"
 6110            let x = ˇ
 6111        "#
 6112        .unindent(),
 6113    );
 6114
 6115    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6116    cx.update_editor(|editor, cx| {
 6117        editor.handle_input("\"", cx);
 6118    });
 6119    cx.assert_editor_state(
 6120        &r#"
 6121            let x = "ˇ"
 6122        "#
 6123        .unindent(),
 6124    );
 6125
 6126    // Inserting another quotation mark. The cursor moves across the existing
 6127    // automatically-inserted quotation mark.
 6128    cx.update_editor(|editor, cx| {
 6129        editor.handle_input("\"", cx);
 6130    });
 6131    cx.assert_editor_state(
 6132        &r#"
 6133            let x = ""ˇ
 6134        "#
 6135        .unindent(),
 6136    );
 6137
 6138    // Reset
 6139    cx.set_state(
 6140        &r#"
 6141            let x = ˇ
 6142        "#
 6143        .unindent(),
 6144    );
 6145
 6146    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6147    cx.update_editor(|editor, cx| {
 6148        editor.handle_input("\"", cx);
 6149        editor.handle_input(" ", cx);
 6150        editor.move_left(&Default::default(), cx);
 6151        editor.handle_input("\\", cx);
 6152        editor.handle_input("\"", cx);
 6153    });
 6154    cx.assert_editor_state(
 6155        &r#"
 6156            let x = "\"ˇ "
 6157        "#
 6158        .unindent(),
 6159    );
 6160
 6161    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6162    // mark. Nothing is inserted.
 6163    cx.update_editor(|editor, cx| {
 6164        editor.move_right(&Default::default(), cx);
 6165        editor.handle_input("\"", cx);
 6166    });
 6167    cx.assert_editor_state(
 6168        &r#"
 6169            let x = "\" "ˇ
 6170        "#
 6171        .unindent(),
 6172    );
 6173}
 6174
 6175#[gpui::test]
 6176async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6177    init_test(cx, |_| {});
 6178
 6179    let language = Arc::new(Language::new(
 6180        LanguageConfig {
 6181            brackets: BracketPairConfig {
 6182                pairs: vec![
 6183                    BracketPair {
 6184                        start: "{".to_string(),
 6185                        end: "}".to_string(),
 6186                        close: true,
 6187                        surround: true,
 6188                        newline: true,
 6189                    },
 6190                    BracketPair {
 6191                        start: "/* ".to_string(),
 6192                        end: "*/".to_string(),
 6193                        close: true,
 6194                        surround: true,
 6195                        ..Default::default()
 6196                    },
 6197                ],
 6198                ..Default::default()
 6199            },
 6200            ..Default::default()
 6201        },
 6202        Some(tree_sitter_rust::LANGUAGE.into()),
 6203    ));
 6204
 6205    let text = r#"
 6206        a
 6207        b
 6208        c
 6209    "#
 6210    .unindent();
 6211
 6212    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6213    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6214    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6215    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6216        .await;
 6217
 6218    view.update(cx, |view, cx| {
 6219        view.change_selections(None, cx, |s| {
 6220            s.select_display_ranges([
 6221                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6222                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6223                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6224            ])
 6225        });
 6226
 6227        view.handle_input("{", cx);
 6228        view.handle_input("{", cx);
 6229        view.handle_input("{", cx);
 6230        assert_eq!(
 6231            view.text(cx),
 6232            "
 6233                {{{a}}}
 6234                {{{b}}}
 6235                {{{c}}}
 6236            "
 6237            .unindent()
 6238        );
 6239        assert_eq!(
 6240            view.selections.display_ranges(cx),
 6241            [
 6242                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6243                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6244                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6245            ]
 6246        );
 6247
 6248        view.undo(&Undo, cx);
 6249        view.undo(&Undo, cx);
 6250        view.undo(&Undo, cx);
 6251        assert_eq!(
 6252            view.text(cx),
 6253            "
 6254                a
 6255                b
 6256                c
 6257            "
 6258            .unindent()
 6259        );
 6260        assert_eq!(
 6261            view.selections.display_ranges(cx),
 6262            [
 6263                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6264                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6265                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6266            ]
 6267        );
 6268
 6269        // Ensure inserting the first character of a multi-byte bracket pair
 6270        // doesn't surround the selections with the bracket.
 6271        view.handle_input("/", cx);
 6272        assert_eq!(
 6273            view.text(cx),
 6274            "
 6275                /
 6276                /
 6277                /
 6278            "
 6279            .unindent()
 6280        );
 6281        assert_eq!(
 6282            view.selections.display_ranges(cx),
 6283            [
 6284                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6285                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6286                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6287            ]
 6288        );
 6289
 6290        view.undo(&Undo, cx);
 6291        assert_eq!(
 6292            view.text(cx),
 6293            "
 6294                a
 6295                b
 6296                c
 6297            "
 6298            .unindent()
 6299        );
 6300        assert_eq!(
 6301            view.selections.display_ranges(cx),
 6302            [
 6303                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6304                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6305                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6306            ]
 6307        );
 6308
 6309        // Ensure inserting the last character of a multi-byte bracket pair
 6310        // doesn't surround the selections with the bracket.
 6311        view.handle_input("*", cx);
 6312        assert_eq!(
 6313            view.text(cx),
 6314            "
 6315                *
 6316                *
 6317                *
 6318            "
 6319            .unindent()
 6320        );
 6321        assert_eq!(
 6322            view.selections.display_ranges(cx),
 6323            [
 6324                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6325                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6326                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6327            ]
 6328        );
 6329    });
 6330}
 6331
 6332#[gpui::test]
 6333async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6334    init_test(cx, |_| {});
 6335
 6336    let language = Arc::new(Language::new(
 6337        LanguageConfig {
 6338            brackets: BracketPairConfig {
 6339                pairs: vec![BracketPair {
 6340                    start: "{".to_string(),
 6341                    end: "}".to_string(),
 6342                    close: true,
 6343                    surround: true,
 6344                    newline: true,
 6345                }],
 6346                ..Default::default()
 6347            },
 6348            autoclose_before: "}".to_string(),
 6349            ..Default::default()
 6350        },
 6351        Some(tree_sitter_rust::LANGUAGE.into()),
 6352    ));
 6353
 6354    let text = r#"
 6355        a
 6356        b
 6357        c
 6358    "#
 6359    .unindent();
 6360
 6361    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6362    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6363    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6364    editor
 6365        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6366        .await;
 6367
 6368    editor.update(cx, |editor, cx| {
 6369        editor.change_selections(None, cx, |s| {
 6370            s.select_ranges([
 6371                Point::new(0, 1)..Point::new(0, 1),
 6372                Point::new(1, 1)..Point::new(1, 1),
 6373                Point::new(2, 1)..Point::new(2, 1),
 6374            ])
 6375        });
 6376
 6377        editor.handle_input("{", cx);
 6378        editor.handle_input("{", cx);
 6379        editor.handle_input("_", cx);
 6380        assert_eq!(
 6381            editor.text(cx),
 6382            "
 6383                a{{_}}
 6384                b{{_}}
 6385                c{{_}}
 6386            "
 6387            .unindent()
 6388        );
 6389        assert_eq!(
 6390            editor.selections.ranges::<Point>(cx),
 6391            [
 6392                Point::new(0, 4)..Point::new(0, 4),
 6393                Point::new(1, 4)..Point::new(1, 4),
 6394                Point::new(2, 4)..Point::new(2, 4)
 6395            ]
 6396        );
 6397
 6398        editor.backspace(&Default::default(), cx);
 6399        editor.backspace(&Default::default(), cx);
 6400        assert_eq!(
 6401            editor.text(cx),
 6402            "
 6403                a{}
 6404                b{}
 6405                c{}
 6406            "
 6407            .unindent()
 6408        );
 6409        assert_eq!(
 6410            editor.selections.ranges::<Point>(cx),
 6411            [
 6412                Point::new(0, 2)..Point::new(0, 2),
 6413                Point::new(1, 2)..Point::new(1, 2),
 6414                Point::new(2, 2)..Point::new(2, 2)
 6415            ]
 6416        );
 6417
 6418        editor.delete_to_previous_word_start(&Default::default(), cx);
 6419        assert_eq!(
 6420            editor.text(cx),
 6421            "
 6422                a
 6423                b
 6424                c
 6425            "
 6426            .unindent()
 6427        );
 6428        assert_eq!(
 6429            editor.selections.ranges::<Point>(cx),
 6430            [
 6431                Point::new(0, 1)..Point::new(0, 1),
 6432                Point::new(1, 1)..Point::new(1, 1),
 6433                Point::new(2, 1)..Point::new(2, 1)
 6434            ]
 6435        );
 6436    });
 6437}
 6438
 6439#[gpui::test]
 6440async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6441    init_test(cx, |settings| {
 6442        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6443    });
 6444
 6445    let mut cx = EditorTestContext::new(cx).await;
 6446
 6447    let language = Arc::new(Language::new(
 6448        LanguageConfig {
 6449            brackets: BracketPairConfig {
 6450                pairs: vec![
 6451                    BracketPair {
 6452                        start: "{".to_string(),
 6453                        end: "}".to_string(),
 6454                        close: true,
 6455                        surround: true,
 6456                        newline: true,
 6457                    },
 6458                    BracketPair {
 6459                        start: "(".to_string(),
 6460                        end: ")".to_string(),
 6461                        close: true,
 6462                        surround: true,
 6463                        newline: true,
 6464                    },
 6465                    BracketPair {
 6466                        start: "[".to_string(),
 6467                        end: "]".to_string(),
 6468                        close: false,
 6469                        surround: true,
 6470                        newline: true,
 6471                    },
 6472                ],
 6473                ..Default::default()
 6474            },
 6475            autoclose_before: "})]".to_string(),
 6476            ..Default::default()
 6477        },
 6478        Some(tree_sitter_rust::LANGUAGE.into()),
 6479    ));
 6480
 6481    cx.language_registry().add(language.clone());
 6482    cx.update_buffer(|buffer, cx| {
 6483        buffer.set_language(Some(language), cx);
 6484    });
 6485
 6486    cx.set_state(
 6487        &"
 6488            {(ˇ)}
 6489            [[ˇ]]
 6490            {(ˇ)}
 6491        "
 6492        .unindent(),
 6493    );
 6494
 6495    cx.update_editor(|view, cx| {
 6496        view.backspace(&Default::default(), cx);
 6497        view.backspace(&Default::default(), cx);
 6498    });
 6499
 6500    cx.assert_editor_state(
 6501        &"
 6502            ˇ
 6503            ˇ]]
 6504            ˇ
 6505        "
 6506        .unindent(),
 6507    );
 6508
 6509    cx.update_editor(|view, cx| {
 6510        view.handle_input("{", cx);
 6511        view.handle_input("{", cx);
 6512        view.move_right(&MoveRight, cx);
 6513        view.move_right(&MoveRight, cx);
 6514        view.move_left(&MoveLeft, cx);
 6515        view.move_left(&MoveLeft, cx);
 6516        view.backspace(&Default::default(), cx);
 6517    });
 6518
 6519    cx.assert_editor_state(
 6520        &"
 6521            {ˇ}
 6522            {ˇ}]]
 6523            {ˇ}
 6524        "
 6525        .unindent(),
 6526    );
 6527
 6528    cx.update_editor(|view, cx| {
 6529        view.backspace(&Default::default(), cx);
 6530    });
 6531
 6532    cx.assert_editor_state(
 6533        &"
 6534            ˇ
 6535            ˇ]]
 6536            ˇ
 6537        "
 6538        .unindent(),
 6539    );
 6540}
 6541
 6542#[gpui::test]
 6543async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6544    init_test(cx, |_| {});
 6545
 6546    let language = Arc::new(Language::new(
 6547        LanguageConfig::default(),
 6548        Some(tree_sitter_rust::LANGUAGE.into()),
 6549    ));
 6550
 6551    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6552    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6553    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6554    editor
 6555        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6556        .await;
 6557
 6558    editor.update(cx, |editor, cx| {
 6559        editor.set_auto_replace_emoji_shortcode(true);
 6560
 6561        editor.handle_input("Hello ", cx);
 6562        editor.handle_input(":wave", cx);
 6563        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6564
 6565        editor.handle_input(":", cx);
 6566        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6567
 6568        editor.handle_input(" :smile", cx);
 6569        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6570
 6571        editor.handle_input(":", cx);
 6572        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6573
 6574        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6575        editor.handle_input(":wave", cx);
 6576        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6577
 6578        editor.handle_input(":", cx);
 6579        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6580
 6581        editor.handle_input(":1", cx);
 6582        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6583
 6584        editor.handle_input(":", cx);
 6585        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6586
 6587        // Ensure shortcode does not get replaced when it is part of a word
 6588        editor.handle_input(" Test:wave", cx);
 6589        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6590
 6591        editor.handle_input(":", cx);
 6592        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6593
 6594        editor.set_auto_replace_emoji_shortcode(false);
 6595
 6596        // Ensure shortcode does not get replaced when auto replace is off
 6597        editor.handle_input(" :wave", cx);
 6598        assert_eq!(
 6599            editor.text(cx),
 6600            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6601        );
 6602
 6603        editor.handle_input(":", cx);
 6604        assert_eq!(
 6605            editor.text(cx),
 6606            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6607        );
 6608    });
 6609}
 6610
 6611#[gpui::test]
 6612async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6613    init_test(cx, |_| {});
 6614
 6615    let (text, insertion_ranges) = marked_text_ranges(
 6616        indoc! {"
 6617            a.ˇ b
 6618            a.ˇ b
 6619            a.ˇ b
 6620        "},
 6621        false,
 6622    );
 6623
 6624    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6625    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6626
 6627    editor.update(cx, |editor, cx| {
 6628        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6629
 6630        editor
 6631            .insert_snippet(&insertion_ranges, snippet, cx)
 6632            .unwrap();
 6633
 6634        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6635            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6636            assert_eq!(editor.text(cx), expected_text);
 6637            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6638        }
 6639
 6640        assert(
 6641            editor,
 6642            cx,
 6643            indoc! {"
 6644                a.f(«one», two, «three») b
 6645                a.f(«one», two, «three») b
 6646                a.f(«one», two, «three») b
 6647            "},
 6648        );
 6649
 6650        // Can't move earlier than the first tab stop
 6651        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6652        assert(
 6653            editor,
 6654            cx,
 6655            indoc! {"
 6656                a.f(«one», two, «three») b
 6657                a.f(«one», two, «three») b
 6658                a.f(«one», two, «three») b
 6659            "},
 6660        );
 6661
 6662        assert!(editor.move_to_next_snippet_tabstop(cx));
 6663        assert(
 6664            editor,
 6665            cx,
 6666            indoc! {"
 6667                a.f(one, «two», three) b
 6668                a.f(one, «two», three) b
 6669                a.f(one, «two», three) b
 6670            "},
 6671        );
 6672
 6673        editor.move_to_prev_snippet_tabstop(cx);
 6674        assert(
 6675            editor,
 6676            cx,
 6677            indoc! {"
 6678                a.f(«one», two, «three») b
 6679                a.f(«one», two, «three») b
 6680                a.f(«one», two, «three») b
 6681            "},
 6682        );
 6683
 6684        assert!(editor.move_to_next_snippet_tabstop(cx));
 6685        assert(
 6686            editor,
 6687            cx,
 6688            indoc! {"
 6689                a.f(one, «two», three) b
 6690                a.f(one, «two», three) b
 6691                a.f(one, «two», three) b
 6692            "},
 6693        );
 6694        assert!(editor.move_to_next_snippet_tabstop(cx));
 6695        assert(
 6696            editor,
 6697            cx,
 6698            indoc! {"
 6699                a.f(one, two, three)ˇ b
 6700                a.f(one, two, three)ˇ b
 6701                a.f(one, two, three)ˇ b
 6702            "},
 6703        );
 6704
 6705        // As soon as the last tab stop is reached, snippet state is gone
 6706        editor.move_to_prev_snippet_tabstop(cx);
 6707        assert(
 6708            editor,
 6709            cx,
 6710            indoc! {"
 6711                a.f(one, two, three)ˇ b
 6712                a.f(one, two, three)ˇ b
 6713                a.f(one, two, three)ˇ b
 6714            "},
 6715        );
 6716    });
 6717}
 6718
 6719#[gpui::test]
 6720async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6721    init_test(cx, |_| {});
 6722
 6723    let fs = FakeFs::new(cx.executor());
 6724    fs.insert_file("/file.rs", Default::default()).await;
 6725
 6726    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6727
 6728    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6729    language_registry.add(rust_lang());
 6730    let mut fake_servers = language_registry.register_fake_lsp(
 6731        "Rust",
 6732        FakeLspAdapter {
 6733            capabilities: lsp::ServerCapabilities {
 6734                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6735                ..Default::default()
 6736            },
 6737            ..Default::default()
 6738        },
 6739    );
 6740
 6741    let buffer = project
 6742        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6743        .await
 6744        .unwrap();
 6745
 6746    cx.executor().start_waiting();
 6747    let fake_server = fake_servers.next().await.unwrap();
 6748
 6749    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6750    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6751    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6752    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6753
 6754    let save = editor
 6755        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6756        .unwrap();
 6757    fake_server
 6758        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6759            assert_eq!(
 6760                params.text_document.uri,
 6761                lsp::Url::from_file_path("/file.rs").unwrap()
 6762            );
 6763            assert_eq!(params.options.tab_size, 4);
 6764            Ok(Some(vec![lsp::TextEdit::new(
 6765                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6766                ", ".to_string(),
 6767            )]))
 6768        })
 6769        .next()
 6770        .await;
 6771    cx.executor().start_waiting();
 6772    save.await;
 6773
 6774    assert_eq!(
 6775        editor.update(cx, |editor, cx| editor.text(cx)),
 6776        "one, two\nthree\n"
 6777    );
 6778    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6779
 6780    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6781    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6782
 6783    // Ensure we can still save even if formatting hangs.
 6784    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6785        assert_eq!(
 6786            params.text_document.uri,
 6787            lsp::Url::from_file_path("/file.rs").unwrap()
 6788        );
 6789        futures::future::pending::<()>().await;
 6790        unreachable!()
 6791    });
 6792    let save = editor
 6793        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6794        .unwrap();
 6795    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6796    cx.executor().start_waiting();
 6797    save.await;
 6798    assert_eq!(
 6799        editor.update(cx, |editor, cx| editor.text(cx)),
 6800        "one\ntwo\nthree\n"
 6801    );
 6802    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6803
 6804    // For non-dirty buffer, no formatting request should be sent
 6805    let save = editor
 6806        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6807        .unwrap();
 6808    let _pending_format_request = fake_server
 6809        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6810            panic!("Should not be invoked on non-dirty buffer");
 6811        })
 6812        .next();
 6813    cx.executor().start_waiting();
 6814    save.await;
 6815
 6816    // Set rust language override and assert overridden tabsize is sent to language server
 6817    update_test_language_settings(cx, |settings| {
 6818        settings.languages.insert(
 6819            "Rust".into(),
 6820            LanguageSettingsContent {
 6821                tab_size: NonZeroU32::new(8),
 6822                ..Default::default()
 6823            },
 6824        );
 6825    });
 6826
 6827    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6828    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6829    let save = editor
 6830        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6831        .unwrap();
 6832    fake_server
 6833        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6834            assert_eq!(
 6835                params.text_document.uri,
 6836                lsp::Url::from_file_path("/file.rs").unwrap()
 6837            );
 6838            assert_eq!(params.options.tab_size, 8);
 6839            Ok(Some(vec![]))
 6840        })
 6841        .next()
 6842        .await;
 6843    cx.executor().start_waiting();
 6844    save.await;
 6845}
 6846
 6847#[gpui::test]
 6848async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6849    init_test(cx, |_| {});
 6850
 6851    let cols = 4;
 6852    let rows = 10;
 6853    let sample_text_1 = sample_text(rows, cols, 'a');
 6854    assert_eq!(
 6855        sample_text_1,
 6856        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6857    );
 6858    let sample_text_2 = sample_text(rows, cols, 'l');
 6859    assert_eq!(
 6860        sample_text_2,
 6861        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6862    );
 6863    let sample_text_3 = sample_text(rows, cols, 'v');
 6864    assert_eq!(
 6865        sample_text_3,
 6866        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6867    );
 6868
 6869    let fs = FakeFs::new(cx.executor());
 6870    fs.insert_tree(
 6871        "/a",
 6872        json!({
 6873            "main.rs": sample_text_1,
 6874            "other.rs": sample_text_2,
 6875            "lib.rs": sample_text_3,
 6876        }),
 6877    )
 6878    .await;
 6879
 6880    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6881    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6882    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6883
 6884    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6885    language_registry.add(rust_lang());
 6886    let mut fake_servers = language_registry.register_fake_lsp(
 6887        "Rust",
 6888        FakeLspAdapter {
 6889            capabilities: lsp::ServerCapabilities {
 6890                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6891                ..Default::default()
 6892            },
 6893            ..Default::default()
 6894        },
 6895    );
 6896
 6897    let worktree = project.update(cx, |project, cx| {
 6898        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6899        assert_eq!(worktrees.len(), 1);
 6900        worktrees.pop().unwrap()
 6901    });
 6902    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6903
 6904    let buffer_1 = project
 6905        .update(cx, |project, cx| {
 6906            project.open_buffer((worktree_id, "main.rs"), cx)
 6907        })
 6908        .await
 6909        .unwrap();
 6910    let buffer_2 = project
 6911        .update(cx, |project, cx| {
 6912            project.open_buffer((worktree_id, "other.rs"), cx)
 6913        })
 6914        .await
 6915        .unwrap();
 6916    let buffer_3 = project
 6917        .update(cx, |project, cx| {
 6918            project.open_buffer((worktree_id, "lib.rs"), cx)
 6919        })
 6920        .await
 6921        .unwrap();
 6922
 6923    let multi_buffer = cx.new_model(|cx| {
 6924        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6925        multi_buffer.push_excerpts(
 6926            buffer_1.clone(),
 6927            [
 6928                ExcerptRange {
 6929                    context: Point::new(0, 0)..Point::new(3, 0),
 6930                    primary: None,
 6931                },
 6932                ExcerptRange {
 6933                    context: Point::new(5, 0)..Point::new(7, 0),
 6934                    primary: None,
 6935                },
 6936                ExcerptRange {
 6937                    context: Point::new(9, 0)..Point::new(10, 4),
 6938                    primary: None,
 6939                },
 6940            ],
 6941            cx,
 6942        );
 6943        multi_buffer.push_excerpts(
 6944            buffer_2.clone(),
 6945            [
 6946                ExcerptRange {
 6947                    context: Point::new(0, 0)..Point::new(3, 0),
 6948                    primary: None,
 6949                },
 6950                ExcerptRange {
 6951                    context: Point::new(5, 0)..Point::new(7, 0),
 6952                    primary: None,
 6953                },
 6954                ExcerptRange {
 6955                    context: Point::new(9, 0)..Point::new(10, 4),
 6956                    primary: None,
 6957                },
 6958            ],
 6959            cx,
 6960        );
 6961        multi_buffer.push_excerpts(
 6962            buffer_3.clone(),
 6963            [
 6964                ExcerptRange {
 6965                    context: Point::new(0, 0)..Point::new(3, 0),
 6966                    primary: None,
 6967                },
 6968                ExcerptRange {
 6969                    context: Point::new(5, 0)..Point::new(7, 0),
 6970                    primary: None,
 6971                },
 6972                ExcerptRange {
 6973                    context: Point::new(9, 0)..Point::new(10, 4),
 6974                    primary: None,
 6975                },
 6976            ],
 6977            cx,
 6978        );
 6979        multi_buffer
 6980    });
 6981    let multi_buffer_editor = cx.new_view(|cx| {
 6982        Editor::new(
 6983            EditorMode::Full,
 6984            multi_buffer,
 6985            Some(project.clone()),
 6986            true,
 6987            cx,
 6988        )
 6989    });
 6990
 6991    multi_buffer_editor.update(cx, |editor, cx| {
 6992        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6993        editor.insert("|one|two|three|", cx);
 6994    });
 6995    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6996    multi_buffer_editor.update(cx, |editor, cx| {
 6997        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6998            s.select_ranges(Some(60..70))
 6999        });
 7000        editor.insert("|four|five|six|", cx);
 7001    });
 7002    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7003
 7004    // First two buffers should be edited, but not the third one.
 7005    assert_eq!(
 7006        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7007        "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}",
 7008    );
 7009    buffer_1.update(cx, |buffer, _| {
 7010        assert!(buffer.is_dirty());
 7011        assert_eq!(
 7012            buffer.text(),
 7013            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7014        )
 7015    });
 7016    buffer_2.update(cx, |buffer, _| {
 7017        assert!(buffer.is_dirty());
 7018        assert_eq!(
 7019            buffer.text(),
 7020            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7021        )
 7022    });
 7023    buffer_3.update(cx, |buffer, _| {
 7024        assert!(!buffer.is_dirty());
 7025        assert_eq!(buffer.text(), sample_text_3,)
 7026    });
 7027
 7028    cx.executor().start_waiting();
 7029    let save = multi_buffer_editor
 7030        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7031        .unwrap();
 7032
 7033    let fake_server = fake_servers.next().await.unwrap();
 7034    fake_server
 7035        .server
 7036        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7037            Ok(Some(vec![lsp::TextEdit::new(
 7038                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7039                format!("[{} formatted]", params.text_document.uri),
 7040            )]))
 7041        })
 7042        .detach();
 7043    save.await;
 7044
 7045    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7046    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7047    assert_eq!(
 7048        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7049        "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}",
 7050    );
 7051    buffer_1.update(cx, |buffer, _| {
 7052        assert!(!buffer.is_dirty());
 7053        assert_eq!(
 7054            buffer.text(),
 7055            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7056        )
 7057    });
 7058    buffer_2.update(cx, |buffer, _| {
 7059        assert!(!buffer.is_dirty());
 7060        assert_eq!(
 7061            buffer.text(),
 7062            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7063        )
 7064    });
 7065    buffer_3.update(cx, |buffer, _| {
 7066        assert!(!buffer.is_dirty());
 7067        assert_eq!(buffer.text(), sample_text_3,)
 7068    });
 7069}
 7070
 7071#[gpui::test]
 7072async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7073    init_test(cx, |_| {});
 7074
 7075    let fs = FakeFs::new(cx.executor());
 7076    fs.insert_file("/file.rs", Default::default()).await;
 7077
 7078    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7079
 7080    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7081    language_registry.add(rust_lang());
 7082    let mut fake_servers = language_registry.register_fake_lsp(
 7083        "Rust",
 7084        FakeLspAdapter {
 7085            capabilities: lsp::ServerCapabilities {
 7086                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7087                ..Default::default()
 7088            },
 7089            ..Default::default()
 7090        },
 7091    );
 7092
 7093    let buffer = project
 7094        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7095        .await
 7096        .unwrap();
 7097
 7098    cx.executor().start_waiting();
 7099    let fake_server = fake_servers.next().await.unwrap();
 7100
 7101    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7102    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7103    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7104    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7105
 7106    let save = editor
 7107        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7108        .unwrap();
 7109    fake_server
 7110        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7111            assert_eq!(
 7112                params.text_document.uri,
 7113                lsp::Url::from_file_path("/file.rs").unwrap()
 7114            );
 7115            assert_eq!(params.options.tab_size, 4);
 7116            Ok(Some(vec![lsp::TextEdit::new(
 7117                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7118                ", ".to_string(),
 7119            )]))
 7120        })
 7121        .next()
 7122        .await;
 7123    cx.executor().start_waiting();
 7124    save.await;
 7125    assert_eq!(
 7126        editor.update(cx, |editor, cx| editor.text(cx)),
 7127        "one, two\nthree\n"
 7128    );
 7129    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7130
 7131    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7132    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7133
 7134    // Ensure we can still save even if formatting hangs.
 7135    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7136        move |params, _| async move {
 7137            assert_eq!(
 7138                params.text_document.uri,
 7139                lsp::Url::from_file_path("/file.rs").unwrap()
 7140            );
 7141            futures::future::pending::<()>().await;
 7142            unreachable!()
 7143        },
 7144    );
 7145    let save = editor
 7146        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7147        .unwrap();
 7148    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7149    cx.executor().start_waiting();
 7150    save.await;
 7151    assert_eq!(
 7152        editor.update(cx, |editor, cx| editor.text(cx)),
 7153        "one\ntwo\nthree\n"
 7154    );
 7155    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7156
 7157    // For non-dirty buffer, no formatting request should be sent
 7158    let save = editor
 7159        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7160        .unwrap();
 7161    let _pending_format_request = fake_server
 7162        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7163            panic!("Should not be invoked on non-dirty buffer");
 7164        })
 7165        .next();
 7166    cx.executor().start_waiting();
 7167    save.await;
 7168
 7169    // Set Rust language override and assert overridden tabsize is sent to language server
 7170    update_test_language_settings(cx, |settings| {
 7171        settings.languages.insert(
 7172            "Rust".into(),
 7173            LanguageSettingsContent {
 7174                tab_size: NonZeroU32::new(8),
 7175                ..Default::default()
 7176            },
 7177        );
 7178    });
 7179
 7180    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7181    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7182    let save = editor
 7183        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7184        .unwrap();
 7185    fake_server
 7186        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7187            assert_eq!(
 7188                params.text_document.uri,
 7189                lsp::Url::from_file_path("/file.rs").unwrap()
 7190            );
 7191            assert_eq!(params.options.tab_size, 8);
 7192            Ok(Some(vec![]))
 7193        })
 7194        .next()
 7195        .await;
 7196    cx.executor().start_waiting();
 7197    save.await;
 7198}
 7199
 7200#[gpui::test]
 7201async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7202    init_test(cx, |settings| {
 7203        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7204            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7205        ))
 7206    });
 7207
 7208    let fs = FakeFs::new(cx.executor());
 7209    fs.insert_file("/file.rs", Default::default()).await;
 7210
 7211    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7212
 7213    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7214    language_registry.add(Arc::new(Language::new(
 7215        LanguageConfig {
 7216            name: "Rust".into(),
 7217            matcher: LanguageMatcher {
 7218                path_suffixes: vec!["rs".to_string()],
 7219                ..Default::default()
 7220            },
 7221            ..LanguageConfig::default()
 7222        },
 7223        Some(tree_sitter_rust::LANGUAGE.into()),
 7224    )));
 7225    update_test_language_settings(cx, |settings| {
 7226        // Enable Prettier formatting for the same buffer, and ensure
 7227        // LSP is called instead of Prettier.
 7228        settings.defaults.prettier = Some(PrettierSettings {
 7229            allowed: true,
 7230            ..PrettierSettings::default()
 7231        });
 7232    });
 7233    let mut fake_servers = language_registry.register_fake_lsp(
 7234        "Rust",
 7235        FakeLspAdapter {
 7236            capabilities: lsp::ServerCapabilities {
 7237                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7238                ..Default::default()
 7239            },
 7240            ..Default::default()
 7241        },
 7242    );
 7243
 7244    let buffer = project
 7245        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7246        .await
 7247        .unwrap();
 7248
 7249    cx.executor().start_waiting();
 7250    let fake_server = fake_servers.next().await.unwrap();
 7251
 7252    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7253    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7254    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7255
 7256    let format = editor
 7257        .update(cx, |editor, cx| {
 7258            editor.perform_format(
 7259                project.clone(),
 7260                FormatTrigger::Manual,
 7261                FormatTarget::Buffer,
 7262                cx,
 7263            )
 7264        })
 7265        .unwrap();
 7266    fake_server
 7267        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7268            assert_eq!(
 7269                params.text_document.uri,
 7270                lsp::Url::from_file_path("/file.rs").unwrap()
 7271            );
 7272            assert_eq!(params.options.tab_size, 4);
 7273            Ok(Some(vec![lsp::TextEdit::new(
 7274                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7275                ", ".to_string(),
 7276            )]))
 7277        })
 7278        .next()
 7279        .await;
 7280    cx.executor().start_waiting();
 7281    format.await;
 7282    assert_eq!(
 7283        editor.update(cx, |editor, cx| editor.text(cx)),
 7284        "one, two\nthree\n"
 7285    );
 7286
 7287    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7288    // Ensure we don't lock if formatting hangs.
 7289    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7290        assert_eq!(
 7291            params.text_document.uri,
 7292            lsp::Url::from_file_path("/file.rs").unwrap()
 7293        );
 7294        futures::future::pending::<()>().await;
 7295        unreachable!()
 7296    });
 7297    let format = editor
 7298        .update(cx, |editor, cx| {
 7299            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7300        })
 7301        .unwrap();
 7302    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7303    cx.executor().start_waiting();
 7304    format.await;
 7305    assert_eq!(
 7306        editor.update(cx, |editor, cx| editor.text(cx)),
 7307        "one\ntwo\nthree\n"
 7308    );
 7309}
 7310
 7311#[gpui::test]
 7312async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7313    init_test(cx, |_| {});
 7314
 7315    let mut cx = EditorLspTestContext::new_rust(
 7316        lsp::ServerCapabilities {
 7317            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7318            ..Default::default()
 7319        },
 7320        cx,
 7321    )
 7322    .await;
 7323
 7324    cx.set_state(indoc! {"
 7325        one.twoˇ
 7326    "});
 7327
 7328    // The format request takes a long time. When it completes, it inserts
 7329    // a newline and an indent before the `.`
 7330    cx.lsp
 7331        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7332            let executor = cx.background_executor().clone();
 7333            async move {
 7334                executor.timer(Duration::from_millis(100)).await;
 7335                Ok(Some(vec![lsp::TextEdit {
 7336                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7337                    new_text: "\n    ".into(),
 7338                }]))
 7339            }
 7340        });
 7341
 7342    // Submit a format request.
 7343    let format_1 = cx
 7344        .update_editor(|editor, cx| editor.format(&Format, cx))
 7345        .unwrap();
 7346    cx.executor().run_until_parked();
 7347
 7348    // Submit a second format request.
 7349    let format_2 = cx
 7350        .update_editor(|editor, cx| editor.format(&Format, cx))
 7351        .unwrap();
 7352    cx.executor().run_until_parked();
 7353
 7354    // Wait for both format requests to complete
 7355    cx.executor().advance_clock(Duration::from_millis(200));
 7356    cx.executor().start_waiting();
 7357    format_1.await.unwrap();
 7358    cx.executor().start_waiting();
 7359    format_2.await.unwrap();
 7360
 7361    // The formatting edits only happens once.
 7362    cx.assert_editor_state(indoc! {"
 7363        one
 7364            .twoˇ
 7365    "});
 7366}
 7367
 7368#[gpui::test]
 7369async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7370    init_test(cx, |settings| {
 7371        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7372    });
 7373
 7374    let mut cx = EditorLspTestContext::new_rust(
 7375        lsp::ServerCapabilities {
 7376            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7377            ..Default::default()
 7378        },
 7379        cx,
 7380    )
 7381    .await;
 7382
 7383    // Set up a buffer white some trailing whitespace and no trailing newline.
 7384    cx.set_state(
 7385        &[
 7386            "one ",   //
 7387            "twoˇ",   //
 7388            "three ", //
 7389            "four",   //
 7390        ]
 7391        .join("\n"),
 7392    );
 7393
 7394    // Submit a format request.
 7395    let format = cx
 7396        .update_editor(|editor, cx| editor.format(&Format, cx))
 7397        .unwrap();
 7398
 7399    // Record which buffer changes have been sent to the language server
 7400    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7401    cx.lsp
 7402        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7403            let buffer_changes = buffer_changes.clone();
 7404            move |params, _| {
 7405                buffer_changes.lock().extend(
 7406                    params
 7407                        .content_changes
 7408                        .into_iter()
 7409                        .map(|e| (e.range.unwrap(), e.text)),
 7410                );
 7411            }
 7412        });
 7413
 7414    // Handle formatting requests to the language server.
 7415    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7416        let buffer_changes = buffer_changes.clone();
 7417        move |_, _| {
 7418            // When formatting is requested, trailing whitespace has already been stripped,
 7419            // and the trailing newline has already been added.
 7420            assert_eq!(
 7421                &buffer_changes.lock()[1..],
 7422                &[
 7423                    (
 7424                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7425                        "".into()
 7426                    ),
 7427                    (
 7428                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7429                        "".into()
 7430                    ),
 7431                    (
 7432                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7433                        "\n".into()
 7434                    ),
 7435                ]
 7436            );
 7437
 7438            // Insert blank lines between each line of the buffer.
 7439            async move {
 7440                Ok(Some(vec![
 7441                    lsp::TextEdit {
 7442                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7443                        new_text: "\n".into(),
 7444                    },
 7445                    lsp::TextEdit {
 7446                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7447                        new_text: "\n".into(),
 7448                    },
 7449                ]))
 7450            }
 7451        }
 7452    });
 7453
 7454    // After formatting the buffer, the trailing whitespace is stripped,
 7455    // a newline is appended, and the edits provided by the language server
 7456    // have been applied.
 7457    format.await.unwrap();
 7458    cx.assert_editor_state(
 7459        &[
 7460            "one",   //
 7461            "",      //
 7462            "twoˇ",  //
 7463            "",      //
 7464            "three", //
 7465            "four",  //
 7466            "",      //
 7467        ]
 7468        .join("\n"),
 7469    );
 7470
 7471    // Undoing the formatting undoes the trailing whitespace removal, the
 7472    // trailing newline, and the LSP edits.
 7473    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7474    cx.assert_editor_state(
 7475        &[
 7476            "one ",   //
 7477            "twoˇ",   //
 7478            "three ", //
 7479            "four",   //
 7480        ]
 7481        .join("\n"),
 7482    );
 7483}
 7484
 7485#[gpui::test]
 7486async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7487    cx: &mut gpui::TestAppContext,
 7488) {
 7489    init_test(cx, |_| {});
 7490
 7491    cx.update(|cx| {
 7492        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7493            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7494                settings.auto_signature_help = Some(true);
 7495            });
 7496        });
 7497    });
 7498
 7499    let mut cx = EditorLspTestContext::new_rust(
 7500        lsp::ServerCapabilities {
 7501            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7502                ..Default::default()
 7503            }),
 7504            ..Default::default()
 7505        },
 7506        cx,
 7507    )
 7508    .await;
 7509
 7510    let language = Language::new(
 7511        LanguageConfig {
 7512            name: "Rust".into(),
 7513            brackets: BracketPairConfig {
 7514                pairs: vec![
 7515                    BracketPair {
 7516                        start: "{".to_string(),
 7517                        end: "}".to_string(),
 7518                        close: true,
 7519                        surround: true,
 7520                        newline: true,
 7521                    },
 7522                    BracketPair {
 7523                        start: "(".to_string(),
 7524                        end: ")".to_string(),
 7525                        close: true,
 7526                        surround: true,
 7527                        newline: true,
 7528                    },
 7529                    BracketPair {
 7530                        start: "/*".to_string(),
 7531                        end: " */".to_string(),
 7532                        close: true,
 7533                        surround: true,
 7534                        newline: true,
 7535                    },
 7536                    BracketPair {
 7537                        start: "[".to_string(),
 7538                        end: "]".to_string(),
 7539                        close: false,
 7540                        surround: false,
 7541                        newline: true,
 7542                    },
 7543                    BracketPair {
 7544                        start: "\"".to_string(),
 7545                        end: "\"".to_string(),
 7546                        close: true,
 7547                        surround: true,
 7548                        newline: false,
 7549                    },
 7550                    BracketPair {
 7551                        start: "<".to_string(),
 7552                        end: ">".to_string(),
 7553                        close: false,
 7554                        surround: true,
 7555                        newline: true,
 7556                    },
 7557                ],
 7558                ..Default::default()
 7559            },
 7560            autoclose_before: "})]".to_string(),
 7561            ..Default::default()
 7562        },
 7563        Some(tree_sitter_rust::LANGUAGE.into()),
 7564    );
 7565    let language = Arc::new(language);
 7566
 7567    cx.language_registry().add(language.clone());
 7568    cx.update_buffer(|buffer, cx| {
 7569        buffer.set_language(Some(language), cx);
 7570    });
 7571
 7572    cx.set_state(
 7573        &r#"
 7574            fn main() {
 7575                sampleˇ
 7576            }
 7577        "#
 7578        .unindent(),
 7579    );
 7580
 7581    cx.update_editor(|view, cx| {
 7582        view.handle_input("(", cx);
 7583    });
 7584    cx.assert_editor_state(
 7585        &"
 7586            fn main() {
 7587                sample(ˇ)
 7588            }
 7589        "
 7590        .unindent(),
 7591    );
 7592
 7593    let mocked_response = lsp::SignatureHelp {
 7594        signatures: vec![lsp::SignatureInformation {
 7595            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7596            documentation: None,
 7597            parameters: Some(vec![
 7598                lsp::ParameterInformation {
 7599                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7600                    documentation: None,
 7601                },
 7602                lsp::ParameterInformation {
 7603                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7604                    documentation: None,
 7605                },
 7606            ]),
 7607            active_parameter: None,
 7608        }],
 7609        active_signature: Some(0),
 7610        active_parameter: Some(0),
 7611    };
 7612    handle_signature_help_request(&mut cx, mocked_response).await;
 7613
 7614    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7615        .await;
 7616
 7617    cx.editor(|editor, _| {
 7618        let signature_help_state = editor.signature_help_state.popover().cloned();
 7619        assert!(signature_help_state.is_some());
 7620        let ParsedMarkdown {
 7621            text, highlights, ..
 7622        } = signature_help_state.unwrap().parsed_content;
 7623        assert_eq!(text, "param1: u8, param2: u8");
 7624        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7625    });
 7626}
 7627
 7628#[gpui::test]
 7629async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7630    init_test(cx, |_| {});
 7631
 7632    cx.update(|cx| {
 7633        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7634            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7635                settings.auto_signature_help = Some(false);
 7636                settings.show_signature_help_after_edits = Some(false);
 7637            });
 7638        });
 7639    });
 7640
 7641    let mut cx = EditorLspTestContext::new_rust(
 7642        lsp::ServerCapabilities {
 7643            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7644                ..Default::default()
 7645            }),
 7646            ..Default::default()
 7647        },
 7648        cx,
 7649    )
 7650    .await;
 7651
 7652    let language = Language::new(
 7653        LanguageConfig {
 7654            name: "Rust".into(),
 7655            brackets: BracketPairConfig {
 7656                pairs: vec![
 7657                    BracketPair {
 7658                        start: "{".to_string(),
 7659                        end: "}".to_string(),
 7660                        close: true,
 7661                        surround: true,
 7662                        newline: true,
 7663                    },
 7664                    BracketPair {
 7665                        start: "(".to_string(),
 7666                        end: ")".to_string(),
 7667                        close: true,
 7668                        surround: true,
 7669                        newline: true,
 7670                    },
 7671                    BracketPair {
 7672                        start: "/*".to_string(),
 7673                        end: " */".to_string(),
 7674                        close: true,
 7675                        surround: true,
 7676                        newline: true,
 7677                    },
 7678                    BracketPair {
 7679                        start: "[".to_string(),
 7680                        end: "]".to_string(),
 7681                        close: false,
 7682                        surround: false,
 7683                        newline: true,
 7684                    },
 7685                    BracketPair {
 7686                        start: "\"".to_string(),
 7687                        end: "\"".to_string(),
 7688                        close: true,
 7689                        surround: true,
 7690                        newline: false,
 7691                    },
 7692                    BracketPair {
 7693                        start: "<".to_string(),
 7694                        end: ">".to_string(),
 7695                        close: false,
 7696                        surround: true,
 7697                        newline: true,
 7698                    },
 7699                ],
 7700                ..Default::default()
 7701            },
 7702            autoclose_before: "})]".to_string(),
 7703            ..Default::default()
 7704        },
 7705        Some(tree_sitter_rust::LANGUAGE.into()),
 7706    );
 7707    let language = Arc::new(language);
 7708
 7709    cx.language_registry().add(language.clone());
 7710    cx.update_buffer(|buffer, cx| {
 7711        buffer.set_language(Some(language), cx);
 7712    });
 7713
 7714    // Ensure that signature_help is not called when no signature help is enabled.
 7715    cx.set_state(
 7716        &r#"
 7717            fn main() {
 7718                sampleˇ
 7719            }
 7720        "#
 7721        .unindent(),
 7722    );
 7723    cx.update_editor(|view, cx| {
 7724        view.handle_input("(", cx);
 7725    });
 7726    cx.assert_editor_state(
 7727        &"
 7728            fn main() {
 7729                sample(ˇ)
 7730            }
 7731        "
 7732        .unindent(),
 7733    );
 7734    cx.editor(|editor, _| {
 7735        assert!(editor.signature_help_state.task().is_none());
 7736    });
 7737
 7738    let mocked_response = lsp::SignatureHelp {
 7739        signatures: vec![lsp::SignatureInformation {
 7740            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7741            documentation: None,
 7742            parameters: Some(vec![
 7743                lsp::ParameterInformation {
 7744                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7745                    documentation: None,
 7746                },
 7747                lsp::ParameterInformation {
 7748                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7749                    documentation: None,
 7750                },
 7751            ]),
 7752            active_parameter: None,
 7753        }],
 7754        active_signature: Some(0),
 7755        active_parameter: Some(0),
 7756    };
 7757
 7758    // Ensure that signature_help is called when enabled afte edits
 7759    cx.update(|cx| {
 7760        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7761            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7762                settings.auto_signature_help = Some(false);
 7763                settings.show_signature_help_after_edits = Some(true);
 7764            });
 7765        });
 7766    });
 7767    cx.set_state(
 7768        &r#"
 7769            fn main() {
 7770                sampleˇ
 7771            }
 7772        "#
 7773        .unindent(),
 7774    );
 7775    cx.update_editor(|view, cx| {
 7776        view.handle_input("(", cx);
 7777    });
 7778    cx.assert_editor_state(
 7779        &"
 7780            fn main() {
 7781                sample(ˇ)
 7782            }
 7783        "
 7784        .unindent(),
 7785    );
 7786    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7787    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7788        .await;
 7789    cx.update_editor(|editor, _| {
 7790        let signature_help_state = editor.signature_help_state.popover().cloned();
 7791        assert!(signature_help_state.is_some());
 7792        let ParsedMarkdown {
 7793            text, highlights, ..
 7794        } = signature_help_state.unwrap().parsed_content;
 7795        assert_eq!(text, "param1: u8, param2: u8");
 7796        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7797        editor.signature_help_state = SignatureHelpState::default();
 7798    });
 7799
 7800    // Ensure that signature_help is called when auto signature help override is enabled
 7801    cx.update(|cx| {
 7802        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7803            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7804                settings.auto_signature_help = Some(true);
 7805                settings.show_signature_help_after_edits = Some(false);
 7806            });
 7807        });
 7808    });
 7809    cx.set_state(
 7810        &r#"
 7811            fn main() {
 7812                sampleˇ
 7813            }
 7814        "#
 7815        .unindent(),
 7816    );
 7817    cx.update_editor(|view, cx| {
 7818        view.handle_input("(", cx);
 7819    });
 7820    cx.assert_editor_state(
 7821        &"
 7822            fn main() {
 7823                sample(ˇ)
 7824            }
 7825        "
 7826        .unindent(),
 7827    );
 7828    handle_signature_help_request(&mut cx, mocked_response).await;
 7829    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7830        .await;
 7831    cx.editor(|editor, _| {
 7832        let signature_help_state = editor.signature_help_state.popover().cloned();
 7833        assert!(signature_help_state.is_some());
 7834        let ParsedMarkdown {
 7835            text, highlights, ..
 7836        } = signature_help_state.unwrap().parsed_content;
 7837        assert_eq!(text, "param1: u8, param2: u8");
 7838        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7839    });
 7840}
 7841
 7842#[gpui::test]
 7843async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7844    init_test(cx, |_| {});
 7845    cx.update(|cx| {
 7846        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7847            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7848                settings.auto_signature_help = Some(true);
 7849            });
 7850        });
 7851    });
 7852
 7853    let mut cx = EditorLspTestContext::new_rust(
 7854        lsp::ServerCapabilities {
 7855            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7856                ..Default::default()
 7857            }),
 7858            ..Default::default()
 7859        },
 7860        cx,
 7861    )
 7862    .await;
 7863
 7864    // A test that directly calls `show_signature_help`
 7865    cx.update_editor(|editor, cx| {
 7866        editor.show_signature_help(&ShowSignatureHelp, cx);
 7867    });
 7868
 7869    let mocked_response = lsp::SignatureHelp {
 7870        signatures: vec![lsp::SignatureInformation {
 7871            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7872            documentation: None,
 7873            parameters: Some(vec![
 7874                lsp::ParameterInformation {
 7875                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7876                    documentation: None,
 7877                },
 7878                lsp::ParameterInformation {
 7879                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7880                    documentation: None,
 7881                },
 7882            ]),
 7883            active_parameter: None,
 7884        }],
 7885        active_signature: Some(0),
 7886        active_parameter: Some(0),
 7887    };
 7888    handle_signature_help_request(&mut cx, mocked_response).await;
 7889
 7890    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7891        .await;
 7892
 7893    cx.editor(|editor, _| {
 7894        let signature_help_state = editor.signature_help_state.popover().cloned();
 7895        assert!(signature_help_state.is_some());
 7896        let ParsedMarkdown {
 7897            text, highlights, ..
 7898        } = signature_help_state.unwrap().parsed_content;
 7899        assert_eq!(text, "param1: u8, param2: u8");
 7900        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7901    });
 7902
 7903    // When exiting outside from inside the brackets, `signature_help` is closed.
 7904    cx.set_state(indoc! {"
 7905        fn main() {
 7906            sample(ˇ);
 7907        }
 7908
 7909        fn sample(param1: u8, param2: u8) {}
 7910    "});
 7911
 7912    cx.update_editor(|editor, cx| {
 7913        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7914    });
 7915
 7916    let mocked_response = lsp::SignatureHelp {
 7917        signatures: Vec::new(),
 7918        active_signature: None,
 7919        active_parameter: None,
 7920    };
 7921    handle_signature_help_request(&mut cx, mocked_response).await;
 7922
 7923    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7924        .await;
 7925
 7926    cx.editor(|editor, _| {
 7927        assert!(!editor.signature_help_state.is_shown());
 7928    });
 7929
 7930    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7931    cx.set_state(indoc! {"
 7932        fn main() {
 7933            sample(ˇ);
 7934        }
 7935
 7936        fn sample(param1: u8, param2: u8) {}
 7937    "});
 7938
 7939    let mocked_response = lsp::SignatureHelp {
 7940        signatures: vec![lsp::SignatureInformation {
 7941            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7942            documentation: None,
 7943            parameters: Some(vec![
 7944                lsp::ParameterInformation {
 7945                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7946                    documentation: None,
 7947                },
 7948                lsp::ParameterInformation {
 7949                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7950                    documentation: None,
 7951                },
 7952            ]),
 7953            active_parameter: None,
 7954        }],
 7955        active_signature: Some(0),
 7956        active_parameter: Some(0),
 7957    };
 7958    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7959    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7960        .await;
 7961    cx.editor(|editor, _| {
 7962        assert!(editor.signature_help_state.is_shown());
 7963    });
 7964
 7965    // Restore the popover with more parameter input
 7966    cx.set_state(indoc! {"
 7967        fn main() {
 7968            sample(param1, param2ˇ);
 7969        }
 7970
 7971        fn sample(param1: u8, param2: u8) {}
 7972    "});
 7973
 7974    let mocked_response = lsp::SignatureHelp {
 7975        signatures: vec![lsp::SignatureInformation {
 7976            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7977            documentation: None,
 7978            parameters: Some(vec![
 7979                lsp::ParameterInformation {
 7980                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7981                    documentation: None,
 7982                },
 7983                lsp::ParameterInformation {
 7984                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7985                    documentation: None,
 7986                },
 7987            ]),
 7988            active_parameter: None,
 7989        }],
 7990        active_signature: Some(0),
 7991        active_parameter: Some(1),
 7992    };
 7993    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7994    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7995        .await;
 7996
 7997    // When selecting a range, the popover is gone.
 7998    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7999    cx.update_editor(|editor, cx| {
 8000        editor.change_selections(None, cx, |s| {
 8001            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8002        })
 8003    });
 8004    cx.assert_editor_state(indoc! {"
 8005        fn main() {
 8006            sample(param1, «ˇparam2»);
 8007        }
 8008
 8009        fn sample(param1: u8, param2: u8) {}
 8010    "});
 8011    cx.editor(|editor, _| {
 8012        assert!(!editor.signature_help_state.is_shown());
 8013    });
 8014
 8015    // When unselecting again, the popover is back if within the brackets.
 8016    cx.update_editor(|editor, cx| {
 8017        editor.change_selections(None, cx, |s| {
 8018            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8019        })
 8020    });
 8021    cx.assert_editor_state(indoc! {"
 8022        fn main() {
 8023            sample(param1, ˇparam2);
 8024        }
 8025
 8026        fn sample(param1: u8, param2: u8) {}
 8027    "});
 8028    handle_signature_help_request(&mut cx, mocked_response).await;
 8029    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8030        .await;
 8031    cx.editor(|editor, _| {
 8032        assert!(editor.signature_help_state.is_shown());
 8033    });
 8034
 8035    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8036    cx.update_editor(|editor, cx| {
 8037        editor.change_selections(None, cx, |s| {
 8038            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8039            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8040        })
 8041    });
 8042    cx.assert_editor_state(indoc! {"
 8043        fn main() {
 8044            sample(param1, ˇparam2);
 8045        }
 8046
 8047        fn sample(param1: u8, param2: u8) {}
 8048    "});
 8049
 8050    let mocked_response = lsp::SignatureHelp {
 8051        signatures: vec![lsp::SignatureInformation {
 8052            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8053            documentation: None,
 8054            parameters: Some(vec![
 8055                lsp::ParameterInformation {
 8056                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8057                    documentation: None,
 8058                },
 8059                lsp::ParameterInformation {
 8060                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8061                    documentation: None,
 8062                },
 8063            ]),
 8064            active_parameter: None,
 8065        }],
 8066        active_signature: Some(0),
 8067        active_parameter: Some(1),
 8068    };
 8069    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8070    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8071        .await;
 8072    cx.update_editor(|editor, cx| {
 8073        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8074    });
 8075    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8076        .await;
 8077    cx.update_editor(|editor, cx| {
 8078        editor.change_selections(None, cx, |s| {
 8079            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8080        })
 8081    });
 8082    cx.assert_editor_state(indoc! {"
 8083        fn main() {
 8084            sample(param1, «ˇparam2»);
 8085        }
 8086
 8087        fn sample(param1: u8, param2: u8) {}
 8088    "});
 8089    cx.update_editor(|editor, cx| {
 8090        editor.change_selections(None, cx, |s| {
 8091            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8092        })
 8093    });
 8094    cx.assert_editor_state(indoc! {"
 8095        fn main() {
 8096            sample(param1, ˇparam2);
 8097        }
 8098
 8099        fn sample(param1: u8, param2: u8) {}
 8100    "});
 8101    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8102        .await;
 8103}
 8104
 8105#[gpui::test]
 8106async fn test_completion(cx: &mut gpui::TestAppContext) {
 8107    init_test(cx, |_| {});
 8108
 8109    let mut cx = EditorLspTestContext::new_rust(
 8110        lsp::ServerCapabilities {
 8111            completion_provider: Some(lsp::CompletionOptions {
 8112                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8113                resolve_provider: Some(true),
 8114                ..Default::default()
 8115            }),
 8116            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8117            ..Default::default()
 8118        },
 8119        cx,
 8120    )
 8121    .await;
 8122    let counter = Arc::new(AtomicUsize::new(0));
 8123
 8124    cx.set_state(indoc! {"
 8125        oneˇ
 8126        two
 8127        three
 8128    "});
 8129    cx.simulate_keystroke(".");
 8130    handle_completion_request(
 8131        &mut cx,
 8132        indoc! {"
 8133            one.|<>
 8134            two
 8135            three
 8136        "},
 8137        vec!["first_completion", "second_completion"],
 8138        counter.clone(),
 8139    )
 8140    .await;
 8141    cx.condition(|editor, _| editor.context_menu_visible())
 8142        .await;
 8143    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8144
 8145    let _handler = handle_signature_help_request(
 8146        &mut cx,
 8147        lsp::SignatureHelp {
 8148            signatures: vec![lsp::SignatureInformation {
 8149                label: "test signature".to_string(),
 8150                documentation: None,
 8151                parameters: Some(vec![lsp::ParameterInformation {
 8152                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8153                    documentation: None,
 8154                }]),
 8155                active_parameter: None,
 8156            }],
 8157            active_signature: None,
 8158            active_parameter: None,
 8159        },
 8160    );
 8161    cx.update_editor(|editor, cx| {
 8162        assert!(
 8163            !editor.signature_help_state.is_shown(),
 8164            "No signature help was called for"
 8165        );
 8166        editor.show_signature_help(&ShowSignatureHelp, cx);
 8167    });
 8168    cx.run_until_parked();
 8169    cx.update_editor(|editor, _| {
 8170        assert!(
 8171            !editor.signature_help_state.is_shown(),
 8172            "No signature help should be shown when completions menu is open"
 8173        );
 8174    });
 8175
 8176    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8177        editor.context_menu_next(&Default::default(), cx);
 8178        editor
 8179            .confirm_completion(&ConfirmCompletion::default(), cx)
 8180            .unwrap()
 8181    });
 8182    cx.assert_editor_state(indoc! {"
 8183        one.second_completionˇ
 8184        two
 8185        three
 8186    "});
 8187
 8188    handle_resolve_completion_request(
 8189        &mut cx,
 8190        Some(vec![
 8191            (
 8192                //This overlaps with the primary completion edit which is
 8193                //misbehavior from the LSP spec, test that we filter it out
 8194                indoc! {"
 8195                    one.second_ˇcompletion
 8196                    two
 8197                    threeˇ
 8198                "},
 8199                "overlapping additional edit",
 8200            ),
 8201            (
 8202                indoc! {"
 8203                    one.second_completion
 8204                    two
 8205                    threeˇ
 8206                "},
 8207                "\nadditional edit",
 8208            ),
 8209        ]),
 8210    )
 8211    .await;
 8212    apply_additional_edits.await.unwrap();
 8213    cx.assert_editor_state(indoc! {"
 8214        one.second_completionˇ
 8215        two
 8216        three
 8217        additional edit
 8218    "});
 8219
 8220    cx.set_state(indoc! {"
 8221        one.second_completion
 8222        twoˇ
 8223        threeˇ
 8224        additional edit
 8225    "});
 8226    cx.simulate_keystroke(" ");
 8227    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8228    cx.simulate_keystroke("s");
 8229    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8230
 8231    cx.assert_editor_state(indoc! {"
 8232        one.second_completion
 8233        two sˇ
 8234        three sˇ
 8235        additional edit
 8236    "});
 8237    handle_completion_request(
 8238        &mut cx,
 8239        indoc! {"
 8240            one.second_completion
 8241            two s
 8242            three <s|>
 8243            additional edit
 8244        "},
 8245        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8246        counter.clone(),
 8247    )
 8248    .await;
 8249    cx.condition(|editor, _| editor.context_menu_visible())
 8250        .await;
 8251    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8252
 8253    cx.simulate_keystroke("i");
 8254
 8255    handle_completion_request(
 8256        &mut cx,
 8257        indoc! {"
 8258            one.second_completion
 8259            two si
 8260            three <si|>
 8261            additional edit
 8262        "},
 8263        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8264        counter.clone(),
 8265    )
 8266    .await;
 8267    cx.condition(|editor, _| editor.context_menu_visible())
 8268        .await;
 8269    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8270
 8271    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8272        editor
 8273            .confirm_completion(&ConfirmCompletion::default(), cx)
 8274            .unwrap()
 8275    });
 8276    cx.assert_editor_state(indoc! {"
 8277        one.second_completion
 8278        two sixth_completionˇ
 8279        three sixth_completionˇ
 8280        additional edit
 8281    "});
 8282
 8283    handle_resolve_completion_request(&mut cx, None).await;
 8284    apply_additional_edits.await.unwrap();
 8285
 8286    cx.update(|cx| {
 8287        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8288            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8289                settings.show_completions_on_input = Some(false);
 8290            });
 8291        })
 8292    });
 8293    cx.set_state("editorˇ");
 8294    cx.simulate_keystroke(".");
 8295    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8296    cx.simulate_keystroke("c");
 8297    cx.simulate_keystroke("l");
 8298    cx.simulate_keystroke("o");
 8299    cx.assert_editor_state("editor.cloˇ");
 8300    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8301    cx.update_editor(|editor, cx| {
 8302        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8303    });
 8304    handle_completion_request(
 8305        &mut cx,
 8306        "editor.<clo|>",
 8307        vec!["close", "clobber"],
 8308        counter.clone(),
 8309    )
 8310    .await;
 8311    cx.condition(|editor, _| editor.context_menu_visible())
 8312        .await;
 8313    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8314
 8315    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8316        editor
 8317            .confirm_completion(&ConfirmCompletion::default(), cx)
 8318            .unwrap()
 8319    });
 8320    cx.assert_editor_state("editor.closeˇ");
 8321    handle_resolve_completion_request(&mut cx, None).await;
 8322    apply_additional_edits.await.unwrap();
 8323}
 8324
 8325#[gpui::test]
 8326async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8327    init_test(cx, |_| {});
 8328    let mut cx = EditorLspTestContext::new_rust(
 8329        lsp::ServerCapabilities {
 8330            completion_provider: Some(lsp::CompletionOptions {
 8331                trigger_characters: Some(vec![".".to_string()]),
 8332                ..Default::default()
 8333            }),
 8334            ..Default::default()
 8335        },
 8336        cx,
 8337    )
 8338    .await;
 8339    cx.lsp
 8340        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8341            Ok(Some(lsp::CompletionResponse::Array(vec![
 8342                lsp::CompletionItem {
 8343                    label: "first".into(),
 8344                    ..Default::default()
 8345                },
 8346                lsp::CompletionItem {
 8347                    label: "last".into(),
 8348                    ..Default::default()
 8349                },
 8350            ])))
 8351        });
 8352    cx.set_state("variableˇ");
 8353    cx.simulate_keystroke(".");
 8354    cx.executor().run_until_parked();
 8355
 8356    cx.update_editor(|editor, _| {
 8357        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8358            assert_eq!(
 8359                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8360                &["first", "last"]
 8361            );
 8362        } else {
 8363            panic!("expected completion menu to be open");
 8364        }
 8365    });
 8366
 8367    cx.update_editor(|editor, cx| {
 8368        editor.move_page_down(&MovePageDown::default(), cx);
 8369        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8370            assert!(
 8371                menu.selected_item == 1,
 8372                "expected PageDown to select the last item from the context menu"
 8373            );
 8374        } else {
 8375            panic!("expected completion menu to stay open after PageDown");
 8376        }
 8377    });
 8378
 8379    cx.update_editor(|editor, cx| {
 8380        editor.move_page_up(&MovePageUp::default(), cx);
 8381        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8382            assert!(
 8383                menu.selected_item == 0,
 8384                "expected PageUp to select the first item from the context menu"
 8385            );
 8386        } else {
 8387            panic!("expected completion menu to stay open after PageUp");
 8388        }
 8389    });
 8390}
 8391
 8392#[gpui::test]
 8393async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8394    init_test(cx, |_| {});
 8395    let mut cx = EditorLspTestContext::new_rust(
 8396        lsp::ServerCapabilities {
 8397            completion_provider: Some(lsp::CompletionOptions {
 8398                trigger_characters: Some(vec![".".to_string()]),
 8399                ..Default::default()
 8400            }),
 8401            ..Default::default()
 8402        },
 8403        cx,
 8404    )
 8405    .await;
 8406    cx.lsp
 8407        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8408            Ok(Some(lsp::CompletionResponse::Array(vec![
 8409                lsp::CompletionItem {
 8410                    label: "Range".into(),
 8411                    sort_text: Some("a".into()),
 8412                    ..Default::default()
 8413                },
 8414                lsp::CompletionItem {
 8415                    label: "r".into(),
 8416                    sort_text: Some("b".into()),
 8417                    ..Default::default()
 8418                },
 8419                lsp::CompletionItem {
 8420                    label: "ret".into(),
 8421                    sort_text: Some("c".into()),
 8422                    ..Default::default()
 8423                },
 8424                lsp::CompletionItem {
 8425                    label: "return".into(),
 8426                    sort_text: Some("d".into()),
 8427                    ..Default::default()
 8428                },
 8429                lsp::CompletionItem {
 8430                    label: "slice".into(),
 8431                    sort_text: Some("d".into()),
 8432                    ..Default::default()
 8433                },
 8434            ])))
 8435        });
 8436    cx.set_state("");
 8437    cx.executor().run_until_parked();
 8438    cx.update_editor(|editor, cx| {
 8439        editor.show_completions(
 8440            &ShowCompletions {
 8441                trigger: Some("r".into()),
 8442            },
 8443            cx,
 8444        );
 8445    });
 8446    cx.executor().run_until_parked();
 8447
 8448    cx.update_editor(|editor, _| {
 8449        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8450            assert_eq!(
 8451                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8452                &["r", "ret", "Range", "return"]
 8453            );
 8454        } else {
 8455            panic!("expected completion menu to be open");
 8456        }
 8457    });
 8458}
 8459
 8460#[gpui::test]
 8461async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8462    init_test(cx, |_| {});
 8463
 8464    let mut cx = EditorLspTestContext::new_rust(
 8465        lsp::ServerCapabilities {
 8466            completion_provider: Some(lsp::CompletionOptions {
 8467                trigger_characters: Some(vec![".".to_string()]),
 8468                resolve_provider: Some(true),
 8469                ..Default::default()
 8470            }),
 8471            ..Default::default()
 8472        },
 8473        cx,
 8474    )
 8475    .await;
 8476
 8477    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8478    cx.simulate_keystroke(".");
 8479    let completion_item = lsp::CompletionItem {
 8480        label: "Some".into(),
 8481        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8482        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8483        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8484            kind: lsp::MarkupKind::Markdown,
 8485            value: "```rust\nSome(2)\n```".to_string(),
 8486        })),
 8487        deprecated: Some(false),
 8488        sort_text: Some("Some".to_string()),
 8489        filter_text: Some("Some".to_string()),
 8490        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8491        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8492            range: lsp::Range {
 8493                start: lsp::Position {
 8494                    line: 0,
 8495                    character: 22,
 8496                },
 8497                end: lsp::Position {
 8498                    line: 0,
 8499                    character: 22,
 8500                },
 8501            },
 8502            new_text: "Some(2)".to_string(),
 8503        })),
 8504        additional_text_edits: Some(vec![lsp::TextEdit {
 8505            range: lsp::Range {
 8506                start: lsp::Position {
 8507                    line: 0,
 8508                    character: 20,
 8509                },
 8510                end: lsp::Position {
 8511                    line: 0,
 8512                    character: 22,
 8513                },
 8514            },
 8515            new_text: "".to_string(),
 8516        }]),
 8517        ..Default::default()
 8518    };
 8519
 8520    let closure_completion_item = completion_item.clone();
 8521    let counter = Arc::new(AtomicUsize::new(0));
 8522    let counter_clone = counter.clone();
 8523    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8524        let task_completion_item = closure_completion_item.clone();
 8525        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8526        async move {
 8527            Ok(Some(lsp::CompletionResponse::Array(vec![
 8528                task_completion_item,
 8529            ])))
 8530        }
 8531    });
 8532
 8533    cx.condition(|editor, _| editor.context_menu_visible())
 8534        .await;
 8535    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8536    assert!(request.next().await.is_some());
 8537    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8538
 8539    cx.simulate_keystroke("S");
 8540    cx.simulate_keystroke("o");
 8541    cx.simulate_keystroke("m");
 8542    cx.condition(|editor, _| editor.context_menu_visible())
 8543        .await;
 8544    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8545    assert!(request.next().await.is_some());
 8546    assert!(request.next().await.is_some());
 8547    assert!(request.next().await.is_some());
 8548    request.close();
 8549    assert!(request.next().await.is_none());
 8550    assert_eq!(
 8551        counter.load(atomic::Ordering::Acquire),
 8552        4,
 8553        "With the completions menu open, only one LSP request should happen per input"
 8554    );
 8555}
 8556
 8557#[gpui::test]
 8558async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8559    init_test(cx, |_| {});
 8560    let mut cx = EditorTestContext::new(cx).await;
 8561    let language = Arc::new(Language::new(
 8562        LanguageConfig {
 8563            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8564            ..Default::default()
 8565        },
 8566        Some(tree_sitter_rust::LANGUAGE.into()),
 8567    ));
 8568    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8569
 8570    // If multiple selections intersect a line, the line is only toggled once.
 8571    cx.set_state(indoc! {"
 8572        fn a() {
 8573            «//b();
 8574            ˇ»// «c();
 8575            //ˇ»  d();
 8576        }
 8577    "});
 8578
 8579    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8580
 8581    cx.assert_editor_state(indoc! {"
 8582        fn a() {
 8583            «b();
 8584            c();
 8585            ˇ» d();
 8586        }
 8587    "});
 8588
 8589    // The comment prefix is inserted at the same column for every line in a
 8590    // selection.
 8591    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8592
 8593    cx.assert_editor_state(indoc! {"
 8594        fn a() {
 8595            // «b();
 8596            // c();
 8597            ˇ»//  d();
 8598        }
 8599    "});
 8600
 8601    // If a selection ends at the beginning of a line, that line is not toggled.
 8602    cx.set_selections_state(indoc! {"
 8603        fn a() {
 8604            // b();
 8605            «// c();
 8606        ˇ»    //  d();
 8607        }
 8608    "});
 8609
 8610    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8611
 8612    cx.assert_editor_state(indoc! {"
 8613        fn a() {
 8614            // b();
 8615            «c();
 8616        ˇ»    //  d();
 8617        }
 8618    "});
 8619
 8620    // If a selection span a single line and is empty, the line is toggled.
 8621    cx.set_state(indoc! {"
 8622        fn a() {
 8623            a();
 8624            b();
 8625        ˇ
 8626        }
 8627    "});
 8628
 8629    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8630
 8631    cx.assert_editor_state(indoc! {"
 8632        fn a() {
 8633            a();
 8634            b();
 8635        //•ˇ
 8636        }
 8637    "});
 8638
 8639    // If a selection span multiple lines, empty lines are not toggled.
 8640    cx.set_state(indoc! {"
 8641        fn a() {
 8642            «a();
 8643
 8644            c();ˇ»
 8645        }
 8646    "});
 8647
 8648    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8649
 8650    cx.assert_editor_state(indoc! {"
 8651        fn a() {
 8652            // «a();
 8653
 8654            // c();ˇ»
 8655        }
 8656    "});
 8657
 8658    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8659    cx.set_state(indoc! {"
 8660        fn a() {
 8661            «// a();
 8662            /// b();
 8663            //! c();ˇ»
 8664        }
 8665    "});
 8666
 8667    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8668
 8669    cx.assert_editor_state(indoc! {"
 8670        fn a() {
 8671            «a();
 8672            b();
 8673            c();ˇ»
 8674        }
 8675    "});
 8676}
 8677
 8678#[gpui::test]
 8679async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8680    init_test(cx, |_| {});
 8681    let mut cx = EditorTestContext::new(cx).await;
 8682    let language = Arc::new(Language::new(
 8683        LanguageConfig {
 8684            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8685            ..Default::default()
 8686        },
 8687        Some(tree_sitter_rust::LANGUAGE.into()),
 8688    ));
 8689    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8690
 8691    let toggle_comments = &ToggleComments {
 8692        advance_downwards: false,
 8693        ignore_indent: true,
 8694    };
 8695
 8696    // If multiple selections intersect a line, the line is only toggled once.
 8697    cx.set_state(indoc! {"
 8698        fn a() {
 8699        //    «b();
 8700        //    c();
 8701        //    ˇ» d();
 8702        }
 8703    "});
 8704
 8705    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8706
 8707    cx.assert_editor_state(indoc! {"
 8708        fn a() {
 8709            «b();
 8710            c();
 8711            ˇ» d();
 8712        }
 8713    "});
 8714
 8715    // The comment prefix is inserted at the beginning of each line
 8716    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8717
 8718    cx.assert_editor_state(indoc! {"
 8719        fn a() {
 8720        //    «b();
 8721        //    c();
 8722        //    ˇ» d();
 8723        }
 8724    "});
 8725
 8726    // If a selection ends at the beginning of a line, that line is not toggled.
 8727    cx.set_selections_state(indoc! {"
 8728        fn a() {
 8729        //    b();
 8730        //    «c();
 8731        ˇ»//     d();
 8732        }
 8733    "});
 8734
 8735    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8736
 8737    cx.assert_editor_state(indoc! {"
 8738        fn a() {
 8739        //    b();
 8740            «c();
 8741        ˇ»//     d();
 8742        }
 8743    "});
 8744
 8745    // If a selection span a single line and is empty, the line is toggled.
 8746    cx.set_state(indoc! {"
 8747        fn a() {
 8748            a();
 8749            b();
 8750        ˇ
 8751        }
 8752    "});
 8753
 8754    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8755
 8756    cx.assert_editor_state(indoc! {"
 8757        fn a() {
 8758            a();
 8759            b();
 8760        //ˇ
 8761        }
 8762    "});
 8763
 8764    // If a selection span multiple lines, empty lines are not toggled.
 8765    cx.set_state(indoc! {"
 8766        fn a() {
 8767            «a();
 8768
 8769            c();ˇ»
 8770        }
 8771    "});
 8772
 8773    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8774
 8775    cx.assert_editor_state(indoc! {"
 8776        fn a() {
 8777        //    «a();
 8778
 8779        //    c();ˇ»
 8780        }
 8781    "});
 8782
 8783    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8784    cx.set_state(indoc! {"
 8785        fn a() {
 8786        //    «a();
 8787        ///    b();
 8788        //!    c();ˇ»
 8789        }
 8790    "});
 8791
 8792    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8793
 8794    cx.assert_editor_state(indoc! {"
 8795        fn a() {
 8796            «a();
 8797            b();
 8798            c();ˇ»
 8799        }
 8800    "});
 8801}
 8802
 8803#[gpui::test]
 8804async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8805    init_test(cx, |_| {});
 8806
 8807    let language = Arc::new(Language::new(
 8808        LanguageConfig {
 8809            line_comments: vec!["// ".into()],
 8810            ..Default::default()
 8811        },
 8812        Some(tree_sitter_rust::LANGUAGE.into()),
 8813    ));
 8814
 8815    let mut cx = EditorTestContext::new(cx).await;
 8816
 8817    cx.language_registry().add(language.clone());
 8818    cx.update_buffer(|buffer, cx| {
 8819        buffer.set_language(Some(language), cx);
 8820    });
 8821
 8822    let toggle_comments = &ToggleComments {
 8823        advance_downwards: true,
 8824        ignore_indent: false,
 8825    };
 8826
 8827    // Single cursor on one line -> advance
 8828    // Cursor moves horizontally 3 characters as well on non-blank line
 8829    cx.set_state(indoc!(
 8830        "fn a() {
 8831             ˇdog();
 8832             cat();
 8833        }"
 8834    ));
 8835    cx.update_editor(|editor, cx| {
 8836        editor.toggle_comments(toggle_comments, cx);
 8837    });
 8838    cx.assert_editor_state(indoc!(
 8839        "fn a() {
 8840             // dog();
 8841             catˇ();
 8842        }"
 8843    ));
 8844
 8845    // Single selection on one line -> don't advance
 8846    cx.set_state(indoc!(
 8847        "fn a() {
 8848             «dog()ˇ»;
 8849             cat();
 8850        }"
 8851    ));
 8852    cx.update_editor(|editor, cx| {
 8853        editor.toggle_comments(toggle_comments, cx);
 8854    });
 8855    cx.assert_editor_state(indoc!(
 8856        "fn a() {
 8857             // «dog()ˇ»;
 8858             cat();
 8859        }"
 8860    ));
 8861
 8862    // Multiple cursors on one line -> advance
 8863    cx.set_state(indoc!(
 8864        "fn a() {
 8865             ˇdˇog();
 8866             cat();
 8867        }"
 8868    ));
 8869    cx.update_editor(|editor, cx| {
 8870        editor.toggle_comments(toggle_comments, cx);
 8871    });
 8872    cx.assert_editor_state(indoc!(
 8873        "fn a() {
 8874             // dog();
 8875             catˇ(ˇ);
 8876        }"
 8877    ));
 8878
 8879    // Multiple cursors on one line, with selection -> don't advance
 8880    cx.set_state(indoc!(
 8881        "fn a() {
 8882             ˇdˇog«()ˇ»;
 8883             cat();
 8884        }"
 8885    ));
 8886    cx.update_editor(|editor, cx| {
 8887        editor.toggle_comments(toggle_comments, cx);
 8888    });
 8889    cx.assert_editor_state(indoc!(
 8890        "fn a() {
 8891             // ˇdˇog«()ˇ»;
 8892             cat();
 8893        }"
 8894    ));
 8895
 8896    // Single cursor on one line -> advance
 8897    // Cursor moves to column 0 on blank line
 8898    cx.set_state(indoc!(
 8899        "fn a() {
 8900             ˇdog();
 8901
 8902             cat();
 8903        }"
 8904    ));
 8905    cx.update_editor(|editor, cx| {
 8906        editor.toggle_comments(toggle_comments, cx);
 8907    });
 8908    cx.assert_editor_state(indoc!(
 8909        "fn a() {
 8910             // dog();
 8911        ˇ
 8912             cat();
 8913        }"
 8914    ));
 8915
 8916    // Single cursor on one line -> advance
 8917    // Cursor starts and ends at column 0
 8918    cx.set_state(indoc!(
 8919        "fn a() {
 8920         ˇ    dog();
 8921             cat();
 8922        }"
 8923    ));
 8924    cx.update_editor(|editor, cx| {
 8925        editor.toggle_comments(toggle_comments, cx);
 8926    });
 8927    cx.assert_editor_state(indoc!(
 8928        "fn a() {
 8929             // dog();
 8930         ˇ    cat();
 8931        }"
 8932    ));
 8933}
 8934
 8935#[gpui::test]
 8936async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8937    init_test(cx, |_| {});
 8938
 8939    let mut cx = EditorTestContext::new(cx).await;
 8940
 8941    let html_language = Arc::new(
 8942        Language::new(
 8943            LanguageConfig {
 8944                name: "HTML".into(),
 8945                block_comment: Some(("<!-- ".into(), " -->".into())),
 8946                ..Default::default()
 8947            },
 8948            Some(tree_sitter_html::language()),
 8949        )
 8950        .with_injection_query(
 8951            r#"
 8952            (script_element
 8953                (raw_text) @content
 8954                (#set! "language" "javascript"))
 8955            "#,
 8956        )
 8957        .unwrap(),
 8958    );
 8959
 8960    let javascript_language = Arc::new(Language::new(
 8961        LanguageConfig {
 8962            name: "JavaScript".into(),
 8963            line_comments: vec!["// ".into()],
 8964            ..Default::default()
 8965        },
 8966        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8967    ));
 8968
 8969    cx.language_registry().add(html_language.clone());
 8970    cx.language_registry().add(javascript_language.clone());
 8971    cx.update_buffer(|buffer, cx| {
 8972        buffer.set_language(Some(html_language), cx);
 8973    });
 8974
 8975    // Toggle comments for empty selections
 8976    cx.set_state(
 8977        &r#"
 8978            <p>A</p>ˇ
 8979            <p>B</p>ˇ
 8980            <p>C</p>ˇ
 8981        "#
 8982        .unindent(),
 8983    );
 8984    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8985    cx.assert_editor_state(
 8986        &r#"
 8987            <!-- <p>A</p>ˇ -->
 8988            <!-- <p>B</p>ˇ -->
 8989            <!-- <p>C</p>ˇ -->
 8990        "#
 8991        .unindent(),
 8992    );
 8993    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8994    cx.assert_editor_state(
 8995        &r#"
 8996            <p>A</p>ˇ
 8997            <p>B</p>ˇ
 8998            <p>C</p>ˇ
 8999        "#
 9000        .unindent(),
 9001    );
 9002
 9003    // Toggle comments for mixture of empty and non-empty selections, where
 9004    // multiple selections occupy a given line.
 9005    cx.set_state(
 9006        &r#"
 9007            <p>A«</p>
 9008            <p>ˇ»B</p>ˇ
 9009            <p>C«</p>
 9010            <p>ˇ»D</p>ˇ
 9011        "#
 9012        .unindent(),
 9013    );
 9014
 9015    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9016    cx.assert_editor_state(
 9017        &r#"
 9018            <!-- <p>A«</p>
 9019            <p>ˇ»B</p>ˇ -->
 9020            <!-- <p>C«</p>
 9021            <p>ˇ»D</p>ˇ -->
 9022        "#
 9023        .unindent(),
 9024    );
 9025    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9026    cx.assert_editor_state(
 9027        &r#"
 9028            <p>A«</p>
 9029            <p>ˇ»B</p>ˇ
 9030            <p>C«</p>
 9031            <p>ˇ»D</p>ˇ
 9032        "#
 9033        .unindent(),
 9034    );
 9035
 9036    // Toggle comments when different languages are active for different
 9037    // selections.
 9038    cx.set_state(
 9039        &r#"
 9040            ˇ<script>
 9041                ˇvar x = new Y();
 9042            ˇ</script>
 9043        "#
 9044        .unindent(),
 9045    );
 9046    cx.executor().run_until_parked();
 9047    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9048    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9049    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9050    cx.assert_editor_state(
 9051        &r#"
 9052            <!-- ˇ<script> -->
 9053                // ˇvar x = new Y();
 9054            // ˇ</script>
 9055        "#
 9056        .unindent(),
 9057    );
 9058}
 9059
 9060#[gpui::test]
 9061fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9062    init_test(cx, |_| {});
 9063
 9064    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9065    let multibuffer = cx.new_model(|cx| {
 9066        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9067        multibuffer.push_excerpts(
 9068            buffer.clone(),
 9069            [
 9070                ExcerptRange {
 9071                    context: Point::new(0, 0)..Point::new(0, 4),
 9072                    primary: None,
 9073                },
 9074                ExcerptRange {
 9075                    context: Point::new(1, 0)..Point::new(1, 4),
 9076                    primary: None,
 9077                },
 9078            ],
 9079            cx,
 9080        );
 9081        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9082        multibuffer
 9083    });
 9084
 9085    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9086    view.update(cx, |view, cx| {
 9087        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9088        view.change_selections(None, cx, |s| {
 9089            s.select_ranges([
 9090                Point::new(0, 0)..Point::new(0, 0),
 9091                Point::new(1, 0)..Point::new(1, 0),
 9092            ])
 9093        });
 9094
 9095        view.handle_input("X", cx);
 9096        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9097        assert_eq!(
 9098            view.selections.ranges(cx),
 9099            [
 9100                Point::new(0, 1)..Point::new(0, 1),
 9101                Point::new(1, 1)..Point::new(1, 1),
 9102            ]
 9103        );
 9104
 9105        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9106        view.change_selections(None, cx, |s| {
 9107            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9108        });
 9109        view.backspace(&Default::default(), cx);
 9110        assert_eq!(view.text(cx), "Xa\nbbb");
 9111        assert_eq!(
 9112            view.selections.ranges(cx),
 9113            [Point::new(1, 0)..Point::new(1, 0)]
 9114        );
 9115
 9116        view.change_selections(None, cx, |s| {
 9117            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9118        });
 9119        view.backspace(&Default::default(), cx);
 9120        assert_eq!(view.text(cx), "X\nbb");
 9121        assert_eq!(
 9122            view.selections.ranges(cx),
 9123            [Point::new(0, 1)..Point::new(0, 1)]
 9124        );
 9125    });
 9126}
 9127
 9128#[gpui::test]
 9129fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9130    init_test(cx, |_| {});
 9131
 9132    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9133    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9134        indoc! {"
 9135            [aaaa
 9136            (bbbb]
 9137            cccc)",
 9138        },
 9139        markers.clone(),
 9140    );
 9141    let excerpt_ranges = markers.into_iter().map(|marker| {
 9142        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9143        ExcerptRange {
 9144            context,
 9145            primary: None,
 9146        }
 9147    });
 9148    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9149    let multibuffer = cx.new_model(|cx| {
 9150        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9151        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9152        multibuffer
 9153    });
 9154
 9155    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9156    view.update(cx, |view, cx| {
 9157        let (expected_text, selection_ranges) = marked_text_ranges(
 9158            indoc! {"
 9159                aaaa
 9160                bˇbbb
 9161                bˇbbˇb
 9162                cccc"
 9163            },
 9164            true,
 9165        );
 9166        assert_eq!(view.text(cx), expected_text);
 9167        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9168
 9169        view.handle_input("X", cx);
 9170
 9171        let (expected_text, expected_selections) = marked_text_ranges(
 9172            indoc! {"
 9173                aaaa
 9174                bXˇbbXb
 9175                bXˇbbXˇb
 9176                cccc"
 9177            },
 9178            false,
 9179        );
 9180        assert_eq!(view.text(cx), expected_text);
 9181        assert_eq!(view.selections.ranges(cx), expected_selections);
 9182
 9183        view.newline(&Newline, cx);
 9184        let (expected_text, expected_selections) = marked_text_ranges(
 9185            indoc! {"
 9186                aaaa
 9187                bX
 9188                ˇbbX
 9189                b
 9190                bX
 9191                ˇbbX
 9192                ˇb
 9193                cccc"
 9194            },
 9195            false,
 9196        );
 9197        assert_eq!(view.text(cx), expected_text);
 9198        assert_eq!(view.selections.ranges(cx), expected_selections);
 9199    });
 9200}
 9201
 9202#[gpui::test]
 9203fn test_refresh_selections(cx: &mut TestAppContext) {
 9204    init_test(cx, |_| {});
 9205
 9206    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9207    let mut excerpt1_id = None;
 9208    let multibuffer = cx.new_model(|cx| {
 9209        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9210        excerpt1_id = multibuffer
 9211            .push_excerpts(
 9212                buffer.clone(),
 9213                [
 9214                    ExcerptRange {
 9215                        context: Point::new(0, 0)..Point::new(1, 4),
 9216                        primary: None,
 9217                    },
 9218                    ExcerptRange {
 9219                        context: Point::new(1, 0)..Point::new(2, 4),
 9220                        primary: None,
 9221                    },
 9222                ],
 9223                cx,
 9224            )
 9225            .into_iter()
 9226            .next();
 9227        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9228        multibuffer
 9229    });
 9230
 9231    let editor = cx.add_window(|cx| {
 9232        let mut editor = build_editor(multibuffer.clone(), cx);
 9233        let snapshot = editor.snapshot(cx);
 9234        editor.change_selections(None, cx, |s| {
 9235            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9236        });
 9237        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9238        assert_eq!(
 9239            editor.selections.ranges(cx),
 9240            [
 9241                Point::new(1, 3)..Point::new(1, 3),
 9242                Point::new(2, 1)..Point::new(2, 1),
 9243            ]
 9244        );
 9245        editor
 9246    });
 9247
 9248    // Refreshing selections is a no-op when excerpts haven't changed.
 9249    _ = editor.update(cx, |editor, cx| {
 9250        editor.change_selections(None, cx, |s| s.refresh());
 9251        assert_eq!(
 9252            editor.selections.ranges(cx),
 9253            [
 9254                Point::new(1, 3)..Point::new(1, 3),
 9255                Point::new(2, 1)..Point::new(2, 1),
 9256            ]
 9257        );
 9258    });
 9259
 9260    multibuffer.update(cx, |multibuffer, cx| {
 9261        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9262    });
 9263    _ = editor.update(cx, |editor, cx| {
 9264        // Removing an excerpt causes the first selection to become degenerate.
 9265        assert_eq!(
 9266            editor.selections.ranges(cx),
 9267            [
 9268                Point::new(0, 0)..Point::new(0, 0),
 9269                Point::new(0, 1)..Point::new(0, 1)
 9270            ]
 9271        );
 9272
 9273        // Refreshing selections will relocate the first selection to the original buffer
 9274        // location.
 9275        editor.change_selections(None, cx, |s| s.refresh());
 9276        assert_eq!(
 9277            editor.selections.ranges(cx),
 9278            [
 9279                Point::new(0, 1)..Point::new(0, 1),
 9280                Point::new(0, 3)..Point::new(0, 3)
 9281            ]
 9282        );
 9283        assert!(editor.selections.pending_anchor().is_some());
 9284    });
 9285}
 9286
 9287#[gpui::test]
 9288fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9289    init_test(cx, |_| {});
 9290
 9291    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9292    let mut excerpt1_id = None;
 9293    let multibuffer = cx.new_model(|cx| {
 9294        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9295        excerpt1_id = multibuffer
 9296            .push_excerpts(
 9297                buffer.clone(),
 9298                [
 9299                    ExcerptRange {
 9300                        context: Point::new(0, 0)..Point::new(1, 4),
 9301                        primary: None,
 9302                    },
 9303                    ExcerptRange {
 9304                        context: Point::new(1, 0)..Point::new(2, 4),
 9305                        primary: None,
 9306                    },
 9307                ],
 9308                cx,
 9309            )
 9310            .into_iter()
 9311            .next();
 9312        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9313        multibuffer
 9314    });
 9315
 9316    let editor = cx.add_window(|cx| {
 9317        let mut editor = build_editor(multibuffer.clone(), cx);
 9318        let snapshot = editor.snapshot(cx);
 9319        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9320        assert_eq!(
 9321            editor.selections.ranges(cx),
 9322            [Point::new(1, 3)..Point::new(1, 3)]
 9323        );
 9324        editor
 9325    });
 9326
 9327    multibuffer.update(cx, |multibuffer, cx| {
 9328        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9329    });
 9330    _ = editor.update(cx, |editor, cx| {
 9331        assert_eq!(
 9332            editor.selections.ranges(cx),
 9333            [Point::new(0, 0)..Point::new(0, 0)]
 9334        );
 9335
 9336        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9337        editor.change_selections(None, cx, |s| s.refresh());
 9338        assert_eq!(
 9339            editor.selections.ranges(cx),
 9340            [Point::new(0, 3)..Point::new(0, 3)]
 9341        );
 9342        assert!(editor.selections.pending_anchor().is_some());
 9343    });
 9344}
 9345
 9346#[gpui::test]
 9347async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9348    init_test(cx, |_| {});
 9349
 9350    let language = Arc::new(
 9351        Language::new(
 9352            LanguageConfig {
 9353                brackets: BracketPairConfig {
 9354                    pairs: vec![
 9355                        BracketPair {
 9356                            start: "{".to_string(),
 9357                            end: "}".to_string(),
 9358                            close: true,
 9359                            surround: true,
 9360                            newline: true,
 9361                        },
 9362                        BracketPair {
 9363                            start: "/* ".to_string(),
 9364                            end: " */".to_string(),
 9365                            close: true,
 9366                            surround: true,
 9367                            newline: true,
 9368                        },
 9369                    ],
 9370                    ..Default::default()
 9371                },
 9372                ..Default::default()
 9373            },
 9374            Some(tree_sitter_rust::LANGUAGE.into()),
 9375        )
 9376        .with_indents_query("")
 9377        .unwrap(),
 9378    );
 9379
 9380    let text = concat!(
 9381        "{   }\n",     //
 9382        "  x\n",       //
 9383        "  /*   */\n", //
 9384        "x\n",         //
 9385        "{{} }\n",     //
 9386    );
 9387
 9388    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9389    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9390    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9391    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9392        .await;
 9393
 9394    view.update(cx, |view, cx| {
 9395        view.change_selections(None, cx, |s| {
 9396            s.select_display_ranges([
 9397                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9398                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9399                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9400            ])
 9401        });
 9402        view.newline(&Newline, cx);
 9403
 9404        assert_eq!(
 9405            view.buffer().read(cx).read(cx).text(),
 9406            concat!(
 9407                "{ \n",    // Suppress rustfmt
 9408                "\n",      //
 9409                "}\n",     //
 9410                "  x\n",   //
 9411                "  /* \n", //
 9412                "  \n",    //
 9413                "  */\n",  //
 9414                "x\n",     //
 9415                "{{} \n",  //
 9416                "}\n",     //
 9417            )
 9418        );
 9419    });
 9420}
 9421
 9422#[gpui::test]
 9423fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9424    init_test(cx, |_| {});
 9425
 9426    let editor = cx.add_window(|cx| {
 9427        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9428        build_editor(buffer.clone(), cx)
 9429    });
 9430
 9431    _ = editor.update(cx, |editor, cx| {
 9432        struct Type1;
 9433        struct Type2;
 9434
 9435        let buffer = editor.buffer.read(cx).snapshot(cx);
 9436
 9437        let anchor_range =
 9438            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9439
 9440        editor.highlight_background::<Type1>(
 9441            &[
 9442                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9443                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9444                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9445                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9446            ],
 9447            |_| Hsla::red(),
 9448            cx,
 9449        );
 9450        editor.highlight_background::<Type2>(
 9451            &[
 9452                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9453                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9454                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9455                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9456            ],
 9457            |_| Hsla::green(),
 9458            cx,
 9459        );
 9460
 9461        let snapshot = editor.snapshot(cx);
 9462        let mut highlighted_ranges = editor.background_highlights_in_range(
 9463            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9464            &snapshot,
 9465            cx.theme().colors(),
 9466        );
 9467        // Enforce a consistent ordering based on color without relying on the ordering of the
 9468        // highlight's `TypeId` which is non-executor.
 9469        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9470        assert_eq!(
 9471            highlighted_ranges,
 9472            &[
 9473                (
 9474                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9475                    Hsla::red(),
 9476                ),
 9477                (
 9478                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9479                    Hsla::red(),
 9480                ),
 9481                (
 9482                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9483                    Hsla::green(),
 9484                ),
 9485                (
 9486                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9487                    Hsla::green(),
 9488                ),
 9489            ]
 9490        );
 9491        assert_eq!(
 9492            editor.background_highlights_in_range(
 9493                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9494                &snapshot,
 9495                cx.theme().colors(),
 9496            ),
 9497            &[(
 9498                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9499                Hsla::red(),
 9500            )]
 9501        );
 9502    });
 9503}
 9504
 9505#[gpui::test]
 9506async fn test_following(cx: &mut gpui::TestAppContext) {
 9507    init_test(cx, |_| {});
 9508
 9509    let fs = FakeFs::new(cx.executor());
 9510    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9511
 9512    let buffer = project.update(cx, |project, cx| {
 9513        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9514        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9515    });
 9516    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9517    let follower = cx.update(|cx| {
 9518        cx.open_window(
 9519            WindowOptions {
 9520                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9521                    gpui::Point::new(px(0.), px(0.)),
 9522                    gpui::Point::new(px(10.), px(80.)),
 9523                ))),
 9524                ..Default::default()
 9525            },
 9526            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9527        )
 9528        .unwrap()
 9529    });
 9530
 9531    let is_still_following = Rc::new(RefCell::new(true));
 9532    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9533    let pending_update = Rc::new(RefCell::new(None));
 9534    _ = follower.update(cx, {
 9535        let update = pending_update.clone();
 9536        let is_still_following = is_still_following.clone();
 9537        let follower_edit_event_count = follower_edit_event_count.clone();
 9538        |_, cx| {
 9539            cx.subscribe(
 9540                &leader.root_view(cx).unwrap(),
 9541                move |_, leader, event, cx| {
 9542                    leader
 9543                        .read(cx)
 9544                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9545                },
 9546            )
 9547            .detach();
 9548
 9549            cx.subscribe(
 9550                &follower.root_view(cx).unwrap(),
 9551                move |_, _, event: &EditorEvent, _cx| {
 9552                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9553                        *is_still_following.borrow_mut() = false;
 9554                    }
 9555
 9556                    if let EditorEvent::BufferEdited = event {
 9557                        *follower_edit_event_count.borrow_mut() += 1;
 9558                    }
 9559                },
 9560            )
 9561            .detach();
 9562        }
 9563    });
 9564
 9565    // Update the selections only
 9566    _ = leader.update(cx, |leader, cx| {
 9567        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9568    });
 9569    follower
 9570        .update(cx, |follower, cx| {
 9571            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9572        })
 9573        .unwrap()
 9574        .await
 9575        .unwrap();
 9576    _ = follower.update(cx, |follower, cx| {
 9577        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9578    });
 9579    assert!(*is_still_following.borrow());
 9580    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9581
 9582    // Update the scroll position only
 9583    _ = leader.update(cx, |leader, cx| {
 9584        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9585    });
 9586    follower
 9587        .update(cx, |follower, cx| {
 9588            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9589        })
 9590        .unwrap()
 9591        .await
 9592        .unwrap();
 9593    assert_eq!(
 9594        follower
 9595            .update(cx, |follower, cx| follower.scroll_position(cx))
 9596            .unwrap(),
 9597        gpui::Point::new(1.5, 3.5)
 9598    );
 9599    assert!(*is_still_following.borrow());
 9600    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9601
 9602    // Update the selections and scroll position. The follower's scroll position is updated
 9603    // via autoscroll, not via the leader's exact scroll position.
 9604    _ = leader.update(cx, |leader, cx| {
 9605        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9606        leader.request_autoscroll(Autoscroll::newest(), cx);
 9607        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9608    });
 9609    follower
 9610        .update(cx, |follower, cx| {
 9611            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9612        })
 9613        .unwrap()
 9614        .await
 9615        .unwrap();
 9616    _ = follower.update(cx, |follower, cx| {
 9617        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9618        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9619    });
 9620    assert!(*is_still_following.borrow());
 9621
 9622    // Creating a pending selection that precedes another selection
 9623    _ = leader.update(cx, |leader, cx| {
 9624        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9625        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9626    });
 9627    follower
 9628        .update(cx, |follower, cx| {
 9629            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9630        })
 9631        .unwrap()
 9632        .await
 9633        .unwrap();
 9634    _ = follower.update(cx, |follower, cx| {
 9635        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9636    });
 9637    assert!(*is_still_following.borrow());
 9638
 9639    // Extend the pending selection so that it surrounds another selection
 9640    _ = leader.update(cx, |leader, cx| {
 9641        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9642    });
 9643    follower
 9644        .update(cx, |follower, cx| {
 9645            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9646        })
 9647        .unwrap()
 9648        .await
 9649        .unwrap();
 9650    _ = follower.update(cx, |follower, cx| {
 9651        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9652    });
 9653
 9654    // Scrolling locally breaks the follow
 9655    _ = follower.update(cx, |follower, cx| {
 9656        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9657        follower.set_scroll_anchor(
 9658            ScrollAnchor {
 9659                anchor: top_anchor,
 9660                offset: gpui::Point::new(0.0, 0.5),
 9661            },
 9662            cx,
 9663        );
 9664    });
 9665    assert!(!(*is_still_following.borrow()));
 9666}
 9667
 9668#[gpui::test]
 9669async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9670    init_test(cx, |_| {});
 9671
 9672    let fs = FakeFs::new(cx.executor());
 9673    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9674    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9675    let pane = workspace
 9676        .update(cx, |workspace, _| workspace.active_pane().clone())
 9677        .unwrap();
 9678
 9679    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9680
 9681    let leader = pane.update(cx, |_, cx| {
 9682        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9683        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9684    });
 9685
 9686    // Start following the editor when it has no excerpts.
 9687    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9688    let follower_1 = cx
 9689        .update_window(*workspace.deref(), |_, cx| {
 9690            Editor::from_state_proto(
 9691                workspace.root_view(cx).unwrap(),
 9692                ViewId {
 9693                    creator: Default::default(),
 9694                    id: 0,
 9695                },
 9696                &mut state_message,
 9697                cx,
 9698            )
 9699        })
 9700        .unwrap()
 9701        .unwrap()
 9702        .await
 9703        .unwrap();
 9704
 9705    let update_message = Rc::new(RefCell::new(None));
 9706    follower_1.update(cx, {
 9707        let update = update_message.clone();
 9708        |_, cx| {
 9709            cx.subscribe(&leader, move |_, leader, event, cx| {
 9710                leader
 9711                    .read(cx)
 9712                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9713            })
 9714            .detach();
 9715        }
 9716    });
 9717
 9718    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9719        (
 9720            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9721            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9722        )
 9723    });
 9724
 9725    // Insert some excerpts.
 9726    leader.update(cx, |leader, cx| {
 9727        leader.buffer.update(cx, |multibuffer, cx| {
 9728            let excerpt_ids = multibuffer.push_excerpts(
 9729                buffer_1.clone(),
 9730                [
 9731                    ExcerptRange {
 9732                        context: 1..6,
 9733                        primary: None,
 9734                    },
 9735                    ExcerptRange {
 9736                        context: 12..15,
 9737                        primary: None,
 9738                    },
 9739                    ExcerptRange {
 9740                        context: 0..3,
 9741                        primary: None,
 9742                    },
 9743                ],
 9744                cx,
 9745            );
 9746            multibuffer.insert_excerpts_after(
 9747                excerpt_ids[0],
 9748                buffer_2.clone(),
 9749                [
 9750                    ExcerptRange {
 9751                        context: 8..12,
 9752                        primary: None,
 9753                    },
 9754                    ExcerptRange {
 9755                        context: 0..6,
 9756                        primary: None,
 9757                    },
 9758                ],
 9759                cx,
 9760            );
 9761        });
 9762    });
 9763
 9764    // Apply the update of adding the excerpts.
 9765    follower_1
 9766        .update(cx, |follower, cx| {
 9767            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9768        })
 9769        .await
 9770        .unwrap();
 9771    assert_eq!(
 9772        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9773        leader.update(cx, |editor, cx| editor.text(cx))
 9774    );
 9775    update_message.borrow_mut().take();
 9776
 9777    // Start following separately after it already has excerpts.
 9778    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9779    let follower_2 = cx
 9780        .update_window(*workspace.deref(), |_, cx| {
 9781            Editor::from_state_proto(
 9782                workspace.root_view(cx).unwrap().clone(),
 9783                ViewId {
 9784                    creator: Default::default(),
 9785                    id: 0,
 9786                },
 9787                &mut state_message,
 9788                cx,
 9789            )
 9790        })
 9791        .unwrap()
 9792        .unwrap()
 9793        .await
 9794        .unwrap();
 9795    assert_eq!(
 9796        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9797        leader.update(cx, |editor, cx| editor.text(cx))
 9798    );
 9799
 9800    // Remove some excerpts.
 9801    leader.update(cx, |leader, cx| {
 9802        leader.buffer.update(cx, |multibuffer, cx| {
 9803            let excerpt_ids = multibuffer.excerpt_ids();
 9804            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9805            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9806        });
 9807    });
 9808
 9809    // Apply the update of removing the excerpts.
 9810    follower_1
 9811        .update(cx, |follower, cx| {
 9812            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9813        })
 9814        .await
 9815        .unwrap();
 9816    follower_2
 9817        .update(cx, |follower, cx| {
 9818            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9819        })
 9820        .await
 9821        .unwrap();
 9822    update_message.borrow_mut().take();
 9823    assert_eq!(
 9824        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9825        leader.update(cx, |editor, cx| editor.text(cx))
 9826    );
 9827}
 9828
 9829#[gpui::test]
 9830async fn go_to_prev_overlapping_diagnostic(
 9831    executor: BackgroundExecutor,
 9832    cx: &mut gpui::TestAppContext,
 9833) {
 9834    init_test(cx, |_| {});
 9835
 9836    let mut cx = EditorTestContext::new(cx).await;
 9837    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9838
 9839    cx.set_state(indoc! {"
 9840        ˇfn func(abc def: i32) -> u32 {
 9841        }
 9842    "});
 9843
 9844    cx.update(|cx| {
 9845        project.update(cx, |project, cx| {
 9846            project
 9847                .update_diagnostics(
 9848                    LanguageServerId(0),
 9849                    lsp::PublishDiagnosticsParams {
 9850                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9851                        version: None,
 9852                        diagnostics: vec![
 9853                            lsp::Diagnostic {
 9854                                range: lsp::Range::new(
 9855                                    lsp::Position::new(0, 11),
 9856                                    lsp::Position::new(0, 12),
 9857                                ),
 9858                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9859                                ..Default::default()
 9860                            },
 9861                            lsp::Diagnostic {
 9862                                range: lsp::Range::new(
 9863                                    lsp::Position::new(0, 12),
 9864                                    lsp::Position::new(0, 15),
 9865                                ),
 9866                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9867                                ..Default::default()
 9868                            },
 9869                            lsp::Diagnostic {
 9870                                range: lsp::Range::new(
 9871                                    lsp::Position::new(0, 25),
 9872                                    lsp::Position::new(0, 28),
 9873                                ),
 9874                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9875                                ..Default::default()
 9876                            },
 9877                        ],
 9878                    },
 9879                    &[],
 9880                    cx,
 9881                )
 9882                .unwrap()
 9883        });
 9884    });
 9885
 9886    executor.run_until_parked();
 9887
 9888    cx.update_editor(|editor, cx| {
 9889        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9890    });
 9891
 9892    cx.assert_editor_state(indoc! {"
 9893        fn func(abc def: i32) -> ˇu32 {
 9894        }
 9895    "});
 9896
 9897    cx.update_editor(|editor, cx| {
 9898        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9899    });
 9900
 9901    cx.assert_editor_state(indoc! {"
 9902        fn func(abc ˇdef: i32) -> u32 {
 9903        }
 9904    "});
 9905
 9906    cx.update_editor(|editor, cx| {
 9907        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9908    });
 9909
 9910    cx.assert_editor_state(indoc! {"
 9911        fn func(abcˇ def: i32) -> u32 {
 9912        }
 9913    "});
 9914
 9915    cx.update_editor(|editor, cx| {
 9916        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9917    });
 9918
 9919    cx.assert_editor_state(indoc! {"
 9920        fn func(abc def: i32) -> ˇu32 {
 9921        }
 9922    "});
 9923}
 9924
 9925#[gpui::test]
 9926async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9927    init_test(cx, |_| {});
 9928
 9929    let mut cx = EditorTestContext::new(cx).await;
 9930
 9931    cx.set_state(indoc! {"
 9932        fn func(abˇc def: i32) -> u32 {
 9933        }
 9934    "});
 9935    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9936
 9937    cx.update(|cx| {
 9938        project.update(cx, |project, cx| {
 9939            project.update_diagnostics(
 9940                LanguageServerId(0),
 9941                lsp::PublishDiagnosticsParams {
 9942                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9943                    version: None,
 9944                    diagnostics: vec![lsp::Diagnostic {
 9945                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9946                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9947                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9948                        ..Default::default()
 9949                    }],
 9950                },
 9951                &[],
 9952                cx,
 9953            )
 9954        })
 9955    }).unwrap();
 9956    cx.run_until_parked();
 9957    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9958    cx.run_until_parked();
 9959    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9960}
 9961
 9962#[gpui::test]
 9963async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9964    init_test(cx, |_| {});
 9965
 9966    let mut cx = EditorTestContext::new(cx).await;
 9967
 9968    let diff_base = r#"
 9969        use some::mod;
 9970
 9971        const A: u32 = 42;
 9972
 9973        fn main() {
 9974            println!("hello");
 9975
 9976            println!("world");
 9977        }
 9978        "#
 9979    .unindent();
 9980
 9981    // Edits are modified, removed, modified, added
 9982    cx.set_state(
 9983        &r#"
 9984        use some::modified;
 9985
 9986        ˇ
 9987        fn main() {
 9988            println!("hello there");
 9989
 9990            println!("around the");
 9991            println!("world");
 9992        }
 9993        "#
 9994        .unindent(),
 9995    );
 9996
 9997    cx.set_diff_base(Some(&diff_base));
 9998    executor.run_until_parked();
 9999
10000    cx.update_editor(|editor, cx| {
10001        //Wrap around the bottom of the buffer
10002        for _ in 0..3 {
10003            editor.go_to_next_hunk(&GoToHunk, cx);
10004        }
10005    });
10006
10007    cx.assert_editor_state(
10008        &r#"
10009        ˇuse some::modified;
10010
10011
10012        fn main() {
10013            println!("hello there");
10014
10015            println!("around the");
10016            println!("world");
10017        }
10018        "#
10019        .unindent(),
10020    );
10021
10022    cx.update_editor(|editor, cx| {
10023        //Wrap around the top of the buffer
10024        for _ in 0..2 {
10025            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10026        }
10027    });
10028
10029    cx.assert_editor_state(
10030        &r#"
10031        use some::modified;
10032
10033
10034        fn main() {
10035        ˇ    println!("hello there");
10036
10037            println!("around the");
10038            println!("world");
10039        }
10040        "#
10041        .unindent(),
10042    );
10043
10044    cx.update_editor(|editor, cx| {
10045        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10046    });
10047
10048    cx.assert_editor_state(
10049        &r#"
10050        use some::modified;
10051
10052        ˇ
10053        fn main() {
10054            println!("hello there");
10055
10056            println!("around the");
10057            println!("world");
10058        }
10059        "#
10060        .unindent(),
10061    );
10062
10063    cx.update_editor(|editor, cx| {
10064        for _ in 0..3 {
10065            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10066        }
10067    });
10068
10069    cx.assert_editor_state(
10070        &r#"
10071        use some::modified;
10072
10073
10074        fn main() {
10075        ˇ    println!("hello there");
10076
10077            println!("around the");
10078            println!("world");
10079        }
10080        "#
10081        .unindent(),
10082    );
10083
10084    cx.update_editor(|editor, cx| {
10085        editor.fold(&Fold, cx);
10086
10087        //Make sure that the fold only gets one hunk
10088        for _ in 0..4 {
10089            editor.go_to_next_hunk(&GoToHunk, cx);
10090        }
10091    });
10092
10093    cx.assert_editor_state(
10094        &r#"
10095        ˇuse some::modified;
10096
10097
10098        fn main() {
10099            println!("hello there");
10100
10101            println!("around the");
10102            println!("world");
10103        }
10104        "#
10105        .unindent(),
10106    );
10107}
10108
10109#[test]
10110fn test_split_words() {
10111    fn split(text: &str) -> Vec<&str> {
10112        split_words(text).collect()
10113    }
10114
10115    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10116    assert_eq!(split("hello_world"), &["hello_", "world"]);
10117    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10118    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10119    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10120    assert_eq!(split("helloworld"), &["helloworld"]);
10121
10122    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10123}
10124
10125#[gpui::test]
10126async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10127    init_test(cx, |_| {});
10128
10129    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10130    let mut assert = |before, after| {
10131        let _state_context = cx.set_state(before);
10132        cx.update_editor(|editor, cx| {
10133            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10134        });
10135        cx.assert_editor_state(after);
10136    };
10137
10138    // Outside bracket jumps to outside of matching bracket
10139    assert("console.logˇ(var);", "console.log(var)ˇ;");
10140    assert("console.log(var)ˇ;", "console.logˇ(var);");
10141
10142    // Inside bracket jumps to inside of matching bracket
10143    assert("console.log(ˇvar);", "console.log(varˇ);");
10144    assert("console.log(varˇ);", "console.log(ˇvar);");
10145
10146    // When outside a bracket and inside, favor jumping to the inside bracket
10147    assert(
10148        "console.log('foo', [1, 2, 3]ˇ);",
10149        "console.log(ˇ'foo', [1, 2, 3]);",
10150    );
10151    assert(
10152        "console.log(ˇ'foo', [1, 2, 3]);",
10153        "console.log('foo', [1, 2, 3]ˇ);",
10154    );
10155
10156    // Bias forward if two options are equally likely
10157    assert(
10158        "let result = curried_fun()ˇ();",
10159        "let result = curried_fun()()ˇ;",
10160    );
10161
10162    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10163    assert(
10164        indoc! {"
10165            function test() {
10166                console.log('test')ˇ
10167            }"},
10168        indoc! {"
10169            function test() {
10170                console.logˇ('test')
10171            }"},
10172    );
10173}
10174
10175#[gpui::test]
10176async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10177    init_test(cx, |_| {});
10178
10179    let fs = FakeFs::new(cx.executor());
10180    fs.insert_tree(
10181        "/a",
10182        json!({
10183            "main.rs": "fn main() { let a = 5; }",
10184            "other.rs": "// Test file",
10185        }),
10186    )
10187    .await;
10188    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10189
10190    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10191    language_registry.add(Arc::new(Language::new(
10192        LanguageConfig {
10193            name: "Rust".into(),
10194            matcher: LanguageMatcher {
10195                path_suffixes: vec!["rs".to_string()],
10196                ..Default::default()
10197            },
10198            brackets: BracketPairConfig {
10199                pairs: vec![BracketPair {
10200                    start: "{".to_string(),
10201                    end: "}".to_string(),
10202                    close: true,
10203                    surround: true,
10204                    newline: true,
10205                }],
10206                disabled_scopes_by_bracket_ix: Vec::new(),
10207            },
10208            ..Default::default()
10209        },
10210        Some(tree_sitter_rust::LANGUAGE.into()),
10211    )));
10212    let mut fake_servers = language_registry.register_fake_lsp(
10213        "Rust",
10214        FakeLspAdapter {
10215            capabilities: lsp::ServerCapabilities {
10216                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10217                    first_trigger_character: "{".to_string(),
10218                    more_trigger_character: None,
10219                }),
10220                ..Default::default()
10221            },
10222            ..Default::default()
10223        },
10224    );
10225
10226    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10227
10228    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10229
10230    let worktree_id = workspace
10231        .update(cx, |workspace, cx| {
10232            workspace.project().update(cx, |project, cx| {
10233                project.worktrees(cx).next().unwrap().read(cx).id()
10234            })
10235        })
10236        .unwrap();
10237
10238    let buffer = project
10239        .update(cx, |project, cx| {
10240            project.open_local_buffer("/a/main.rs", cx)
10241        })
10242        .await
10243        .unwrap();
10244    cx.executor().run_until_parked();
10245    cx.executor().start_waiting();
10246    let fake_server = fake_servers.next().await.unwrap();
10247    let editor_handle = workspace
10248        .update(cx, |workspace, cx| {
10249            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10250        })
10251        .unwrap()
10252        .await
10253        .unwrap()
10254        .downcast::<Editor>()
10255        .unwrap();
10256
10257    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10258        assert_eq!(
10259            params.text_document_position.text_document.uri,
10260            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10261        );
10262        assert_eq!(
10263            params.text_document_position.position,
10264            lsp::Position::new(0, 21),
10265        );
10266
10267        Ok(Some(vec![lsp::TextEdit {
10268            new_text: "]".to_string(),
10269            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10270        }]))
10271    });
10272
10273    editor_handle.update(cx, |editor, cx| {
10274        editor.focus(cx);
10275        editor.change_selections(None, cx, |s| {
10276            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10277        });
10278        editor.handle_input("{", cx);
10279    });
10280
10281    cx.executor().run_until_parked();
10282
10283    buffer.update(cx, |buffer, _| {
10284        assert_eq!(
10285            buffer.text(),
10286            "fn main() { let a = {5}; }",
10287            "No extra braces from on type formatting should appear in the buffer"
10288        )
10289    });
10290}
10291
10292#[gpui::test]
10293async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10294    init_test(cx, |_| {});
10295
10296    let fs = FakeFs::new(cx.executor());
10297    fs.insert_tree(
10298        "/a",
10299        json!({
10300            "main.rs": "fn main() { let a = 5; }",
10301            "other.rs": "// Test file",
10302        }),
10303    )
10304    .await;
10305
10306    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10307
10308    let server_restarts = Arc::new(AtomicUsize::new(0));
10309    let closure_restarts = Arc::clone(&server_restarts);
10310    let language_server_name = "test language server";
10311    let language_name: LanguageName = "Rust".into();
10312
10313    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10314    language_registry.add(Arc::new(Language::new(
10315        LanguageConfig {
10316            name: language_name.clone(),
10317            matcher: LanguageMatcher {
10318                path_suffixes: vec!["rs".to_string()],
10319                ..Default::default()
10320            },
10321            ..Default::default()
10322        },
10323        Some(tree_sitter_rust::LANGUAGE.into()),
10324    )));
10325    let mut fake_servers = language_registry.register_fake_lsp(
10326        "Rust",
10327        FakeLspAdapter {
10328            name: language_server_name,
10329            initialization_options: Some(json!({
10330                "testOptionValue": true
10331            })),
10332            initializer: Some(Box::new(move |fake_server| {
10333                let task_restarts = Arc::clone(&closure_restarts);
10334                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10335                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10336                    futures::future::ready(Ok(()))
10337                });
10338            })),
10339            ..Default::default()
10340        },
10341    );
10342
10343    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10344    let _buffer = project
10345        .update(cx, |project, cx| {
10346            project.open_local_buffer("/a/main.rs", cx)
10347        })
10348        .await
10349        .unwrap();
10350    let _fake_server = fake_servers.next().await.unwrap();
10351    update_test_language_settings(cx, |language_settings| {
10352        language_settings.languages.insert(
10353            language_name.clone(),
10354            LanguageSettingsContent {
10355                tab_size: NonZeroU32::new(8),
10356                ..Default::default()
10357            },
10358        );
10359    });
10360    cx.executor().run_until_parked();
10361    assert_eq!(
10362        server_restarts.load(atomic::Ordering::Acquire),
10363        0,
10364        "Should not restart LSP server on an unrelated change"
10365    );
10366
10367    update_test_project_settings(cx, |project_settings| {
10368        project_settings.lsp.insert(
10369            "Some other server name".into(),
10370            LspSettings {
10371                binary: None,
10372                settings: None,
10373                initialization_options: Some(json!({
10374                    "some other init value": false
10375                })),
10376            },
10377        );
10378    });
10379    cx.executor().run_until_parked();
10380    assert_eq!(
10381        server_restarts.load(atomic::Ordering::Acquire),
10382        0,
10383        "Should not restart LSP server on an unrelated LSP settings change"
10384    );
10385
10386    update_test_project_settings(cx, |project_settings| {
10387        project_settings.lsp.insert(
10388            language_server_name.into(),
10389            LspSettings {
10390                binary: None,
10391                settings: None,
10392                initialization_options: Some(json!({
10393                    "anotherInitValue": false
10394                })),
10395            },
10396        );
10397    });
10398    cx.executor().run_until_parked();
10399    assert_eq!(
10400        server_restarts.load(atomic::Ordering::Acquire),
10401        1,
10402        "Should restart LSP server on a related LSP settings change"
10403    );
10404
10405    update_test_project_settings(cx, |project_settings| {
10406        project_settings.lsp.insert(
10407            language_server_name.into(),
10408            LspSettings {
10409                binary: None,
10410                settings: None,
10411                initialization_options: Some(json!({
10412                    "anotherInitValue": false
10413                })),
10414            },
10415        );
10416    });
10417    cx.executor().run_until_parked();
10418    assert_eq!(
10419        server_restarts.load(atomic::Ordering::Acquire),
10420        1,
10421        "Should not restart LSP server on a related LSP settings change that is the same"
10422    );
10423
10424    update_test_project_settings(cx, |project_settings| {
10425        project_settings.lsp.insert(
10426            language_server_name.into(),
10427            LspSettings {
10428                binary: None,
10429                settings: None,
10430                initialization_options: None,
10431            },
10432        );
10433    });
10434    cx.executor().run_until_parked();
10435    assert_eq!(
10436        server_restarts.load(atomic::Ordering::Acquire),
10437        2,
10438        "Should restart LSP server on another related LSP settings change"
10439    );
10440}
10441
10442#[gpui::test]
10443async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10444    init_test(cx, |_| {});
10445
10446    let mut cx = EditorLspTestContext::new_rust(
10447        lsp::ServerCapabilities {
10448            completion_provider: Some(lsp::CompletionOptions {
10449                trigger_characters: Some(vec![".".to_string()]),
10450                resolve_provider: Some(true),
10451                ..Default::default()
10452            }),
10453            ..Default::default()
10454        },
10455        cx,
10456    )
10457    .await;
10458
10459    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10460    cx.simulate_keystroke(".");
10461    let completion_item = lsp::CompletionItem {
10462        label: "some".into(),
10463        kind: Some(lsp::CompletionItemKind::SNIPPET),
10464        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10465        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10466            kind: lsp::MarkupKind::Markdown,
10467            value: "```rust\nSome(2)\n```".to_string(),
10468        })),
10469        deprecated: Some(false),
10470        sort_text: Some("fffffff2".to_string()),
10471        filter_text: Some("some".to_string()),
10472        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10473        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10474            range: lsp::Range {
10475                start: lsp::Position {
10476                    line: 0,
10477                    character: 22,
10478                },
10479                end: lsp::Position {
10480                    line: 0,
10481                    character: 22,
10482                },
10483            },
10484            new_text: "Some(2)".to_string(),
10485        })),
10486        additional_text_edits: Some(vec![lsp::TextEdit {
10487            range: lsp::Range {
10488                start: lsp::Position {
10489                    line: 0,
10490                    character: 20,
10491                },
10492                end: lsp::Position {
10493                    line: 0,
10494                    character: 22,
10495                },
10496            },
10497            new_text: "".to_string(),
10498        }]),
10499        ..Default::default()
10500    };
10501
10502    let closure_completion_item = completion_item.clone();
10503    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10504        let task_completion_item = closure_completion_item.clone();
10505        async move {
10506            Ok(Some(lsp::CompletionResponse::Array(vec![
10507                task_completion_item,
10508            ])))
10509        }
10510    });
10511
10512    request.next().await;
10513
10514    cx.condition(|editor, _| editor.context_menu_visible())
10515        .await;
10516    let apply_additional_edits = cx.update_editor(|editor, cx| {
10517        editor
10518            .confirm_completion(&ConfirmCompletion::default(), cx)
10519            .unwrap()
10520    });
10521    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10522
10523    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10524        let task_completion_item = completion_item.clone();
10525        async move { Ok(task_completion_item) }
10526    })
10527    .next()
10528    .await
10529    .unwrap();
10530    apply_additional_edits.await.unwrap();
10531    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10532}
10533
10534#[gpui::test]
10535async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10536    init_test(cx, |_| {});
10537
10538    let mut cx = EditorLspTestContext::new(
10539        Language::new(
10540            LanguageConfig {
10541                matcher: LanguageMatcher {
10542                    path_suffixes: vec!["jsx".into()],
10543                    ..Default::default()
10544                },
10545                overrides: [(
10546                    "element".into(),
10547                    LanguageConfigOverride {
10548                        word_characters: Override::Set(['-'].into_iter().collect()),
10549                        ..Default::default()
10550                    },
10551                )]
10552                .into_iter()
10553                .collect(),
10554                ..Default::default()
10555            },
10556            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10557        )
10558        .with_override_query("(jsx_self_closing_element) @element")
10559        .unwrap(),
10560        lsp::ServerCapabilities {
10561            completion_provider: Some(lsp::CompletionOptions {
10562                trigger_characters: Some(vec![":".to_string()]),
10563                ..Default::default()
10564            }),
10565            ..Default::default()
10566        },
10567        cx,
10568    )
10569    .await;
10570
10571    cx.lsp
10572        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10573            Ok(Some(lsp::CompletionResponse::Array(vec![
10574                lsp::CompletionItem {
10575                    label: "bg-blue".into(),
10576                    ..Default::default()
10577                },
10578                lsp::CompletionItem {
10579                    label: "bg-red".into(),
10580                    ..Default::default()
10581                },
10582                lsp::CompletionItem {
10583                    label: "bg-yellow".into(),
10584                    ..Default::default()
10585                },
10586            ])))
10587        });
10588
10589    cx.set_state(r#"<p class="bgˇ" />"#);
10590
10591    // Trigger completion when typing a dash, because the dash is an extra
10592    // word character in the 'element' scope, which contains the cursor.
10593    cx.simulate_keystroke("-");
10594    cx.executor().run_until_parked();
10595    cx.update_editor(|editor, _| {
10596        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10597            assert_eq!(
10598                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10599                &["bg-red", "bg-blue", "bg-yellow"]
10600            );
10601        } else {
10602            panic!("expected completion menu to be open");
10603        }
10604    });
10605
10606    cx.simulate_keystroke("l");
10607    cx.executor().run_until_parked();
10608    cx.update_editor(|editor, _| {
10609        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10610            assert_eq!(
10611                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10612                &["bg-blue", "bg-yellow"]
10613            );
10614        } else {
10615            panic!("expected completion menu to be open");
10616        }
10617    });
10618
10619    // When filtering completions, consider the character after the '-' to
10620    // be the start of a subword.
10621    cx.set_state(r#"<p class="yelˇ" />"#);
10622    cx.simulate_keystroke("l");
10623    cx.executor().run_until_parked();
10624    cx.update_editor(|editor, _| {
10625        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10626            assert_eq!(
10627                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10628                &["bg-yellow"]
10629            );
10630        } else {
10631            panic!("expected completion menu to be open");
10632        }
10633    });
10634}
10635
10636#[gpui::test]
10637async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10638    init_test(cx, |settings| {
10639        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10640            FormatterList(vec![Formatter::Prettier].into()),
10641        ))
10642    });
10643
10644    let fs = FakeFs::new(cx.executor());
10645    fs.insert_file("/file.ts", Default::default()).await;
10646
10647    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10648    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10649
10650    language_registry.add(Arc::new(Language::new(
10651        LanguageConfig {
10652            name: "TypeScript".into(),
10653            matcher: LanguageMatcher {
10654                path_suffixes: vec!["ts".to_string()],
10655                ..Default::default()
10656            },
10657            ..Default::default()
10658        },
10659        Some(tree_sitter_rust::LANGUAGE.into()),
10660    )));
10661    update_test_language_settings(cx, |settings| {
10662        settings.defaults.prettier = Some(PrettierSettings {
10663            allowed: true,
10664            ..PrettierSettings::default()
10665        });
10666    });
10667
10668    let test_plugin = "test_plugin";
10669    let _ = language_registry.register_fake_lsp(
10670        "TypeScript",
10671        FakeLspAdapter {
10672            prettier_plugins: vec![test_plugin],
10673            ..Default::default()
10674        },
10675    );
10676
10677    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10678    let buffer = project
10679        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10680        .await
10681        .unwrap();
10682
10683    let buffer_text = "one\ntwo\nthree\n";
10684    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10685    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10686    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10687
10688    editor
10689        .update(cx, |editor, cx| {
10690            editor.perform_format(
10691                project.clone(),
10692                FormatTrigger::Manual,
10693                FormatTarget::Buffer,
10694                cx,
10695            )
10696        })
10697        .unwrap()
10698        .await;
10699    assert_eq!(
10700        editor.update(cx, |editor, cx| editor.text(cx)),
10701        buffer_text.to_string() + prettier_format_suffix,
10702        "Test prettier formatting was not applied to the original buffer text",
10703    );
10704
10705    update_test_language_settings(cx, |settings| {
10706        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10707    });
10708    let format = editor.update(cx, |editor, cx| {
10709        editor.perform_format(
10710            project.clone(),
10711            FormatTrigger::Manual,
10712            FormatTarget::Buffer,
10713            cx,
10714        )
10715    });
10716    format.await.unwrap();
10717    assert_eq!(
10718        editor.update(cx, |editor, cx| editor.text(cx)),
10719        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10720        "Autoformatting (via test prettier) was not applied to the original buffer text",
10721    );
10722}
10723
10724#[gpui::test]
10725async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10726    init_test(cx, |_| {});
10727    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10728    let base_text = indoc! {r#"struct Row;
10729struct Row1;
10730struct Row2;
10731
10732struct Row4;
10733struct Row5;
10734struct Row6;
10735
10736struct Row8;
10737struct Row9;
10738struct Row10;"#};
10739
10740    // When addition hunks are not adjacent to carets, no hunk revert is performed
10741    assert_hunk_revert(
10742        indoc! {r#"struct Row;
10743                   struct Row1;
10744                   struct Row1.1;
10745                   struct Row1.2;
10746                   struct Row2;ˇ
10747
10748                   struct Row4;
10749                   struct Row5;
10750                   struct Row6;
10751
10752                   struct Row8;
10753                   ˇstruct Row9;
10754                   struct Row9.1;
10755                   struct Row9.2;
10756                   struct Row9.3;
10757                   struct Row10;"#},
10758        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10759        indoc! {r#"struct Row;
10760                   struct Row1;
10761                   struct Row1.1;
10762                   struct Row1.2;
10763                   struct Row2;ˇ
10764
10765                   struct Row4;
10766                   struct Row5;
10767                   struct Row6;
10768
10769                   struct Row8;
10770                   ˇstruct Row9;
10771                   struct Row9.1;
10772                   struct Row9.2;
10773                   struct Row9.3;
10774                   struct Row10;"#},
10775        base_text,
10776        &mut cx,
10777    );
10778    // Same for selections
10779    assert_hunk_revert(
10780        indoc! {r#"struct Row;
10781                   struct Row1;
10782                   struct Row2;
10783                   struct Row2.1;
10784                   struct Row2.2;
10785                   «ˇ
10786                   struct Row4;
10787                   struct» Row5;
10788                   «struct Row6;
10789                   ˇ»
10790                   struct Row9.1;
10791                   struct Row9.2;
10792                   struct Row9.3;
10793                   struct Row8;
10794                   struct Row9;
10795                   struct Row10;"#},
10796        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10797        indoc! {r#"struct Row;
10798                   struct Row1;
10799                   struct Row2;
10800                   struct Row2.1;
10801                   struct Row2.2;
10802                   «ˇ
10803                   struct Row4;
10804                   struct» Row5;
10805                   «struct Row6;
10806                   ˇ»
10807                   struct Row9.1;
10808                   struct Row9.2;
10809                   struct Row9.3;
10810                   struct Row8;
10811                   struct Row9;
10812                   struct Row10;"#},
10813        base_text,
10814        &mut cx,
10815    );
10816
10817    // When carets and selections intersect the addition hunks, those are reverted.
10818    // Adjacent carets got merged.
10819    assert_hunk_revert(
10820        indoc! {r#"struct Row;
10821                   ˇ// something on the top
10822                   struct Row1;
10823                   struct Row2;
10824                   struct Roˇw3.1;
10825                   struct Row2.2;
10826                   struct Row2.3;ˇ
10827
10828                   struct Row4;
10829                   struct ˇRow5.1;
10830                   struct Row5.2;
10831                   struct «Rowˇ»5.3;
10832                   struct Row5;
10833                   struct Row6;
10834                   ˇ
10835                   struct Row9.1;
10836                   struct «Rowˇ»9.2;
10837                   struct «ˇRow»9.3;
10838                   struct Row8;
10839                   struct Row9;
10840                   «ˇ// something on bottom»
10841                   struct Row10;"#},
10842        vec![
10843            DiffHunkStatus::Added,
10844            DiffHunkStatus::Added,
10845            DiffHunkStatus::Added,
10846            DiffHunkStatus::Added,
10847            DiffHunkStatus::Added,
10848        ],
10849        indoc! {r#"struct Row;
10850                   ˇstruct Row1;
10851                   struct Row2;
10852                   ˇ
10853                   struct Row4;
10854                   ˇstruct Row5;
10855                   struct Row6;
10856                   ˇ
10857                   ˇstruct Row8;
10858                   struct Row9;
10859                   ˇstruct Row10;"#},
10860        base_text,
10861        &mut cx,
10862    );
10863}
10864
10865#[gpui::test]
10866async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10867    init_test(cx, |_| {});
10868    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10869    let base_text = indoc! {r#"struct Row;
10870struct Row1;
10871struct Row2;
10872
10873struct Row4;
10874struct Row5;
10875struct Row6;
10876
10877struct Row8;
10878struct Row9;
10879struct Row10;"#};
10880
10881    // Modification hunks behave the same as the addition ones.
10882    assert_hunk_revert(
10883        indoc! {r#"struct Row;
10884                   struct Row1;
10885                   struct Row33;
10886                   ˇ
10887                   struct Row4;
10888                   struct Row5;
10889                   struct Row6;
10890                   ˇ
10891                   struct Row99;
10892                   struct Row9;
10893                   struct Row10;"#},
10894        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10895        indoc! {r#"struct Row;
10896                   struct Row1;
10897                   struct Row33;
10898                   ˇ
10899                   struct Row4;
10900                   struct Row5;
10901                   struct Row6;
10902                   ˇ
10903                   struct Row99;
10904                   struct Row9;
10905                   struct Row10;"#},
10906        base_text,
10907        &mut cx,
10908    );
10909    assert_hunk_revert(
10910        indoc! {r#"struct Row;
10911                   struct Row1;
10912                   struct Row33;
10913                   «ˇ
10914                   struct Row4;
10915                   struct» Row5;
10916                   «struct Row6;
10917                   ˇ»
10918                   struct Row99;
10919                   struct Row9;
10920                   struct Row10;"#},
10921        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10922        indoc! {r#"struct Row;
10923                   struct Row1;
10924                   struct Row33;
10925                   «ˇ
10926                   struct Row4;
10927                   struct» Row5;
10928                   «struct Row6;
10929                   ˇ»
10930                   struct Row99;
10931                   struct Row9;
10932                   struct Row10;"#},
10933        base_text,
10934        &mut cx,
10935    );
10936
10937    assert_hunk_revert(
10938        indoc! {r#"ˇstruct Row1.1;
10939                   struct Row1;
10940                   «ˇstr»uct Row22;
10941
10942                   struct ˇRow44;
10943                   struct Row5;
10944                   struct «Rˇ»ow66;ˇ
10945
10946                   «struˇ»ct Row88;
10947                   struct Row9;
10948                   struct Row1011;ˇ"#},
10949        vec![
10950            DiffHunkStatus::Modified,
10951            DiffHunkStatus::Modified,
10952            DiffHunkStatus::Modified,
10953            DiffHunkStatus::Modified,
10954            DiffHunkStatus::Modified,
10955            DiffHunkStatus::Modified,
10956        ],
10957        indoc! {r#"struct Row;
10958                   ˇstruct Row1;
10959                   struct Row2;
10960                   ˇ
10961                   struct Row4;
10962                   ˇstruct Row5;
10963                   struct Row6;
10964                   ˇ
10965                   struct Row8;
10966                   ˇstruct Row9;
10967                   struct Row10;ˇ"#},
10968        base_text,
10969        &mut cx,
10970    );
10971}
10972
10973#[gpui::test]
10974async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10975    init_test(cx, |_| {});
10976    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10977    let base_text = indoc! {r#"struct Row;
10978struct Row1;
10979struct Row2;
10980
10981struct Row4;
10982struct Row5;
10983struct Row6;
10984
10985struct Row8;
10986struct Row9;
10987struct Row10;"#};
10988
10989    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10990    assert_hunk_revert(
10991        indoc! {r#"struct Row;
10992                   struct Row2;
10993
10994                   ˇstruct Row4;
10995                   struct Row5;
10996                   struct Row6;
10997                   ˇ
10998                   struct Row8;
10999                   struct Row10;"#},
11000        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11001        indoc! {r#"struct Row;
11002                   struct Row2;
11003
11004                   ˇstruct Row4;
11005                   struct Row5;
11006                   struct Row6;
11007                   ˇ
11008                   struct Row8;
11009                   struct Row10;"#},
11010        base_text,
11011        &mut cx,
11012    );
11013    assert_hunk_revert(
11014        indoc! {r#"struct Row;
11015                   struct Row2;
11016
11017                   «ˇstruct Row4;
11018                   struct» Row5;
11019                   «struct Row6;
11020                   ˇ»
11021                   struct Row8;
11022                   struct Row10;"#},
11023        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11024        indoc! {r#"struct Row;
11025                   struct Row2;
11026
11027                   «ˇstruct Row4;
11028                   struct» Row5;
11029                   «struct Row6;
11030                   ˇ»
11031                   struct Row8;
11032                   struct Row10;"#},
11033        base_text,
11034        &mut cx,
11035    );
11036
11037    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11038    assert_hunk_revert(
11039        indoc! {r#"struct Row;
11040                   ˇstruct Row2;
11041
11042                   struct Row4;
11043                   struct Row5;
11044                   struct Row6;
11045
11046                   struct Row8;ˇ
11047                   struct Row10;"#},
11048        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11049        indoc! {r#"struct Row;
11050                   struct Row1;
11051                   ˇstruct Row2;
11052
11053                   struct Row4;
11054                   struct Row5;
11055                   struct Row6;
11056
11057                   struct Row8;ˇ
11058                   struct Row9;
11059                   struct Row10;"#},
11060        base_text,
11061        &mut cx,
11062    );
11063    assert_hunk_revert(
11064        indoc! {r#"struct Row;
11065                   struct Row2«ˇ;
11066                   struct Row4;
11067                   struct» Row5;
11068                   «struct Row6;
11069
11070                   struct Row8;ˇ»
11071                   struct Row10;"#},
11072        vec![
11073            DiffHunkStatus::Removed,
11074            DiffHunkStatus::Removed,
11075            DiffHunkStatus::Removed,
11076        ],
11077        indoc! {r#"struct Row;
11078                   struct Row1;
11079                   struct Row2«ˇ;
11080
11081                   struct Row4;
11082                   struct» Row5;
11083                   «struct Row6;
11084
11085                   struct Row8;ˇ»
11086                   struct Row9;
11087                   struct Row10;"#},
11088        base_text,
11089        &mut cx,
11090    );
11091}
11092
11093#[gpui::test]
11094async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11095    init_test(cx, |_| {});
11096
11097    let cols = 4;
11098    let rows = 10;
11099    let sample_text_1 = sample_text(rows, cols, 'a');
11100    assert_eq!(
11101        sample_text_1,
11102        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11103    );
11104    let sample_text_2 = sample_text(rows, cols, 'l');
11105    assert_eq!(
11106        sample_text_2,
11107        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11108    );
11109    let sample_text_3 = sample_text(rows, cols, 'v');
11110    assert_eq!(
11111        sample_text_3,
11112        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11113    );
11114
11115    fn diff_every_buffer_row(
11116        buffer: &Model<Buffer>,
11117        sample_text: String,
11118        cols: usize,
11119        cx: &mut gpui::TestAppContext,
11120    ) {
11121        // revert first character in each row, creating one large diff hunk per buffer
11122        let is_first_char = |offset: usize| offset % cols == 0;
11123        buffer.update(cx, |buffer, cx| {
11124            buffer.set_text(
11125                sample_text
11126                    .chars()
11127                    .enumerate()
11128                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
11129                    .collect::<String>(),
11130                cx,
11131            );
11132            buffer.set_diff_base(Some(sample_text), cx);
11133        });
11134        cx.executor().run_until_parked();
11135    }
11136
11137    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11138    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11139
11140    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11141    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11142
11143    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11144    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11145
11146    let multibuffer = cx.new_model(|cx| {
11147        let mut multibuffer = MultiBuffer::new(ReadWrite);
11148        multibuffer.push_excerpts(
11149            buffer_1.clone(),
11150            [
11151                ExcerptRange {
11152                    context: Point::new(0, 0)..Point::new(3, 0),
11153                    primary: None,
11154                },
11155                ExcerptRange {
11156                    context: Point::new(5, 0)..Point::new(7, 0),
11157                    primary: None,
11158                },
11159                ExcerptRange {
11160                    context: Point::new(9, 0)..Point::new(10, 4),
11161                    primary: None,
11162                },
11163            ],
11164            cx,
11165        );
11166        multibuffer.push_excerpts(
11167            buffer_2.clone(),
11168            [
11169                ExcerptRange {
11170                    context: Point::new(0, 0)..Point::new(3, 0),
11171                    primary: None,
11172                },
11173                ExcerptRange {
11174                    context: Point::new(5, 0)..Point::new(7, 0),
11175                    primary: None,
11176                },
11177                ExcerptRange {
11178                    context: Point::new(9, 0)..Point::new(10, 4),
11179                    primary: None,
11180                },
11181            ],
11182            cx,
11183        );
11184        multibuffer.push_excerpts(
11185            buffer_3.clone(),
11186            [
11187                ExcerptRange {
11188                    context: Point::new(0, 0)..Point::new(3, 0),
11189                    primary: None,
11190                },
11191                ExcerptRange {
11192                    context: Point::new(5, 0)..Point::new(7, 0),
11193                    primary: None,
11194                },
11195                ExcerptRange {
11196                    context: Point::new(9, 0)..Point::new(10, 4),
11197                    primary: None,
11198                },
11199            ],
11200            cx,
11201        );
11202        multibuffer
11203    });
11204
11205    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11206    editor.update(cx, |editor, cx| {
11207        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
11208        editor.select_all(&SelectAll, cx);
11209        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11210    });
11211    cx.executor().run_until_parked();
11212    // When all ranges are selected, all buffer hunks are reverted.
11213    editor.update(cx, |editor, cx| {
11214        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");
11215    });
11216    buffer_1.update(cx, |buffer, _| {
11217        assert_eq!(buffer.text(), sample_text_1);
11218    });
11219    buffer_2.update(cx, |buffer, _| {
11220        assert_eq!(buffer.text(), sample_text_2);
11221    });
11222    buffer_3.update(cx, |buffer, _| {
11223        assert_eq!(buffer.text(), sample_text_3);
11224    });
11225
11226    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11227    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11228    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11229    editor.update(cx, |editor, cx| {
11230        editor.change_selections(None, cx, |s| {
11231            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11232        });
11233        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11234    });
11235    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11236    // but not affect buffer_2 and its related excerpts.
11237    editor.update(cx, |editor, cx| {
11238        assert_eq!(
11239            editor.text(cx),
11240            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
11241        );
11242    });
11243    buffer_1.update(cx, |buffer, _| {
11244        assert_eq!(buffer.text(), sample_text_1);
11245    });
11246    buffer_2.update(cx, |buffer, _| {
11247        assert_eq!(
11248            buffer.text(),
11249            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11250        );
11251    });
11252    buffer_3.update(cx, |buffer, _| {
11253        assert_eq!(
11254            buffer.text(),
11255            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11256        );
11257    });
11258}
11259
11260#[gpui::test]
11261async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11262    init_test(cx, |_| {});
11263
11264    let cols = 4;
11265    let rows = 10;
11266    let sample_text_1 = sample_text(rows, cols, 'a');
11267    assert_eq!(
11268        sample_text_1,
11269        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11270    );
11271    let sample_text_2 = sample_text(rows, cols, 'l');
11272    assert_eq!(
11273        sample_text_2,
11274        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11275    );
11276    let sample_text_3 = sample_text(rows, cols, 'v');
11277    assert_eq!(
11278        sample_text_3,
11279        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11280    );
11281
11282    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11283    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11284    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11285
11286    let multi_buffer = cx.new_model(|cx| {
11287        let mut multibuffer = MultiBuffer::new(ReadWrite);
11288        multibuffer.push_excerpts(
11289            buffer_1.clone(),
11290            [
11291                ExcerptRange {
11292                    context: Point::new(0, 0)..Point::new(3, 0),
11293                    primary: None,
11294                },
11295                ExcerptRange {
11296                    context: Point::new(5, 0)..Point::new(7, 0),
11297                    primary: None,
11298                },
11299                ExcerptRange {
11300                    context: Point::new(9, 0)..Point::new(10, 4),
11301                    primary: None,
11302                },
11303            ],
11304            cx,
11305        );
11306        multibuffer.push_excerpts(
11307            buffer_2.clone(),
11308            [
11309                ExcerptRange {
11310                    context: Point::new(0, 0)..Point::new(3, 0),
11311                    primary: None,
11312                },
11313                ExcerptRange {
11314                    context: Point::new(5, 0)..Point::new(7, 0),
11315                    primary: None,
11316                },
11317                ExcerptRange {
11318                    context: Point::new(9, 0)..Point::new(10, 4),
11319                    primary: None,
11320                },
11321            ],
11322            cx,
11323        );
11324        multibuffer.push_excerpts(
11325            buffer_3.clone(),
11326            [
11327                ExcerptRange {
11328                    context: Point::new(0, 0)..Point::new(3, 0),
11329                    primary: None,
11330                },
11331                ExcerptRange {
11332                    context: Point::new(5, 0)..Point::new(7, 0),
11333                    primary: None,
11334                },
11335                ExcerptRange {
11336                    context: Point::new(9, 0)..Point::new(10, 4),
11337                    primary: None,
11338                },
11339            ],
11340            cx,
11341        );
11342        multibuffer
11343    });
11344
11345    let fs = FakeFs::new(cx.executor());
11346    fs.insert_tree(
11347        "/a",
11348        json!({
11349            "main.rs": sample_text_1,
11350            "other.rs": sample_text_2,
11351            "lib.rs": sample_text_3,
11352        }),
11353    )
11354    .await;
11355    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11356    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11357    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11358    let multi_buffer_editor = cx.new_view(|cx| {
11359        Editor::new(
11360            EditorMode::Full,
11361            multi_buffer,
11362            Some(project.clone()),
11363            true,
11364            cx,
11365        )
11366    });
11367    let multibuffer_item_id = workspace
11368        .update(cx, |workspace, cx| {
11369            assert!(
11370                workspace.active_item(cx).is_none(),
11371                "active item should be None before the first item is added"
11372            );
11373            workspace.add_item_to_active_pane(
11374                Box::new(multi_buffer_editor.clone()),
11375                None,
11376                true,
11377                cx,
11378            );
11379            let active_item = workspace
11380                .active_item(cx)
11381                .expect("should have an active item after adding the multi buffer");
11382            assert!(
11383                !active_item.is_singleton(cx),
11384                "A multi buffer was expected to active after adding"
11385            );
11386            active_item.item_id()
11387        })
11388        .unwrap();
11389    cx.executor().run_until_parked();
11390
11391    multi_buffer_editor.update(cx, |editor, cx| {
11392        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11393        editor.open_excerpts(&OpenExcerpts, cx);
11394    });
11395    cx.executor().run_until_parked();
11396    let first_item_id = workspace
11397        .update(cx, |workspace, cx| {
11398            let active_item = workspace
11399                .active_item(cx)
11400                .expect("should have an active item after navigating into the 1st buffer");
11401            let first_item_id = active_item.item_id();
11402            assert_ne!(
11403                first_item_id, multibuffer_item_id,
11404                "Should navigate into the 1st buffer and activate it"
11405            );
11406            assert!(
11407                active_item.is_singleton(cx),
11408                "New active item should be a singleton buffer"
11409            );
11410            assert_eq!(
11411                active_item
11412                    .act_as::<Editor>(cx)
11413                    .expect("should have navigated into an editor for the 1st buffer")
11414                    .read(cx)
11415                    .text(cx),
11416                sample_text_1
11417            );
11418
11419            workspace
11420                .go_back(workspace.active_pane().downgrade(), cx)
11421                .detach_and_log_err(cx);
11422
11423            first_item_id
11424        })
11425        .unwrap();
11426    cx.executor().run_until_parked();
11427    workspace
11428        .update(cx, |workspace, cx| {
11429            let active_item = workspace
11430                .active_item(cx)
11431                .expect("should have an active item after navigating back");
11432            assert_eq!(
11433                active_item.item_id(),
11434                multibuffer_item_id,
11435                "Should navigate back to the multi buffer"
11436            );
11437            assert!(!active_item.is_singleton(cx));
11438        })
11439        .unwrap();
11440
11441    multi_buffer_editor.update(cx, |editor, cx| {
11442        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11443            s.select_ranges(Some(39..40))
11444        });
11445        editor.open_excerpts(&OpenExcerpts, cx);
11446    });
11447    cx.executor().run_until_parked();
11448    let second_item_id = workspace
11449        .update(cx, |workspace, cx| {
11450            let active_item = workspace
11451                .active_item(cx)
11452                .expect("should have an active item after navigating into the 2nd buffer");
11453            let second_item_id = active_item.item_id();
11454            assert_ne!(
11455                second_item_id, multibuffer_item_id,
11456                "Should navigate away from the multibuffer"
11457            );
11458            assert_ne!(
11459                second_item_id, first_item_id,
11460                "Should navigate into the 2nd buffer and activate it"
11461            );
11462            assert!(
11463                active_item.is_singleton(cx),
11464                "New active item should be a singleton buffer"
11465            );
11466            assert_eq!(
11467                active_item
11468                    .act_as::<Editor>(cx)
11469                    .expect("should have navigated into an editor")
11470                    .read(cx)
11471                    .text(cx),
11472                sample_text_2
11473            );
11474
11475            workspace
11476                .go_back(workspace.active_pane().downgrade(), cx)
11477                .detach_and_log_err(cx);
11478
11479            second_item_id
11480        })
11481        .unwrap();
11482    cx.executor().run_until_parked();
11483    workspace
11484        .update(cx, |workspace, cx| {
11485            let active_item = workspace
11486                .active_item(cx)
11487                .expect("should have an active item after navigating back from the 2nd buffer");
11488            assert_eq!(
11489                active_item.item_id(),
11490                multibuffer_item_id,
11491                "Should navigate back from the 2nd buffer to the multi buffer"
11492            );
11493            assert!(!active_item.is_singleton(cx));
11494        })
11495        .unwrap();
11496
11497    multi_buffer_editor.update(cx, |editor, cx| {
11498        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11499            s.select_ranges(Some(60..70))
11500        });
11501        editor.open_excerpts(&OpenExcerpts, cx);
11502    });
11503    cx.executor().run_until_parked();
11504    workspace
11505        .update(cx, |workspace, cx| {
11506            let active_item = workspace
11507                .active_item(cx)
11508                .expect("should have an active item after navigating into the 3rd buffer");
11509            let third_item_id = active_item.item_id();
11510            assert_ne!(
11511                third_item_id, multibuffer_item_id,
11512                "Should navigate into the 3rd buffer and activate it"
11513            );
11514            assert_ne!(third_item_id, first_item_id);
11515            assert_ne!(third_item_id, second_item_id);
11516            assert!(
11517                active_item.is_singleton(cx),
11518                "New active item should be a singleton buffer"
11519            );
11520            assert_eq!(
11521                active_item
11522                    .act_as::<Editor>(cx)
11523                    .expect("should have navigated into an editor")
11524                    .read(cx)
11525                    .text(cx),
11526                sample_text_3
11527            );
11528
11529            workspace
11530                .go_back(workspace.active_pane().downgrade(), cx)
11531                .detach_and_log_err(cx);
11532        })
11533        .unwrap();
11534    cx.executor().run_until_parked();
11535    workspace
11536        .update(cx, |workspace, cx| {
11537            let active_item = workspace
11538                .active_item(cx)
11539                .expect("should have an active item after navigating back from the 3rd buffer");
11540            assert_eq!(
11541                active_item.item_id(),
11542                multibuffer_item_id,
11543                "Should navigate back from the 3rd buffer to the multi buffer"
11544            );
11545            assert!(!active_item.is_singleton(cx));
11546        })
11547        .unwrap();
11548}
11549
11550#[gpui::test]
11551async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11552    init_test(cx, |_| {});
11553
11554    let mut cx = EditorTestContext::new(cx).await;
11555
11556    let diff_base = r#"
11557        use some::mod;
11558
11559        const A: u32 = 42;
11560
11561        fn main() {
11562            println!("hello");
11563
11564            println!("world");
11565        }
11566        "#
11567    .unindent();
11568
11569    cx.set_state(
11570        &r#"
11571        use some::modified;
11572
11573        ˇ
11574        fn main() {
11575            println!("hello there");
11576
11577            println!("around the");
11578            println!("world");
11579        }
11580        "#
11581        .unindent(),
11582    );
11583
11584    cx.set_diff_base(Some(&diff_base));
11585    executor.run_until_parked();
11586
11587    cx.update_editor(|editor, cx| {
11588        editor.go_to_next_hunk(&GoToHunk, cx);
11589        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11590    });
11591    executor.run_until_parked();
11592    cx.assert_diff_hunks(
11593        r#"
11594          use some::modified;
11595
11596
11597          fn main() {
11598        -     println!("hello");
11599        +     println!("hello there");
11600
11601              println!("around the");
11602              println!("world");
11603          }
11604        "#
11605        .unindent(),
11606    );
11607
11608    cx.update_editor(|editor, cx| {
11609        for _ in 0..3 {
11610            editor.go_to_next_hunk(&GoToHunk, cx);
11611            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11612        }
11613    });
11614    executor.run_until_parked();
11615    cx.assert_editor_state(
11616        &r#"
11617        use some::modified;
11618
11619        ˇ
11620        fn main() {
11621            println!("hello there");
11622
11623            println!("around the");
11624            println!("world");
11625        }
11626        "#
11627        .unindent(),
11628    );
11629
11630    cx.assert_diff_hunks(
11631        r#"
11632        - use some::mod;
11633        + use some::modified;
11634
11635        - const A: u32 = 42;
11636
11637          fn main() {
11638        -     println!("hello");
11639        +     println!("hello there");
11640
11641        +     println!("around the");
11642              println!("world");
11643          }
11644        "#
11645        .unindent(),
11646    );
11647
11648    cx.update_editor(|editor, cx| {
11649        editor.cancel(&Cancel, cx);
11650    });
11651
11652    cx.assert_diff_hunks(
11653        r#"
11654          use some::modified;
11655
11656
11657          fn main() {
11658              println!("hello there");
11659
11660              println!("around the");
11661              println!("world");
11662          }
11663        "#
11664        .unindent(),
11665    );
11666}
11667
11668#[gpui::test]
11669async fn test_diff_base_change_with_expanded_diff_hunks(
11670    executor: BackgroundExecutor,
11671    cx: &mut gpui::TestAppContext,
11672) {
11673    init_test(cx, |_| {});
11674
11675    let mut cx = EditorTestContext::new(cx).await;
11676
11677    let diff_base = r#"
11678        use some::mod1;
11679        use some::mod2;
11680
11681        const A: u32 = 42;
11682        const B: u32 = 42;
11683        const C: u32 = 42;
11684
11685        fn main() {
11686            println!("hello");
11687
11688            println!("world");
11689        }
11690        "#
11691    .unindent();
11692
11693    cx.set_state(
11694        &r#"
11695        use some::mod2;
11696
11697        const A: u32 = 42;
11698        const C: u32 = 42;
11699
11700        fn main(ˇ) {
11701            //println!("hello");
11702
11703            println!("world");
11704            //
11705            //
11706        }
11707        "#
11708        .unindent(),
11709    );
11710
11711    cx.set_diff_base(Some(&diff_base));
11712    executor.run_until_parked();
11713
11714    cx.update_editor(|editor, cx| {
11715        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11716    });
11717    executor.run_until_parked();
11718    cx.assert_diff_hunks(
11719        r#"
11720        - use some::mod1;
11721          use some::mod2;
11722
11723          const A: u32 = 42;
11724        - const B: u32 = 42;
11725          const C: u32 = 42;
11726
11727          fn main() {
11728        -     println!("hello");
11729        +     //println!("hello");
11730
11731              println!("world");
11732        +     //
11733        +     //
11734          }
11735        "#
11736        .unindent(),
11737    );
11738
11739    cx.set_diff_base(Some("new diff base!"));
11740    executor.run_until_parked();
11741    cx.assert_diff_hunks(
11742        r#"
11743          use some::mod2;
11744
11745          const A: u32 = 42;
11746          const C: u32 = 42;
11747
11748          fn main() {
11749              //println!("hello");
11750
11751              println!("world");
11752              //
11753              //
11754          }
11755        "#
11756        .unindent(),
11757    );
11758
11759    cx.update_editor(|editor, cx| {
11760        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11761    });
11762    executor.run_until_parked();
11763    cx.assert_diff_hunks(
11764        r#"
11765        - new diff base!
11766        + use some::mod2;
11767        +
11768        + const A: u32 = 42;
11769        + const C: u32 = 42;
11770        +
11771        + fn main() {
11772        +     //println!("hello");
11773        +
11774        +     println!("world");
11775        +     //
11776        +     //
11777        + }
11778        "#
11779        .unindent(),
11780    );
11781}
11782
11783#[gpui::test]
11784async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11785    init_test(cx, |_| {});
11786
11787    let mut cx = EditorTestContext::new(cx).await;
11788
11789    let diff_base = r#"
11790        use some::mod1;
11791        use some::mod2;
11792
11793        const A: u32 = 42;
11794        const B: u32 = 42;
11795        const C: u32 = 42;
11796
11797        fn main() {
11798            println!("hello");
11799
11800            println!("world");
11801        }
11802
11803        fn another() {
11804            println!("another");
11805        }
11806
11807        fn another2() {
11808            println!("another2");
11809        }
11810        "#
11811    .unindent();
11812
11813    cx.set_state(
11814        &r#"
11815        «use some::mod2;
11816
11817        const A: u32 = 42;
11818        const C: u32 = 42;
11819
11820        fn main() {
11821            //println!("hello");
11822
11823            println!("world");
11824            //
11825            //ˇ»
11826        }
11827
11828        fn another() {
11829            println!("another");
11830            println!("another");
11831        }
11832
11833            println!("another2");
11834        }
11835        "#
11836        .unindent(),
11837    );
11838
11839    cx.set_diff_base(Some(&diff_base));
11840    executor.run_until_parked();
11841
11842    cx.update_editor(|editor, cx| {
11843        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11844    });
11845    executor.run_until_parked();
11846
11847    cx.assert_diff_hunks(
11848        r#"
11849        - use some::mod1;
11850          use some::mod2;
11851
11852          const A: u32 = 42;
11853        - const B: u32 = 42;
11854          const C: u32 = 42;
11855
11856          fn main() {
11857        -     println!("hello");
11858        +     //println!("hello");
11859
11860              println!("world");
11861        +     //
11862        +     //
11863          }
11864
11865          fn another() {
11866              println!("another");
11867        +     println!("another");
11868          }
11869
11870        - fn another2() {
11871              println!("another2");
11872          }
11873        "#
11874        .unindent(),
11875    );
11876
11877    // Fold across some of the diff hunks. They should no longer appear expanded.
11878    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11879    cx.executor().run_until_parked();
11880
11881    // Hunks are not shown if their position is within a fold
11882    cx.assert_diff_hunks(
11883        r#"
11884          use some::mod2;
11885
11886          const A: u32 = 42;
11887          const C: u32 = 42;
11888
11889          fn main() {
11890              //println!("hello");
11891
11892              println!("world");
11893              //
11894              //
11895          }
11896
11897          fn another() {
11898              println!("another");
11899        +     println!("another");
11900          }
11901
11902        - fn another2() {
11903              println!("another2");
11904          }
11905        "#
11906        .unindent(),
11907    );
11908
11909    cx.update_editor(|editor, cx| {
11910        editor.select_all(&SelectAll, cx);
11911        editor.unfold_lines(&UnfoldLines, cx);
11912    });
11913    cx.executor().run_until_parked();
11914
11915    // The deletions reappear when unfolding.
11916    cx.assert_diff_hunks(
11917        r#"
11918        - use some::mod1;
11919          use some::mod2;
11920
11921          const A: u32 = 42;
11922        - const B: u32 = 42;
11923          const C: u32 = 42;
11924
11925          fn main() {
11926        -     println!("hello");
11927        +     //println!("hello");
11928
11929              println!("world");
11930        +     //
11931        +     //
11932          }
11933
11934          fn another() {
11935              println!("another");
11936        +     println!("another");
11937          }
11938
11939        - fn another2() {
11940              println!("another2");
11941          }
11942        "#
11943        .unindent(),
11944    );
11945}
11946
11947#[gpui::test]
11948async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11949    init_test(cx, |_| {});
11950
11951    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11952    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11953    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11954    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11955    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11956    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11957
11958    let buffer_1 = cx.new_model(|cx| {
11959        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11960        buffer.set_diff_base(Some(file_1_old.into()), cx);
11961        buffer
11962    });
11963    let buffer_2 = cx.new_model(|cx| {
11964        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11965        buffer.set_diff_base(Some(file_2_old.into()), cx);
11966        buffer
11967    });
11968    let buffer_3 = cx.new_model(|cx| {
11969        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11970        buffer.set_diff_base(Some(file_3_old.into()), cx);
11971        buffer
11972    });
11973
11974    let multi_buffer = cx.new_model(|cx| {
11975        let mut multibuffer = MultiBuffer::new(ReadWrite);
11976        multibuffer.push_excerpts(
11977            buffer_1.clone(),
11978            [
11979                ExcerptRange {
11980                    context: Point::new(0, 0)..Point::new(3, 0),
11981                    primary: None,
11982                },
11983                ExcerptRange {
11984                    context: Point::new(5, 0)..Point::new(7, 0),
11985                    primary: None,
11986                },
11987                ExcerptRange {
11988                    context: Point::new(9, 0)..Point::new(10, 3),
11989                    primary: None,
11990                },
11991            ],
11992            cx,
11993        );
11994        multibuffer.push_excerpts(
11995            buffer_2.clone(),
11996            [
11997                ExcerptRange {
11998                    context: Point::new(0, 0)..Point::new(3, 0),
11999                    primary: None,
12000                },
12001                ExcerptRange {
12002                    context: Point::new(5, 0)..Point::new(7, 0),
12003                    primary: None,
12004                },
12005                ExcerptRange {
12006                    context: Point::new(9, 0)..Point::new(10, 3),
12007                    primary: None,
12008                },
12009            ],
12010            cx,
12011        );
12012        multibuffer.push_excerpts(
12013            buffer_3.clone(),
12014            [
12015                ExcerptRange {
12016                    context: Point::new(0, 0)..Point::new(3, 0),
12017                    primary: None,
12018                },
12019                ExcerptRange {
12020                    context: Point::new(5, 0)..Point::new(7, 0),
12021                    primary: None,
12022                },
12023                ExcerptRange {
12024                    context: Point::new(9, 0)..Point::new(10, 3),
12025                    primary: None,
12026                },
12027            ],
12028            cx,
12029        );
12030        multibuffer
12031    });
12032
12033    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12034    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12035    cx.run_until_parked();
12036
12037    cx.assert_editor_state(
12038        &"
12039            ˇaaa
12040            ccc
12041            ddd
12042
12043            ggg
12044            hhh
12045
12046
12047            lll
12048            mmm
12049            NNN
12050
12051            qqq
12052            rrr
12053
12054            uuu
12055            111
12056            222
12057            333
12058
12059            666
12060            777
12061
12062            000
12063            !!!"
12064        .unindent(),
12065    );
12066
12067    cx.update_editor(|editor, cx| {
12068        editor.select_all(&SelectAll, cx);
12069        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12070    });
12071    cx.executor().run_until_parked();
12072
12073    cx.assert_diff_hunks(
12074        "
12075            aaa
12076          - bbb
12077            ccc
12078            ddd
12079
12080            ggg
12081            hhh
12082
12083
12084            lll
12085            mmm
12086          - nnn
12087          + NNN
12088
12089            qqq
12090            rrr
12091
12092            uuu
12093            111
12094            222
12095            333
12096
12097          + 666
12098            777
12099
12100            000
12101            !!!"
12102        .unindent(),
12103    );
12104}
12105
12106#[gpui::test]
12107async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12108    init_test(cx, |_| {});
12109
12110    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12111    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12112
12113    let buffer = cx.new_model(|cx| {
12114        let mut buffer = Buffer::local(text.to_string(), cx);
12115        buffer.set_diff_base(Some(base.into()), cx);
12116        buffer
12117    });
12118
12119    let multi_buffer = cx.new_model(|cx| {
12120        let mut multibuffer = MultiBuffer::new(ReadWrite);
12121        multibuffer.push_excerpts(
12122            buffer.clone(),
12123            [
12124                ExcerptRange {
12125                    context: Point::new(0, 0)..Point::new(2, 0),
12126                    primary: None,
12127                },
12128                ExcerptRange {
12129                    context: Point::new(5, 0)..Point::new(7, 0),
12130                    primary: None,
12131                },
12132            ],
12133            cx,
12134        );
12135        multibuffer
12136    });
12137
12138    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12139    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12140    cx.run_until_parked();
12141
12142    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12143    cx.executor().run_until_parked();
12144
12145    cx.assert_diff_hunks(
12146        "
12147            aaa
12148          - bbb
12149          + BBB
12150
12151          - ddd
12152          - eee
12153          + EEE
12154            fff
12155        "
12156        .unindent(),
12157    );
12158}
12159
12160#[gpui::test]
12161async fn test_edits_around_expanded_insertion_hunks(
12162    executor: BackgroundExecutor,
12163    cx: &mut gpui::TestAppContext,
12164) {
12165    init_test(cx, |_| {});
12166
12167    let mut cx = EditorTestContext::new(cx).await;
12168
12169    let diff_base = r#"
12170        use some::mod1;
12171        use some::mod2;
12172
12173        const A: u32 = 42;
12174
12175        fn main() {
12176            println!("hello");
12177
12178            println!("world");
12179        }
12180        "#
12181    .unindent();
12182    executor.run_until_parked();
12183    cx.set_state(
12184        &r#"
12185        use some::mod1;
12186        use some::mod2;
12187
12188        const A: u32 = 42;
12189        const B: u32 = 42;
12190        const C: u32 = 42;
12191        ˇ
12192
12193        fn main() {
12194            println!("hello");
12195
12196            println!("world");
12197        }
12198        "#
12199        .unindent(),
12200    );
12201
12202    cx.set_diff_base(Some(&diff_base));
12203    executor.run_until_parked();
12204
12205    cx.update_editor(|editor, cx| {
12206        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12207    });
12208    executor.run_until_parked();
12209
12210    cx.assert_diff_hunks(
12211        r#"
12212        use some::mod1;
12213        use some::mod2;
12214
12215        const A: u32 = 42;
12216      + const B: u32 = 42;
12217      + const C: u32 = 42;
12218      +
12219
12220        fn main() {
12221            println!("hello");
12222
12223            println!("world");
12224        }
12225        "#
12226        .unindent(),
12227    );
12228
12229    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12230    executor.run_until_parked();
12231
12232    cx.assert_diff_hunks(
12233        r#"
12234        use some::mod1;
12235        use some::mod2;
12236
12237        const A: u32 = 42;
12238      + const B: u32 = 42;
12239      + const C: u32 = 42;
12240      + const D: u32 = 42;
12241      +
12242
12243        fn main() {
12244            println!("hello");
12245
12246            println!("world");
12247        }
12248        "#
12249        .unindent(),
12250    );
12251
12252    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12253    executor.run_until_parked();
12254
12255    cx.assert_diff_hunks(
12256        r#"
12257        use some::mod1;
12258        use some::mod2;
12259
12260        const A: u32 = 42;
12261      + const B: u32 = 42;
12262      + const C: u32 = 42;
12263      + const D: u32 = 42;
12264      + const E: u32 = 42;
12265      +
12266
12267        fn main() {
12268            println!("hello");
12269
12270            println!("world");
12271        }
12272        "#
12273        .unindent(),
12274    );
12275
12276    cx.update_editor(|editor, cx| {
12277        editor.delete_line(&DeleteLine, cx);
12278    });
12279    executor.run_until_parked();
12280
12281    cx.assert_diff_hunks(
12282        r#"
12283        use some::mod1;
12284        use some::mod2;
12285
12286        const A: u32 = 42;
12287      + const B: u32 = 42;
12288      + const C: u32 = 42;
12289      + const D: u32 = 42;
12290      + const E: u32 = 42;
12291
12292        fn main() {
12293            println!("hello");
12294
12295            println!("world");
12296        }
12297        "#
12298        .unindent(),
12299    );
12300
12301    cx.update_editor(|editor, cx| {
12302        editor.move_up(&MoveUp, cx);
12303        editor.delete_line(&DeleteLine, cx);
12304        editor.move_up(&MoveUp, cx);
12305        editor.delete_line(&DeleteLine, cx);
12306        editor.move_up(&MoveUp, cx);
12307        editor.delete_line(&DeleteLine, cx);
12308    });
12309    executor.run_until_parked();
12310    cx.assert_editor_state(
12311        &r#"
12312        use some::mod1;
12313        use some::mod2;
12314
12315        const A: u32 = 42;
12316        const B: u32 = 42;
12317        ˇ
12318        fn main() {
12319            println!("hello");
12320
12321            println!("world");
12322        }
12323        "#
12324        .unindent(),
12325    );
12326
12327    cx.assert_diff_hunks(
12328        r#"
12329        use some::mod1;
12330        use some::mod2;
12331
12332        const A: u32 = 42;
12333      + const B: u32 = 42;
12334
12335        fn main() {
12336            println!("hello");
12337
12338            println!("world");
12339        }
12340        "#
12341        .unindent(),
12342    );
12343
12344    cx.update_editor(|editor, cx| {
12345        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12346        editor.delete_line(&DeleteLine, cx);
12347    });
12348    executor.run_until_parked();
12349    cx.assert_diff_hunks(
12350        r#"
12351        use some::mod1;
12352      - use some::mod2;
12353      -
12354      - const A: u32 = 42;
12355
12356        fn main() {
12357            println!("hello");
12358
12359            println!("world");
12360        }
12361        "#
12362        .unindent(),
12363    );
12364}
12365
12366#[gpui::test]
12367async fn test_edits_around_expanded_deletion_hunks(
12368    executor: BackgroundExecutor,
12369    cx: &mut gpui::TestAppContext,
12370) {
12371    init_test(cx, |_| {});
12372
12373    let mut cx = EditorTestContext::new(cx).await;
12374
12375    let diff_base = r#"
12376        use some::mod1;
12377        use some::mod2;
12378
12379        const A: u32 = 42;
12380        const B: u32 = 42;
12381        const C: u32 = 42;
12382
12383
12384        fn main() {
12385            println!("hello");
12386
12387            println!("world");
12388        }
12389    "#
12390    .unindent();
12391    executor.run_until_parked();
12392    cx.set_state(
12393        &r#"
12394        use some::mod1;
12395        use some::mod2;
12396
12397        ˇconst B: u32 = 42;
12398        const C: u32 = 42;
12399
12400
12401        fn main() {
12402            println!("hello");
12403
12404            println!("world");
12405        }
12406        "#
12407        .unindent(),
12408    );
12409
12410    cx.set_diff_base(Some(&diff_base));
12411    executor.run_until_parked();
12412
12413    cx.update_editor(|editor, cx| {
12414        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12415    });
12416    executor.run_until_parked();
12417
12418    cx.assert_diff_hunks(
12419        r#"
12420        use some::mod1;
12421        use some::mod2;
12422
12423      - const A: u32 = 42;
12424        const B: u32 = 42;
12425        const C: u32 = 42;
12426
12427
12428        fn main() {
12429            println!("hello");
12430
12431            println!("world");
12432        }
12433        "#
12434        .unindent(),
12435    );
12436
12437    cx.update_editor(|editor, cx| {
12438        editor.delete_line(&DeleteLine, cx);
12439    });
12440    executor.run_until_parked();
12441    cx.assert_editor_state(
12442        &r#"
12443        use some::mod1;
12444        use some::mod2;
12445
12446        ˇconst C: u32 = 42;
12447
12448
12449        fn main() {
12450            println!("hello");
12451
12452            println!("world");
12453        }
12454        "#
12455        .unindent(),
12456    );
12457    cx.assert_diff_hunks(
12458        r#"
12459        use some::mod1;
12460        use some::mod2;
12461
12462      - const A: u32 = 42;
12463      - const B: u32 = 42;
12464        const C: u32 = 42;
12465
12466
12467        fn main() {
12468            println!("hello");
12469
12470            println!("world");
12471        }
12472        "#
12473        .unindent(),
12474    );
12475
12476    cx.update_editor(|editor, cx| {
12477        editor.delete_line(&DeleteLine, cx);
12478    });
12479    executor.run_until_parked();
12480    cx.assert_editor_state(
12481        &r#"
12482        use some::mod1;
12483        use some::mod2;
12484
12485        ˇ
12486
12487        fn main() {
12488            println!("hello");
12489
12490            println!("world");
12491        }
12492        "#
12493        .unindent(),
12494    );
12495    cx.assert_diff_hunks(
12496        r#"
12497        use some::mod1;
12498        use some::mod2;
12499
12500      - const A: u32 = 42;
12501      - const B: u32 = 42;
12502      - const C: u32 = 42;
12503
12504
12505        fn main() {
12506            println!("hello");
12507
12508            println!("world");
12509        }
12510        "#
12511        .unindent(),
12512    );
12513
12514    cx.update_editor(|editor, cx| {
12515        editor.handle_input("replacement", cx);
12516    });
12517    executor.run_until_parked();
12518    cx.assert_editor_state(
12519        &r#"
12520        use some::mod1;
12521        use some::mod2;
12522
12523        replacementˇ
12524
12525        fn main() {
12526            println!("hello");
12527
12528            println!("world");
12529        }
12530        "#
12531        .unindent(),
12532    );
12533    cx.assert_diff_hunks(
12534        r#"
12535        use some::mod1;
12536        use some::mod2;
12537
12538      - const A: u32 = 42;
12539      - const B: u32 = 42;
12540      - const C: u32 = 42;
12541      -
12542      + replacement
12543
12544        fn main() {
12545            println!("hello");
12546
12547            println!("world");
12548        }
12549        "#
12550        .unindent(),
12551    );
12552}
12553
12554#[gpui::test]
12555async fn test_edit_after_expanded_modification_hunk(
12556    executor: BackgroundExecutor,
12557    cx: &mut gpui::TestAppContext,
12558) {
12559    init_test(cx, |_| {});
12560
12561    let mut cx = EditorTestContext::new(cx).await;
12562
12563    let diff_base = r#"
12564        use some::mod1;
12565        use some::mod2;
12566
12567        const A: u32 = 42;
12568        const B: u32 = 42;
12569        const C: u32 = 42;
12570        const D: u32 = 42;
12571
12572
12573        fn main() {
12574            println!("hello");
12575
12576            println!("world");
12577        }"#
12578    .unindent();
12579
12580    cx.set_state(
12581        &r#"
12582        use some::mod1;
12583        use some::mod2;
12584
12585        const A: u32 = 42;
12586        const B: u32 = 42;
12587        const C: u32 = 43ˇ
12588        const D: u32 = 42;
12589
12590
12591        fn main() {
12592            println!("hello");
12593
12594            println!("world");
12595        }"#
12596        .unindent(),
12597    );
12598
12599    cx.set_diff_base(Some(&diff_base));
12600    executor.run_until_parked();
12601    cx.update_editor(|editor, cx| {
12602        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12603    });
12604    executor.run_until_parked();
12605
12606    cx.assert_diff_hunks(
12607        r#"
12608        use some::mod1;
12609        use some::mod2;
12610
12611        const A: u32 = 42;
12612        const B: u32 = 42;
12613      - const C: u32 = 42;
12614      + const C: u32 = 43
12615        const D: u32 = 42;
12616
12617
12618        fn main() {
12619            println!("hello");
12620
12621            println!("world");
12622        }"#
12623        .unindent(),
12624    );
12625
12626    cx.update_editor(|editor, cx| {
12627        editor.handle_input("\nnew_line\n", cx);
12628    });
12629    executor.run_until_parked();
12630
12631    cx.assert_diff_hunks(
12632        r#"
12633        use some::mod1;
12634        use some::mod2;
12635
12636        const A: u32 = 42;
12637        const B: u32 = 42;
12638      - const C: u32 = 42;
12639      + const C: u32 = 43
12640      + new_line
12641      +
12642        const D: u32 = 42;
12643
12644
12645        fn main() {
12646            println!("hello");
12647
12648            println!("world");
12649        }"#
12650        .unindent(),
12651    );
12652}
12653
12654async fn setup_indent_guides_editor(
12655    text: &str,
12656    cx: &mut gpui::TestAppContext,
12657) -> (BufferId, EditorTestContext) {
12658    init_test(cx, |_| {});
12659
12660    let mut cx = EditorTestContext::new(cx).await;
12661
12662    let buffer_id = cx.update_editor(|editor, cx| {
12663        editor.set_text(text, cx);
12664        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12665
12666        buffer_ids[0]
12667    });
12668
12669    (buffer_id, cx)
12670}
12671
12672fn assert_indent_guides(
12673    range: Range<u32>,
12674    expected: Vec<IndentGuide>,
12675    active_indices: Option<Vec<usize>>,
12676    cx: &mut EditorTestContext,
12677) {
12678    let indent_guides = cx.update_editor(|editor, cx| {
12679        let snapshot = editor.snapshot(cx).display_snapshot;
12680        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12681            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12682            true,
12683            &snapshot,
12684            cx,
12685        );
12686
12687        indent_guides.sort_by(|a, b| {
12688            a.depth.cmp(&b.depth).then(
12689                a.start_row
12690                    .cmp(&b.start_row)
12691                    .then(a.end_row.cmp(&b.end_row)),
12692            )
12693        });
12694        indent_guides
12695    });
12696
12697    if let Some(expected) = active_indices {
12698        let active_indices = cx.update_editor(|editor, cx| {
12699            let snapshot = editor.snapshot(cx).display_snapshot;
12700            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12701        });
12702
12703        assert_eq!(
12704            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12705            expected,
12706            "Active indent guide indices do not match"
12707        );
12708    }
12709
12710    let expected: Vec<_> = expected
12711        .into_iter()
12712        .map(|guide| MultiBufferIndentGuide {
12713            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12714            buffer: guide,
12715        })
12716        .collect();
12717
12718    assert_eq!(indent_guides, expected, "Indent guides do not match");
12719}
12720
12721fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12722    IndentGuide {
12723        buffer_id,
12724        start_row,
12725        end_row,
12726        depth,
12727        tab_size: 4,
12728        settings: IndentGuideSettings {
12729            enabled: true,
12730            line_width: 1,
12731            active_line_width: 1,
12732            ..Default::default()
12733        },
12734    }
12735}
12736
12737#[gpui::test]
12738async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12739    let (buffer_id, mut cx) = setup_indent_guides_editor(
12740        &"
12741    fn main() {
12742        let a = 1;
12743    }"
12744        .unindent(),
12745        cx,
12746    )
12747    .await;
12748
12749    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12750}
12751
12752#[gpui::test]
12753async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12754    let (buffer_id, mut cx) = setup_indent_guides_editor(
12755        &"
12756    fn main() {
12757        let a = 1;
12758        let b = 2;
12759    }"
12760        .unindent(),
12761        cx,
12762    )
12763    .await;
12764
12765    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12766}
12767
12768#[gpui::test]
12769async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12770    let (buffer_id, mut cx) = setup_indent_guides_editor(
12771        &"
12772    fn main() {
12773        let a = 1;
12774        if a == 3 {
12775            let b = 2;
12776        } else {
12777            let c = 3;
12778        }
12779    }"
12780        .unindent(),
12781        cx,
12782    )
12783    .await;
12784
12785    assert_indent_guides(
12786        0..8,
12787        vec![
12788            indent_guide(buffer_id, 1, 6, 0),
12789            indent_guide(buffer_id, 3, 3, 1),
12790            indent_guide(buffer_id, 5, 5, 1),
12791        ],
12792        None,
12793        &mut cx,
12794    );
12795}
12796
12797#[gpui::test]
12798async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12799    let (buffer_id, mut cx) = setup_indent_guides_editor(
12800        &"
12801    fn main() {
12802        let a = 1;
12803            let b = 2;
12804        let c = 3;
12805    }"
12806        .unindent(),
12807        cx,
12808    )
12809    .await;
12810
12811    assert_indent_guides(
12812        0..5,
12813        vec![
12814            indent_guide(buffer_id, 1, 3, 0),
12815            indent_guide(buffer_id, 2, 2, 1),
12816        ],
12817        None,
12818        &mut cx,
12819    );
12820}
12821
12822#[gpui::test]
12823async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12824    let (buffer_id, mut cx) = setup_indent_guides_editor(
12825        &"
12826        fn main() {
12827            let a = 1;
12828
12829            let c = 3;
12830        }"
12831        .unindent(),
12832        cx,
12833    )
12834    .await;
12835
12836    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12837}
12838
12839#[gpui::test]
12840async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12841    let (buffer_id, mut cx) = setup_indent_guides_editor(
12842        &"
12843        fn main() {
12844            let a = 1;
12845
12846            let c = 3;
12847
12848            if a == 3 {
12849                let b = 2;
12850            } else {
12851                let c = 3;
12852            }
12853        }"
12854        .unindent(),
12855        cx,
12856    )
12857    .await;
12858
12859    assert_indent_guides(
12860        0..11,
12861        vec![
12862            indent_guide(buffer_id, 1, 9, 0),
12863            indent_guide(buffer_id, 6, 6, 1),
12864            indent_guide(buffer_id, 8, 8, 1),
12865        ],
12866        None,
12867        &mut cx,
12868    );
12869}
12870
12871#[gpui::test]
12872async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12873    let (buffer_id, mut cx) = setup_indent_guides_editor(
12874        &"
12875        fn main() {
12876            let a = 1;
12877
12878            let c = 3;
12879
12880            if a == 3 {
12881                let b = 2;
12882            } else {
12883                let c = 3;
12884            }
12885        }"
12886        .unindent(),
12887        cx,
12888    )
12889    .await;
12890
12891    assert_indent_guides(
12892        1..11,
12893        vec![
12894            indent_guide(buffer_id, 1, 9, 0),
12895            indent_guide(buffer_id, 6, 6, 1),
12896            indent_guide(buffer_id, 8, 8, 1),
12897        ],
12898        None,
12899        &mut cx,
12900    );
12901}
12902
12903#[gpui::test]
12904async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12905    let (buffer_id, mut cx) = setup_indent_guides_editor(
12906        &"
12907        fn main() {
12908            let a = 1;
12909
12910            let c = 3;
12911
12912            if a == 3 {
12913                let b = 2;
12914            } else {
12915                let c = 3;
12916            }
12917        }"
12918        .unindent(),
12919        cx,
12920    )
12921    .await;
12922
12923    assert_indent_guides(
12924        1..10,
12925        vec![
12926            indent_guide(buffer_id, 1, 9, 0),
12927            indent_guide(buffer_id, 6, 6, 1),
12928            indent_guide(buffer_id, 8, 8, 1),
12929        ],
12930        None,
12931        &mut cx,
12932    );
12933}
12934
12935#[gpui::test]
12936async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12937    let (buffer_id, mut cx) = setup_indent_guides_editor(
12938        &"
12939        block1
12940            block2
12941                block3
12942                    block4
12943            block2
12944        block1
12945        block1"
12946            .unindent(),
12947        cx,
12948    )
12949    .await;
12950
12951    assert_indent_guides(
12952        1..10,
12953        vec![
12954            indent_guide(buffer_id, 1, 4, 0),
12955            indent_guide(buffer_id, 2, 3, 1),
12956            indent_guide(buffer_id, 3, 3, 2),
12957        ],
12958        None,
12959        &mut cx,
12960    );
12961}
12962
12963#[gpui::test]
12964async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12965    let (buffer_id, mut cx) = setup_indent_guides_editor(
12966        &"
12967        block1
12968            block2
12969                block3
12970
12971        block1
12972        block1"
12973            .unindent(),
12974        cx,
12975    )
12976    .await;
12977
12978    assert_indent_guides(
12979        0..6,
12980        vec![
12981            indent_guide(buffer_id, 1, 2, 0),
12982            indent_guide(buffer_id, 2, 2, 1),
12983        ],
12984        None,
12985        &mut cx,
12986    );
12987}
12988
12989#[gpui::test]
12990async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12991    let (buffer_id, mut cx) = setup_indent_guides_editor(
12992        &"
12993        block1
12994
12995
12996
12997            block2
12998        "
12999        .unindent(),
13000        cx,
13001    )
13002    .await;
13003
13004    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13005}
13006
13007#[gpui::test]
13008async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13009    let (buffer_id, mut cx) = setup_indent_guides_editor(
13010        &"
13011        def a:
13012        \tb = 3
13013        \tif True:
13014        \t\tc = 4
13015        \t\td = 5
13016        \tprint(b)
13017        "
13018        .unindent(),
13019        cx,
13020    )
13021    .await;
13022
13023    assert_indent_guides(
13024        0..6,
13025        vec![
13026            indent_guide(buffer_id, 1, 6, 0),
13027            indent_guide(buffer_id, 3, 4, 1),
13028        ],
13029        None,
13030        &mut cx,
13031    );
13032}
13033
13034#[gpui::test]
13035async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13036    let (buffer_id, mut cx) = setup_indent_guides_editor(
13037        &"
13038    fn main() {
13039        let a = 1;
13040    }"
13041        .unindent(),
13042        cx,
13043    )
13044    .await;
13045
13046    cx.update_editor(|editor, cx| {
13047        editor.change_selections(None, cx, |s| {
13048            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13049        });
13050    });
13051
13052    assert_indent_guides(
13053        0..3,
13054        vec![indent_guide(buffer_id, 1, 1, 0)],
13055        Some(vec![0]),
13056        &mut cx,
13057    );
13058}
13059
13060#[gpui::test]
13061async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13062    let (buffer_id, mut cx) = setup_indent_guides_editor(
13063        &"
13064    fn main() {
13065        if 1 == 2 {
13066            let a = 1;
13067        }
13068    }"
13069        .unindent(),
13070        cx,
13071    )
13072    .await;
13073
13074    cx.update_editor(|editor, cx| {
13075        editor.change_selections(None, cx, |s| {
13076            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13077        });
13078    });
13079
13080    assert_indent_guides(
13081        0..4,
13082        vec![
13083            indent_guide(buffer_id, 1, 3, 0),
13084            indent_guide(buffer_id, 2, 2, 1),
13085        ],
13086        Some(vec![1]),
13087        &mut cx,
13088    );
13089
13090    cx.update_editor(|editor, cx| {
13091        editor.change_selections(None, cx, |s| {
13092            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13093        });
13094    });
13095
13096    assert_indent_guides(
13097        0..4,
13098        vec![
13099            indent_guide(buffer_id, 1, 3, 0),
13100            indent_guide(buffer_id, 2, 2, 1),
13101        ],
13102        Some(vec![1]),
13103        &mut cx,
13104    );
13105
13106    cx.update_editor(|editor, cx| {
13107        editor.change_selections(None, cx, |s| {
13108            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13109        });
13110    });
13111
13112    assert_indent_guides(
13113        0..4,
13114        vec![
13115            indent_guide(buffer_id, 1, 3, 0),
13116            indent_guide(buffer_id, 2, 2, 1),
13117        ],
13118        Some(vec![0]),
13119        &mut cx,
13120    );
13121}
13122
13123#[gpui::test]
13124async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13125    let (buffer_id, mut cx) = setup_indent_guides_editor(
13126        &"
13127    fn main() {
13128        let a = 1;
13129
13130        let b = 2;
13131    }"
13132        .unindent(),
13133        cx,
13134    )
13135    .await;
13136
13137    cx.update_editor(|editor, cx| {
13138        editor.change_selections(None, cx, |s| {
13139            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13140        });
13141    });
13142
13143    assert_indent_guides(
13144        0..5,
13145        vec![indent_guide(buffer_id, 1, 3, 0)],
13146        Some(vec![0]),
13147        &mut cx,
13148    );
13149}
13150
13151#[gpui::test]
13152async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13153    let (buffer_id, mut cx) = setup_indent_guides_editor(
13154        &"
13155    def m:
13156        a = 1
13157        pass"
13158            .unindent(),
13159        cx,
13160    )
13161    .await;
13162
13163    cx.update_editor(|editor, cx| {
13164        editor.change_selections(None, cx, |s| {
13165            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13166        });
13167    });
13168
13169    assert_indent_guides(
13170        0..3,
13171        vec![indent_guide(buffer_id, 1, 2, 0)],
13172        Some(vec![0]),
13173        &mut cx,
13174    );
13175}
13176
13177#[gpui::test]
13178fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13179    init_test(cx, |_| {});
13180
13181    let editor = cx.add_window(|cx| {
13182        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13183        build_editor(buffer, cx)
13184    });
13185
13186    let render_args = Arc::new(Mutex::new(None));
13187    let snapshot = editor
13188        .update(cx, |editor, cx| {
13189            let snapshot = editor.buffer().read(cx).snapshot(cx);
13190            let range =
13191                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13192
13193            struct RenderArgs {
13194                row: MultiBufferRow,
13195                folded: bool,
13196                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13197            }
13198
13199            let crease = Crease::new(
13200                range,
13201                FoldPlaceholder::test(),
13202                {
13203                    let toggle_callback = render_args.clone();
13204                    move |row, folded, callback, _cx| {
13205                        *toggle_callback.lock() = Some(RenderArgs {
13206                            row,
13207                            folded,
13208                            callback,
13209                        });
13210                        div()
13211                    }
13212                },
13213                |_row, _folded, _cx| div(),
13214            );
13215
13216            editor.insert_creases(Some(crease), cx);
13217            let snapshot = editor.snapshot(cx);
13218            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13219            snapshot
13220        })
13221        .unwrap();
13222
13223    let render_args = render_args.lock().take().unwrap();
13224    assert_eq!(render_args.row, MultiBufferRow(1));
13225    assert!(!render_args.folded);
13226    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13227
13228    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13229        .unwrap();
13230    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13231    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13232
13233    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13234        .unwrap();
13235    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13236    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13237}
13238
13239#[gpui::test]
13240async fn test_input_text(cx: &mut gpui::TestAppContext) {
13241    init_test(cx, |_| {});
13242    let mut cx = EditorTestContext::new(cx).await;
13243
13244    cx.set_state(
13245        &r#"ˇone
13246        two
13247
13248        three
13249        fourˇ
13250        five
13251
13252        siˇx"#
13253            .unindent(),
13254    );
13255
13256    cx.dispatch_action(HandleInput(String::new()));
13257    cx.assert_editor_state(
13258        &r#"ˇone
13259        two
13260
13261        three
13262        fourˇ
13263        five
13264
13265        siˇx"#
13266            .unindent(),
13267    );
13268
13269    cx.dispatch_action(HandleInput("AAAA".to_string()));
13270    cx.assert_editor_state(
13271        &r#"AAAAˇone
13272        two
13273
13274        three
13275        fourAAAAˇ
13276        five
13277
13278        siAAAAˇx"#
13279            .unindent(),
13280    );
13281}
13282
13283#[gpui::test]
13284async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13285    init_test(cx, |_| {});
13286
13287    let mut cx = EditorTestContext::new(cx).await;
13288    cx.set_state(
13289        r#"let foo = 1;
13290let foo = 2;
13291let foo = 3;
13292let fooˇ = 4;
13293let foo = 5;
13294let foo = 6;
13295let foo = 7;
13296let foo = 8;
13297let foo = 9;
13298let foo = 10;
13299let foo = 11;
13300let foo = 12;
13301let foo = 13;
13302let foo = 14;
13303let foo = 15;"#,
13304    );
13305
13306    cx.update_editor(|e, cx| {
13307        assert_eq!(
13308            e.next_scroll_position,
13309            NextScrollCursorCenterTopBottom::Center,
13310            "Default next scroll direction is center",
13311        );
13312
13313        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13314        assert_eq!(
13315            e.next_scroll_position,
13316            NextScrollCursorCenterTopBottom::Top,
13317            "After center, next scroll direction should be top",
13318        );
13319
13320        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13321        assert_eq!(
13322            e.next_scroll_position,
13323            NextScrollCursorCenterTopBottom::Bottom,
13324            "After top, next scroll direction should be bottom",
13325        );
13326
13327        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13328        assert_eq!(
13329            e.next_scroll_position,
13330            NextScrollCursorCenterTopBottom::Center,
13331            "After bottom, scrolling should start over",
13332        );
13333
13334        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13335        assert_eq!(
13336            e.next_scroll_position,
13337            NextScrollCursorCenterTopBottom::Top,
13338            "Scrolling continues if retriggered fast enough"
13339        );
13340    });
13341
13342    cx.executor()
13343        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13344    cx.executor().run_until_parked();
13345    cx.update_editor(|e, _| {
13346        assert_eq!(
13347            e.next_scroll_position,
13348            NextScrollCursorCenterTopBottom::Center,
13349            "If scrolling is not triggered fast enough, it should reset"
13350        );
13351    });
13352}
13353
13354#[gpui::test]
13355async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13356    init_test(cx, |_| {});
13357    let mut cx = EditorLspTestContext::new_rust(
13358        lsp::ServerCapabilities {
13359            definition_provider: Some(lsp::OneOf::Left(true)),
13360            references_provider: Some(lsp::OneOf::Left(true)),
13361            ..lsp::ServerCapabilities::default()
13362        },
13363        cx,
13364    )
13365    .await;
13366
13367    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13368        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13369            move |params, _| async move {
13370                if empty_go_to_definition {
13371                    Ok(None)
13372                } else {
13373                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13374                        uri: params.text_document_position_params.text_document.uri,
13375                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13376                    })))
13377                }
13378            },
13379        );
13380        let references =
13381            cx.lsp
13382                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13383                    Ok(Some(vec![lsp::Location {
13384                        uri: params.text_document_position.text_document.uri,
13385                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13386                    }]))
13387                });
13388        (go_to_definition, references)
13389    };
13390
13391    cx.set_state(
13392        &r#"fn one() {
13393            let mut a = ˇtwo();
13394        }
13395
13396        fn two() {}"#
13397            .unindent(),
13398    );
13399    set_up_lsp_handlers(false, &mut cx);
13400    let navigated = cx
13401        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13402        .await
13403        .expect("Failed to navigate to definition");
13404    assert_eq!(
13405        navigated,
13406        Navigated::Yes,
13407        "Should have navigated to definition from the GetDefinition response"
13408    );
13409    cx.assert_editor_state(
13410        &r#"fn one() {
13411            let mut a = two();
13412        }
13413
13414        fn «twoˇ»() {}"#
13415            .unindent(),
13416    );
13417
13418    let editors = cx.update_workspace(|workspace, cx| {
13419        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13420    });
13421    cx.update_editor(|_, test_editor_cx| {
13422        assert_eq!(
13423            editors.len(),
13424            1,
13425            "Initially, only one, test, editor should be open in the workspace"
13426        );
13427        assert_eq!(
13428            test_editor_cx.view(),
13429            editors.last().expect("Asserted len is 1")
13430        );
13431    });
13432
13433    set_up_lsp_handlers(true, &mut cx);
13434    let navigated = cx
13435        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13436        .await
13437        .expect("Failed to navigate to lookup references");
13438    assert_eq!(
13439        navigated,
13440        Navigated::Yes,
13441        "Should have navigated to references as a fallback after empty GoToDefinition response"
13442    );
13443    // We should not change the selections in the existing file,
13444    // if opening another milti buffer with the references
13445    cx.assert_editor_state(
13446        &r#"fn one() {
13447            let mut a = two();
13448        }
13449
13450        fn «twoˇ»() {}"#
13451            .unindent(),
13452    );
13453    let editors = cx.update_workspace(|workspace, cx| {
13454        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13455    });
13456    cx.update_editor(|_, test_editor_cx| {
13457        assert_eq!(
13458            editors.len(),
13459            2,
13460            "After falling back to references search, we open a new editor with the results"
13461        );
13462        let references_fallback_text = editors
13463            .into_iter()
13464            .find(|new_editor| new_editor != test_editor_cx.view())
13465            .expect("Should have one non-test editor now")
13466            .read(test_editor_cx)
13467            .text(test_editor_cx);
13468        assert_eq!(
13469            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13470            "Should use the range from the references response and not the GoToDefinition one"
13471        );
13472    });
13473}
13474
13475#[gpui::test]
13476async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13477    init_test(cx, |_| {});
13478
13479    let language = Arc::new(Language::new(
13480        LanguageConfig::default(),
13481        Some(tree_sitter_rust::LANGUAGE.into()),
13482    ));
13483
13484    let text = r#"
13485        #[cfg(test)]
13486        mod tests() {
13487            #[test]
13488            fn runnable_1() {
13489                let a = 1;
13490            }
13491
13492            #[test]
13493            fn runnable_2() {
13494                let a = 1;
13495                let b = 2;
13496            }
13497        }
13498    "#
13499    .unindent();
13500
13501    let fs = FakeFs::new(cx.executor());
13502    fs.insert_file("/file.rs", Default::default()).await;
13503
13504    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13505    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13506    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13507    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13508    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13509
13510    let editor = cx.new_view(|cx| {
13511        Editor::new(
13512            EditorMode::Full,
13513            multi_buffer,
13514            Some(project.clone()),
13515            true,
13516            cx,
13517        )
13518    });
13519
13520    editor.update(cx, |editor, cx| {
13521        editor.tasks.insert(
13522            (buffer.read(cx).remote_id(), 3),
13523            RunnableTasks {
13524                templates: vec![],
13525                offset: MultiBufferOffset(43),
13526                column: 0,
13527                extra_variables: HashMap::default(),
13528                context_range: BufferOffset(43)..BufferOffset(85),
13529            },
13530        );
13531        editor.tasks.insert(
13532            (buffer.read(cx).remote_id(), 8),
13533            RunnableTasks {
13534                templates: vec![],
13535                offset: MultiBufferOffset(86),
13536                column: 0,
13537                extra_variables: HashMap::default(),
13538                context_range: BufferOffset(86)..BufferOffset(191),
13539            },
13540        );
13541
13542        // Test finding task when cursor is inside function body
13543        editor.change_selections(None, cx, |s| {
13544            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13545        });
13546        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13547        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13548
13549        // Test finding task when cursor is on function name
13550        editor.change_selections(None, cx, |s| {
13551            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13552        });
13553        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13554        assert_eq!(row, 8, "Should find task when cursor is on function name");
13555    });
13556}
13557
13558fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13559    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13560    point..point
13561}
13562
13563fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13564    let (text, ranges) = marked_text_ranges(marked_text, true);
13565    assert_eq!(view.text(cx), text);
13566    assert_eq!(
13567        view.selections.ranges(cx),
13568        ranges,
13569        "Assert selections are {}",
13570        marked_text
13571    );
13572}
13573
13574pub fn handle_signature_help_request(
13575    cx: &mut EditorLspTestContext,
13576    mocked_response: lsp::SignatureHelp,
13577) -> impl Future<Output = ()> {
13578    let mut request =
13579        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13580            let mocked_response = mocked_response.clone();
13581            async move { Ok(Some(mocked_response)) }
13582        });
13583
13584    async move {
13585        request.next().await;
13586    }
13587}
13588
13589/// Handle completion request passing a marked string specifying where the completion
13590/// should be triggered from using '|' character, what range should be replaced, and what completions
13591/// should be returned using '<' and '>' to delimit the range
13592pub fn handle_completion_request(
13593    cx: &mut EditorLspTestContext,
13594    marked_string: &str,
13595    completions: Vec<&'static str>,
13596    counter: Arc<AtomicUsize>,
13597) -> impl Future<Output = ()> {
13598    let complete_from_marker: TextRangeMarker = '|'.into();
13599    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13600    let (_, mut marked_ranges) = marked_text_ranges_by(
13601        marked_string,
13602        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13603    );
13604
13605    let complete_from_position =
13606        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13607    let replace_range =
13608        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13609
13610    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13611        let completions = completions.clone();
13612        counter.fetch_add(1, atomic::Ordering::Release);
13613        async move {
13614            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13615            assert_eq!(
13616                params.text_document_position.position,
13617                complete_from_position
13618            );
13619            Ok(Some(lsp::CompletionResponse::Array(
13620                completions
13621                    .iter()
13622                    .map(|completion_text| lsp::CompletionItem {
13623                        label: completion_text.to_string(),
13624                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13625                            range: replace_range,
13626                            new_text: completion_text.to_string(),
13627                        })),
13628                        ..Default::default()
13629                    })
13630                    .collect(),
13631            )))
13632        }
13633    });
13634
13635    async move {
13636        request.next().await;
13637    }
13638}
13639
13640fn handle_resolve_completion_request(
13641    cx: &mut EditorLspTestContext,
13642    edits: Option<Vec<(&'static str, &'static str)>>,
13643) -> impl Future<Output = ()> {
13644    let edits = edits.map(|edits| {
13645        edits
13646            .iter()
13647            .map(|(marked_string, new_text)| {
13648                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13649                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13650                lsp::TextEdit::new(replace_range, new_text.to_string())
13651            })
13652            .collect::<Vec<_>>()
13653    });
13654
13655    let mut request =
13656        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13657            let edits = edits.clone();
13658            async move {
13659                Ok(lsp::CompletionItem {
13660                    additional_text_edits: edits,
13661                    ..Default::default()
13662                })
13663            }
13664        });
13665
13666    async move {
13667        request.next().await;
13668    }
13669}
13670
13671pub(crate) fn update_test_language_settings(
13672    cx: &mut TestAppContext,
13673    f: impl Fn(&mut AllLanguageSettingsContent),
13674) {
13675    cx.update(|cx| {
13676        SettingsStore::update_global(cx, |store, cx| {
13677            store.update_user_settings::<AllLanguageSettings>(cx, f);
13678        });
13679    });
13680}
13681
13682pub(crate) fn update_test_project_settings(
13683    cx: &mut TestAppContext,
13684    f: impl Fn(&mut ProjectSettings),
13685) {
13686    cx.update(|cx| {
13687        SettingsStore::update_global(cx, |store, cx| {
13688            store.update_user_settings::<ProjectSettings>(cx, f);
13689        });
13690    });
13691}
13692
13693pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13694    cx.update(|cx| {
13695        assets::Assets.load_test_fonts(cx);
13696        let store = SettingsStore::test(cx);
13697        cx.set_global(store);
13698        theme::init(theme::LoadThemes::JustBase, cx);
13699        release_channel::init(SemanticVersion::default(), cx);
13700        client::init_settings(cx);
13701        language::init(cx);
13702        Project::init_settings(cx);
13703        workspace::init_settings(cx);
13704        crate::init(cx);
13705    });
13706
13707    update_test_language_settings(cx, f);
13708}
13709
13710pub(crate) fn rust_lang() -> Arc<Language> {
13711    Arc::new(Language::new(
13712        LanguageConfig {
13713            name: "Rust".into(),
13714            matcher: LanguageMatcher {
13715                path_suffixes: vec!["rs".to_string()],
13716                ..Default::default()
13717            },
13718            ..Default::default()
13719        },
13720        Some(tree_sitter_rust::LANGUAGE.into()),
13721    ))
13722}
13723
13724#[track_caller]
13725fn assert_hunk_revert(
13726    not_reverted_text_with_selections: &str,
13727    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13728    expected_reverted_text_with_selections: &str,
13729    base_text: &str,
13730    cx: &mut EditorLspTestContext,
13731) {
13732    cx.set_state(not_reverted_text_with_selections);
13733    cx.update_editor(|editor, cx| {
13734        editor
13735            .buffer()
13736            .read(cx)
13737            .as_singleton()
13738            .unwrap()
13739            .update(cx, |buffer, cx| {
13740                buffer.set_diff_base(Some(base_text.into()), cx);
13741            });
13742    });
13743    cx.executor().run_until_parked();
13744
13745    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13746        let snapshot = editor.buffer().read(cx).snapshot(cx);
13747        let reverted_hunk_statuses = snapshot
13748            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13749            .map(|hunk| hunk_status(&hunk))
13750            .collect::<Vec<_>>();
13751
13752        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13753        reverted_hunk_statuses
13754    });
13755    cx.executor().run_until_parked();
13756    cx.assert_editor_state(expected_reverted_text_with_selections);
13757    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13758}