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