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 buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  173    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  174    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  175    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  176
  177    _ = editor.update(cx, |editor, cx| {
  178        editor.start_transaction_at(now, cx);
  179        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  180
  181        editor.insert("cd", cx);
  182        editor.end_transaction_at(now, cx);
  183        assert_eq!(editor.text(cx), "12cd56");
  184        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  185
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  188        editor.insert("e", cx);
  189        editor.end_transaction_at(now, cx);
  190        assert_eq!(editor.text(cx), "12cde6");
  191        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  192
  193        now += group_interval + Duration::from_millis(1);
  194        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  195
  196        // Simulate an edit in another editor
  197        buffer.update(cx, |buffer, cx| {
  198            buffer.start_transaction_at(now, cx);
  199            buffer.edit([(0..1, "a")], None, cx);
  200            buffer.edit([(1..1, "b")], None, cx);
  201            buffer.end_transaction_at(now, cx);
  202        });
  203
  204        assert_eq!(editor.text(cx), "ab2cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  206
  207        // Last transaction happened past the group interval in a different editor.
  208        // Undo it individually and don't restore selections.
  209        editor.undo(&Undo, cx);
  210        assert_eq!(editor.text(cx), "12cde6");
  211        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  212
  213        // First two transactions happened within the group interval in this editor.
  214        // Undo them together and restore selections.
  215        editor.undo(&Undo, cx);
  216        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  217        assert_eq!(editor.text(cx), "123456");
  218        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  219
  220        // Redo the first two transactions together.
  221        editor.redo(&Redo, cx);
  222        assert_eq!(editor.text(cx), "12cde6");
  223        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  224
  225        // Redo the last transaction on its own.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  229
  230        // Test empty transactions.
  231        editor.start_transaction_at(now, cx);
  232        editor.end_transaction_at(now, cx);
  233        editor.undo(&Undo, cx);
  234        assert_eq!(editor.text(cx), "12cde6");
  235    });
  236}
  237
  238#[gpui::test]
  239fn test_ime_composition(cx: &mut TestAppContext) {
  240    init_test(cx, |_| {});
  241
  242    let buffer = cx.new_model(|cx| {
  243        let mut buffer = language::Buffer::local("abcde", cx);
  244        // Ensure automatic grouping doesn't occur.
  245        buffer.set_group_interval(Duration::ZERO);
  246        buffer
  247    });
  248
  249    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  250    cx.add_window(|cx| {
  251        let mut editor = build_editor(buffer.clone(), cx);
  252
  253        // Start a new IME composition.
  254        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  255        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  257        assert_eq!(editor.text(cx), "äbcde");
  258        assert_eq!(
  259            editor.marked_text_ranges(cx),
  260            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  261        );
  262
  263        // Finalize IME composition.
  264        editor.replace_text_in_range(None, "ā", cx);
  265        assert_eq!(editor.text(cx), "ābcde");
  266        assert_eq!(editor.marked_text_ranges(cx), None);
  267
  268        // IME composition edits are grouped and are undone/redone at once.
  269        editor.undo(&Default::default(), cx);
  270        assert_eq!(editor.text(cx), "abcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272        editor.redo(&Default::default(), cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  278        assert_eq!(
  279            editor.marked_text_ranges(cx),
  280            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  281        );
  282
  283        // Undoing during an IME composition cancels it.
  284        editor.undo(&Default::default(), cx);
  285        assert_eq!(editor.text(cx), "ābcde");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287
  288        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  289        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  290        assert_eq!(editor.text(cx), "ābcdè");
  291        assert_eq!(
  292            editor.marked_text_ranges(cx),
  293            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  294        );
  295
  296        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  297        editor.replace_text_in_range(Some(4..999), "ę", cx);
  298        assert_eq!(editor.text(cx), "ābcdę");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition with multiple cursors.
  302        editor.change_selections(None, cx, |s| {
  303            s.select_ranges([
  304                OffsetUtf16(1)..OffsetUtf16(1),
  305                OffsetUtf16(3)..OffsetUtf16(3),
  306                OffsetUtf16(5)..OffsetUtf16(5),
  307            ])
  308        });
  309        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  310        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  311        assert_eq!(
  312            editor.marked_text_ranges(cx),
  313            Some(vec![
  314                OffsetUtf16(0)..OffsetUtf16(3),
  315                OffsetUtf16(4)..OffsetUtf16(7),
  316                OffsetUtf16(8)..OffsetUtf16(11)
  317            ])
  318        );
  319
  320        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  321        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  322        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  323        assert_eq!(
  324            editor.marked_text_ranges(cx),
  325            Some(vec![
  326                OffsetUtf16(1)..OffsetUtf16(2),
  327                OffsetUtf16(5)..OffsetUtf16(6),
  328                OffsetUtf16(9)..OffsetUtf16(10)
  329            ])
  330        );
  331
  332        // Finalize IME composition with multiple cursors.
  333        editor.replace_text_in_range(Some(9..10), "2", cx);
  334        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  335        assert_eq!(editor.marked_text_ranges(cx), None);
  336
  337        editor
  338    });
  339}
  340
  341#[gpui::test]
  342fn test_selection_with_mouse(cx: &mut TestAppContext) {
  343    init_test(cx, |_| {});
  344
  345    let editor = cx.add_window(|cx| {
  346        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  347        build_editor(buffer, cx)
  348    });
  349
  350    _ = editor.update(cx, |view, cx| {
  351        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  352    });
  353    assert_eq!(
  354        editor
  355            .update(cx, |view, cx| view.selections.display_ranges(cx))
  356            .unwrap(),
  357        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  358    );
  359
  360    _ = editor.update(cx, |view, cx| {
  361        view.update_selection(
  362            DisplayPoint::new(DisplayRow(3), 3),
  363            0,
  364            gpui::Point::<f32>::default(),
  365            cx,
  366        );
  367    });
  368
  369    assert_eq!(
  370        editor
  371            .update(cx, |view, cx| view.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  374    );
  375
  376    _ = editor.update(cx, |view, cx| {
  377        view.update_selection(
  378            DisplayPoint::new(DisplayRow(1), 1),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            cx,
  382        );
  383    });
  384
  385    assert_eq!(
  386        editor
  387            .update(cx, |view, cx| view.selections.display_ranges(cx))
  388            .unwrap(),
  389        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  390    );
  391
  392    _ = editor.update(cx, |view, cx| {
  393        view.end_selection(cx);
  394        view.update_selection(
  395            DisplayPoint::new(DisplayRow(3), 3),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            cx,
  399        );
  400    });
  401
  402    assert_eq!(
  403        editor
  404            .update(cx, |view, cx| view.selections.display_ranges(cx))
  405            .unwrap(),
  406        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  407    );
  408
  409    _ = editor.update(cx, |view, cx| {
  410        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  411        view.update_selection(
  412            DisplayPoint::new(DisplayRow(0), 0),
  413            0,
  414            gpui::Point::<f32>::default(),
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |view, cx| view.selections.display_ranges(cx))
  422            .unwrap(),
  423        [
  424            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  425            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  426        ]
  427    );
  428
  429    _ = editor.update(cx, |view, cx| {
  430        view.end_selection(cx);
  431    });
  432
  433    assert_eq!(
  434        editor
  435            .update(cx, |view, cx| view.selections.display_ranges(cx))
  436            .unwrap(),
  437        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  438    );
  439}
  440
  441#[gpui::test]
  442fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  443    init_test(cx, |_| {});
  444
  445    let editor = cx.add_window(|cx| {
  446        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  447        build_editor(buffer, cx)
  448    });
  449
  450    _ = editor.update(cx, |view, cx| {
  451        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.end_selection(cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    assert_eq!(
  467        editor
  468            .update(cx, |view, cx| view.selections.display_ranges(cx))
  469            .unwrap(),
  470        [
  471            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  472            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  473        ]
  474    );
  475
  476    _ = editor.update(cx, |view, cx| {
  477        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  478    });
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.end_selection(cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |view, cx| view.selections.display_ranges(cx))
  487            .unwrap(),
  488        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  489    );
  490}
  491
  492#[gpui::test]
  493fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  494    init_test(cx, |_| {});
  495
  496    let view = cx.add_window(|cx| {
  497        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  498        build_editor(buffer, cx)
  499    });
  500
  501    _ = view.update(cx, |view, cx| {
  502        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  503        assert_eq!(
  504            view.selections.display_ranges(cx),
  505            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  506        );
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.update_selection(
  511            DisplayPoint::new(DisplayRow(3), 3),
  512            0,
  513            gpui::Point::<f32>::default(),
  514            cx,
  515        );
  516        assert_eq!(
  517            view.selections.display_ranges(cx),
  518            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  519        );
  520    });
  521
  522    _ = view.update(cx, |view, cx| {
  523        view.cancel(&Cancel, cx);
  524        view.update_selection(
  525            DisplayPoint::new(DisplayRow(1), 1),
  526            0,
  527            gpui::Point::<f32>::default(),
  528            cx,
  529        );
  530        assert_eq!(
  531            view.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  533        );
  534    });
  535}
  536
  537#[gpui::test]
  538fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  539    init_test(cx, |_| {});
  540
  541    let view = cx.add_window(|cx| {
  542        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  543        build_editor(buffer, cx)
  544    });
  545
  546    _ = view.update(cx, |view, cx| {
  547        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  548        assert_eq!(
  549            view.selections.display_ranges(cx),
  550            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  551        );
  552
  553        view.move_down(&Default::default(), cx);
  554        assert_eq!(
  555            view.selections.display_ranges(cx),
  556            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  557        );
  558
  559        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  560        assert_eq!(
  561            view.selections.display_ranges(cx),
  562            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  563        );
  564
  565        view.move_up(&Default::default(), cx);
  566        assert_eq!(
  567            view.selections.display_ranges(cx),
  568            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  569        );
  570    });
  571}
  572
  573#[gpui::test]
  574fn test_clone(cx: &mut TestAppContext) {
  575    init_test(cx, |_| {});
  576
  577    let (text, selection_ranges) = marked_text_ranges(
  578        indoc! {"
  579            one
  580            two
  581            threeˇ
  582            four
  583            fiveˇ
  584        "},
  585        true,
  586    );
  587
  588    let editor = cx.add_window(|cx| {
  589        let buffer = MultiBuffer::build_simple(&text, cx);
  590        build_editor(buffer, cx)
  591    });
  592
  593    _ = editor.update(cx, |editor, cx| {
  594        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  595        editor.fold_ranges(
  596            [
  597                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  598                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  599            ],
  600            true,
  601            cx,
  602        );
  603    });
  604
  605    let cloned_editor = editor
  606        .update(cx, |editor, cx| {
  607            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  608        })
  609        .unwrap()
  610        .unwrap();
  611
  612    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  613    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614
  615    assert_eq!(
  616        cloned_editor
  617            .update(cx, |e, cx| e.display_text(cx))
  618            .unwrap(),
  619        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  620    );
  621    assert_eq!(
  622        cloned_snapshot
  623            .folds_in_range(0..text.len())
  624            .collect::<Vec<_>>(),
  625        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  626    );
  627    assert_set_eq!(
  628        cloned_editor
  629            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  630            .unwrap(),
  631        editor
  632            .update(cx, |editor, cx| editor.selections.ranges(cx))
  633            .unwrap()
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |e, cx| e.selections.display_ranges(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |e, cx| e.selections.display_ranges(cx))
  641            .unwrap()
  642    );
  643}
  644
  645#[gpui::test]
  646async fn test_navigation_history(cx: &mut TestAppContext) {
  647    init_test(cx, |_| {});
  648
  649    use workspace::item::Item;
  650
  651    let fs = FakeFs::new(cx.executor());
  652    let project = Project::test(fs, [], cx).await;
  653    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  654    let pane = workspace
  655        .update(cx, |workspace, _| workspace.active_pane().clone())
  656        .unwrap();
  657
  658    _ = workspace.update(cx, |_v, cx| {
  659        cx.new_view(|cx| {
  660            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  661            let mut editor = build_editor(buffer.clone(), cx);
  662            let handle = cx.view();
  663            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  664
  665            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  666                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  667            }
  668
  669            // Move the cursor a small distance.
  670            // Nothing is added to the navigation history.
  671            editor.change_selections(None, cx, |s| {
  672                s.select_display_ranges([
  673                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  674                ])
  675            });
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  679                ])
  680            });
  681            assert!(pop_history(&mut editor, cx).is_none());
  682
  683            // Move the cursor a large distance.
  684            // The history can jump back to the previous position.
  685            editor.change_selections(None, cx, |s| {
  686                s.select_display_ranges([
  687                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  688                ])
  689            });
  690            let nav_entry = pop_history(&mut editor, cx).unwrap();
  691            editor.navigate(nav_entry.data.unwrap(), cx);
  692            assert_eq!(nav_entry.item.id(), cx.entity_id());
  693            assert_eq!(
  694                editor.selections.display_ranges(cx),
  695                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  696            );
  697            assert!(pop_history(&mut editor, cx).is_none());
  698
  699            // Move the cursor a small distance via the mouse.
  700            // Nothing is added to the navigation history.
  701            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  702            editor.end_selection(cx);
  703            assert_eq!(
  704                editor.selections.display_ranges(cx),
  705                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  706            );
  707            assert!(pop_history(&mut editor, cx).is_none());
  708
  709            // Move the cursor a large distance via the mouse.
  710            // The history can jump back to the previous position.
  711            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  712            editor.end_selection(cx);
  713            assert_eq!(
  714                editor.selections.display_ranges(cx),
  715                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  716            );
  717            let nav_entry = pop_history(&mut editor, cx).unwrap();
  718            editor.navigate(nav_entry.data.unwrap(), cx);
  719            assert_eq!(nav_entry.item.id(), cx.entity_id());
  720            assert_eq!(
  721                editor.selections.display_ranges(cx),
  722                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  723            );
  724            assert!(pop_history(&mut editor, cx).is_none());
  725
  726            // Set scroll position to check later
  727            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  728            let original_scroll_position = editor.scroll_manager.anchor();
  729
  730            // Jump to the end of the document and adjust scroll
  731            editor.move_to_end(&MoveToEnd, cx);
  732            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  733            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  734
  735            let nav_entry = pop_history(&mut editor, cx).unwrap();
  736            editor.navigate(nav_entry.data.unwrap(), cx);
  737            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  740            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  741            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  742            let invalid_point = Point::new(9999, 0);
  743            editor.navigate(
  744                Box::new(NavigationData {
  745                    cursor_anchor: invalid_anchor,
  746                    cursor_position: invalid_point,
  747                    scroll_anchor: ScrollAnchor {
  748                        anchor: invalid_anchor,
  749                        offset: Default::default(),
  750                    },
  751                    scroll_top_row: invalid_point.row,
  752                }),
  753                cx,
  754            );
  755            assert_eq!(
  756                editor.selections.display_ranges(cx),
  757                &[editor.max_point(cx)..editor.max_point(cx)]
  758            );
  759            assert_eq!(
  760                editor.scroll_position(cx),
  761                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  762            );
  763
  764            editor
  765        })
  766    });
  767}
  768
  769#[gpui::test]
  770fn test_cancel(cx: &mut TestAppContext) {
  771    init_test(cx, |_| {});
  772
  773    let view = cx.add_window(|cx| {
  774        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  775        build_editor(buffer, cx)
  776    });
  777
  778    _ = view.update(cx, |view, cx| {
  779        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  780        view.update_selection(
  781            DisplayPoint::new(DisplayRow(1), 1),
  782            0,
  783            gpui::Point::<f32>::default(),
  784            cx,
  785        );
  786        view.end_selection(cx);
  787
  788        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  789        view.update_selection(
  790            DisplayPoint::new(DisplayRow(0), 3),
  791            0,
  792            gpui::Point::<f32>::default(),
  793            cx,
  794        );
  795        view.end_selection(cx);
  796        assert_eq!(
  797            view.selections.display_ranges(cx),
  798            [
  799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  800                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  801            ]
  802        );
  803    });
  804
  805    _ = view.update(cx, |view, cx| {
  806        view.cancel(&Cancel, cx);
  807        assert_eq!(
  808            view.selections.display_ranges(cx),
  809            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820}
  821
  822#[gpui::test]
  823fn test_fold_action(cx: &mut TestAppContext) {
  824    init_test(cx, |_| {});
  825
  826    let view = cx.add_window(|cx| {
  827        let buffer = MultiBuffer::build_simple(
  828            &"
  829                impl Foo {
  830                    // Hello!
  831
  832                    fn a() {
  833                        1
  834                    }
  835
  836                    fn b() {
  837                        2
  838                    }
  839
  840                    fn c() {
  841                        3
  842                    }
  843                }
  844            "
  845            .unindent(),
  846            cx,
  847        );
  848        build_editor(buffer.clone(), cx)
  849    });
  850
  851    _ = view.update(cx, |view, cx| {
  852        view.change_selections(None, cx, |s| {
  853            s.select_display_ranges([
  854                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  855            ]);
  856        });
  857        view.fold(&Fold, cx);
  858        assert_eq!(
  859            view.display_text(cx),
  860            "
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {⋯
  869                    }
  870
  871                    fn c() {⋯
  872                    }
  873                }
  874            "
  875            .unindent(),
  876        );
  877
  878        view.fold(&Fold, cx);
  879        assert_eq!(
  880            view.display_text(cx),
  881            "
  882                impl Foo {⋯
  883                }
  884            "
  885            .unindent(),
  886        );
  887
  888        view.unfold_lines(&UnfoldLines, cx);
  889        assert_eq!(
  890            view.display_text(cx),
  891            "
  892                impl Foo {
  893                    // Hello!
  894
  895                    fn a() {
  896                        1
  897                    }
  898
  899                    fn b() {⋯
  900                    }
  901
  902                    fn c() {⋯
  903                    }
  904                }
  905            "
  906            .unindent(),
  907        );
  908
  909        view.unfold_lines(&UnfoldLines, cx);
  910        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  911    });
  912}
  913
  914#[gpui::test]
  915fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  916    init_test(cx, |_| {});
  917
  918    let view = cx.add_window(|cx| {
  919        let buffer = MultiBuffer::build_simple(
  920            &"
  921                class Foo:
  922                    # Hello!
  923
  924                    def a():
  925                        print(1)
  926
  927                    def b():
  928                        print(2)
  929
  930                    def c():
  931                        print(3)
  932            "
  933            .unindent(),
  934            cx,
  935        );
  936        build_editor(buffer.clone(), cx)
  937    });
  938
  939    _ = view.update(cx, |view, cx| {
  940        view.change_selections(None, cx, |s| {
  941            s.select_display_ranges([
  942                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  943            ]);
  944        });
  945        view.fold(&Fold, cx);
  946        assert_eq!(
  947            view.display_text(cx),
  948            "
  949                class Foo:
  950                    # Hello!
  951
  952                    def a():
  953                        print(1)
  954
  955                    def b():⋯
  956
  957                    def c():⋯
  958            "
  959            .unindent(),
  960        );
  961
  962        view.fold(&Fold, cx);
  963        assert_eq!(
  964            view.display_text(cx),
  965            "
  966                class Foo:⋯
  967            "
  968            .unindent(),
  969        );
  970
  971        view.unfold_lines(&UnfoldLines, cx);
  972        assert_eq!(
  973            view.display_text(cx),
  974            "
  975                class Foo:
  976                    # Hello!
  977
  978                    def a():
  979                        print(1)
  980
  981                    def b():⋯
  982
  983                    def c():⋯
  984            "
  985            .unindent(),
  986        );
  987
  988        view.unfold_lines(&UnfoldLines, cx);
  989        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  990    });
  991}
  992
  993#[gpui::test]
  994fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  995    init_test(cx, |_| {});
  996
  997    let view = cx.add_window(|cx| {
  998        let buffer = MultiBuffer::build_simple(
  999            &"
 1000                class Foo:
 1001                    # Hello!
 1002
 1003                    def a():
 1004                        print(1)
 1005
 1006                    def b():
 1007                        print(2)
 1008
 1009
 1010                    def c():
 1011                        print(3)
 1012
 1013
 1014            "
 1015            .unindent(),
 1016            cx,
 1017        );
 1018        build_editor(buffer.clone(), cx)
 1019    });
 1020
 1021    _ = view.update(cx, |view, cx| {
 1022        view.change_selections(None, cx, |s| {
 1023            s.select_display_ranges([
 1024                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1025            ]);
 1026        });
 1027        view.fold(&Fold, cx);
 1028        assert_eq!(
 1029            view.display_text(cx),
 1030            "
 1031                class Foo:
 1032                    # Hello!
 1033
 1034                    def a():
 1035                        print(1)
 1036
 1037                    def b():⋯
 1038
 1039
 1040                    def c():⋯
 1041
 1042
 1043            "
 1044            .unindent(),
 1045        );
 1046
 1047        view.fold(&Fold, cx);
 1048        assert_eq!(
 1049            view.display_text(cx),
 1050            "
 1051                class Foo:⋯
 1052
 1053
 1054            "
 1055            .unindent(),
 1056        );
 1057
 1058        view.unfold_lines(&UnfoldLines, cx);
 1059        assert_eq!(
 1060            view.display_text(cx),
 1061            "
 1062                class Foo:
 1063                    # Hello!
 1064
 1065                    def a():
 1066                        print(1)
 1067
 1068                    def b():⋯
 1069
 1070
 1071                    def c():⋯
 1072
 1073
 1074            "
 1075            .unindent(),
 1076        );
 1077
 1078        view.unfold_lines(&UnfoldLines, cx);
 1079        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1080    });
 1081}
 1082
 1083#[gpui::test]
 1084fn test_fold_at_level(cx: &mut TestAppContext) {
 1085    init_test(cx, |_| {});
 1086
 1087    let view = cx.add_window(|cx| {
 1088        let buffer = MultiBuffer::build_simple(
 1089            &"
 1090                class Foo:
 1091                    # Hello!
 1092
 1093                    def a():
 1094                        print(1)
 1095
 1096                    def b():
 1097                        print(2)
 1098
 1099
 1100                class Bar:
 1101                    # World!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():
 1107                        print(2)
 1108
 1109
 1110            "
 1111            .unindent(),
 1112            cx,
 1113        );
 1114        build_editor(buffer.clone(), cx)
 1115    });
 1116
 1117    _ = view.update(cx, |view, cx| {
 1118        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1119        assert_eq!(
 1120            view.display_text(cx),
 1121            "
 1122                class Foo:
 1123                    # Hello!
 1124
 1125                    def a():⋯
 1126
 1127                    def b():⋯
 1128
 1129
 1130                class Bar:
 1131                    # World!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138            "
 1139            .unindent(),
 1140        );
 1141
 1142        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1143        assert_eq!(
 1144            view.display_text(cx),
 1145            "
 1146                class Foo:⋯
 1147
 1148
 1149                class Bar:⋯
 1150
 1151
 1152            "
 1153            .unindent(),
 1154        );
 1155
 1156        view.unfold_all(&UnfoldAll, cx);
 1157        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1158        assert_eq!(
 1159            view.display_text(cx),
 1160            "
 1161                class Foo:
 1162                    # Hello!
 1163
 1164                    def a():
 1165                        print(1)
 1166
 1167                    def b():
 1168                        print(2)
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():
 1175                        print(1)
 1176
 1177                    def b():
 1178                        print(2)
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1186    });
 1187}
 1188
 1189#[gpui::test]
 1190fn test_move_cursor(cx: &mut TestAppContext) {
 1191    init_test(cx, |_| {});
 1192
 1193    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1194    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1195
 1196    buffer.update(cx, |buffer, cx| {
 1197        buffer.edit(
 1198            vec![
 1199                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1200                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1201            ],
 1202            None,
 1203            cx,
 1204        );
 1205    });
 1206    _ = view.update(cx, |view, cx| {
 1207        assert_eq!(
 1208            view.selections.display_ranges(cx),
 1209            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1210        );
 1211
 1212        view.move_down(&MoveDown, cx);
 1213        assert_eq!(
 1214            view.selections.display_ranges(cx),
 1215            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1216        );
 1217
 1218        view.move_right(&MoveRight, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1222        );
 1223
 1224        view.move_left(&MoveLeft, cx);
 1225        assert_eq!(
 1226            view.selections.display_ranges(cx),
 1227            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1228        );
 1229
 1230        view.move_up(&MoveUp, cx);
 1231        assert_eq!(
 1232            view.selections.display_ranges(cx),
 1233            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1234        );
 1235
 1236        view.move_to_end(&MoveToEnd, cx);
 1237        assert_eq!(
 1238            view.selections.display_ranges(cx),
 1239            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1240        );
 1241
 1242        view.move_to_beginning(&MoveToBeginning, cx);
 1243        assert_eq!(
 1244            view.selections.display_ranges(cx),
 1245            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1246        );
 1247
 1248        view.change_selections(None, cx, |s| {
 1249            s.select_display_ranges([
 1250                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1251            ]);
 1252        });
 1253        view.select_to_beginning(&SelectToBeginning, cx);
 1254        assert_eq!(
 1255            view.selections.display_ranges(cx),
 1256            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1257        );
 1258
 1259        view.select_to_end(&SelectToEnd, cx);
 1260        assert_eq!(
 1261            view.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1263        );
 1264    });
 1265}
 1266
 1267// TODO: Re-enable this test
 1268#[cfg(target_os = "macos")]
 1269#[gpui::test]
 1270fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1271    init_test(cx, |_| {});
 1272
 1273    let view = cx.add_window(|cx| {
 1274        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1275        build_editor(buffer.clone(), cx)
 1276    });
 1277
 1278    assert_eq!('ⓐ'.len_utf8(), 3);
 1279    assert_eq!('α'.len_utf8(), 2);
 1280
 1281    _ = view.update(cx, |view, cx| {
 1282        view.fold_ranges(
 1283            vec![
 1284                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1285                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1286                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1287            ],
 1288            true,
 1289            cx,
 1290        );
 1291        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1292
 1293        view.move_right(&MoveRight, cx);
 1294        assert_eq!(
 1295            view.selections.display_ranges(cx),
 1296            &[empty_range(0, "".len())]
 1297        );
 1298        view.move_right(&MoveRight, cx);
 1299        assert_eq!(
 1300            view.selections.display_ranges(cx),
 1301            &[empty_range(0, "ⓐⓑ".len())]
 1302        );
 1303        view.move_right(&MoveRight, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(0, "ⓐⓑ⋯".len())]
 1307        );
 1308
 1309        view.move_down(&MoveDown, cx);
 1310        assert_eq!(
 1311            view.selections.display_ranges(cx),
 1312            &[empty_range(1, "ab⋯e".len())]
 1313        );
 1314        view.move_left(&MoveLeft, cx);
 1315        assert_eq!(
 1316            view.selections.display_ranges(cx),
 1317            &[empty_range(1, "ab⋯".len())]
 1318        );
 1319        view.move_left(&MoveLeft, cx);
 1320        assert_eq!(
 1321            view.selections.display_ranges(cx),
 1322            &[empty_range(1, "ab".len())]
 1323        );
 1324        view.move_left(&MoveLeft, cx);
 1325        assert_eq!(
 1326            view.selections.display_ranges(cx),
 1327            &[empty_range(1, "a".len())]
 1328        );
 1329
 1330        view.move_down(&MoveDown, cx);
 1331        assert_eq!(
 1332            view.selections.display_ranges(cx),
 1333            &[empty_range(2, "α".len())]
 1334        );
 1335        view.move_right(&MoveRight, cx);
 1336        assert_eq!(
 1337            view.selections.display_ranges(cx),
 1338            &[empty_range(2, "αβ".len())]
 1339        );
 1340        view.move_right(&MoveRight, cx);
 1341        assert_eq!(
 1342            view.selections.display_ranges(cx),
 1343            &[empty_range(2, "αβ⋯".len())]
 1344        );
 1345        view.move_right(&MoveRight, cx);
 1346        assert_eq!(
 1347            view.selections.display_ranges(cx),
 1348            &[empty_range(2, "αβ⋯ε".len())]
 1349        );
 1350
 1351        view.move_up(&MoveUp, cx);
 1352        assert_eq!(
 1353            view.selections.display_ranges(cx),
 1354            &[empty_range(1, "ab⋯e".len())]
 1355        );
 1356        view.move_down(&MoveDown, cx);
 1357        assert_eq!(
 1358            view.selections.display_ranges(cx),
 1359            &[empty_range(2, "αβ⋯ε".len())]
 1360        );
 1361        view.move_up(&MoveUp, cx);
 1362        assert_eq!(
 1363            view.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366
 1367        view.move_up(&MoveUp, cx);
 1368        assert_eq!(
 1369            view.selections.display_ranges(cx),
 1370            &[empty_range(0, "ⓐⓑ".len())]
 1371        );
 1372        view.move_left(&MoveLeft, cx);
 1373        assert_eq!(
 1374            view.selections.display_ranges(cx),
 1375            &[empty_range(0, "".len())]
 1376        );
 1377        view.move_left(&MoveLeft, cx);
 1378        assert_eq!(
 1379            view.selections.display_ranges(cx),
 1380            &[empty_range(0, "".len())]
 1381        );
 1382    });
 1383}
 1384
 1385#[gpui::test]
 1386fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1387    init_test(cx, |_| {});
 1388
 1389    let view = cx.add_window(|cx| {
 1390        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1391        build_editor(buffer.clone(), cx)
 1392    });
 1393    _ = view.update(cx, |view, cx| {
 1394        view.change_selections(None, cx, |s| {
 1395            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1396        });
 1397        view.move_down(&MoveDown, cx);
 1398        assert_eq!(
 1399            view.selections.display_ranges(cx),
 1400            &[empty_range(1, "abcd".len())]
 1401        );
 1402
 1403        view.move_down(&MoveDown, cx);
 1404        assert_eq!(
 1405            view.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβγ".len())]
 1407        );
 1408
 1409        view.move_down(&MoveDown, cx);
 1410        assert_eq!(
 1411            view.selections.display_ranges(cx),
 1412            &[empty_range(3, "abcd".len())]
 1413        );
 1414
 1415        view.move_down(&MoveDown, cx);
 1416        assert_eq!(
 1417            view.selections.display_ranges(cx),
 1418            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1419        );
 1420
 1421        view.move_up(&MoveUp, cx);
 1422        assert_eq!(
 1423            view.selections.display_ranges(cx),
 1424            &[empty_range(3, "abcd".len())]
 1425        );
 1426
 1427        view.move_up(&MoveUp, cx);
 1428        assert_eq!(
 1429            view.selections.display_ranges(cx),
 1430            &[empty_range(2, "αβγ".len())]
 1431        );
 1432    });
 1433}
 1434
 1435#[gpui::test]
 1436fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1437    init_test(cx, |_| {});
 1438    let move_to_beg = MoveToBeginningOfLine {
 1439        stop_at_soft_wraps: true,
 1440    };
 1441
 1442    let move_to_end = MoveToEndOfLine {
 1443        stop_at_soft_wraps: true,
 1444    };
 1445
 1446    let view = cx.add_window(|cx| {
 1447        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1448        build_editor(buffer, cx)
 1449    });
 1450    _ = view.update(cx, |view, cx| {
 1451        view.change_selections(None, cx, |s| {
 1452            s.select_display_ranges([
 1453                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1454                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1455            ]);
 1456        });
 1457    });
 1458
 1459    _ = view.update(cx, |view, cx| {
 1460        view.move_to_beginning_of_line(&move_to_beg, cx);
 1461        assert_eq!(
 1462            view.selections.display_ranges(cx),
 1463            &[
 1464                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1465                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1466            ]
 1467        );
 1468    });
 1469
 1470    _ = view.update(cx, |view, cx| {
 1471        view.move_to_beginning_of_line(&move_to_beg, cx);
 1472        assert_eq!(
 1473            view.selections.display_ranges(cx),
 1474            &[
 1475                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1476                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1477            ]
 1478        );
 1479    });
 1480
 1481    _ = view.update(cx, |view, cx| {
 1482        view.move_to_beginning_of_line(&move_to_beg, cx);
 1483        assert_eq!(
 1484            view.selections.display_ranges(cx),
 1485            &[
 1486                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1487                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1488            ]
 1489        );
 1490    });
 1491
 1492    _ = view.update(cx, |view, cx| {
 1493        view.move_to_end_of_line(&move_to_end, cx);
 1494        assert_eq!(
 1495            view.selections.display_ranges(cx),
 1496            &[
 1497                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1498                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1499            ]
 1500        );
 1501    });
 1502
 1503    // Moving to the end of line again is a no-op.
 1504    _ = view.update(cx, |view, cx| {
 1505        view.move_to_end_of_line(&move_to_end, cx);
 1506        assert_eq!(
 1507            view.selections.display_ranges(cx),
 1508            &[
 1509                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1510                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1511            ]
 1512        );
 1513    });
 1514
 1515    _ = view.update(cx, |view, cx| {
 1516        view.move_left(&MoveLeft, cx);
 1517        view.select_to_beginning_of_line(
 1518            &SelectToBeginningOfLine {
 1519                stop_at_soft_wraps: true,
 1520            },
 1521            cx,
 1522        );
 1523        assert_eq!(
 1524            view.selections.display_ranges(cx),
 1525            &[
 1526                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1527                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1528            ]
 1529        );
 1530    });
 1531
 1532    _ = view.update(cx, |view, cx| {
 1533        view.select_to_beginning_of_line(
 1534            &SelectToBeginningOfLine {
 1535                stop_at_soft_wraps: true,
 1536            },
 1537            cx,
 1538        );
 1539        assert_eq!(
 1540            view.selections.display_ranges(cx),
 1541            &[
 1542                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1543                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1544            ]
 1545        );
 1546    });
 1547
 1548    _ = view.update(cx, |view, cx| {
 1549        view.select_to_beginning_of_line(
 1550            &SelectToBeginningOfLine {
 1551                stop_at_soft_wraps: true,
 1552            },
 1553            cx,
 1554        );
 1555        assert_eq!(
 1556            view.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = view.update(cx, |view, cx| {
 1565        view.select_to_end_of_line(
 1566            &SelectToEndOfLine {
 1567                stop_at_soft_wraps: true,
 1568            },
 1569            cx,
 1570        );
 1571        assert_eq!(
 1572            view.selections.display_ranges(cx),
 1573            &[
 1574                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1575                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1576            ]
 1577        );
 1578    });
 1579
 1580    _ = view.update(cx, |view, cx| {
 1581        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1582        assert_eq!(view.display_text(cx), "ab\n  de");
 1583        assert_eq!(
 1584            view.selections.display_ranges(cx),
 1585            &[
 1586                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1587                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1588            ]
 1589        );
 1590    });
 1591
 1592    _ = view.update(cx, |view, cx| {
 1593        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1594        assert_eq!(view.display_text(cx), "\n");
 1595        assert_eq!(
 1596            view.selections.display_ranges(cx),
 1597            &[
 1598                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1599                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1600            ]
 1601        );
 1602    });
 1603}
 1604
 1605#[gpui::test]
 1606fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1607    init_test(cx, |_| {});
 1608    let move_to_beg = MoveToBeginningOfLine {
 1609        stop_at_soft_wraps: false,
 1610    };
 1611
 1612    let move_to_end = MoveToEndOfLine {
 1613        stop_at_soft_wraps: false,
 1614    };
 1615
 1616    let view = cx.add_window(|cx| {
 1617        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1618        build_editor(buffer, cx)
 1619    });
 1620
 1621    _ = view.update(cx, |view, cx| {
 1622        view.set_wrap_width(Some(140.0.into()), cx);
 1623
 1624        // We expect the following lines after wrapping
 1625        // ```
 1626        // thequickbrownfox
 1627        // jumpedoverthelazydo
 1628        // gs
 1629        // ```
 1630        // The final `gs` was soft-wrapped onto a new line.
 1631        assert_eq!(
 1632            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1633            view.display_text(cx),
 1634        );
 1635
 1636        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1637        // Start the cursor at the `k` on the first line
 1638        view.change_selections(None, cx, |s| {
 1639            s.select_display_ranges([
 1640                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1641            ]);
 1642        });
 1643
 1644        // Moving to the beginning of the line should put us at the beginning of the line.
 1645        view.move_to_beginning_of_line(&move_to_beg, cx);
 1646        assert_eq!(
 1647            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1648            view.selections.display_ranges(cx)
 1649        );
 1650
 1651        // Moving to the end of the line should put us at the end of the line.
 1652        view.move_to_end_of_line(&move_to_end, cx);
 1653        assert_eq!(
 1654            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1655            view.selections.display_ranges(cx)
 1656        );
 1657
 1658        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1659        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1660        view.change_selections(None, cx, |s| {
 1661            s.select_display_ranges([
 1662                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1663            ]);
 1664        });
 1665
 1666        // Moving to the beginning of the line should put us at the start of the second line of
 1667        // display text, i.e., the `j`.
 1668        view.move_to_beginning_of_line(&move_to_beg, cx);
 1669        assert_eq!(
 1670            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1671            view.selections.display_ranges(cx)
 1672        );
 1673
 1674        // Moving to the beginning of the line again should be a no-op.
 1675        view.move_to_beginning_of_line(&move_to_beg, cx);
 1676        assert_eq!(
 1677            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1678            view.selections.display_ranges(cx)
 1679        );
 1680
 1681        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1682        // next display line.
 1683        view.move_to_end_of_line(&move_to_end, cx);
 1684        assert_eq!(
 1685            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1686            view.selections.display_ranges(cx)
 1687        );
 1688
 1689        // Moving to the end of the line again should be a no-op.
 1690        view.move_to_end_of_line(&move_to_end, cx);
 1691        assert_eq!(
 1692            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1693            view.selections.display_ranges(cx)
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701
 1702    let view = cx.add_window(|cx| {
 1703        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1704        build_editor(buffer, cx)
 1705    });
 1706    _ = view.update(cx, |view, cx| {
 1707        view.change_selections(None, cx, |s| {
 1708            s.select_display_ranges([
 1709                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1710                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1711            ])
 1712        });
 1713
 1714        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1715        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1716
 1717        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1718        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1719
 1720        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1721        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1722
 1723        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1724        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1725
 1726        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1727        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1728
 1729        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1730        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1731
 1732        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1733        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1734
 1735        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1736        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1737
 1738        view.move_right(&MoveRight, cx);
 1739        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1740        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1741
 1742        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1743        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1744
 1745        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1746        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1747    });
 1748}
 1749
 1750#[gpui::test]
 1751fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1752    init_test(cx, |_| {});
 1753
 1754    let view = cx.add_window(|cx| {
 1755        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1756        build_editor(buffer, cx)
 1757    });
 1758
 1759    _ = view.update(cx, |view, cx| {
 1760        view.set_wrap_width(Some(140.0.into()), cx);
 1761        assert_eq!(
 1762            view.display_text(cx),
 1763            "use one::{\n    two::three::\n    four::five\n};"
 1764        );
 1765
 1766        view.change_selections(None, cx, |s| {
 1767            s.select_display_ranges([
 1768                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1769            ]);
 1770        });
 1771
 1772        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1773        assert_eq!(
 1774            view.selections.display_ranges(cx),
 1775            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1776        );
 1777
 1778        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1779        assert_eq!(
 1780            view.selections.display_ranges(cx),
 1781            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1782        );
 1783
 1784        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1785        assert_eq!(
 1786            view.selections.display_ranges(cx),
 1787            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1788        );
 1789
 1790        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1791        assert_eq!(
 1792            view.selections.display_ranges(cx),
 1793            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1794        );
 1795
 1796        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1797        assert_eq!(
 1798            view.selections.display_ranges(cx),
 1799            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1800        );
 1801
 1802        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1803        assert_eq!(
 1804            view.selections.display_ranges(cx),
 1805            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1806        );
 1807    });
 1808}
 1809
 1810#[gpui::test]
 1811async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1812    init_test(cx, |_| {});
 1813    let mut cx = EditorTestContext::new(cx).await;
 1814
 1815    let line_height = cx.editor(|editor, cx| {
 1816        editor
 1817            .style()
 1818            .unwrap()
 1819            .text
 1820            .line_height_in_pixels(cx.rem_size())
 1821    });
 1822    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1823
 1824    cx.set_state(
 1825        &r#"ˇone
 1826        two
 1827
 1828        three
 1829        fourˇ
 1830        five
 1831
 1832        six"#
 1833            .unindent(),
 1834    );
 1835
 1836    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1837    cx.assert_editor_state(
 1838        &r#"one
 1839        two
 1840        ˇ
 1841        three
 1842        four
 1843        five
 1844        ˇ
 1845        six"#
 1846            .unindent(),
 1847    );
 1848
 1849    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1850    cx.assert_editor_state(
 1851        &r#"one
 1852        two
 1853
 1854        three
 1855        four
 1856        five
 1857        ˇ
 1858        sixˇ"#
 1859            .unindent(),
 1860    );
 1861
 1862    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1863    cx.assert_editor_state(
 1864        &r#"one
 1865        two
 1866
 1867        three
 1868        four
 1869        five
 1870
 1871        sixˇ"#
 1872            .unindent(),
 1873    );
 1874
 1875    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1876    cx.assert_editor_state(
 1877        &r#"one
 1878        two
 1879
 1880        three
 1881        four
 1882        five
 1883        ˇ
 1884        six"#
 1885            .unindent(),
 1886    );
 1887
 1888    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1889    cx.assert_editor_state(
 1890        &r#"one
 1891        two
 1892        ˇ
 1893        three
 1894        four
 1895        five
 1896
 1897        six"#
 1898            .unindent(),
 1899    );
 1900
 1901    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1902    cx.assert_editor_state(
 1903        &r#"ˇone
 1904        two
 1905
 1906        three
 1907        four
 1908        five
 1909
 1910        six"#
 1911            .unindent(),
 1912    );
 1913}
 1914
 1915#[gpui::test]
 1916async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1917    init_test(cx, |_| {});
 1918    let mut cx = EditorTestContext::new(cx).await;
 1919    let line_height = cx.editor(|editor, cx| {
 1920        editor
 1921            .style()
 1922            .unwrap()
 1923            .text
 1924            .line_height_in_pixels(cx.rem_size())
 1925    });
 1926    let window = cx.window;
 1927    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1928
 1929    cx.set_state(
 1930        r#"ˇone
 1931        two
 1932        three
 1933        four
 1934        five
 1935        six
 1936        seven
 1937        eight
 1938        nine
 1939        ten
 1940        "#,
 1941    );
 1942
 1943    cx.update_editor(|editor, cx| {
 1944        assert_eq!(
 1945            editor.snapshot(cx).scroll_position(),
 1946            gpui::Point::new(0., 0.)
 1947        );
 1948        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1949        assert_eq!(
 1950            editor.snapshot(cx).scroll_position(),
 1951            gpui::Point::new(0., 3.)
 1952        );
 1953        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1954        assert_eq!(
 1955            editor.snapshot(cx).scroll_position(),
 1956            gpui::Point::new(0., 6.)
 1957        );
 1958        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1959        assert_eq!(
 1960            editor.snapshot(cx).scroll_position(),
 1961            gpui::Point::new(0., 3.)
 1962        );
 1963
 1964        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1965        assert_eq!(
 1966            editor.snapshot(cx).scroll_position(),
 1967            gpui::Point::new(0., 1.)
 1968        );
 1969        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1970        assert_eq!(
 1971            editor.snapshot(cx).scroll_position(),
 1972            gpui::Point::new(0., 3.)
 1973        );
 1974    });
 1975}
 1976
 1977#[gpui::test]
 1978async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1979    init_test(cx, |_| {});
 1980    let mut cx = EditorTestContext::new(cx).await;
 1981
 1982    let line_height = cx.update_editor(|editor, cx| {
 1983        editor.set_vertical_scroll_margin(2, cx);
 1984        editor
 1985            .style()
 1986            .unwrap()
 1987            .text
 1988            .line_height_in_pixels(cx.rem_size())
 1989    });
 1990    let window = cx.window;
 1991    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1992
 1993    cx.set_state(
 1994        r#"ˇone
 1995            two
 1996            three
 1997            four
 1998            five
 1999            six
 2000            seven
 2001            eight
 2002            nine
 2003            ten
 2004        "#,
 2005    );
 2006    cx.update_editor(|editor, cx| {
 2007        assert_eq!(
 2008            editor.snapshot(cx).scroll_position(),
 2009            gpui::Point::new(0., 0.0)
 2010        );
 2011    });
 2012
 2013    // Add a cursor below the visible area. Since both cursors cannot fit
 2014    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2015    // allows the vertical scroll margin below that cursor.
 2016    cx.update_editor(|editor, cx| {
 2017        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2018            selections.select_ranges([
 2019                Point::new(0, 0)..Point::new(0, 0),
 2020                Point::new(6, 0)..Point::new(6, 0),
 2021            ]);
 2022        })
 2023    });
 2024    cx.update_editor(|editor, cx| {
 2025        assert_eq!(
 2026            editor.snapshot(cx).scroll_position(),
 2027            gpui::Point::new(0., 3.0)
 2028        );
 2029    });
 2030
 2031    // Move down. The editor cursor scrolls down to track the newest cursor.
 2032    cx.update_editor(|editor, cx| {
 2033        editor.move_down(&Default::default(), cx);
 2034    });
 2035    cx.update_editor(|editor, cx| {
 2036        assert_eq!(
 2037            editor.snapshot(cx).scroll_position(),
 2038            gpui::Point::new(0., 4.0)
 2039        );
 2040    });
 2041
 2042    // Add a cursor above the visible area. Since both cursors fit on screen,
 2043    // the editor scrolls to show both.
 2044    cx.update_editor(|editor, cx| {
 2045        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2046            selections.select_ranges([
 2047                Point::new(1, 0)..Point::new(1, 0),
 2048                Point::new(6, 0)..Point::new(6, 0),
 2049            ]);
 2050        })
 2051    });
 2052    cx.update_editor(|editor, cx| {
 2053        assert_eq!(
 2054            editor.snapshot(cx).scroll_position(),
 2055            gpui::Point::new(0., 1.0)
 2056        );
 2057    });
 2058}
 2059
 2060#[gpui::test]
 2061async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2062    init_test(cx, |_| {});
 2063    let mut cx = EditorTestContext::new(cx).await;
 2064
 2065    let line_height = cx.editor(|editor, cx| {
 2066        editor
 2067            .style()
 2068            .unwrap()
 2069            .text
 2070            .line_height_in_pixels(cx.rem_size())
 2071    });
 2072    let window = cx.window;
 2073    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2074    cx.set_state(
 2075        &r#"
 2076        ˇone
 2077        two
 2078        threeˇ
 2079        four
 2080        five
 2081        six
 2082        seven
 2083        eight
 2084        nine
 2085        ten
 2086        "#
 2087        .unindent(),
 2088    );
 2089
 2090    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2091    cx.assert_editor_state(
 2092        &r#"
 2093        one
 2094        two
 2095        three
 2096        ˇfour
 2097        five
 2098        sixˇ
 2099        seven
 2100        eight
 2101        nine
 2102        ten
 2103        "#
 2104        .unindent(),
 2105    );
 2106
 2107    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2108    cx.assert_editor_state(
 2109        &r#"
 2110        one
 2111        two
 2112        three
 2113        four
 2114        five
 2115        six
 2116        ˇseven
 2117        eight
 2118        nineˇ
 2119        ten
 2120        "#
 2121        .unindent(),
 2122    );
 2123
 2124    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2125    cx.assert_editor_state(
 2126        &r#"
 2127        one
 2128        two
 2129        three
 2130        ˇfour
 2131        five
 2132        sixˇ
 2133        seven
 2134        eight
 2135        nine
 2136        ten
 2137        "#
 2138        .unindent(),
 2139    );
 2140
 2141    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2142    cx.assert_editor_state(
 2143        &r#"
 2144        ˇone
 2145        two
 2146        threeˇ
 2147        four
 2148        five
 2149        six
 2150        seven
 2151        eight
 2152        nine
 2153        ten
 2154        "#
 2155        .unindent(),
 2156    );
 2157
 2158    // Test select collapsing
 2159    cx.update_editor(|editor, cx| {
 2160        editor.move_page_down(&MovePageDown::default(), cx);
 2161        editor.move_page_down(&MovePageDown::default(), cx);
 2162        editor.move_page_down(&MovePageDown::default(), cx);
 2163    });
 2164    cx.assert_editor_state(
 2165        &r#"
 2166        one
 2167        two
 2168        three
 2169        four
 2170        five
 2171        six
 2172        seven
 2173        eight
 2174        nine
 2175        ˇten
 2176        ˇ"#
 2177        .unindent(),
 2178    );
 2179}
 2180
 2181#[gpui::test]
 2182async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2183    init_test(cx, |_| {});
 2184    let mut cx = EditorTestContext::new(cx).await;
 2185    cx.set_state("one «two threeˇ» four");
 2186    cx.update_editor(|editor, cx| {
 2187        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2188        assert_eq!(editor.text(cx), " four");
 2189    });
 2190}
 2191
 2192#[gpui::test]
 2193fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2194    init_test(cx, |_| {});
 2195
 2196    let view = cx.add_window(|cx| {
 2197        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2198        build_editor(buffer.clone(), cx)
 2199    });
 2200
 2201    _ = view.update(cx, |view, cx| {
 2202        view.change_selections(None, cx, |s| {
 2203            s.select_display_ranges([
 2204                // an empty selection - the preceding word fragment is deleted
 2205                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2206                // characters selected - they are deleted
 2207                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2208            ])
 2209        });
 2210        view.delete_to_previous_word_start(
 2211            &DeleteToPreviousWordStart {
 2212                ignore_newlines: false,
 2213            },
 2214            cx,
 2215        );
 2216        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2217    });
 2218
 2219    _ = view.update(cx, |view, cx| {
 2220        view.change_selections(None, cx, |s| {
 2221            s.select_display_ranges([
 2222                // an empty selection - the following word fragment is deleted
 2223                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2224                // characters selected - they are deleted
 2225                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2226            ])
 2227        });
 2228        view.delete_to_next_word_end(
 2229            &DeleteToNextWordEnd {
 2230                ignore_newlines: false,
 2231            },
 2232            cx,
 2233        );
 2234        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2235    });
 2236}
 2237
 2238#[gpui::test]
 2239fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2240    init_test(cx, |_| {});
 2241
 2242    let view = cx.add_window(|cx| {
 2243        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2244        build_editor(buffer.clone(), cx)
 2245    });
 2246    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2247        ignore_newlines: false,
 2248    };
 2249    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2250        ignore_newlines: true,
 2251    };
 2252
 2253    _ = view.update(cx, |view, cx| {
 2254        view.change_selections(None, cx, |s| {
 2255            s.select_display_ranges([
 2256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2257            ])
 2258        });
 2259        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2260        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2261        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2262        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 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\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");
 2267        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2268        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2269        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_next_word_end = DeleteToNextWordEnd {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2286        ignore_newlines: true,
 2287    };
 2288
 2289    _ = view.update(cx, |view, cx| {
 2290        view.change_selections(None, cx, |s| {
 2291            s.select_display_ranges([
 2292                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2293            ])
 2294        });
 2295        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2296        assert_eq!(
 2297            view.buffer.read(cx).read(cx).text(),
 2298            "one\n   two\nthree\n   four"
 2299        );
 2300        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2301        assert_eq!(
 2302            view.buffer.read(cx).read(cx).text(),
 2303            "\n   two\nthree\n   four"
 2304        );
 2305        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2307        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2308        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2309        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2310        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2311        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2312        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2313    });
 2314}
 2315
 2316#[gpui::test]
 2317fn test_newline(cx: &mut TestAppContext) {
 2318    init_test(cx, |_| {});
 2319
 2320    let view = cx.add_window(|cx| {
 2321        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2322        build_editor(buffer.clone(), cx)
 2323    });
 2324
 2325    _ = view.update(cx, |view, cx| {
 2326        view.change_selections(None, cx, |s| {
 2327            s.select_display_ranges([
 2328                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2329                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2330                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2331            ])
 2332        });
 2333
 2334        view.newline(&Newline, cx);
 2335        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2336    });
 2337}
 2338
 2339#[gpui::test]
 2340fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2341    init_test(cx, |_| {});
 2342
 2343    let editor = cx.add_window(|cx| {
 2344        let buffer = MultiBuffer::build_simple(
 2345            "
 2346                a
 2347                b(
 2348                    X
 2349                )
 2350                c(
 2351                    X
 2352                )
 2353            "
 2354            .unindent()
 2355            .as_str(),
 2356            cx,
 2357        );
 2358        let mut editor = build_editor(buffer.clone(), cx);
 2359        editor.change_selections(None, cx, |s| {
 2360            s.select_ranges([
 2361                Point::new(2, 4)..Point::new(2, 5),
 2362                Point::new(5, 4)..Point::new(5, 5),
 2363            ])
 2364        });
 2365        editor
 2366    });
 2367
 2368    _ = editor.update(cx, |editor, cx| {
 2369        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2370        editor.buffer.update(cx, |buffer, cx| {
 2371            buffer.edit(
 2372                [
 2373                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2374                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2375                ],
 2376                None,
 2377                cx,
 2378            );
 2379            assert_eq!(
 2380                buffer.read(cx).text(),
 2381                "
 2382                    a
 2383                    b()
 2384                    c()
 2385                "
 2386                .unindent()
 2387            );
 2388        });
 2389        assert_eq!(
 2390            editor.selections.ranges(cx),
 2391            &[
 2392                Point::new(1, 2)..Point::new(1, 2),
 2393                Point::new(2, 2)..Point::new(2, 2),
 2394            ],
 2395        );
 2396
 2397        editor.newline(&Newline, cx);
 2398        assert_eq!(
 2399            editor.text(cx),
 2400            "
 2401                a
 2402                b(
 2403                )
 2404                c(
 2405                )
 2406            "
 2407            .unindent()
 2408        );
 2409
 2410        // The selections are moved after the inserted newlines
 2411        assert_eq!(
 2412            editor.selections.ranges(cx),
 2413            &[
 2414                Point::new(2, 0)..Point::new(2, 0),
 2415                Point::new(4, 0)..Point::new(4, 0),
 2416            ],
 2417        );
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2423    init_test(cx, |settings| {
 2424        settings.defaults.tab_size = NonZeroU32::new(4)
 2425    });
 2426
 2427    let language = Arc::new(
 2428        Language::new(
 2429            LanguageConfig::default(),
 2430            Some(tree_sitter_rust::LANGUAGE.into()),
 2431        )
 2432        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2433        .unwrap(),
 2434    );
 2435
 2436    let mut cx = EditorTestContext::new(cx).await;
 2437    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2438    cx.set_state(indoc! {"
 2439        const a: ˇA = (
 2440 2441                «const_functionˇ»(ˇ),
 2442                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2443 2444        ˇ);ˇ
 2445    "});
 2446
 2447    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2448    cx.assert_editor_state(indoc! {"
 2449        ˇ
 2450        const a: A = (
 2451            ˇ
 2452            (
 2453                ˇ
 2454                ˇ
 2455                const_function(),
 2456                ˇ
 2457                ˇ
 2458                ˇ
 2459                ˇ
 2460                something_else,
 2461                ˇ
 2462            )
 2463            ˇ
 2464            ˇ
 2465        );
 2466    "});
 2467}
 2468
 2469#[gpui::test]
 2470async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2471    init_test(cx, |settings| {
 2472        settings.defaults.tab_size = NonZeroU32::new(4)
 2473    });
 2474
 2475    let language = Arc::new(
 2476        Language::new(
 2477            LanguageConfig::default(),
 2478            Some(tree_sitter_rust::LANGUAGE.into()),
 2479        )
 2480        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2481        .unwrap(),
 2482    );
 2483
 2484    let mut cx = EditorTestContext::new(cx).await;
 2485    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2486    cx.set_state(indoc! {"
 2487        const a: ˇA = (
 2488 2489                «const_functionˇ»(ˇ),
 2490                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2491 2492        ˇ);ˇ
 2493    "});
 2494
 2495    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2496    cx.assert_editor_state(indoc! {"
 2497        const a: A = (
 2498            ˇ
 2499            (
 2500                ˇ
 2501                const_function(),
 2502                ˇ
 2503                ˇ
 2504                something_else,
 2505                ˇ
 2506                ˇ
 2507                ˇ
 2508                ˇ
 2509            )
 2510            ˇ
 2511        );
 2512        ˇ
 2513        ˇ
 2514    "});
 2515}
 2516
 2517#[gpui::test]
 2518async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2519    init_test(cx, |settings| {
 2520        settings.defaults.tab_size = NonZeroU32::new(4)
 2521    });
 2522
 2523    let language = Arc::new(Language::new(
 2524        LanguageConfig {
 2525            line_comments: vec!["//".into()],
 2526            ..LanguageConfig::default()
 2527        },
 2528        None,
 2529    ));
 2530    {
 2531        let mut cx = EditorTestContext::new(cx).await;
 2532        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2533        cx.set_state(indoc! {"
 2534        // Fooˇ
 2535    "});
 2536
 2537        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2538        cx.assert_editor_state(indoc! {"
 2539        // Foo
 2540        //ˇ
 2541    "});
 2542        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2543        cx.set_state(indoc! {"
 2544        ˇ// Foo
 2545    "});
 2546        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2547        cx.assert_editor_state(indoc! {"
 2548
 2549        ˇ// Foo
 2550    "});
 2551    }
 2552    // Ensure that comment continuations can be disabled.
 2553    update_test_language_settings(cx, |settings| {
 2554        settings.defaults.extend_comment_on_newline = Some(false);
 2555    });
 2556    let mut cx = EditorTestContext::new(cx).await;
 2557    cx.set_state(indoc! {"
 2558        // Fooˇ
 2559    "});
 2560    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2561    cx.assert_editor_state(indoc! {"
 2562        // Foo
 2563        ˇ
 2564    "});
 2565}
 2566
 2567#[gpui::test]
 2568fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2569    init_test(cx, |_| {});
 2570
 2571    let editor = cx.add_window(|cx| {
 2572        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2573        let mut editor = build_editor(buffer.clone(), cx);
 2574        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2575        editor
 2576    });
 2577
 2578    _ = editor.update(cx, |editor, cx| {
 2579        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2580        editor.buffer.update(cx, |buffer, cx| {
 2581            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2582            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2583        });
 2584        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2585
 2586        editor.insert("Z", cx);
 2587        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2588
 2589        // The selections are moved after the inserted characters
 2590        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2591    });
 2592}
 2593
 2594#[gpui::test]
 2595async fn test_tab(cx: &mut gpui::TestAppContext) {
 2596    init_test(cx, |settings| {
 2597        settings.defaults.tab_size = NonZeroU32::new(3)
 2598    });
 2599
 2600    let mut cx = EditorTestContext::new(cx).await;
 2601    cx.set_state(indoc! {"
 2602        ˇabˇc
 2603        ˇ🏀ˇ🏀ˇefg
 2604 2605    "});
 2606    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2607    cx.assert_editor_state(indoc! {"
 2608           ˇab ˇc
 2609           ˇ🏀  ˇ🏀  ˇefg
 2610        d  ˇ
 2611    "});
 2612
 2613    cx.set_state(indoc! {"
 2614        a
 2615        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2616    "});
 2617    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2618    cx.assert_editor_state(indoc! {"
 2619        a
 2620           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2621    "});
 2622}
 2623
 2624#[gpui::test]
 2625async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2626    init_test(cx, |_| {});
 2627
 2628    let mut cx = EditorTestContext::new(cx).await;
 2629    let language = Arc::new(
 2630        Language::new(
 2631            LanguageConfig::default(),
 2632            Some(tree_sitter_rust::LANGUAGE.into()),
 2633        )
 2634        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2635        .unwrap(),
 2636    );
 2637    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2638
 2639    // cursors that are already at the suggested indent level insert
 2640    // a soft tab. cursors that are to the left of the suggested indent
 2641    // auto-indent their line.
 2642    cx.set_state(indoc! {"
 2643        ˇ
 2644        const a: B = (
 2645            c(
 2646                d(
 2647        ˇ
 2648                )
 2649        ˇ
 2650        ˇ    )
 2651        );
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655            ˇ
 2656        const a: B = (
 2657            c(
 2658                d(
 2659                    ˇ
 2660                )
 2661                ˇ
 2662            ˇ)
 2663        );
 2664    "});
 2665
 2666    // handle auto-indent when there are multiple cursors on the same line
 2667    cx.set_state(indoc! {"
 2668        const a: B = (
 2669            c(
 2670        ˇ    ˇ
 2671        ˇ    )
 2672        );
 2673    "});
 2674    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2675    cx.assert_editor_state(indoc! {"
 2676        const a: B = (
 2677            c(
 2678                ˇ
 2679            ˇ)
 2680        );
 2681    "});
 2682}
 2683
 2684#[gpui::test]
 2685async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2686    init_test(cx, |settings| {
 2687        settings.defaults.tab_size = NonZeroU32::new(4)
 2688    });
 2689
 2690    let language = Arc::new(
 2691        Language::new(
 2692            LanguageConfig::default(),
 2693            Some(tree_sitter_rust::LANGUAGE.into()),
 2694        )
 2695        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2696        .unwrap(),
 2697    );
 2698
 2699    let mut cx = EditorTestContext::new(cx).await;
 2700    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2701    cx.set_state(indoc! {"
 2702        fn a() {
 2703            if b {
 2704        \t ˇc
 2705            }
 2706        }
 2707    "});
 2708
 2709    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2710    cx.assert_editor_state(indoc! {"
 2711        fn a() {
 2712            if b {
 2713                ˇc
 2714            }
 2715        }
 2716    "});
 2717}
 2718
 2719#[gpui::test]
 2720async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2721    init_test(cx, |settings| {
 2722        settings.defaults.tab_size = NonZeroU32::new(4);
 2723    });
 2724
 2725    let mut cx = EditorTestContext::new(cx).await;
 2726
 2727    cx.set_state(indoc! {"
 2728          «oneˇ» «twoˇ»
 2729        three
 2730         four
 2731    "});
 2732    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734            «oneˇ» «twoˇ»
 2735        three
 2736         four
 2737    "});
 2738
 2739    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2740    cx.assert_editor_state(indoc! {"
 2741        «oneˇ» «twoˇ»
 2742        three
 2743         four
 2744    "});
 2745
 2746    // select across line ending
 2747    cx.set_state(indoc! {"
 2748        one two
 2749        t«hree
 2750        ˇ» four
 2751    "});
 2752    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2753    cx.assert_editor_state(indoc! {"
 2754        one two
 2755            t«hree
 2756        ˇ» four
 2757    "});
 2758
 2759    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2760    cx.assert_editor_state(indoc! {"
 2761        one two
 2762        t«hree
 2763        ˇ» four
 2764    "});
 2765
 2766    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2767    cx.set_state(indoc! {"
 2768        one two
 2769        ˇthree
 2770            four
 2771    "});
 2772    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2773    cx.assert_editor_state(indoc! {"
 2774        one two
 2775            ˇthree
 2776            four
 2777    "});
 2778
 2779    cx.set_state(indoc! {"
 2780        one two
 2781        ˇ    three
 2782            four
 2783    "});
 2784    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2785    cx.assert_editor_state(indoc! {"
 2786        one two
 2787        ˇthree
 2788            four
 2789    "});
 2790}
 2791
 2792#[gpui::test]
 2793async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2794    init_test(cx, |settings| {
 2795        settings.defaults.hard_tabs = Some(true);
 2796    });
 2797
 2798    let mut cx = EditorTestContext::new(cx).await;
 2799
 2800    // select two ranges on one line
 2801    cx.set_state(indoc! {"
 2802        «oneˇ» «twoˇ»
 2803        three
 2804        four
 2805    "});
 2806    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2807    cx.assert_editor_state(indoc! {"
 2808        \t«oneˇ» «twoˇ»
 2809        three
 2810        four
 2811    "});
 2812    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2813    cx.assert_editor_state(indoc! {"
 2814        \t\t«oneˇ» «twoˇ»
 2815        three
 2816        four
 2817    "});
 2818    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2819    cx.assert_editor_state(indoc! {"
 2820        \t«oneˇ» «twoˇ»
 2821        three
 2822        four
 2823    "});
 2824    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2825    cx.assert_editor_state(indoc! {"
 2826        «oneˇ» «twoˇ»
 2827        three
 2828        four
 2829    "});
 2830
 2831    // select across a line ending
 2832    cx.set_state(indoc! {"
 2833        one two
 2834        t«hree
 2835        ˇ»four
 2836    "});
 2837    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2838    cx.assert_editor_state(indoc! {"
 2839        one two
 2840        \tt«hree
 2841        ˇ»four
 2842    "});
 2843    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2844    cx.assert_editor_state(indoc! {"
 2845        one two
 2846        \t\tt«hree
 2847        ˇ»four
 2848    "});
 2849    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2850    cx.assert_editor_state(indoc! {"
 2851        one two
 2852        \tt«hree
 2853        ˇ»four
 2854    "});
 2855    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2856    cx.assert_editor_state(indoc! {"
 2857        one two
 2858        t«hree
 2859        ˇ»four
 2860    "});
 2861
 2862    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2863    cx.set_state(indoc! {"
 2864        one two
 2865        ˇthree
 2866        four
 2867    "});
 2868    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2869    cx.assert_editor_state(indoc! {"
 2870        one two
 2871        ˇthree
 2872        four
 2873    "});
 2874    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2875    cx.assert_editor_state(indoc! {"
 2876        one two
 2877        \tˇthree
 2878        four
 2879    "});
 2880    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2881    cx.assert_editor_state(indoc! {"
 2882        one two
 2883        ˇthree
 2884        four
 2885    "});
 2886}
 2887
 2888#[gpui::test]
 2889fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2890    init_test(cx, |settings| {
 2891        settings.languages.extend([
 2892            (
 2893                "TOML".into(),
 2894                LanguageSettingsContent {
 2895                    tab_size: NonZeroU32::new(2),
 2896                    ..Default::default()
 2897                },
 2898            ),
 2899            (
 2900                "Rust".into(),
 2901                LanguageSettingsContent {
 2902                    tab_size: NonZeroU32::new(4),
 2903                    ..Default::default()
 2904                },
 2905            ),
 2906        ]);
 2907    });
 2908
 2909    let toml_language = Arc::new(Language::new(
 2910        LanguageConfig {
 2911            name: "TOML".into(),
 2912            ..Default::default()
 2913        },
 2914        None,
 2915    ));
 2916    let rust_language = Arc::new(Language::new(
 2917        LanguageConfig {
 2918            name: "Rust".into(),
 2919            ..Default::default()
 2920        },
 2921        None,
 2922    ));
 2923
 2924    let toml_buffer =
 2925        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2926    let rust_buffer = cx.new_model(|cx| {
 2927        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2928    });
 2929    let multibuffer = cx.new_model(|cx| {
 2930        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2931        multibuffer.push_excerpts(
 2932            toml_buffer.clone(),
 2933            [ExcerptRange {
 2934                context: Point::new(0, 0)..Point::new(2, 0),
 2935                primary: None,
 2936            }],
 2937            cx,
 2938        );
 2939        multibuffer.push_excerpts(
 2940            rust_buffer.clone(),
 2941            [ExcerptRange {
 2942                context: Point::new(0, 0)..Point::new(1, 0),
 2943                primary: None,
 2944            }],
 2945            cx,
 2946        );
 2947        multibuffer
 2948    });
 2949
 2950    cx.add_window(|cx| {
 2951        let mut editor = build_editor(multibuffer, cx);
 2952
 2953        assert_eq!(
 2954            editor.text(cx),
 2955            indoc! {"
 2956                a = 1
 2957                b = 2
 2958
 2959                const c: usize = 3;
 2960            "}
 2961        );
 2962
 2963        select_ranges(
 2964            &mut editor,
 2965            indoc! {"
 2966                «aˇ» = 1
 2967                b = 2
 2968
 2969                «const c:ˇ» usize = 3;
 2970            "},
 2971            cx,
 2972        );
 2973
 2974        editor.tab(&Tab, cx);
 2975        assert_text_with_selections(
 2976            &mut editor,
 2977            indoc! {"
 2978                  «aˇ» = 1
 2979                b = 2
 2980
 2981                    «const c:ˇ» usize = 3;
 2982            "},
 2983            cx,
 2984        );
 2985        editor.tab_prev(&TabPrev, cx);
 2986        assert_text_with_selections(
 2987            &mut editor,
 2988            indoc! {"
 2989                «aˇ» = 1
 2990                b = 2
 2991
 2992                «const c:ˇ» usize = 3;
 2993            "},
 2994            cx,
 2995        );
 2996
 2997        editor
 2998    });
 2999}
 3000
 3001#[gpui::test]
 3002async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3003    init_test(cx, |_| {});
 3004
 3005    let mut cx = EditorTestContext::new(cx).await;
 3006
 3007    // Basic backspace
 3008    cx.set_state(indoc! {"
 3009        onˇe two three
 3010        fou«rˇ» five six
 3011        seven «ˇeight nine
 3012        »ten
 3013    "});
 3014    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3015    cx.assert_editor_state(indoc! {"
 3016        oˇe two three
 3017        fouˇ five six
 3018        seven ˇten
 3019    "});
 3020
 3021    // Test backspace inside and around indents
 3022    cx.set_state(indoc! {"
 3023        zero
 3024            ˇone
 3025                ˇtwo
 3026            ˇ ˇ ˇ  three
 3027        ˇ  ˇ  four
 3028    "});
 3029    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3030    cx.assert_editor_state(indoc! {"
 3031        zero
 3032        ˇone
 3033            ˇtwo
 3034        ˇ  threeˇ  four
 3035    "});
 3036
 3037    // Test backspace with line_mode set to true
 3038    cx.update_editor(|e, _| e.selections.line_mode = true);
 3039    cx.set_state(indoc! {"
 3040        The ˇquick ˇbrown
 3041        fox jumps over
 3042        the lazy dog
 3043        ˇThe qu«ick bˇ»rown"});
 3044    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3045    cx.assert_editor_state(indoc! {"
 3046        ˇfox jumps over
 3047        the lazy dogˇ"});
 3048}
 3049
 3050#[gpui::test]
 3051async fn test_delete(cx: &mut gpui::TestAppContext) {
 3052    init_test(cx, |_| {});
 3053
 3054    let mut cx = EditorTestContext::new(cx).await;
 3055    cx.set_state(indoc! {"
 3056        onˇe two three
 3057        fou«rˇ» five six
 3058        seven «ˇeight nine
 3059        »ten
 3060    "});
 3061    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3062    cx.assert_editor_state(indoc! {"
 3063        onˇ two three
 3064        fouˇ five six
 3065        seven ˇten
 3066    "});
 3067
 3068    // Test backspace with line_mode set to true
 3069    cx.update_editor(|e, _| e.selections.line_mode = true);
 3070    cx.set_state(indoc! {"
 3071        The ˇquick ˇbrown
 3072        fox «ˇjum»ps over
 3073        the lazy dog
 3074        ˇThe qu«ick bˇ»rown"});
 3075    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3076    cx.assert_editor_state("ˇthe lazy dogˇ");
 3077}
 3078
 3079#[gpui::test]
 3080fn test_delete_line(cx: &mut TestAppContext) {
 3081    init_test(cx, |_| {});
 3082
 3083    let view = cx.add_window(|cx| {
 3084        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3085        build_editor(buffer, cx)
 3086    });
 3087    _ = view.update(cx, |view, cx| {
 3088        view.change_selections(None, cx, |s| {
 3089            s.select_display_ranges([
 3090                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3091                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3092                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3093            ])
 3094        });
 3095        view.delete_line(&DeleteLine, cx);
 3096        assert_eq!(view.display_text(cx), "ghi");
 3097        assert_eq!(
 3098            view.selections.display_ranges(cx),
 3099            vec![
 3100                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3101                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3102            ]
 3103        );
 3104    });
 3105
 3106    let view = cx.add_window(|cx| {
 3107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3108        build_editor(buffer, cx)
 3109    });
 3110    _ = view.update(cx, |view, cx| {
 3111        view.change_selections(None, cx, |s| {
 3112            s.select_display_ranges([
 3113                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3114            ])
 3115        });
 3116        view.delete_line(&DeleteLine, cx);
 3117        assert_eq!(view.display_text(cx), "ghi\n");
 3118        assert_eq!(
 3119            view.selections.display_ranges(cx),
 3120            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3121        );
 3122    });
 3123}
 3124
 3125#[gpui::test]
 3126fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3127    init_test(cx, |_| {});
 3128
 3129    cx.add_window(|cx| {
 3130        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3131        let mut editor = build_editor(buffer.clone(), cx);
 3132        let buffer = buffer.read(cx).as_singleton().unwrap();
 3133
 3134        assert_eq!(
 3135            editor.selections.ranges::<Point>(cx),
 3136            &[Point::new(0, 0)..Point::new(0, 0)]
 3137        );
 3138
 3139        // When on single line, replace newline at end by space
 3140        editor.join_lines(&JoinLines, cx);
 3141        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3142        assert_eq!(
 3143            editor.selections.ranges::<Point>(cx),
 3144            &[Point::new(0, 3)..Point::new(0, 3)]
 3145        );
 3146
 3147        // When multiple lines are selected, remove newlines that are spanned by the selection
 3148        editor.change_selections(None, cx, |s| {
 3149            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3150        });
 3151        editor.join_lines(&JoinLines, cx);
 3152        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3153        assert_eq!(
 3154            editor.selections.ranges::<Point>(cx),
 3155            &[Point::new(0, 11)..Point::new(0, 11)]
 3156        );
 3157
 3158        // Undo should be transactional
 3159        editor.undo(&Undo, cx);
 3160        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3161        assert_eq!(
 3162            editor.selections.ranges::<Point>(cx),
 3163            &[Point::new(0, 5)..Point::new(2, 2)]
 3164        );
 3165
 3166        // When joining an empty line don't insert a space
 3167        editor.change_selections(None, cx, |s| {
 3168            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3169        });
 3170        editor.join_lines(&JoinLines, cx);
 3171        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3172        assert_eq!(
 3173            editor.selections.ranges::<Point>(cx),
 3174            [Point::new(2, 3)..Point::new(2, 3)]
 3175        );
 3176
 3177        // We can remove trailing newlines
 3178        editor.join_lines(&JoinLines, cx);
 3179        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3180        assert_eq!(
 3181            editor.selections.ranges::<Point>(cx),
 3182            [Point::new(2, 3)..Point::new(2, 3)]
 3183        );
 3184
 3185        // We don't blow up on the last line
 3186        editor.join_lines(&JoinLines, cx);
 3187        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3188        assert_eq!(
 3189            editor.selections.ranges::<Point>(cx),
 3190            [Point::new(2, 3)..Point::new(2, 3)]
 3191        );
 3192
 3193        // reset to test indentation
 3194        editor.buffer.update(cx, |buffer, cx| {
 3195            buffer.edit(
 3196                [
 3197                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3198                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3199                ],
 3200                None,
 3201                cx,
 3202            )
 3203        });
 3204
 3205        // We remove any leading spaces
 3206        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3207        editor.change_selections(None, cx, |s| {
 3208            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3209        });
 3210        editor.join_lines(&JoinLines, cx);
 3211        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3212
 3213        // We don't insert a space for a line containing only spaces
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3216
 3217        // We ignore any leading tabs
 3218        editor.join_lines(&JoinLines, cx);
 3219        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3220
 3221        editor
 3222    });
 3223}
 3224
 3225#[gpui::test]
 3226fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3227    init_test(cx, |_| {});
 3228
 3229    cx.add_window(|cx| {
 3230        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3231        let mut editor = build_editor(buffer.clone(), cx);
 3232        let buffer = buffer.read(cx).as_singleton().unwrap();
 3233
 3234        editor.change_selections(None, cx, |s| {
 3235            s.select_ranges([
 3236                Point::new(0, 2)..Point::new(1, 1),
 3237                Point::new(1, 2)..Point::new(1, 2),
 3238                Point::new(3, 1)..Point::new(3, 2),
 3239            ])
 3240        });
 3241
 3242        editor.join_lines(&JoinLines, cx);
 3243        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3244
 3245        assert_eq!(
 3246            editor.selections.ranges::<Point>(cx),
 3247            [
 3248                Point::new(0, 7)..Point::new(0, 7),
 3249                Point::new(1, 3)..Point::new(1, 3)
 3250            ]
 3251        );
 3252        editor
 3253    });
 3254}
 3255
 3256#[gpui::test]
 3257async fn test_join_lines_with_git_diff_base(
 3258    executor: BackgroundExecutor,
 3259    cx: &mut gpui::TestAppContext,
 3260) {
 3261    init_test(cx, |_| {});
 3262
 3263    let mut cx = EditorTestContext::new(cx).await;
 3264
 3265    let diff_base = r#"
 3266        Line 0
 3267        Line 1
 3268        Line 2
 3269        Line 3
 3270        "#
 3271    .unindent();
 3272
 3273    cx.set_state(
 3274        &r#"
 3275        ˇLine 0
 3276        Line 1
 3277        Line 2
 3278        Line 3
 3279        "#
 3280        .unindent(),
 3281    );
 3282
 3283    cx.set_diff_base(Some(&diff_base));
 3284    executor.run_until_parked();
 3285
 3286    // Join lines
 3287    cx.update_editor(|editor, cx| {
 3288        editor.join_lines(&JoinLines, cx);
 3289    });
 3290    executor.run_until_parked();
 3291
 3292    cx.assert_editor_state(
 3293        &r#"
 3294        Line 0ˇ Line 1
 3295        Line 2
 3296        Line 3
 3297        "#
 3298        .unindent(),
 3299    );
 3300    // Join again
 3301    cx.update_editor(|editor, cx| {
 3302        editor.join_lines(&JoinLines, cx);
 3303    });
 3304    executor.run_until_parked();
 3305
 3306    cx.assert_editor_state(
 3307        &r#"
 3308        Line 0 Line 1ˇ Line 2
 3309        Line 3
 3310        "#
 3311        .unindent(),
 3312    );
 3313}
 3314
 3315#[gpui::test]
 3316async fn test_custom_newlines_cause_no_false_positive_diffs(
 3317    executor: BackgroundExecutor,
 3318    cx: &mut gpui::TestAppContext,
 3319) {
 3320    init_test(cx, |_| {});
 3321    let mut cx = EditorTestContext::new(cx).await;
 3322    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3323    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3324    executor.run_until_parked();
 3325
 3326    cx.update_editor(|editor, cx| {
 3327        assert_eq!(
 3328            editor
 3329                .buffer()
 3330                .read(cx)
 3331                .snapshot(cx)
 3332                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3333                .collect::<Vec<_>>(),
 3334            Vec::new(),
 3335            "Should not have any diffs for files with custom newlines"
 3336        );
 3337    });
 3338}
 3339
 3340#[gpui::test]
 3341async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    let mut cx = EditorTestContext::new(cx).await;
 3345
 3346    // Test sort_lines_case_insensitive()
 3347    cx.set_state(indoc! {"
 3348        «z
 3349        y
 3350        x
 3351        Z
 3352        Y
 3353        Xˇ»
 3354    "});
 3355    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3356    cx.assert_editor_state(indoc! {"
 3357        «x
 3358        X
 3359        y
 3360        Y
 3361        z
 3362        Zˇ»
 3363    "});
 3364
 3365    // Test reverse_lines()
 3366    cx.set_state(indoc! {"
 3367        «5
 3368        4
 3369        3
 3370        2
 3371        1ˇ»
 3372    "});
 3373    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3374    cx.assert_editor_state(indoc! {"
 3375        «1
 3376        2
 3377        3
 3378        4
 3379        5ˇ»
 3380    "});
 3381
 3382    // Skip testing shuffle_line()
 3383
 3384    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3385    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3386
 3387    // Don't manipulate when cursor is on single line, but expand the selection
 3388    cx.set_state(indoc! {"
 3389        ddˇdd
 3390        ccc
 3391        bb
 3392        a
 3393    "});
 3394    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3395    cx.assert_editor_state(indoc! {"
 3396        «ddddˇ»
 3397        ccc
 3398        bb
 3399        a
 3400    "});
 3401
 3402    // Basic manipulate case
 3403    // Start selection moves to column 0
 3404    // End of selection shrinks to fit shorter line
 3405    cx.set_state(indoc! {"
 3406        dd«d
 3407        ccc
 3408        bb
 3409        aaaaaˇ»
 3410    "});
 3411    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3412    cx.assert_editor_state(indoc! {"
 3413        «aaaaa
 3414        bb
 3415        ccc
 3416        dddˇ»
 3417    "});
 3418
 3419    // Manipulate case with newlines
 3420    cx.set_state(indoc! {"
 3421        dd«d
 3422        ccc
 3423
 3424        bb
 3425        aaaaa
 3426
 3427        ˇ»
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «
 3432
 3433        aaaaa
 3434        bb
 3435        ccc
 3436        dddˇ»
 3437
 3438    "});
 3439
 3440    // Adding new line
 3441    cx.set_state(indoc! {"
 3442        aa«a
 3443        bbˇ»b
 3444    "});
 3445    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3446    cx.assert_editor_state(indoc! {"
 3447        «aaa
 3448        bbb
 3449        added_lineˇ»
 3450    "});
 3451
 3452    // Removing line
 3453    cx.set_state(indoc! {"
 3454        aa«a
 3455        bbbˇ»
 3456    "});
 3457    cx.update_editor(|e, cx| {
 3458        e.manipulate_lines(cx, |lines| {
 3459            lines.pop();
 3460        })
 3461    });
 3462    cx.assert_editor_state(indoc! {"
 3463        «aaaˇ»
 3464    "});
 3465
 3466    // Removing all lines
 3467    cx.set_state(indoc! {"
 3468        aa«a
 3469        bbbˇ»
 3470    "});
 3471    cx.update_editor(|e, cx| {
 3472        e.manipulate_lines(cx, |lines| {
 3473            lines.drain(..);
 3474        })
 3475    });
 3476    cx.assert_editor_state(indoc! {"
 3477        ˇ
 3478    "});
 3479}
 3480
 3481#[gpui::test]
 3482async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3483    init_test(cx, |_| {});
 3484
 3485    let mut cx = EditorTestContext::new(cx).await;
 3486
 3487    // Consider continuous selection as single selection
 3488    cx.set_state(indoc! {"
 3489        Aaa«aa
 3490        cˇ»c«c
 3491        bb
 3492        aaaˇ»aa
 3493    "});
 3494    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3495    cx.assert_editor_state(indoc! {"
 3496        «Aaaaa
 3497        ccc
 3498        bb
 3499        aaaaaˇ»
 3500    "});
 3501
 3502    cx.set_state(indoc! {"
 3503        Aaa«aa
 3504        cˇ»c«c
 3505        bb
 3506        aaaˇ»aa
 3507    "});
 3508    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3509    cx.assert_editor_state(indoc! {"
 3510        «Aaaaa
 3511        ccc
 3512        bbˇ»
 3513    "});
 3514
 3515    // Consider non continuous selection as distinct dedup operations
 3516    cx.set_state(indoc! {"
 3517        «aaaaa
 3518        bb
 3519        aaaaa
 3520        aaaaaˇ»
 3521
 3522        aaa«aaˇ»
 3523    "});
 3524    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3525    cx.assert_editor_state(indoc! {"
 3526        «aaaaa
 3527        bbˇ»
 3528
 3529        «aaaaaˇ»
 3530    "});
 3531}
 3532
 3533#[gpui::test]
 3534async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3535    init_test(cx, |_| {});
 3536
 3537    let mut cx = EditorTestContext::new(cx).await;
 3538
 3539    cx.set_state(indoc! {"
 3540        «Aaa
 3541        aAa
 3542        Aaaˇ»
 3543    "});
 3544    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3545    cx.assert_editor_state(indoc! {"
 3546        «Aaa
 3547        aAaˇ»
 3548    "});
 3549
 3550    cx.set_state(indoc! {"
 3551        «Aaa
 3552        aAa
 3553        aaAˇ»
 3554    "});
 3555    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3556    cx.assert_editor_state(indoc! {"
 3557        «Aaaˇ»
 3558    "});
 3559}
 3560
 3561#[gpui::test]
 3562async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3563    init_test(cx, |_| {});
 3564
 3565    let mut cx = EditorTestContext::new(cx).await;
 3566
 3567    // Manipulate with multiple selections on a single line
 3568    cx.set_state(indoc! {"
 3569        dd«dd
 3570        cˇ»c«c
 3571        bb
 3572        aaaˇ»aa
 3573    "});
 3574    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3575    cx.assert_editor_state(indoc! {"
 3576        «aaaaa
 3577        bb
 3578        ccc
 3579        ddddˇ»
 3580    "});
 3581
 3582    // Manipulate with multiple disjoin selections
 3583    cx.set_state(indoc! {"
 3584 3585        4
 3586        3
 3587        2
 3588        1ˇ»
 3589
 3590        dd«dd
 3591        ccc
 3592        bb
 3593        aaaˇ»aa
 3594    "});
 3595    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3596    cx.assert_editor_state(indoc! {"
 3597        «1
 3598        2
 3599        3
 3600        4
 3601        5ˇ»
 3602
 3603        «aaaaa
 3604        bb
 3605        ccc
 3606        ddddˇ»
 3607    "});
 3608
 3609    // Adding lines on each selection
 3610    cx.set_state(indoc! {"
 3611 3612        1ˇ»
 3613
 3614        bb«bb
 3615        aaaˇ»aa
 3616    "});
 3617    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3618    cx.assert_editor_state(indoc! {"
 3619        «2
 3620        1
 3621        added lineˇ»
 3622
 3623        «bbbb
 3624        aaaaa
 3625        added lineˇ»
 3626    "});
 3627
 3628    // Removing lines on each selection
 3629    cx.set_state(indoc! {"
 3630 3631        1ˇ»
 3632
 3633        bb«bb
 3634        aaaˇ»aa
 3635    "});
 3636    cx.update_editor(|e, cx| {
 3637        e.manipulate_lines(cx, |lines| {
 3638            lines.pop();
 3639        })
 3640    });
 3641    cx.assert_editor_state(indoc! {"
 3642        «2ˇ»
 3643
 3644        «bbbbˇ»
 3645    "});
 3646}
 3647
 3648#[gpui::test]
 3649async fn test_manipulate_text(cx: &mut TestAppContext) {
 3650    init_test(cx, |_| {});
 3651
 3652    let mut cx = EditorTestContext::new(cx).await;
 3653
 3654    // Test convert_to_upper_case()
 3655    cx.set_state(indoc! {"
 3656        «hello worldˇ»
 3657    "});
 3658    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3659    cx.assert_editor_state(indoc! {"
 3660        «HELLO WORLDˇ»
 3661    "});
 3662
 3663    // Test convert_to_lower_case()
 3664    cx.set_state(indoc! {"
 3665        «HELLO WORLDˇ»
 3666    "});
 3667    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3668    cx.assert_editor_state(indoc! {"
 3669        «hello worldˇ»
 3670    "});
 3671
 3672    // Test multiple line, single selection case
 3673    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3674    cx.set_state(indoc! {"
 3675        «The quick brown
 3676        fox jumps over
 3677        the lazy dogˇ»
 3678    "});
 3679    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3680    cx.assert_editor_state(indoc! {"
 3681        «The Quick Brown
 3682        Fox Jumps Over
 3683        The Lazy Dogˇ»
 3684    "});
 3685
 3686    // Test multiple line, single selection case
 3687    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3688    cx.set_state(indoc! {"
 3689        «The quick brown
 3690        fox jumps over
 3691        the lazy dogˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «TheQuickBrown
 3696        FoxJumpsOver
 3697        TheLazyDogˇ»
 3698    "});
 3699
 3700    // From here on out, test more complex cases of manipulate_text()
 3701
 3702    // Test no selection case - should affect words cursors are in
 3703    // Cursor at beginning, middle, and end of word
 3704    cx.set_state(indoc! {"
 3705        ˇhello big beauˇtiful worldˇ
 3706    "});
 3707    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3708    cx.assert_editor_state(indoc! {"
 3709        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3710    "});
 3711
 3712    // Test multiple selections on a single line and across multiple lines
 3713    cx.set_state(indoc! {"
 3714        «Theˇ» quick «brown
 3715        foxˇ» jumps «overˇ»
 3716        the «lazyˇ» dog
 3717    "});
 3718    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3719    cx.assert_editor_state(indoc! {"
 3720        «THEˇ» quick «BROWN
 3721        FOXˇ» jumps «OVERˇ»
 3722        the «LAZYˇ» dog
 3723    "});
 3724
 3725    // Test case where text length grows
 3726    cx.set_state(indoc! {"
 3727        «tschüߡ»
 3728    "});
 3729    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3730    cx.assert_editor_state(indoc! {"
 3731        «TSCHÜSSˇ»
 3732    "});
 3733
 3734    // Test to make sure we don't crash when text shrinks
 3735    cx.set_state(indoc! {"
 3736        aaa_bbbˇ
 3737    "});
 3738    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3739    cx.assert_editor_state(indoc! {"
 3740        «aaaBbbˇ»
 3741    "});
 3742
 3743    // Test to make sure we all aware of the fact that each word can grow and shrink
 3744    // Final selections should be aware of this fact
 3745    cx.set_state(indoc! {"
 3746        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3747    "});
 3748    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3749    cx.assert_editor_state(indoc! {"
 3750        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3751    "});
 3752
 3753    cx.set_state(indoc! {"
 3754        «hElLo, WoRld!ˇ»
 3755    "});
 3756    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3757    cx.assert_editor_state(indoc! {"
 3758        «HeLlO, wOrLD!ˇ»
 3759    "});
 3760}
 3761
 3762#[gpui::test]
 3763fn test_duplicate_line(cx: &mut TestAppContext) {
 3764    init_test(cx, |_| {});
 3765
 3766    let view = cx.add_window(|cx| {
 3767        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3768        build_editor(buffer, cx)
 3769    });
 3770    _ = view.update(cx, |view, cx| {
 3771        view.change_selections(None, cx, |s| {
 3772            s.select_display_ranges([
 3773                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3774                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3775                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3776                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3777            ])
 3778        });
 3779        view.duplicate_line_down(&DuplicateLineDown, cx);
 3780        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3781        assert_eq!(
 3782            view.selections.display_ranges(cx),
 3783            vec![
 3784                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3785                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3786                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3787                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3788            ]
 3789        );
 3790    });
 3791
 3792    let view = cx.add_window(|cx| {
 3793        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3794        build_editor(buffer, cx)
 3795    });
 3796    _ = view.update(cx, |view, cx| {
 3797        view.change_selections(None, cx, |s| {
 3798            s.select_display_ranges([
 3799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3800                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3801            ])
 3802        });
 3803        view.duplicate_line_down(&DuplicateLineDown, cx);
 3804        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3805        assert_eq!(
 3806            view.selections.display_ranges(cx),
 3807            vec![
 3808                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3809                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3810            ]
 3811        );
 3812    });
 3813
 3814    // With `move_upwards` the selections stay in place, except for
 3815    // the lines inserted above them
 3816    let view = cx.add_window(|cx| {
 3817        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3818        build_editor(buffer, cx)
 3819    });
 3820    _ = view.update(cx, |view, cx| {
 3821        view.change_selections(None, cx, |s| {
 3822            s.select_display_ranges([
 3823                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3824                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3825                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3826                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3827            ])
 3828        });
 3829        view.duplicate_line_up(&DuplicateLineUp, cx);
 3830        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3831        assert_eq!(
 3832            view.selections.display_ranges(cx),
 3833            vec![
 3834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3835                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3836                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3837                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3838            ]
 3839        );
 3840    });
 3841
 3842    let view = cx.add_window(|cx| {
 3843        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3844        build_editor(buffer, cx)
 3845    });
 3846    _ = view.update(cx, |view, cx| {
 3847        view.change_selections(None, cx, |s| {
 3848            s.select_display_ranges([
 3849                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3850                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3851            ])
 3852        });
 3853        view.duplicate_line_up(&DuplicateLineUp, cx);
 3854        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3855        assert_eq!(
 3856            view.selections.display_ranges(cx),
 3857            vec![
 3858                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3859                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3860            ]
 3861        );
 3862    });
 3863}
 3864
 3865#[gpui::test]
 3866fn test_move_line_up_down(cx: &mut TestAppContext) {
 3867    init_test(cx, |_| {});
 3868
 3869    let view = cx.add_window(|cx| {
 3870        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3871        build_editor(buffer, cx)
 3872    });
 3873    _ = view.update(cx, |view, cx| {
 3874        view.fold_ranges(
 3875            vec![
 3876                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3877                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3878                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3879            ],
 3880            true,
 3881            cx,
 3882        );
 3883        view.change_selections(None, cx, |s| {
 3884            s.select_display_ranges([
 3885                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3886                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3887                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3888                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3889            ])
 3890        });
 3891        assert_eq!(
 3892            view.display_text(cx),
 3893            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3894        );
 3895
 3896        view.move_line_up(&MoveLineUp, cx);
 3897        assert_eq!(
 3898            view.display_text(cx),
 3899            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3900        );
 3901        assert_eq!(
 3902            view.selections.display_ranges(cx),
 3903            vec![
 3904                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3905                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3906                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3907                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3908            ]
 3909        );
 3910    });
 3911
 3912    _ = view.update(cx, |view, cx| {
 3913        view.move_line_down(&MoveLineDown, cx);
 3914        assert_eq!(
 3915            view.display_text(cx),
 3916            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3917        );
 3918        assert_eq!(
 3919            view.selections.display_ranges(cx),
 3920            vec![
 3921                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3922                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3923                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3924                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3925            ]
 3926        );
 3927    });
 3928
 3929    _ = view.update(cx, |view, cx| {
 3930        view.move_line_down(&MoveLineDown, cx);
 3931        assert_eq!(
 3932            view.display_text(cx),
 3933            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3934        );
 3935        assert_eq!(
 3936            view.selections.display_ranges(cx),
 3937            vec![
 3938                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3939                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3940                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3941                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3942            ]
 3943        );
 3944    });
 3945
 3946    _ = view.update(cx, |view, cx| {
 3947        view.move_line_up(&MoveLineUp, cx);
 3948        assert_eq!(
 3949            view.display_text(cx),
 3950            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3951        );
 3952        assert_eq!(
 3953            view.selections.display_ranges(cx),
 3954            vec![
 3955                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3956                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3957                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3958                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3959            ]
 3960        );
 3961    });
 3962}
 3963
 3964#[gpui::test]
 3965fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3966    init_test(cx, |_| {});
 3967
 3968    let editor = cx.add_window(|cx| {
 3969        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3970        build_editor(buffer, cx)
 3971    });
 3972    _ = editor.update(cx, |editor, cx| {
 3973        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3974        editor.insert_blocks(
 3975            [BlockProperties {
 3976                style: BlockStyle::Fixed,
 3977                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 3978                height: 1,
 3979                render: Box::new(|_| div().into_any()),
 3980                priority: 0,
 3981            }],
 3982            Some(Autoscroll::fit()),
 3983            cx,
 3984        );
 3985        editor.change_selections(None, cx, |s| {
 3986            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3987        });
 3988        editor.move_line_down(&MoveLineDown, cx);
 3989    });
 3990}
 3991
 3992#[gpui::test]
 3993fn test_transpose(cx: &mut TestAppContext) {
 3994    init_test(cx, |_| {});
 3995
 3996    _ = cx.add_window(|cx| {
 3997        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3998        editor.set_style(EditorStyle::default(), cx);
 3999        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4000        editor.transpose(&Default::default(), cx);
 4001        assert_eq!(editor.text(cx), "bac");
 4002        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4003
 4004        editor.transpose(&Default::default(), cx);
 4005        assert_eq!(editor.text(cx), "bca");
 4006        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4007
 4008        editor.transpose(&Default::default(), cx);
 4009        assert_eq!(editor.text(cx), "bac");
 4010        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4011
 4012        editor
 4013    });
 4014
 4015    _ = cx.add_window(|cx| {
 4016        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4017        editor.set_style(EditorStyle::default(), cx);
 4018        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4019        editor.transpose(&Default::default(), cx);
 4020        assert_eq!(editor.text(cx), "acb\nde");
 4021        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4022
 4023        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4024        editor.transpose(&Default::default(), cx);
 4025        assert_eq!(editor.text(cx), "acbd\ne");
 4026        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4027
 4028        editor.transpose(&Default::default(), cx);
 4029        assert_eq!(editor.text(cx), "acbde\n");
 4030        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4031
 4032        editor.transpose(&Default::default(), cx);
 4033        assert_eq!(editor.text(cx), "acbd\ne");
 4034        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4035
 4036        editor
 4037    });
 4038
 4039    _ = cx.add_window(|cx| {
 4040        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4041        editor.set_style(EditorStyle::default(), cx);
 4042        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4043        editor.transpose(&Default::default(), cx);
 4044        assert_eq!(editor.text(cx), "bacd\ne");
 4045        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4046
 4047        editor.transpose(&Default::default(), cx);
 4048        assert_eq!(editor.text(cx), "bcade\n");
 4049        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4050
 4051        editor.transpose(&Default::default(), cx);
 4052        assert_eq!(editor.text(cx), "bcda\ne");
 4053        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4054
 4055        editor.transpose(&Default::default(), cx);
 4056        assert_eq!(editor.text(cx), "bcade\n");
 4057        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4058
 4059        editor.transpose(&Default::default(), cx);
 4060        assert_eq!(editor.text(cx), "bcaed\n");
 4061        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4062
 4063        editor
 4064    });
 4065
 4066    _ = cx.add_window(|cx| {
 4067        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4068        editor.set_style(EditorStyle::default(), cx);
 4069        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4070        editor.transpose(&Default::default(), cx);
 4071        assert_eq!(editor.text(cx), "🏀🍐✋");
 4072        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4073
 4074        editor.transpose(&Default::default(), cx);
 4075        assert_eq!(editor.text(cx), "🏀✋🍐");
 4076        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4077
 4078        editor.transpose(&Default::default(), cx);
 4079        assert_eq!(editor.text(cx), "🏀🍐✋");
 4080        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4081
 4082        editor
 4083    });
 4084}
 4085
 4086#[gpui::test]
 4087async fn test_rewrap(cx: &mut TestAppContext) {
 4088    init_test(cx, |_| {});
 4089
 4090    let mut cx = EditorTestContext::new(cx).await;
 4091
 4092    {
 4093        let language = Arc::new(Language::new(
 4094            LanguageConfig {
 4095                line_comments: vec!["// ".into()],
 4096                ..LanguageConfig::default()
 4097            },
 4098            None,
 4099        ));
 4100        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4101
 4102        let unwrapped_text = indoc! {"
 4103            // ˇ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.
 4104        "};
 4105
 4106        let wrapped_text = indoc! {"
 4107            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4108            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4109            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4110            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4111            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4112            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4113            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4114            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4115            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4116            // porttitor id. Aliquam id accumsan eros.ˇ
 4117        "};
 4118
 4119        cx.set_state(unwrapped_text);
 4120        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4121        cx.assert_editor_state(wrapped_text);
 4122    }
 4123
 4124    // Test that rewrapping works inside of a selection
 4125    {
 4126        let language = Arc::new(Language::new(
 4127            LanguageConfig {
 4128                line_comments: vec!["// ".into()],
 4129                ..LanguageConfig::default()
 4130            },
 4131            None,
 4132        ));
 4133        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4134
 4135        let unwrapped_text = indoc! {"
 4136            «// 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.ˇ»
 4137        "};
 4138
 4139        let wrapped_text = indoc! {"
 4140            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4141            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4142            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4143            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4144            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4145            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4146            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4147            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4148            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4149            // porttitor id. Aliquam id accumsan eros.ˇ
 4150        "};
 4151
 4152        cx.set_state(unwrapped_text);
 4153        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4154        cx.assert_editor_state(wrapped_text);
 4155    }
 4156
 4157    // Test that cursors that expand to the same region are collapsed.
 4158    {
 4159        let language = Arc::new(Language::new(
 4160            LanguageConfig {
 4161                line_comments: vec!["// ".into()],
 4162                ..LanguageConfig::default()
 4163            },
 4164            None,
 4165        ));
 4166        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4167
 4168        let unwrapped_text = indoc! {"
 4169            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4170            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4171            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4172            // ˇ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.
 4173        "};
 4174
 4175        let wrapped_text = indoc! {"
 4176            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4177            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4178            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4179            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4180            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4181            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4182            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4183            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4184            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4185            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4186        "};
 4187
 4188        cx.set_state(unwrapped_text);
 4189        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4190        cx.assert_editor_state(wrapped_text);
 4191    }
 4192
 4193    // Test that non-contiguous selections are treated separately.
 4194    {
 4195        let language = Arc::new(Language::new(
 4196            LanguageConfig {
 4197                line_comments: vec!["// ".into()],
 4198                ..LanguageConfig::default()
 4199            },
 4200            None,
 4201        ));
 4202        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4203
 4204        let unwrapped_text = indoc! {"
 4205            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4206            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4207            //
 4208            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4209            // ˇ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.
 4210        "};
 4211
 4212        let wrapped_text = indoc! {"
 4213            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4214            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4215            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4216            //
 4217            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4218            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4219            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4220            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4221            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4222            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4223            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4224        "};
 4225
 4226        cx.set_state(unwrapped_text);
 4227        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4228        cx.assert_editor_state(wrapped_text);
 4229    }
 4230
 4231    // Test that different comment prefixes are supported.
 4232    {
 4233        let language = Arc::new(Language::new(
 4234            LanguageConfig {
 4235                line_comments: vec!["# ".into()],
 4236                ..LanguageConfig::default()
 4237            },
 4238            None,
 4239        ));
 4240        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4241
 4242        let unwrapped_text = indoc! {"
 4243            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. 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.
 4244        "};
 4245
 4246        let wrapped_text = indoc! {"
 4247            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4248            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4249            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4250            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4251            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4252            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4253            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4254            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4255            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4256            # accumsan eros.ˇ
 4257        "};
 4258
 4259        cx.set_state(unwrapped_text);
 4260        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4261        cx.assert_editor_state(wrapped_text);
 4262    }
 4263
 4264    // Test that rewrapping is ignored outside of comments in most languages.
 4265    {
 4266        let language = Arc::new(Language::new(
 4267            LanguageConfig {
 4268                line_comments: vec!["// ".into(), "/// ".into()],
 4269                ..LanguageConfig::default()
 4270            },
 4271            Some(tree_sitter_rust::LANGUAGE.into()),
 4272        ));
 4273        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4274
 4275        let unwrapped_text = indoc! {"
 4276            /// Adds two numbers.
 4277            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4278            fn add(a: u32, b: u32) -> u32 {
 4279                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ˇ
 4280            }
 4281        "};
 4282
 4283        let wrapped_text = indoc! {"
 4284            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4285            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4286            fn add(a: u32, b: u32) -> u32 {
 4287                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ˇ
 4288            }
 4289        "};
 4290
 4291        cx.set_state(unwrapped_text);
 4292        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4293        cx.assert_editor_state(wrapped_text);
 4294    }
 4295
 4296    // Test that rewrapping works in Markdown and Plain Text languages.
 4297    {
 4298        let markdown_language = Arc::new(Language::new(
 4299            LanguageConfig {
 4300                name: "Markdown".into(),
 4301                ..LanguageConfig::default()
 4302            },
 4303            None,
 4304        ));
 4305        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4306
 4307        let unwrapped_text = indoc! {"
 4308            # Hello
 4309
 4310            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.
 4311        "};
 4312
 4313        let wrapped_text = indoc! {"
 4314            # Hello
 4315
 4316            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4317            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4318            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4319            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4320            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4321            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4322            Integer sit amet scelerisque nisi.ˇ
 4323        "};
 4324
 4325        cx.set_state(unwrapped_text);
 4326        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4327        cx.assert_editor_state(wrapped_text);
 4328
 4329        let plaintext_language = Arc::new(Language::new(
 4330            LanguageConfig {
 4331                name: "Plain Text".into(),
 4332                ..LanguageConfig::default()
 4333            },
 4334            None,
 4335        ));
 4336        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4337
 4338        let unwrapped_text = indoc! {"
 4339            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.
 4340        "};
 4341
 4342        let wrapped_text = indoc! {"
 4343            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4344            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4345            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4346            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4347            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4348            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4349            Integer sit amet scelerisque nisi.ˇ
 4350        "};
 4351
 4352        cx.set_state(unwrapped_text);
 4353        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4354        cx.assert_editor_state(wrapped_text);
 4355    }
 4356
 4357    // Test rewrapping unaligned comments in a selection.
 4358    {
 4359        let language = Arc::new(Language::new(
 4360            LanguageConfig {
 4361                line_comments: vec!["// ".into(), "/// ".into()],
 4362                ..LanguageConfig::default()
 4363            },
 4364            Some(tree_sitter_rust::LANGUAGE.into()),
 4365        ));
 4366        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4367
 4368        let unwrapped_text = indoc! {"
 4369            fn foo() {
 4370                if true {
 4371            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4372            // Praesent semper egestas tellus id dignissim.ˇ»
 4373                    do_something();
 4374                } else {
 4375                    //
 4376                }
 4377            }
 4378        "};
 4379
 4380        let wrapped_text = indoc! {"
 4381            fn foo() {
 4382                if true {
 4383                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4384                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4385                    // egestas tellus id dignissim.ˇ
 4386                    do_something();
 4387                } else {
 4388                    //
 4389                }
 4390            }
 4391        "};
 4392
 4393        cx.set_state(unwrapped_text);
 4394        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4395        cx.assert_editor_state(wrapped_text);
 4396
 4397        let unwrapped_text = indoc! {"
 4398            fn foo() {
 4399                if true {
 4400            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4401            // Praesent semper egestas tellus id dignissim.»
 4402                    do_something();
 4403                } else {
 4404                    //
 4405                }
 4406
 4407            }
 4408        "};
 4409
 4410        let wrapped_text = indoc! {"
 4411            fn foo() {
 4412                if true {
 4413                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4414                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4415                    // egestas tellus id dignissim.ˇ
 4416                    do_something();
 4417                } else {
 4418                    //
 4419                }
 4420
 4421            }
 4422        "};
 4423
 4424        cx.set_state(unwrapped_text);
 4425        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4426        cx.assert_editor_state(wrapped_text);
 4427    }
 4428}
 4429
 4430#[gpui::test]
 4431async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4432    init_test(cx, |_| {});
 4433
 4434    let mut cx = EditorTestContext::new(cx).await;
 4435
 4436    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4437    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4438    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4439
 4440    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4441    cx.set_state("two ˇfour ˇsix ˇ");
 4442    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4443    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4444
 4445    // Paste again but with only two cursors. Since the number of cursors doesn't
 4446    // match the number of slices in the clipboard, the entire clipboard text
 4447    // is pasted at each cursor.
 4448    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4449    cx.update_editor(|e, cx| {
 4450        e.handle_input("( ", cx);
 4451        e.paste(&Paste, cx);
 4452        e.handle_input(") ", cx);
 4453    });
 4454    cx.assert_editor_state(
 4455        &([
 4456            "( one✅ ",
 4457            "three ",
 4458            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4459            "three ",
 4460            "five ) ˇ",
 4461        ]
 4462        .join("\n")),
 4463    );
 4464
 4465    // Cut with three selections, one of which is full-line.
 4466    cx.set_state(indoc! {"
 4467        1«2ˇ»3
 4468        4ˇ567
 4469        «8ˇ»9"});
 4470    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4471    cx.assert_editor_state(indoc! {"
 4472        1ˇ3
 4473        ˇ9"});
 4474
 4475    // Paste with three selections, noticing how the copied selection that was full-line
 4476    // gets inserted before the second cursor.
 4477    cx.set_state(indoc! {"
 4478        1ˇ3
 4479 4480        «oˇ»ne"});
 4481    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4482    cx.assert_editor_state(indoc! {"
 4483        12ˇ3
 4484        4567
 4485 4486        8ˇne"});
 4487
 4488    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4489    cx.set_state(indoc! {"
 4490        The quick brown
 4491        fox juˇmps over
 4492        the lazy dog"});
 4493    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4494    assert_eq!(
 4495        cx.read_from_clipboard()
 4496            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4497        Some("fox jumps over\n".to_string())
 4498    );
 4499
 4500    // Paste with three selections, noticing how the copied full-line selection is inserted
 4501    // before the empty selections but replaces the selection that is non-empty.
 4502    cx.set_state(indoc! {"
 4503        Tˇhe quick brown
 4504        «foˇ»x jumps over
 4505        tˇhe lazy dog"});
 4506    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4507    cx.assert_editor_state(indoc! {"
 4508        fox jumps over
 4509        Tˇhe quick brown
 4510        fox jumps over
 4511        ˇx jumps over
 4512        fox jumps over
 4513        tˇhe lazy dog"});
 4514}
 4515
 4516#[gpui::test]
 4517async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4518    init_test(cx, |_| {});
 4519
 4520    let mut cx = EditorTestContext::new(cx).await;
 4521    let language = Arc::new(Language::new(
 4522        LanguageConfig::default(),
 4523        Some(tree_sitter_rust::LANGUAGE.into()),
 4524    ));
 4525    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4526
 4527    // Cut an indented block, without the leading whitespace.
 4528    cx.set_state(indoc! {"
 4529        const a: B = (
 4530            c(),
 4531            «d(
 4532                e,
 4533                f
 4534            )ˇ»
 4535        );
 4536    "});
 4537    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4538    cx.assert_editor_state(indoc! {"
 4539        const a: B = (
 4540            c(),
 4541            ˇ
 4542        );
 4543    "});
 4544
 4545    // Paste it at the same position.
 4546    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4547    cx.assert_editor_state(indoc! {"
 4548        const a: B = (
 4549            c(),
 4550            d(
 4551                e,
 4552                f
 4553 4554        );
 4555    "});
 4556
 4557    // Paste it at a line with a lower indent level.
 4558    cx.set_state(indoc! {"
 4559        ˇ
 4560        const a: B = (
 4561            c(),
 4562        );
 4563    "});
 4564    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4565    cx.assert_editor_state(indoc! {"
 4566        d(
 4567            e,
 4568            f
 4569 4570        const a: B = (
 4571            c(),
 4572        );
 4573    "});
 4574
 4575    // Cut an indented block, with the leading whitespace.
 4576    cx.set_state(indoc! {"
 4577        const a: B = (
 4578            c(),
 4579        «    d(
 4580                e,
 4581                f
 4582            )
 4583        ˇ»);
 4584    "});
 4585    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4586    cx.assert_editor_state(indoc! {"
 4587        const a: B = (
 4588            c(),
 4589        ˇ);
 4590    "});
 4591
 4592    // Paste it at the same position.
 4593    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4594    cx.assert_editor_state(indoc! {"
 4595        const a: B = (
 4596            c(),
 4597            d(
 4598                e,
 4599                f
 4600            )
 4601        ˇ);
 4602    "});
 4603
 4604    // Paste it at a line with a higher indent level.
 4605    cx.set_state(indoc! {"
 4606        const a: B = (
 4607            c(),
 4608            d(
 4609                e,
 4610 4611            )
 4612        );
 4613    "});
 4614    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4615    cx.assert_editor_state(indoc! {"
 4616        const a: B = (
 4617            c(),
 4618            d(
 4619                e,
 4620                f    d(
 4621                    e,
 4622                    f
 4623                )
 4624        ˇ
 4625            )
 4626        );
 4627    "});
 4628}
 4629
 4630#[gpui::test]
 4631fn test_select_all(cx: &mut TestAppContext) {
 4632    init_test(cx, |_| {});
 4633
 4634    let view = cx.add_window(|cx| {
 4635        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4636        build_editor(buffer, cx)
 4637    });
 4638    _ = view.update(cx, |view, cx| {
 4639        view.select_all(&SelectAll, cx);
 4640        assert_eq!(
 4641            view.selections.display_ranges(cx),
 4642            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4643        );
 4644    });
 4645}
 4646
 4647#[gpui::test]
 4648fn test_select_line(cx: &mut TestAppContext) {
 4649    init_test(cx, |_| {});
 4650
 4651    let view = cx.add_window(|cx| {
 4652        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4653        build_editor(buffer, cx)
 4654    });
 4655    _ = view.update(cx, |view, cx| {
 4656        view.change_selections(None, cx, |s| {
 4657            s.select_display_ranges([
 4658                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4659                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4660                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4661                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4662            ])
 4663        });
 4664        view.select_line(&SelectLine, cx);
 4665        assert_eq!(
 4666            view.selections.display_ranges(cx),
 4667            vec![
 4668                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4669                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4670            ]
 4671        );
 4672    });
 4673
 4674    _ = view.update(cx, |view, cx| {
 4675        view.select_line(&SelectLine, cx);
 4676        assert_eq!(
 4677            view.selections.display_ranges(cx),
 4678            vec![
 4679                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4680                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4681            ]
 4682        );
 4683    });
 4684
 4685    _ = view.update(cx, |view, cx| {
 4686        view.select_line(&SelectLine, cx);
 4687        assert_eq!(
 4688            view.selections.display_ranges(cx),
 4689            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4690        );
 4691    });
 4692}
 4693
 4694#[gpui::test]
 4695fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4696    init_test(cx, |_| {});
 4697
 4698    let view = cx.add_window(|cx| {
 4699        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4700        build_editor(buffer, cx)
 4701    });
 4702    _ = view.update(cx, |view, cx| {
 4703        view.fold_ranges(
 4704            vec![
 4705                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4706                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4707                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4708            ],
 4709            true,
 4710            cx,
 4711        );
 4712        view.change_selections(None, cx, |s| {
 4713            s.select_display_ranges([
 4714                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4715                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4716                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4717                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4718            ])
 4719        });
 4720        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4721    });
 4722
 4723    _ = view.update(cx, |view, cx| {
 4724        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4725        assert_eq!(
 4726            view.display_text(cx),
 4727            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4728        );
 4729        assert_eq!(
 4730            view.selections.display_ranges(cx),
 4731            [
 4732                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4733                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4734                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4735                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4736            ]
 4737        );
 4738    });
 4739
 4740    _ = view.update(cx, |view, cx| {
 4741        view.change_selections(None, cx, |s| {
 4742            s.select_display_ranges([
 4743                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4744            ])
 4745        });
 4746        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4747        assert_eq!(
 4748            view.display_text(cx),
 4749            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4750        );
 4751        assert_eq!(
 4752            view.selections.display_ranges(cx),
 4753            [
 4754                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4755                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4756                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4757                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4758                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4759                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4760                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4761                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4762            ]
 4763        );
 4764    });
 4765}
 4766
 4767#[gpui::test]
 4768async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4769    init_test(cx, |_| {});
 4770
 4771    let mut cx = EditorTestContext::new(cx).await;
 4772
 4773    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4774    cx.set_state(indoc!(
 4775        r#"abc
 4776           defˇghi
 4777
 4778           jk
 4779           nlmo
 4780           "#
 4781    ));
 4782
 4783    cx.update_editor(|editor, cx| {
 4784        editor.add_selection_above(&Default::default(), cx);
 4785    });
 4786
 4787    cx.assert_editor_state(indoc!(
 4788        r#"abcˇ
 4789           defˇghi
 4790
 4791           jk
 4792           nlmo
 4793           "#
 4794    ));
 4795
 4796    cx.update_editor(|editor, cx| {
 4797        editor.add_selection_above(&Default::default(), cx);
 4798    });
 4799
 4800    cx.assert_editor_state(indoc!(
 4801        r#"abcˇ
 4802            defˇghi
 4803
 4804            jk
 4805            nlmo
 4806            "#
 4807    ));
 4808
 4809    cx.update_editor(|view, cx| {
 4810        view.add_selection_below(&Default::default(), cx);
 4811    });
 4812
 4813    cx.assert_editor_state(indoc!(
 4814        r#"abc
 4815           defˇghi
 4816
 4817           jk
 4818           nlmo
 4819           "#
 4820    ));
 4821
 4822    cx.update_editor(|view, cx| {
 4823        view.undo_selection(&Default::default(), cx);
 4824    });
 4825
 4826    cx.assert_editor_state(indoc!(
 4827        r#"abcˇ
 4828           defˇghi
 4829
 4830           jk
 4831           nlmo
 4832           "#
 4833    ));
 4834
 4835    cx.update_editor(|view, cx| {
 4836        view.redo_selection(&Default::default(), cx);
 4837    });
 4838
 4839    cx.assert_editor_state(indoc!(
 4840        r#"abc
 4841           defˇghi
 4842
 4843           jk
 4844           nlmo
 4845           "#
 4846    ));
 4847
 4848    cx.update_editor(|view, cx| {
 4849        view.add_selection_below(&Default::default(), cx);
 4850    });
 4851
 4852    cx.assert_editor_state(indoc!(
 4853        r#"abc
 4854           defˇghi
 4855
 4856           jk
 4857           nlmˇo
 4858           "#
 4859    ));
 4860
 4861    cx.update_editor(|view, cx| {
 4862        view.add_selection_below(&Default::default(), cx);
 4863    });
 4864
 4865    cx.assert_editor_state(indoc!(
 4866        r#"abc
 4867           defˇghi
 4868
 4869           jk
 4870           nlmˇo
 4871           "#
 4872    ));
 4873
 4874    // change selections
 4875    cx.set_state(indoc!(
 4876        r#"abc
 4877           def«ˇg»hi
 4878
 4879           jk
 4880           nlmo
 4881           "#
 4882    ));
 4883
 4884    cx.update_editor(|view, cx| {
 4885        view.add_selection_below(&Default::default(), cx);
 4886    });
 4887
 4888    cx.assert_editor_state(indoc!(
 4889        r#"abc
 4890           def«ˇg»hi
 4891
 4892           jk
 4893           nlm«ˇo»
 4894           "#
 4895    ));
 4896
 4897    cx.update_editor(|view, cx| {
 4898        view.add_selection_below(&Default::default(), cx);
 4899    });
 4900
 4901    cx.assert_editor_state(indoc!(
 4902        r#"abc
 4903           def«ˇg»hi
 4904
 4905           jk
 4906           nlm«ˇo»
 4907           "#
 4908    ));
 4909
 4910    cx.update_editor(|view, cx| {
 4911        view.add_selection_above(&Default::default(), cx);
 4912    });
 4913
 4914    cx.assert_editor_state(indoc!(
 4915        r#"abc
 4916           def«ˇg»hi
 4917
 4918           jk
 4919           nlmo
 4920           "#
 4921    ));
 4922
 4923    cx.update_editor(|view, cx| {
 4924        view.add_selection_above(&Default::default(), cx);
 4925    });
 4926
 4927    cx.assert_editor_state(indoc!(
 4928        r#"abc
 4929           def«ˇg»hi
 4930
 4931           jk
 4932           nlmo
 4933           "#
 4934    ));
 4935
 4936    // Change selections again
 4937    cx.set_state(indoc!(
 4938        r#"a«bc
 4939           defgˇ»hi
 4940
 4941           jk
 4942           nlmo
 4943           "#
 4944    ));
 4945
 4946    cx.update_editor(|view, cx| {
 4947        view.add_selection_below(&Default::default(), cx);
 4948    });
 4949
 4950    cx.assert_editor_state(indoc!(
 4951        r#"a«bcˇ»
 4952           d«efgˇ»hi
 4953
 4954           j«kˇ»
 4955           nlmo
 4956           "#
 4957    ));
 4958
 4959    cx.update_editor(|view, cx| {
 4960        view.add_selection_below(&Default::default(), cx);
 4961    });
 4962    cx.assert_editor_state(indoc!(
 4963        r#"a«bcˇ»
 4964           d«efgˇ»hi
 4965
 4966           j«kˇ»
 4967           n«lmoˇ»
 4968           "#
 4969    ));
 4970    cx.update_editor(|view, cx| {
 4971        view.add_selection_above(&Default::default(), cx);
 4972    });
 4973
 4974    cx.assert_editor_state(indoc!(
 4975        r#"a«bcˇ»
 4976           d«efgˇ»hi
 4977
 4978           j«kˇ»
 4979           nlmo
 4980           "#
 4981    ));
 4982
 4983    // Change selections again
 4984    cx.set_state(indoc!(
 4985        r#"abc
 4986           d«ˇefghi
 4987
 4988           jk
 4989           nlm»o
 4990           "#
 4991    ));
 4992
 4993    cx.update_editor(|view, cx| {
 4994        view.add_selection_above(&Default::default(), cx);
 4995    });
 4996
 4997    cx.assert_editor_state(indoc!(
 4998        r#"a«ˇbc»
 4999           d«ˇef»ghi
 5000
 5001           j«ˇk»
 5002           n«ˇlm»o
 5003           "#
 5004    ));
 5005
 5006    cx.update_editor(|view, cx| {
 5007        view.add_selection_below(&Default::default(), cx);
 5008    });
 5009
 5010    cx.assert_editor_state(indoc!(
 5011        r#"abc
 5012           d«ˇef»ghi
 5013
 5014           j«ˇk»
 5015           n«ˇlm»o
 5016           "#
 5017    ));
 5018}
 5019
 5020#[gpui::test]
 5021async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5022    init_test(cx, |_| {});
 5023
 5024    let mut cx = EditorTestContext::new(cx).await;
 5025    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5026
 5027    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5028        .unwrap();
 5029    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5030
 5031    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5032        .unwrap();
 5033    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5034
 5035    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5036    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5037
 5038    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5039    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5040
 5041    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5042        .unwrap();
 5043    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5044
 5045    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5046        .unwrap();
 5047    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5048}
 5049
 5050#[gpui::test]
 5051async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5052    init_test(cx, |_| {});
 5053
 5054    let mut cx = EditorTestContext::new(cx).await;
 5055    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5056
 5057    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5058        .unwrap();
 5059    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5060}
 5061
 5062#[gpui::test]
 5063async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5064    init_test(cx, |_| {});
 5065
 5066    let mut cx = EditorTestContext::new(cx).await;
 5067    cx.set_state(
 5068        r#"let foo = 2;
 5069lˇet foo = 2;
 5070let fooˇ = 2;
 5071let foo = 2;
 5072let foo = ˇ2;"#,
 5073    );
 5074
 5075    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5076        .unwrap();
 5077    cx.assert_editor_state(
 5078        r#"let foo = 2;
 5079«letˇ» foo = 2;
 5080let «fooˇ» = 2;
 5081let foo = 2;
 5082let foo = «2ˇ»;"#,
 5083    );
 5084
 5085    // noop for multiple selections with different contents
 5086    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5087        .unwrap();
 5088    cx.assert_editor_state(
 5089        r#"let foo = 2;
 5090«letˇ» foo = 2;
 5091let «fooˇ» = 2;
 5092let foo = 2;
 5093let foo = «2ˇ»;"#,
 5094    );
 5095}
 5096
 5097#[gpui::test]
 5098async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5099    init_test(cx, |_| {});
 5100
 5101    let mut cx =
 5102        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5103
 5104    cx.assert_editor_state(indoc! {"
 5105        ˇbbb
 5106        ccc
 5107
 5108        bbb
 5109        ccc
 5110        "});
 5111    cx.dispatch_action(SelectPrevious::default());
 5112    cx.assert_editor_state(indoc! {"
 5113                «bbbˇ»
 5114                ccc
 5115
 5116                bbb
 5117                ccc
 5118                "});
 5119    cx.dispatch_action(SelectPrevious::default());
 5120    cx.assert_editor_state(indoc! {"
 5121                «bbbˇ»
 5122                ccc
 5123
 5124                «bbbˇ»
 5125                ccc
 5126                "});
 5127}
 5128
 5129#[gpui::test]
 5130async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5131    init_test(cx, |_| {});
 5132
 5133    let mut cx = EditorTestContext::new(cx).await;
 5134    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5135
 5136    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5137        .unwrap();
 5138    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5139
 5140    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5141        .unwrap();
 5142    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5143
 5144    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5145    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5146
 5147    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5148    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5149
 5150    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5151        .unwrap();
 5152    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5153
 5154    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5155        .unwrap();
 5156    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5157
 5158    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5159        .unwrap();
 5160    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5161}
 5162
 5163#[gpui::test]
 5164async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5165    init_test(cx, |_| {});
 5166
 5167    let mut cx = EditorTestContext::new(cx).await;
 5168    cx.set_state(
 5169        r#"let foo = 2;
 5170lˇet foo = 2;
 5171let fooˇ = 2;
 5172let foo = 2;
 5173let foo = ˇ2;"#,
 5174    );
 5175
 5176    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5177        .unwrap();
 5178    cx.assert_editor_state(
 5179        r#"let foo = 2;
 5180«letˇ» foo = 2;
 5181let «fooˇ» = 2;
 5182let foo = 2;
 5183let foo = «2ˇ»;"#,
 5184    );
 5185
 5186    // noop for multiple selections with different contents
 5187    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5188        .unwrap();
 5189    cx.assert_editor_state(
 5190        r#"let foo = 2;
 5191«letˇ» foo = 2;
 5192let «fooˇ» = 2;
 5193let foo = 2;
 5194let foo = «2ˇ»;"#,
 5195    );
 5196}
 5197
 5198#[gpui::test]
 5199async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5200    init_test(cx, |_| {});
 5201
 5202    let mut cx = EditorTestContext::new(cx).await;
 5203    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5204
 5205    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5206        .unwrap();
 5207    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5208
 5209    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5210        .unwrap();
 5211    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5212
 5213    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5214    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5215
 5216    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5217    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5218
 5219    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5220        .unwrap();
 5221    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5222
 5223    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5224        .unwrap();
 5225    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5226}
 5227
 5228#[gpui::test]
 5229async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5230    init_test(cx, |_| {});
 5231
 5232    let language = Arc::new(Language::new(
 5233        LanguageConfig::default(),
 5234        Some(tree_sitter_rust::LANGUAGE.into()),
 5235    ));
 5236
 5237    let text = r#"
 5238        use mod1::mod2::{mod3, mod4};
 5239
 5240        fn fn_1(param1: bool, param2: &str) {
 5241            let var1 = "text";
 5242        }
 5243    "#
 5244    .unindent();
 5245
 5246    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5247    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5248    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5249
 5250    editor
 5251        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5252        .await;
 5253
 5254    editor.update(cx, |view, cx| {
 5255        view.change_selections(None, cx, |s| {
 5256            s.select_display_ranges([
 5257                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5258                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5259                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5260            ]);
 5261        });
 5262        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5263    });
 5264    editor.update(cx, |editor, cx| {
 5265        assert_text_with_selections(
 5266            editor,
 5267            indoc! {r#"
 5268                use mod1::mod2::{mod3, «mod4ˇ»};
 5269
 5270                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5271                    let var1 = "«textˇ»";
 5272                }
 5273            "#},
 5274            cx,
 5275        );
 5276    });
 5277
 5278    editor.update(cx, |view, cx| {
 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    assert_eq!(
 5299        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5300        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5301    );
 5302
 5303    // Trying to expand the selected syntax node one more time has no effect.
 5304    editor.update(cx, |view, cx| {
 5305        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5306    });
 5307    assert_eq!(
 5308        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5309        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5310    );
 5311
 5312    editor.update(cx, |view, cx| {
 5313        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5314    });
 5315    editor.update(cx, |editor, cx| {
 5316        assert_text_with_selections(
 5317            editor,
 5318            indoc! {r#"
 5319                use mod1::mod2::«{mod3, mod4}ˇ»;
 5320
 5321                «ˇfn fn_1(param1: bool, param2: &str) {
 5322                    let var1 = "text";
 5323 5324            "#},
 5325            cx,
 5326        );
 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, mo«ˇ»d4};
 5354
 5355                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5356                    let var1 = "te«ˇ»xt";
 5357                }
 5358            "#},
 5359            cx,
 5360        );
 5361    });
 5362
 5363    // Trying to shrink the selected syntax node one more time has no effect.
 5364    editor.update(cx, |view, cx| {
 5365        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5366    });
 5367    editor.update(cx, |editor, cx| {
 5368        assert_text_with_selections(
 5369            editor,
 5370            indoc! {r#"
 5371                use mod1::mod2::{mod3, mo«ˇ»d4};
 5372
 5373                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5374                    let var1 = "te«ˇ»xt";
 5375                }
 5376            "#},
 5377            cx,
 5378        );
 5379    });
 5380
 5381    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5382    // a fold.
 5383    editor.update(cx, |view, cx| {
 5384        view.fold_ranges(
 5385            vec![
 5386                (
 5387                    Point::new(0, 21)..Point::new(0, 24),
 5388                    FoldPlaceholder::test(),
 5389                ),
 5390                (
 5391                    Point::new(3, 20)..Point::new(3, 22),
 5392                    FoldPlaceholder::test(),
 5393                ),
 5394            ],
 5395            true,
 5396            cx,
 5397        );
 5398        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5399    });
 5400    editor.update(cx, |editor, cx| {
 5401        assert_text_with_selections(
 5402            editor,
 5403            indoc! {r#"
 5404                use mod1::mod2::«{mod3, mod4}ˇ»;
 5405
 5406                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5407                    «let var1 = "text";ˇ»
 5408                }
 5409            "#},
 5410            cx,
 5411        );
 5412    });
 5413}
 5414
 5415#[gpui::test]
 5416async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5417    init_test(cx, |_| {});
 5418
 5419    let language = Arc::new(
 5420        Language::new(
 5421            LanguageConfig {
 5422                brackets: BracketPairConfig {
 5423                    pairs: vec![
 5424                        BracketPair {
 5425                            start: "{".to_string(),
 5426                            end: "}".to_string(),
 5427                            close: false,
 5428                            surround: false,
 5429                            newline: true,
 5430                        },
 5431                        BracketPair {
 5432                            start: "(".to_string(),
 5433                            end: ")".to_string(),
 5434                            close: false,
 5435                            surround: false,
 5436                            newline: true,
 5437                        },
 5438                    ],
 5439                    ..Default::default()
 5440                },
 5441                ..Default::default()
 5442            },
 5443            Some(tree_sitter_rust::LANGUAGE.into()),
 5444        )
 5445        .with_indents_query(
 5446            r#"
 5447                (_ "(" ")" @end) @indent
 5448                (_ "{" "}" @end) @indent
 5449            "#,
 5450        )
 5451        .unwrap(),
 5452    );
 5453
 5454    let text = "fn a() {}";
 5455
 5456    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5457    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5458    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5459    editor
 5460        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5461        .await;
 5462
 5463    editor.update(cx, |editor, cx| {
 5464        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5465        editor.newline(&Newline, cx);
 5466        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5467        assert_eq!(
 5468            editor.selections.ranges(cx),
 5469            &[
 5470                Point::new(1, 4)..Point::new(1, 4),
 5471                Point::new(3, 4)..Point::new(3, 4),
 5472                Point::new(5, 0)..Point::new(5, 0)
 5473            ]
 5474        );
 5475    });
 5476}
 5477
 5478#[gpui::test]
 5479async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5480    init_test(cx, |_| {});
 5481
 5482    let mut cx = EditorTestContext::new(cx).await;
 5483
 5484    let language = Arc::new(Language::new(
 5485        LanguageConfig {
 5486            brackets: BracketPairConfig {
 5487                pairs: vec![
 5488                    BracketPair {
 5489                        start: "{".to_string(),
 5490                        end: "}".to_string(),
 5491                        close: true,
 5492                        surround: true,
 5493                        newline: true,
 5494                    },
 5495                    BracketPair {
 5496                        start: "(".to_string(),
 5497                        end: ")".to_string(),
 5498                        close: true,
 5499                        surround: true,
 5500                        newline: true,
 5501                    },
 5502                    BracketPair {
 5503                        start: "/*".to_string(),
 5504                        end: " */".to_string(),
 5505                        close: true,
 5506                        surround: true,
 5507                        newline: true,
 5508                    },
 5509                    BracketPair {
 5510                        start: "[".to_string(),
 5511                        end: "]".to_string(),
 5512                        close: false,
 5513                        surround: false,
 5514                        newline: true,
 5515                    },
 5516                    BracketPair {
 5517                        start: "\"".to_string(),
 5518                        end: "\"".to_string(),
 5519                        close: true,
 5520                        surround: true,
 5521                        newline: false,
 5522                    },
 5523                    BracketPair {
 5524                        start: "<".to_string(),
 5525                        end: ">".to_string(),
 5526                        close: false,
 5527                        surround: true,
 5528                        newline: true,
 5529                    },
 5530                ],
 5531                ..Default::default()
 5532            },
 5533            autoclose_before: "})]".to_string(),
 5534            ..Default::default()
 5535        },
 5536        Some(tree_sitter_rust::LANGUAGE.into()),
 5537    ));
 5538
 5539    cx.language_registry().add(language.clone());
 5540    cx.update_buffer(|buffer, cx| {
 5541        buffer.set_language(Some(language), cx);
 5542    });
 5543
 5544    cx.set_state(
 5545        &r#"
 5546            🏀ˇ
 5547            εˇ
 5548            ❤️ˇ
 5549        "#
 5550        .unindent(),
 5551    );
 5552
 5553    // autoclose multiple nested brackets at multiple cursors
 5554    cx.update_editor(|view, cx| {
 5555        view.handle_input("{", cx);
 5556        view.handle_input("{", cx);
 5557        view.handle_input("{", cx);
 5558    });
 5559    cx.assert_editor_state(
 5560        &"
 5561            🏀{{{ˇ}}}
 5562            ε{{{ˇ}}}
 5563            ❤️{{{ˇ}}}
 5564        "
 5565        .unindent(),
 5566    );
 5567
 5568    // insert a different closing bracket
 5569    cx.update_editor(|view, cx| {
 5570        view.handle_input(")", cx);
 5571    });
 5572    cx.assert_editor_state(
 5573        &"
 5574            🏀{{{)ˇ}}}
 5575            ε{{{)ˇ}}}
 5576            ❤️{{{)ˇ}}}
 5577        "
 5578        .unindent(),
 5579    );
 5580
 5581    // skip over the auto-closed brackets when typing a closing bracket
 5582    cx.update_editor(|view, cx| {
 5583        view.move_right(&MoveRight, cx);
 5584        view.handle_input("}", cx);
 5585        view.handle_input("}", cx);
 5586        view.handle_input("}", cx);
 5587    });
 5588    cx.assert_editor_state(
 5589        &"
 5590            🏀{{{)}}}}ˇ
 5591            ε{{{)}}}}ˇ
 5592            ❤️{{{)}}}}ˇ
 5593        "
 5594        .unindent(),
 5595    );
 5596
 5597    // autoclose multi-character pairs
 5598    cx.set_state(
 5599        &"
 5600            ˇ
 5601            ˇ
 5602        "
 5603        .unindent(),
 5604    );
 5605    cx.update_editor(|view, cx| {
 5606        view.handle_input("/", cx);
 5607        view.handle_input("*", cx);
 5608    });
 5609    cx.assert_editor_state(
 5610        &"
 5611            /*ˇ */
 5612            /*ˇ */
 5613        "
 5614        .unindent(),
 5615    );
 5616
 5617    // one cursor autocloses a multi-character pair, one cursor
 5618    // does not autoclose.
 5619    cx.set_state(
 5620        &"
 5621 5622            ˇ
 5623        "
 5624        .unindent(),
 5625    );
 5626    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5627    cx.assert_editor_state(
 5628        &"
 5629            /*ˇ */
 5630 5631        "
 5632        .unindent(),
 5633    );
 5634
 5635    // Don't autoclose if the next character isn't whitespace and isn't
 5636    // listed in the language's "autoclose_before" section.
 5637    cx.set_state("ˇa b");
 5638    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5639    cx.assert_editor_state("{ˇa b");
 5640
 5641    // Don't autoclose if `close` is false for the bracket pair
 5642    cx.set_state("ˇ");
 5643    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5644    cx.assert_editor_state("");
 5645
 5646    // Surround with brackets if text is selected
 5647    cx.set_state("«aˇ» b");
 5648    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5649    cx.assert_editor_state("{«aˇ»} b");
 5650
 5651    // Autclose pair where the start and end characters are the same
 5652    cx.set_state("");
 5653    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5654    cx.assert_editor_state("a\"ˇ\"");
 5655    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5656    cx.assert_editor_state("a\"\"ˇ");
 5657
 5658    // Don't autoclose pair if autoclose is disabled
 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 and auto_surround is enabled, even if autoclose is disabled
 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
 5669#[gpui::test]
 5670async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5671    init_test(cx, |settings| {
 5672        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5673    });
 5674
 5675    let mut cx = EditorTestContext::new(cx).await;
 5676
 5677    let language = Arc::new(Language::new(
 5678        LanguageConfig {
 5679            brackets: BracketPairConfig {
 5680                pairs: vec![
 5681                    BracketPair {
 5682                        start: "{".to_string(),
 5683                        end: "}".to_string(),
 5684                        close: true,
 5685                        surround: true,
 5686                        newline: true,
 5687                    },
 5688                    BracketPair {
 5689                        start: "(".to_string(),
 5690                        end: ")".to_string(),
 5691                        close: true,
 5692                        surround: true,
 5693                        newline: true,
 5694                    },
 5695                    BracketPair {
 5696                        start: "[".to_string(),
 5697                        end: "]".to_string(),
 5698                        close: false,
 5699                        surround: false,
 5700                        newline: true,
 5701                    },
 5702                ],
 5703                ..Default::default()
 5704            },
 5705            autoclose_before: "})]".to_string(),
 5706            ..Default::default()
 5707        },
 5708        Some(tree_sitter_rust::LANGUAGE.into()),
 5709    ));
 5710
 5711    cx.language_registry().add(language.clone());
 5712    cx.update_buffer(|buffer, cx| {
 5713        buffer.set_language(Some(language), cx);
 5714    });
 5715
 5716    cx.set_state(
 5717        &"
 5718            ˇ
 5719            ˇ
 5720            ˇ
 5721        "
 5722        .unindent(),
 5723    );
 5724
 5725    // ensure only matching closing brackets are skipped over
 5726    cx.update_editor(|view, cx| {
 5727        view.handle_input("}", cx);
 5728        view.move_left(&MoveLeft, cx);
 5729        view.handle_input(")", cx);
 5730        view.move_left(&MoveLeft, cx);
 5731    });
 5732    cx.assert_editor_state(
 5733        &"
 5734            ˇ)}
 5735            ˇ)}
 5736            ˇ)}
 5737        "
 5738        .unindent(),
 5739    );
 5740
 5741    // skip-over closing brackets at multiple cursors
 5742    cx.update_editor(|view, cx| {
 5743        view.handle_input(")", cx);
 5744        view.handle_input("}", cx);
 5745    });
 5746    cx.assert_editor_state(
 5747        &"
 5748            )}ˇ
 5749            )}ˇ
 5750            )}ˇ
 5751        "
 5752        .unindent(),
 5753    );
 5754
 5755    // ignore non-close brackets
 5756    cx.update_editor(|view, cx| {
 5757        view.handle_input("]", cx);
 5758        view.move_left(&MoveLeft, cx);
 5759        view.handle_input("]", cx);
 5760    });
 5761    cx.assert_editor_state(
 5762        &"
 5763            )}]ˇ]
 5764            )}]ˇ]
 5765            )}]ˇ]
 5766        "
 5767        .unindent(),
 5768    );
 5769}
 5770
 5771#[gpui::test]
 5772async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5773    init_test(cx, |_| {});
 5774
 5775    let mut cx = EditorTestContext::new(cx).await;
 5776
 5777    let html_language = Arc::new(
 5778        Language::new(
 5779            LanguageConfig {
 5780                name: "HTML".into(),
 5781                brackets: BracketPairConfig {
 5782                    pairs: vec![
 5783                        BracketPair {
 5784                            start: "<".into(),
 5785                            end: ">".into(),
 5786                            close: true,
 5787                            ..Default::default()
 5788                        },
 5789                        BracketPair {
 5790                            start: "{".into(),
 5791                            end: "}".into(),
 5792                            close: true,
 5793                            ..Default::default()
 5794                        },
 5795                        BracketPair {
 5796                            start: "(".into(),
 5797                            end: ")".into(),
 5798                            close: true,
 5799                            ..Default::default()
 5800                        },
 5801                    ],
 5802                    ..Default::default()
 5803                },
 5804                autoclose_before: "})]>".into(),
 5805                ..Default::default()
 5806            },
 5807            Some(tree_sitter_html::language()),
 5808        )
 5809        .with_injection_query(
 5810            r#"
 5811            (script_element
 5812                (raw_text) @content
 5813                (#set! "language" "javascript"))
 5814            "#,
 5815        )
 5816        .unwrap(),
 5817    );
 5818
 5819    let javascript_language = Arc::new(Language::new(
 5820        LanguageConfig {
 5821            name: "JavaScript".into(),
 5822            brackets: BracketPairConfig {
 5823                pairs: vec![
 5824                    BracketPair {
 5825                        start: "/*".into(),
 5826                        end: " */".into(),
 5827                        close: true,
 5828                        ..Default::default()
 5829                    },
 5830                    BracketPair {
 5831                        start: "{".into(),
 5832                        end: "}".into(),
 5833                        close: true,
 5834                        ..Default::default()
 5835                    },
 5836                    BracketPair {
 5837                        start: "(".into(),
 5838                        end: ")".into(),
 5839                        close: true,
 5840                        ..Default::default()
 5841                    },
 5842                ],
 5843                ..Default::default()
 5844            },
 5845            autoclose_before: "})]>".into(),
 5846            ..Default::default()
 5847        },
 5848        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5849    ));
 5850
 5851    cx.language_registry().add(html_language.clone());
 5852    cx.language_registry().add(javascript_language.clone());
 5853
 5854    cx.update_buffer(|buffer, cx| {
 5855        buffer.set_language(Some(html_language), cx);
 5856    });
 5857
 5858    cx.set_state(
 5859        &r#"
 5860            <body>ˇ
 5861                <script>
 5862                    var x = 1;ˇ
 5863                </script>
 5864            </body>ˇ
 5865        "#
 5866        .unindent(),
 5867    );
 5868
 5869    // Precondition: different languages are active at different locations.
 5870    cx.update_editor(|editor, cx| {
 5871        let snapshot = editor.snapshot(cx);
 5872        let cursors = editor.selections.ranges::<usize>(cx);
 5873        let languages = cursors
 5874            .iter()
 5875            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5876            .collect::<Vec<_>>();
 5877        assert_eq!(
 5878            languages,
 5879            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5880        );
 5881    });
 5882
 5883    // Angle brackets autoclose in HTML, but not JavaScript.
 5884    cx.update_editor(|editor, cx| {
 5885        editor.handle_input("<", cx);
 5886        editor.handle_input("a", cx);
 5887    });
 5888    cx.assert_editor_state(
 5889        &r#"
 5890            <body><aˇ>
 5891                <script>
 5892                    var x = 1;<aˇ
 5893                </script>
 5894            </body><aˇ>
 5895        "#
 5896        .unindent(),
 5897    );
 5898
 5899    // Curly braces and parens autoclose in both HTML and JavaScript.
 5900    cx.update_editor(|editor, cx| {
 5901        editor.handle_input(" b=", cx);
 5902        editor.handle_input("{", cx);
 5903        editor.handle_input("c", cx);
 5904        editor.handle_input("(", cx);
 5905    });
 5906    cx.assert_editor_state(
 5907        &r#"
 5908            <body><a b={c(ˇ)}>
 5909                <script>
 5910                    var x = 1;<a b={c(ˇ)}
 5911                </script>
 5912            </body><a b={c(ˇ)}>
 5913        "#
 5914        .unindent(),
 5915    );
 5916
 5917    // Brackets that were already autoclosed are skipped.
 5918    cx.update_editor(|editor, cx| {
 5919        editor.handle_input(")", cx);
 5920        editor.handle_input("d", cx);
 5921        editor.handle_input("}", cx);
 5922    });
 5923    cx.assert_editor_state(
 5924        &r#"
 5925            <body><a b={c()d}ˇ>
 5926                <script>
 5927                    var x = 1;<a b={c()d}ˇ
 5928                </script>
 5929            </body><a b={c()d}ˇ>
 5930        "#
 5931        .unindent(),
 5932    );
 5933    cx.update_editor(|editor, cx| {
 5934        editor.handle_input(">", cx);
 5935    });
 5936    cx.assert_editor_state(
 5937        &r#"
 5938            <body><a b={c()d}>ˇ
 5939                <script>
 5940                    var x = 1;<a b={c()d}>ˇ
 5941                </script>
 5942            </body><a b={c()d}>ˇ
 5943        "#
 5944        .unindent(),
 5945    );
 5946
 5947    // Reset
 5948    cx.set_state(
 5949        &r#"
 5950            <body>ˇ
 5951                <script>
 5952                    var x = 1;ˇ
 5953                </script>
 5954            </body>ˇ
 5955        "#
 5956        .unindent(),
 5957    );
 5958
 5959    cx.update_editor(|editor, cx| {
 5960        editor.handle_input("<", cx);
 5961    });
 5962    cx.assert_editor_state(
 5963        &r#"
 5964            <body><ˇ>
 5965                <script>
 5966                    var x = 1;<ˇ
 5967                </script>
 5968            </body><ˇ>
 5969        "#
 5970        .unindent(),
 5971    );
 5972
 5973    // When backspacing, the closing angle brackets are removed.
 5974    cx.update_editor(|editor, cx| {
 5975        editor.backspace(&Backspace, cx);
 5976    });
 5977    cx.assert_editor_state(
 5978        &r#"
 5979            <body>ˇ
 5980                <script>
 5981                    var x = 1;ˇ
 5982                </script>
 5983            </body>ˇ
 5984        "#
 5985        .unindent(),
 5986    );
 5987
 5988    // Block comments autoclose in JavaScript, but not HTML.
 5989    cx.update_editor(|editor, cx| {
 5990        editor.handle_input("/", cx);
 5991        editor.handle_input("*", cx);
 5992    });
 5993    cx.assert_editor_state(
 5994        &r#"
 5995            <body>/*ˇ
 5996                <script>
 5997                    var x = 1;/*ˇ */
 5998                </script>
 5999            </body>/*ˇ
 6000        "#
 6001        .unindent(),
 6002    );
 6003}
 6004
 6005#[gpui::test]
 6006async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6007    init_test(cx, |_| {});
 6008
 6009    let mut cx = EditorTestContext::new(cx).await;
 6010
 6011    let rust_language = Arc::new(
 6012        Language::new(
 6013            LanguageConfig {
 6014                name: "Rust".into(),
 6015                brackets: serde_json::from_value(json!([
 6016                    { "start": "{", "end": "}", "close": true, "newline": true },
 6017                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6018                ]))
 6019                .unwrap(),
 6020                autoclose_before: "})]>".into(),
 6021                ..Default::default()
 6022            },
 6023            Some(tree_sitter_rust::LANGUAGE.into()),
 6024        )
 6025        .with_override_query("(string_literal) @string")
 6026        .unwrap(),
 6027    );
 6028
 6029    cx.language_registry().add(rust_language.clone());
 6030    cx.update_buffer(|buffer, cx| {
 6031        buffer.set_language(Some(rust_language), cx);
 6032    });
 6033
 6034    cx.set_state(
 6035        &r#"
 6036            let x = ˇ
 6037        "#
 6038        .unindent(),
 6039    );
 6040
 6041    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6042    cx.update_editor(|editor, cx| {
 6043        editor.handle_input("\"", cx);
 6044    });
 6045    cx.assert_editor_state(
 6046        &r#"
 6047            let x = "ˇ"
 6048        "#
 6049        .unindent(),
 6050    );
 6051
 6052    // Inserting another quotation mark. The cursor moves across the existing
 6053    // automatically-inserted quotation mark.
 6054    cx.update_editor(|editor, cx| {
 6055        editor.handle_input("\"", cx);
 6056    });
 6057    cx.assert_editor_state(
 6058        &r#"
 6059            let x = ""ˇ
 6060        "#
 6061        .unindent(),
 6062    );
 6063
 6064    // Reset
 6065    cx.set_state(
 6066        &r#"
 6067            let x = ˇ
 6068        "#
 6069        .unindent(),
 6070    );
 6071
 6072    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6073    cx.update_editor(|editor, cx| {
 6074        editor.handle_input("\"", cx);
 6075        editor.handle_input(" ", cx);
 6076        editor.move_left(&Default::default(), cx);
 6077        editor.handle_input("\\", cx);
 6078        editor.handle_input("\"", cx);
 6079    });
 6080    cx.assert_editor_state(
 6081        &r#"
 6082            let x = "\"ˇ "
 6083        "#
 6084        .unindent(),
 6085    );
 6086
 6087    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6088    // mark. Nothing is inserted.
 6089    cx.update_editor(|editor, cx| {
 6090        editor.move_right(&Default::default(), cx);
 6091        editor.handle_input("\"", cx);
 6092    });
 6093    cx.assert_editor_state(
 6094        &r#"
 6095            let x = "\" "ˇ
 6096        "#
 6097        .unindent(),
 6098    );
 6099}
 6100
 6101#[gpui::test]
 6102async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6103    init_test(cx, |_| {});
 6104
 6105    let language = Arc::new(Language::new(
 6106        LanguageConfig {
 6107            brackets: BracketPairConfig {
 6108                pairs: vec![
 6109                    BracketPair {
 6110                        start: "{".to_string(),
 6111                        end: "}".to_string(),
 6112                        close: true,
 6113                        surround: true,
 6114                        newline: true,
 6115                    },
 6116                    BracketPair {
 6117                        start: "/* ".to_string(),
 6118                        end: "*/".to_string(),
 6119                        close: true,
 6120                        surround: true,
 6121                        ..Default::default()
 6122                    },
 6123                ],
 6124                ..Default::default()
 6125            },
 6126            ..Default::default()
 6127        },
 6128        Some(tree_sitter_rust::LANGUAGE.into()),
 6129    ));
 6130
 6131    let text = r#"
 6132        a
 6133        b
 6134        c
 6135    "#
 6136    .unindent();
 6137
 6138    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6139    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6140    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6141    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6142        .await;
 6143
 6144    view.update(cx, |view, cx| {
 6145        view.change_selections(None, cx, |s| {
 6146            s.select_display_ranges([
 6147                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6148                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6150            ])
 6151        });
 6152
 6153        view.handle_input("{", cx);
 6154        view.handle_input("{", cx);
 6155        view.handle_input("{", cx);
 6156        assert_eq!(
 6157            view.text(cx),
 6158            "
 6159                {{{a}}}
 6160                {{{b}}}
 6161                {{{c}}}
 6162            "
 6163            .unindent()
 6164        );
 6165        assert_eq!(
 6166            view.selections.display_ranges(cx),
 6167            [
 6168                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6169                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6170                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6171            ]
 6172        );
 6173
 6174        view.undo(&Undo, cx);
 6175        view.undo(&Undo, cx);
 6176        view.undo(&Undo, cx);
 6177        assert_eq!(
 6178            view.text(cx),
 6179            "
 6180                a
 6181                b
 6182                c
 6183            "
 6184            .unindent()
 6185        );
 6186        assert_eq!(
 6187            view.selections.display_ranges(cx),
 6188            [
 6189                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6190                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6191                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6192            ]
 6193        );
 6194
 6195        // Ensure inserting the first character of a multi-byte bracket pair
 6196        // doesn't surround the selections with the bracket.
 6197        view.handle_input("/", cx);
 6198        assert_eq!(
 6199            view.text(cx),
 6200            "
 6201                /
 6202                /
 6203                /
 6204            "
 6205            .unindent()
 6206        );
 6207        assert_eq!(
 6208            view.selections.display_ranges(cx),
 6209            [
 6210                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6211                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6212                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6213            ]
 6214        );
 6215
 6216        view.undo(&Undo, cx);
 6217        assert_eq!(
 6218            view.text(cx),
 6219            "
 6220                a
 6221                b
 6222                c
 6223            "
 6224            .unindent()
 6225        );
 6226        assert_eq!(
 6227            view.selections.display_ranges(cx),
 6228            [
 6229                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6230                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6231                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6232            ]
 6233        );
 6234
 6235        // Ensure inserting the last character of a multi-byte bracket pair
 6236        // doesn't surround the selections with the bracket.
 6237        view.handle_input("*", cx);
 6238        assert_eq!(
 6239            view.text(cx),
 6240            "
 6241                *
 6242                *
 6243                *
 6244            "
 6245            .unindent()
 6246        );
 6247        assert_eq!(
 6248            view.selections.display_ranges(cx),
 6249            [
 6250                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6251                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6252                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6253            ]
 6254        );
 6255    });
 6256}
 6257
 6258#[gpui::test]
 6259async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6260    init_test(cx, |_| {});
 6261
 6262    let language = Arc::new(Language::new(
 6263        LanguageConfig {
 6264            brackets: BracketPairConfig {
 6265                pairs: vec![BracketPair {
 6266                    start: "{".to_string(),
 6267                    end: "}".to_string(),
 6268                    close: true,
 6269                    surround: true,
 6270                    newline: true,
 6271                }],
 6272                ..Default::default()
 6273            },
 6274            autoclose_before: "}".to_string(),
 6275            ..Default::default()
 6276        },
 6277        Some(tree_sitter_rust::LANGUAGE.into()),
 6278    ));
 6279
 6280    let text = r#"
 6281        a
 6282        b
 6283        c
 6284    "#
 6285    .unindent();
 6286
 6287    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6288    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6289    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6290    editor
 6291        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6292        .await;
 6293
 6294    editor.update(cx, |editor, cx| {
 6295        editor.change_selections(None, cx, |s| {
 6296            s.select_ranges([
 6297                Point::new(0, 1)..Point::new(0, 1),
 6298                Point::new(1, 1)..Point::new(1, 1),
 6299                Point::new(2, 1)..Point::new(2, 1),
 6300            ])
 6301        });
 6302
 6303        editor.handle_input("{", cx);
 6304        editor.handle_input("{", cx);
 6305        editor.handle_input("_", cx);
 6306        assert_eq!(
 6307            editor.text(cx),
 6308            "
 6309                a{{_}}
 6310                b{{_}}
 6311                c{{_}}
 6312            "
 6313            .unindent()
 6314        );
 6315        assert_eq!(
 6316            editor.selections.ranges::<Point>(cx),
 6317            [
 6318                Point::new(0, 4)..Point::new(0, 4),
 6319                Point::new(1, 4)..Point::new(1, 4),
 6320                Point::new(2, 4)..Point::new(2, 4)
 6321            ]
 6322        );
 6323
 6324        editor.backspace(&Default::default(), cx);
 6325        editor.backspace(&Default::default(), cx);
 6326        assert_eq!(
 6327            editor.text(cx),
 6328            "
 6329                a{}
 6330                b{}
 6331                c{}
 6332            "
 6333            .unindent()
 6334        );
 6335        assert_eq!(
 6336            editor.selections.ranges::<Point>(cx),
 6337            [
 6338                Point::new(0, 2)..Point::new(0, 2),
 6339                Point::new(1, 2)..Point::new(1, 2),
 6340                Point::new(2, 2)..Point::new(2, 2)
 6341            ]
 6342        );
 6343
 6344        editor.delete_to_previous_word_start(&Default::default(), cx);
 6345        assert_eq!(
 6346            editor.text(cx),
 6347            "
 6348                a
 6349                b
 6350                c
 6351            "
 6352            .unindent()
 6353        );
 6354        assert_eq!(
 6355            editor.selections.ranges::<Point>(cx),
 6356            [
 6357                Point::new(0, 1)..Point::new(0, 1),
 6358                Point::new(1, 1)..Point::new(1, 1),
 6359                Point::new(2, 1)..Point::new(2, 1)
 6360            ]
 6361        );
 6362    });
 6363}
 6364
 6365#[gpui::test]
 6366async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6367    init_test(cx, |settings| {
 6368        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6369    });
 6370
 6371    let mut cx = EditorTestContext::new(cx).await;
 6372
 6373    let language = Arc::new(Language::new(
 6374        LanguageConfig {
 6375            brackets: BracketPairConfig {
 6376                pairs: vec![
 6377                    BracketPair {
 6378                        start: "{".to_string(),
 6379                        end: "}".to_string(),
 6380                        close: true,
 6381                        surround: true,
 6382                        newline: true,
 6383                    },
 6384                    BracketPair {
 6385                        start: "(".to_string(),
 6386                        end: ")".to_string(),
 6387                        close: true,
 6388                        surround: true,
 6389                        newline: true,
 6390                    },
 6391                    BracketPair {
 6392                        start: "[".to_string(),
 6393                        end: "]".to_string(),
 6394                        close: false,
 6395                        surround: true,
 6396                        newline: true,
 6397                    },
 6398                ],
 6399                ..Default::default()
 6400            },
 6401            autoclose_before: "})]".to_string(),
 6402            ..Default::default()
 6403        },
 6404        Some(tree_sitter_rust::LANGUAGE.into()),
 6405    ));
 6406
 6407    cx.language_registry().add(language.clone());
 6408    cx.update_buffer(|buffer, cx| {
 6409        buffer.set_language(Some(language), cx);
 6410    });
 6411
 6412    cx.set_state(
 6413        &"
 6414            {(ˇ)}
 6415            [[ˇ]]
 6416            {(ˇ)}
 6417        "
 6418        .unindent(),
 6419    );
 6420
 6421    cx.update_editor(|view, cx| {
 6422        view.backspace(&Default::default(), cx);
 6423        view.backspace(&Default::default(), cx);
 6424    });
 6425
 6426    cx.assert_editor_state(
 6427        &"
 6428            ˇ
 6429            ˇ]]
 6430            ˇ
 6431        "
 6432        .unindent(),
 6433    );
 6434
 6435    cx.update_editor(|view, cx| {
 6436        view.handle_input("{", cx);
 6437        view.handle_input("{", cx);
 6438        view.move_right(&MoveRight, cx);
 6439        view.move_right(&MoveRight, cx);
 6440        view.move_left(&MoveLeft, cx);
 6441        view.move_left(&MoveLeft, cx);
 6442        view.backspace(&Default::default(), cx);
 6443    });
 6444
 6445    cx.assert_editor_state(
 6446        &"
 6447            {ˇ}
 6448            {ˇ}]]
 6449            {ˇ}
 6450        "
 6451        .unindent(),
 6452    );
 6453
 6454    cx.update_editor(|view, cx| {
 6455        view.backspace(&Default::default(), cx);
 6456    });
 6457
 6458    cx.assert_editor_state(
 6459        &"
 6460            ˇ
 6461            ˇ]]
 6462            ˇ
 6463        "
 6464        .unindent(),
 6465    );
 6466}
 6467
 6468#[gpui::test]
 6469async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6470    init_test(cx, |_| {});
 6471
 6472    let language = Arc::new(Language::new(
 6473        LanguageConfig::default(),
 6474        Some(tree_sitter_rust::LANGUAGE.into()),
 6475    ));
 6476
 6477    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6478    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6479    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6480    editor
 6481        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6482        .await;
 6483
 6484    editor.update(cx, |editor, cx| {
 6485        editor.set_auto_replace_emoji_shortcode(true);
 6486
 6487        editor.handle_input("Hello ", cx);
 6488        editor.handle_input(":wave", cx);
 6489        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6490
 6491        editor.handle_input(":", cx);
 6492        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6493
 6494        editor.handle_input(" :smile", cx);
 6495        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6496
 6497        editor.handle_input(":", cx);
 6498        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6499
 6500        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6501        editor.handle_input(":wave", cx);
 6502        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6503
 6504        editor.handle_input(":", cx);
 6505        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6506
 6507        editor.handle_input(":1", cx);
 6508        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6509
 6510        editor.handle_input(":", cx);
 6511        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6512
 6513        // Ensure shortcode does not get replaced when it is part of a word
 6514        editor.handle_input(" Test:wave", cx);
 6515        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6516
 6517        editor.handle_input(":", cx);
 6518        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6519
 6520        editor.set_auto_replace_emoji_shortcode(false);
 6521
 6522        // Ensure shortcode does not get replaced when auto replace is off
 6523        editor.handle_input(" :wave", cx);
 6524        assert_eq!(
 6525            editor.text(cx),
 6526            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6527        );
 6528
 6529        editor.handle_input(":", cx);
 6530        assert_eq!(
 6531            editor.text(cx),
 6532            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6533        );
 6534    });
 6535}
 6536
 6537#[gpui::test]
 6538async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6539    init_test(cx, |_| {});
 6540
 6541    let (text, insertion_ranges) = marked_text_ranges(
 6542        indoc! {"
 6543            a.ˇ b
 6544            a.ˇ b
 6545            a.ˇ b
 6546        "},
 6547        false,
 6548    );
 6549
 6550    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6551    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6552
 6553    editor.update(cx, |editor, cx| {
 6554        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6555
 6556        editor
 6557            .insert_snippet(&insertion_ranges, snippet, cx)
 6558            .unwrap();
 6559
 6560        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6561            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6562            assert_eq!(editor.text(cx), expected_text);
 6563            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6564        }
 6565
 6566        assert(
 6567            editor,
 6568            cx,
 6569            indoc! {"
 6570                a.f(«one», two, «three») b
 6571                a.f(«one», two, «three») b
 6572                a.f(«one», two, «three») b
 6573            "},
 6574        );
 6575
 6576        // Can't move earlier than the first tab stop
 6577        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6578        assert(
 6579            editor,
 6580            cx,
 6581            indoc! {"
 6582                a.f(«one», two, «three») b
 6583                a.f(«one», two, «three») b
 6584                a.f(«one», two, «three») b
 6585            "},
 6586        );
 6587
 6588        assert!(editor.move_to_next_snippet_tabstop(cx));
 6589        assert(
 6590            editor,
 6591            cx,
 6592            indoc! {"
 6593                a.f(one, «two», three) b
 6594                a.f(one, «two», three) b
 6595                a.f(one, «two», three) b
 6596            "},
 6597        );
 6598
 6599        editor.move_to_prev_snippet_tabstop(cx);
 6600        assert(
 6601            editor,
 6602            cx,
 6603            indoc! {"
 6604                a.f(«one», two, «three») b
 6605                a.f(«one», two, «three») b
 6606                a.f(«one», two, «three») b
 6607            "},
 6608        );
 6609
 6610        assert!(editor.move_to_next_snippet_tabstop(cx));
 6611        assert(
 6612            editor,
 6613            cx,
 6614            indoc! {"
 6615                a.f(one, «two», three) b
 6616                a.f(one, «two», three) b
 6617                a.f(one, «two», three) b
 6618            "},
 6619        );
 6620        assert!(editor.move_to_next_snippet_tabstop(cx));
 6621        assert(
 6622            editor,
 6623            cx,
 6624            indoc! {"
 6625                a.f(one, two, three)ˇ b
 6626                a.f(one, two, three)ˇ b
 6627                a.f(one, two, three)ˇ b
 6628            "},
 6629        );
 6630
 6631        // As soon as the last tab stop is reached, snippet state is gone
 6632        editor.move_to_prev_snippet_tabstop(cx);
 6633        assert(
 6634            editor,
 6635            cx,
 6636            indoc! {"
 6637                a.f(one, two, three)ˇ b
 6638                a.f(one, two, three)ˇ b
 6639                a.f(one, two, three)ˇ b
 6640            "},
 6641        );
 6642    });
 6643}
 6644
 6645#[gpui::test]
 6646async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6647    init_test(cx, |_| {});
 6648
 6649    let fs = FakeFs::new(cx.executor());
 6650    fs.insert_file("/file.rs", Default::default()).await;
 6651
 6652    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6653
 6654    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6655    language_registry.add(rust_lang());
 6656    let mut fake_servers = language_registry.register_fake_lsp(
 6657        "Rust",
 6658        FakeLspAdapter {
 6659            capabilities: lsp::ServerCapabilities {
 6660                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6661                ..Default::default()
 6662            },
 6663            ..Default::default()
 6664        },
 6665    );
 6666
 6667    let buffer = project
 6668        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6669        .await
 6670        .unwrap();
 6671
 6672    cx.executor().start_waiting();
 6673    let fake_server = fake_servers.next().await.unwrap();
 6674
 6675    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6676    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6677    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6678    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6679
 6680    let save = editor
 6681        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6682        .unwrap();
 6683    fake_server
 6684        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6685            assert_eq!(
 6686                params.text_document.uri,
 6687                lsp::Url::from_file_path("/file.rs").unwrap()
 6688            );
 6689            assert_eq!(params.options.tab_size, 4);
 6690            Ok(Some(vec![lsp::TextEdit::new(
 6691                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6692                ", ".to_string(),
 6693            )]))
 6694        })
 6695        .next()
 6696        .await;
 6697    cx.executor().start_waiting();
 6698    save.await;
 6699
 6700    assert_eq!(
 6701        editor.update(cx, |editor, cx| editor.text(cx)),
 6702        "one, two\nthree\n"
 6703    );
 6704    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6705
 6706    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6707    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6708
 6709    // Ensure we can still save even if formatting hangs.
 6710    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6711        assert_eq!(
 6712            params.text_document.uri,
 6713            lsp::Url::from_file_path("/file.rs").unwrap()
 6714        );
 6715        futures::future::pending::<()>().await;
 6716        unreachable!()
 6717    });
 6718    let save = editor
 6719        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6720        .unwrap();
 6721    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6722    cx.executor().start_waiting();
 6723    save.await;
 6724    assert_eq!(
 6725        editor.update(cx, |editor, cx| editor.text(cx)),
 6726        "one\ntwo\nthree\n"
 6727    );
 6728    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6729
 6730    // For non-dirty buffer, no formatting request should be sent
 6731    let save = editor
 6732        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6733        .unwrap();
 6734    let _pending_format_request = fake_server
 6735        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6736            panic!("Should not be invoked on non-dirty buffer");
 6737        })
 6738        .next();
 6739    cx.executor().start_waiting();
 6740    save.await;
 6741
 6742    // Set rust language override and assert overridden tabsize is sent to language server
 6743    update_test_language_settings(cx, |settings| {
 6744        settings.languages.insert(
 6745            "Rust".into(),
 6746            LanguageSettingsContent {
 6747                tab_size: NonZeroU32::new(8),
 6748                ..Default::default()
 6749            },
 6750        );
 6751    });
 6752
 6753    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6754    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6755    let save = editor
 6756        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6757        .unwrap();
 6758    fake_server
 6759        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6760            assert_eq!(
 6761                params.text_document.uri,
 6762                lsp::Url::from_file_path("/file.rs").unwrap()
 6763            );
 6764            assert_eq!(params.options.tab_size, 8);
 6765            Ok(Some(vec![]))
 6766        })
 6767        .next()
 6768        .await;
 6769    cx.executor().start_waiting();
 6770    save.await;
 6771}
 6772
 6773#[gpui::test]
 6774async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6775    init_test(cx, |_| {});
 6776
 6777    let cols = 4;
 6778    let rows = 10;
 6779    let sample_text_1 = sample_text(rows, cols, 'a');
 6780    assert_eq!(
 6781        sample_text_1,
 6782        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6783    );
 6784    let sample_text_2 = sample_text(rows, cols, 'l');
 6785    assert_eq!(
 6786        sample_text_2,
 6787        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6788    );
 6789    let sample_text_3 = sample_text(rows, cols, 'v');
 6790    assert_eq!(
 6791        sample_text_3,
 6792        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6793    );
 6794
 6795    let fs = FakeFs::new(cx.executor());
 6796    fs.insert_tree(
 6797        "/a",
 6798        json!({
 6799            "main.rs": sample_text_1,
 6800            "other.rs": sample_text_2,
 6801            "lib.rs": sample_text_3,
 6802        }),
 6803    )
 6804    .await;
 6805
 6806    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6807    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6808    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6809
 6810    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6811    language_registry.add(rust_lang());
 6812    let mut fake_servers = language_registry.register_fake_lsp(
 6813        "Rust",
 6814        FakeLspAdapter {
 6815            capabilities: lsp::ServerCapabilities {
 6816                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6817                ..Default::default()
 6818            },
 6819            ..Default::default()
 6820        },
 6821    );
 6822
 6823    let worktree = project.update(cx, |project, cx| {
 6824        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6825        assert_eq!(worktrees.len(), 1);
 6826        worktrees.pop().unwrap()
 6827    });
 6828    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6829
 6830    let buffer_1 = project
 6831        .update(cx, |project, cx| {
 6832            project.open_buffer((worktree_id, "main.rs"), cx)
 6833        })
 6834        .await
 6835        .unwrap();
 6836    let buffer_2 = project
 6837        .update(cx, |project, cx| {
 6838            project.open_buffer((worktree_id, "other.rs"), cx)
 6839        })
 6840        .await
 6841        .unwrap();
 6842    let buffer_3 = project
 6843        .update(cx, |project, cx| {
 6844            project.open_buffer((worktree_id, "lib.rs"), cx)
 6845        })
 6846        .await
 6847        .unwrap();
 6848
 6849    let multi_buffer = cx.new_model(|cx| {
 6850        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6851        multi_buffer.push_excerpts(
 6852            buffer_1.clone(),
 6853            [
 6854                ExcerptRange {
 6855                    context: Point::new(0, 0)..Point::new(3, 0),
 6856                    primary: None,
 6857                },
 6858                ExcerptRange {
 6859                    context: Point::new(5, 0)..Point::new(7, 0),
 6860                    primary: None,
 6861                },
 6862                ExcerptRange {
 6863                    context: Point::new(9, 0)..Point::new(10, 4),
 6864                    primary: None,
 6865                },
 6866            ],
 6867            cx,
 6868        );
 6869        multi_buffer.push_excerpts(
 6870            buffer_2.clone(),
 6871            [
 6872                ExcerptRange {
 6873                    context: Point::new(0, 0)..Point::new(3, 0),
 6874                    primary: None,
 6875                },
 6876                ExcerptRange {
 6877                    context: Point::new(5, 0)..Point::new(7, 0),
 6878                    primary: None,
 6879                },
 6880                ExcerptRange {
 6881                    context: Point::new(9, 0)..Point::new(10, 4),
 6882                    primary: None,
 6883                },
 6884            ],
 6885            cx,
 6886        );
 6887        multi_buffer.push_excerpts(
 6888            buffer_3.clone(),
 6889            [
 6890                ExcerptRange {
 6891                    context: Point::new(0, 0)..Point::new(3, 0),
 6892                    primary: None,
 6893                },
 6894                ExcerptRange {
 6895                    context: Point::new(5, 0)..Point::new(7, 0),
 6896                    primary: None,
 6897                },
 6898                ExcerptRange {
 6899                    context: Point::new(9, 0)..Point::new(10, 4),
 6900                    primary: None,
 6901                },
 6902            ],
 6903            cx,
 6904        );
 6905        multi_buffer
 6906    });
 6907    let multi_buffer_editor = cx.new_view(|cx| {
 6908        Editor::new(
 6909            EditorMode::Full,
 6910            multi_buffer,
 6911            Some(project.clone()),
 6912            true,
 6913            cx,
 6914        )
 6915    });
 6916
 6917    multi_buffer_editor.update(cx, |editor, cx| {
 6918        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6919        editor.insert("|one|two|three|", cx);
 6920    });
 6921    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6922    multi_buffer_editor.update(cx, |editor, cx| {
 6923        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6924            s.select_ranges(Some(60..70))
 6925        });
 6926        editor.insert("|four|five|six|", cx);
 6927    });
 6928    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6929
 6930    // First two buffers should be edited, but not the third one.
 6931    assert_eq!(
 6932        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6933        "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}",
 6934    );
 6935    buffer_1.update(cx, |buffer, _| {
 6936        assert!(buffer.is_dirty());
 6937        assert_eq!(
 6938            buffer.text(),
 6939            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6940        )
 6941    });
 6942    buffer_2.update(cx, |buffer, _| {
 6943        assert!(buffer.is_dirty());
 6944        assert_eq!(
 6945            buffer.text(),
 6946            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6947        )
 6948    });
 6949    buffer_3.update(cx, |buffer, _| {
 6950        assert!(!buffer.is_dirty());
 6951        assert_eq!(buffer.text(), sample_text_3,)
 6952    });
 6953
 6954    cx.executor().start_waiting();
 6955    let save = multi_buffer_editor
 6956        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6957        .unwrap();
 6958
 6959    let fake_server = fake_servers.next().await.unwrap();
 6960    fake_server
 6961        .server
 6962        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6963            Ok(Some(vec![lsp::TextEdit::new(
 6964                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6965                format!("[{} formatted]", params.text_document.uri),
 6966            )]))
 6967        })
 6968        .detach();
 6969    save.await;
 6970
 6971    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6972    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6973    assert_eq!(
 6974        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6975        "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}",
 6976    );
 6977    buffer_1.update(cx, |buffer, _| {
 6978        assert!(!buffer.is_dirty());
 6979        assert_eq!(
 6980            buffer.text(),
 6981            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6982        )
 6983    });
 6984    buffer_2.update(cx, |buffer, _| {
 6985        assert!(!buffer.is_dirty());
 6986        assert_eq!(
 6987            buffer.text(),
 6988            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6989        )
 6990    });
 6991    buffer_3.update(cx, |buffer, _| {
 6992        assert!(!buffer.is_dirty());
 6993        assert_eq!(buffer.text(), sample_text_3,)
 6994    });
 6995}
 6996
 6997#[gpui::test]
 6998async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6999    init_test(cx, |_| {});
 7000
 7001    let fs = FakeFs::new(cx.executor());
 7002    fs.insert_file("/file.rs", Default::default()).await;
 7003
 7004    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7005
 7006    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7007    language_registry.add(rust_lang());
 7008    let mut fake_servers = language_registry.register_fake_lsp(
 7009        "Rust",
 7010        FakeLspAdapter {
 7011            capabilities: lsp::ServerCapabilities {
 7012                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7013                ..Default::default()
 7014            },
 7015            ..Default::default()
 7016        },
 7017    );
 7018
 7019    let buffer = project
 7020        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7021        .await
 7022        .unwrap();
 7023
 7024    cx.executor().start_waiting();
 7025    let fake_server = fake_servers.next().await.unwrap();
 7026
 7027    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7028    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7029    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7030    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7031
 7032    let save = editor
 7033        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7034        .unwrap();
 7035    fake_server
 7036        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7037            assert_eq!(
 7038                params.text_document.uri,
 7039                lsp::Url::from_file_path("/file.rs").unwrap()
 7040            );
 7041            assert_eq!(params.options.tab_size, 4);
 7042            Ok(Some(vec![lsp::TextEdit::new(
 7043                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7044                ", ".to_string(),
 7045            )]))
 7046        })
 7047        .next()
 7048        .await;
 7049    cx.executor().start_waiting();
 7050    save.await;
 7051    assert_eq!(
 7052        editor.update(cx, |editor, cx| editor.text(cx)),
 7053        "one, two\nthree\n"
 7054    );
 7055    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7056
 7057    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7058    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7059
 7060    // Ensure we can still save even if formatting hangs.
 7061    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7062        move |params, _| async move {
 7063            assert_eq!(
 7064                params.text_document.uri,
 7065                lsp::Url::from_file_path("/file.rs").unwrap()
 7066            );
 7067            futures::future::pending::<()>().await;
 7068            unreachable!()
 7069        },
 7070    );
 7071    let save = editor
 7072        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7073        .unwrap();
 7074    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7075    cx.executor().start_waiting();
 7076    save.await;
 7077    assert_eq!(
 7078        editor.update(cx, |editor, cx| editor.text(cx)),
 7079        "one\ntwo\nthree\n"
 7080    );
 7081    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7082
 7083    // For non-dirty buffer, no formatting request should be sent
 7084    let save = editor
 7085        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7086        .unwrap();
 7087    let _pending_format_request = fake_server
 7088        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7089            panic!("Should not be invoked on non-dirty buffer");
 7090        })
 7091        .next();
 7092    cx.executor().start_waiting();
 7093    save.await;
 7094
 7095    // Set Rust language override and assert overridden tabsize is sent to language server
 7096    update_test_language_settings(cx, |settings| {
 7097        settings.languages.insert(
 7098            "Rust".into(),
 7099            LanguageSettingsContent {
 7100                tab_size: NonZeroU32::new(8),
 7101                ..Default::default()
 7102            },
 7103        );
 7104    });
 7105
 7106    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7107    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7108    let save = editor
 7109        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7110        .unwrap();
 7111    fake_server
 7112        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7113            assert_eq!(
 7114                params.text_document.uri,
 7115                lsp::Url::from_file_path("/file.rs").unwrap()
 7116            );
 7117            assert_eq!(params.options.tab_size, 8);
 7118            Ok(Some(vec![]))
 7119        })
 7120        .next()
 7121        .await;
 7122    cx.executor().start_waiting();
 7123    save.await;
 7124}
 7125
 7126#[gpui::test]
 7127async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7128    init_test(cx, |settings| {
 7129        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7130            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7131        ))
 7132    });
 7133
 7134    let fs = FakeFs::new(cx.executor());
 7135    fs.insert_file("/file.rs", Default::default()).await;
 7136
 7137    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7138
 7139    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7140    language_registry.add(Arc::new(Language::new(
 7141        LanguageConfig {
 7142            name: "Rust".into(),
 7143            matcher: LanguageMatcher {
 7144                path_suffixes: vec!["rs".to_string()],
 7145                ..Default::default()
 7146            },
 7147            ..LanguageConfig::default()
 7148        },
 7149        Some(tree_sitter_rust::LANGUAGE.into()),
 7150    )));
 7151    update_test_language_settings(cx, |settings| {
 7152        // Enable Prettier formatting for the same buffer, and ensure
 7153        // LSP is called instead of Prettier.
 7154        settings.defaults.prettier = Some(PrettierSettings {
 7155            allowed: true,
 7156            ..PrettierSettings::default()
 7157        });
 7158    });
 7159    let mut fake_servers = language_registry.register_fake_lsp(
 7160        "Rust",
 7161        FakeLspAdapter {
 7162            capabilities: lsp::ServerCapabilities {
 7163                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7164                ..Default::default()
 7165            },
 7166            ..Default::default()
 7167        },
 7168    );
 7169
 7170    let buffer = project
 7171        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7172        .await
 7173        .unwrap();
 7174
 7175    cx.executor().start_waiting();
 7176    let fake_server = fake_servers.next().await.unwrap();
 7177
 7178    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7179    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7180    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7181
 7182    let format = editor
 7183        .update(cx, |editor, cx| {
 7184            editor.perform_format(
 7185                project.clone(),
 7186                FormatTrigger::Manual,
 7187                FormatTarget::Buffer,
 7188                cx,
 7189            )
 7190        })
 7191        .unwrap();
 7192    fake_server
 7193        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7194            assert_eq!(
 7195                params.text_document.uri,
 7196                lsp::Url::from_file_path("/file.rs").unwrap()
 7197            );
 7198            assert_eq!(params.options.tab_size, 4);
 7199            Ok(Some(vec![lsp::TextEdit::new(
 7200                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7201                ", ".to_string(),
 7202            )]))
 7203        })
 7204        .next()
 7205        .await;
 7206    cx.executor().start_waiting();
 7207    format.await;
 7208    assert_eq!(
 7209        editor.update(cx, |editor, cx| editor.text(cx)),
 7210        "one, two\nthree\n"
 7211    );
 7212
 7213    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7214    // Ensure we don't lock if formatting hangs.
 7215    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7216        assert_eq!(
 7217            params.text_document.uri,
 7218            lsp::Url::from_file_path("/file.rs").unwrap()
 7219        );
 7220        futures::future::pending::<()>().await;
 7221        unreachable!()
 7222    });
 7223    let format = editor
 7224        .update(cx, |editor, cx| {
 7225            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7226        })
 7227        .unwrap();
 7228    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7229    cx.executor().start_waiting();
 7230    format.await;
 7231    assert_eq!(
 7232        editor.update(cx, |editor, cx| editor.text(cx)),
 7233        "one\ntwo\nthree\n"
 7234    );
 7235}
 7236
 7237#[gpui::test]
 7238async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7239    init_test(cx, |_| {});
 7240
 7241    let mut cx = EditorLspTestContext::new_rust(
 7242        lsp::ServerCapabilities {
 7243            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7244            ..Default::default()
 7245        },
 7246        cx,
 7247    )
 7248    .await;
 7249
 7250    cx.set_state(indoc! {"
 7251        one.twoˇ
 7252    "});
 7253
 7254    // The format request takes a long time. When it completes, it inserts
 7255    // a newline and an indent before the `.`
 7256    cx.lsp
 7257        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7258            let executor = cx.background_executor().clone();
 7259            async move {
 7260                executor.timer(Duration::from_millis(100)).await;
 7261                Ok(Some(vec![lsp::TextEdit {
 7262                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7263                    new_text: "\n    ".into(),
 7264                }]))
 7265            }
 7266        });
 7267
 7268    // Submit a format request.
 7269    let format_1 = cx
 7270        .update_editor(|editor, cx| editor.format(&Format, cx))
 7271        .unwrap();
 7272    cx.executor().run_until_parked();
 7273
 7274    // Submit a second format request.
 7275    let format_2 = cx
 7276        .update_editor(|editor, cx| editor.format(&Format, cx))
 7277        .unwrap();
 7278    cx.executor().run_until_parked();
 7279
 7280    // Wait for both format requests to complete
 7281    cx.executor().advance_clock(Duration::from_millis(200));
 7282    cx.executor().start_waiting();
 7283    format_1.await.unwrap();
 7284    cx.executor().start_waiting();
 7285    format_2.await.unwrap();
 7286
 7287    // The formatting edits only happens once.
 7288    cx.assert_editor_state(indoc! {"
 7289        one
 7290            .twoˇ
 7291    "});
 7292}
 7293
 7294#[gpui::test]
 7295async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7296    init_test(cx, |settings| {
 7297        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7298    });
 7299
 7300    let mut cx = EditorLspTestContext::new_rust(
 7301        lsp::ServerCapabilities {
 7302            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7303            ..Default::default()
 7304        },
 7305        cx,
 7306    )
 7307    .await;
 7308
 7309    // Set up a buffer white some trailing whitespace and no trailing newline.
 7310    cx.set_state(
 7311        &[
 7312            "one ",   //
 7313            "twoˇ",   //
 7314            "three ", //
 7315            "four",   //
 7316        ]
 7317        .join("\n"),
 7318    );
 7319
 7320    // Submit a format request.
 7321    let format = cx
 7322        .update_editor(|editor, cx| editor.format(&Format, cx))
 7323        .unwrap();
 7324
 7325    // Record which buffer changes have been sent to the language server
 7326    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7327    cx.lsp
 7328        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7329            let buffer_changes = buffer_changes.clone();
 7330            move |params, _| {
 7331                buffer_changes.lock().extend(
 7332                    params
 7333                        .content_changes
 7334                        .into_iter()
 7335                        .map(|e| (e.range.unwrap(), e.text)),
 7336                );
 7337            }
 7338        });
 7339
 7340    // Handle formatting requests to the language server.
 7341    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7342        let buffer_changes = buffer_changes.clone();
 7343        move |_, _| {
 7344            // When formatting is requested, trailing whitespace has already been stripped,
 7345            // and the trailing newline has already been added.
 7346            assert_eq!(
 7347                &buffer_changes.lock()[1..],
 7348                &[
 7349                    (
 7350                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7351                        "".into()
 7352                    ),
 7353                    (
 7354                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7355                        "".into()
 7356                    ),
 7357                    (
 7358                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7359                        "\n".into()
 7360                    ),
 7361                ]
 7362            );
 7363
 7364            // Insert blank lines between each line of the buffer.
 7365            async move {
 7366                Ok(Some(vec![
 7367                    lsp::TextEdit {
 7368                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7369                        new_text: "\n".into(),
 7370                    },
 7371                    lsp::TextEdit {
 7372                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7373                        new_text: "\n".into(),
 7374                    },
 7375                ]))
 7376            }
 7377        }
 7378    });
 7379
 7380    // After formatting the buffer, the trailing whitespace is stripped,
 7381    // a newline is appended, and the edits provided by the language server
 7382    // have been applied.
 7383    format.await.unwrap();
 7384    cx.assert_editor_state(
 7385        &[
 7386            "one",   //
 7387            "",      //
 7388            "twoˇ",  //
 7389            "",      //
 7390            "three", //
 7391            "four",  //
 7392            "",      //
 7393        ]
 7394        .join("\n"),
 7395    );
 7396
 7397    // Undoing the formatting undoes the trailing whitespace removal, the
 7398    // trailing newline, and the LSP edits.
 7399    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7400    cx.assert_editor_state(
 7401        &[
 7402            "one ",   //
 7403            "twoˇ",   //
 7404            "three ", //
 7405            "four",   //
 7406        ]
 7407        .join("\n"),
 7408    );
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7413    cx: &mut gpui::TestAppContext,
 7414) {
 7415    init_test(cx, |_| {});
 7416
 7417    cx.update(|cx| {
 7418        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7419            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7420                settings.auto_signature_help = Some(true);
 7421            });
 7422        });
 7423    });
 7424
 7425    let mut cx = EditorLspTestContext::new_rust(
 7426        lsp::ServerCapabilities {
 7427            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7428                ..Default::default()
 7429            }),
 7430            ..Default::default()
 7431        },
 7432        cx,
 7433    )
 7434    .await;
 7435
 7436    let language = Language::new(
 7437        LanguageConfig {
 7438            name: "Rust".into(),
 7439            brackets: BracketPairConfig {
 7440                pairs: vec![
 7441                    BracketPair {
 7442                        start: "{".to_string(),
 7443                        end: "}".to_string(),
 7444                        close: true,
 7445                        surround: true,
 7446                        newline: true,
 7447                    },
 7448                    BracketPair {
 7449                        start: "(".to_string(),
 7450                        end: ")".to_string(),
 7451                        close: true,
 7452                        surround: true,
 7453                        newline: true,
 7454                    },
 7455                    BracketPair {
 7456                        start: "/*".to_string(),
 7457                        end: " */".to_string(),
 7458                        close: true,
 7459                        surround: true,
 7460                        newline: true,
 7461                    },
 7462                    BracketPair {
 7463                        start: "[".to_string(),
 7464                        end: "]".to_string(),
 7465                        close: false,
 7466                        surround: false,
 7467                        newline: true,
 7468                    },
 7469                    BracketPair {
 7470                        start: "\"".to_string(),
 7471                        end: "\"".to_string(),
 7472                        close: true,
 7473                        surround: true,
 7474                        newline: false,
 7475                    },
 7476                    BracketPair {
 7477                        start: "<".to_string(),
 7478                        end: ">".to_string(),
 7479                        close: false,
 7480                        surround: true,
 7481                        newline: true,
 7482                    },
 7483                ],
 7484                ..Default::default()
 7485            },
 7486            autoclose_before: "})]".to_string(),
 7487            ..Default::default()
 7488        },
 7489        Some(tree_sitter_rust::LANGUAGE.into()),
 7490    );
 7491    let language = Arc::new(language);
 7492
 7493    cx.language_registry().add(language.clone());
 7494    cx.update_buffer(|buffer, cx| {
 7495        buffer.set_language(Some(language), cx);
 7496    });
 7497
 7498    cx.set_state(
 7499        &r#"
 7500            fn main() {
 7501                sampleˇ
 7502            }
 7503        "#
 7504        .unindent(),
 7505    );
 7506
 7507    cx.update_editor(|view, cx| {
 7508        view.handle_input("(", cx);
 7509    });
 7510    cx.assert_editor_state(
 7511        &"
 7512            fn main() {
 7513                sample(ˇ)
 7514            }
 7515        "
 7516        .unindent(),
 7517    );
 7518
 7519    let mocked_response = lsp::SignatureHelp {
 7520        signatures: vec![lsp::SignatureInformation {
 7521            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7522            documentation: None,
 7523            parameters: Some(vec![
 7524                lsp::ParameterInformation {
 7525                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7526                    documentation: None,
 7527                },
 7528                lsp::ParameterInformation {
 7529                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7530                    documentation: None,
 7531                },
 7532            ]),
 7533            active_parameter: None,
 7534        }],
 7535        active_signature: Some(0),
 7536        active_parameter: Some(0),
 7537    };
 7538    handle_signature_help_request(&mut cx, mocked_response).await;
 7539
 7540    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7541        .await;
 7542
 7543    cx.editor(|editor, _| {
 7544        let signature_help_state = editor.signature_help_state.popover().cloned();
 7545        assert!(signature_help_state.is_some());
 7546        let ParsedMarkdown {
 7547            text, highlights, ..
 7548        } = signature_help_state.unwrap().parsed_content;
 7549        assert_eq!(text, "param1: u8, param2: u8");
 7550        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7551    });
 7552}
 7553
 7554#[gpui::test]
 7555async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7556    init_test(cx, |_| {});
 7557
 7558    cx.update(|cx| {
 7559        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7560            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7561                settings.auto_signature_help = Some(false);
 7562                settings.show_signature_help_after_edits = Some(false);
 7563            });
 7564        });
 7565    });
 7566
 7567    let mut cx = EditorLspTestContext::new_rust(
 7568        lsp::ServerCapabilities {
 7569            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7570                ..Default::default()
 7571            }),
 7572            ..Default::default()
 7573        },
 7574        cx,
 7575    )
 7576    .await;
 7577
 7578    let language = Language::new(
 7579        LanguageConfig {
 7580            name: "Rust".into(),
 7581            brackets: BracketPairConfig {
 7582                pairs: vec![
 7583                    BracketPair {
 7584                        start: "{".to_string(),
 7585                        end: "}".to_string(),
 7586                        close: true,
 7587                        surround: true,
 7588                        newline: true,
 7589                    },
 7590                    BracketPair {
 7591                        start: "(".to_string(),
 7592                        end: ")".to_string(),
 7593                        close: true,
 7594                        surround: true,
 7595                        newline: true,
 7596                    },
 7597                    BracketPair {
 7598                        start: "/*".to_string(),
 7599                        end: " */".to_string(),
 7600                        close: true,
 7601                        surround: true,
 7602                        newline: true,
 7603                    },
 7604                    BracketPair {
 7605                        start: "[".to_string(),
 7606                        end: "]".to_string(),
 7607                        close: false,
 7608                        surround: false,
 7609                        newline: true,
 7610                    },
 7611                    BracketPair {
 7612                        start: "\"".to_string(),
 7613                        end: "\"".to_string(),
 7614                        close: true,
 7615                        surround: true,
 7616                        newline: false,
 7617                    },
 7618                    BracketPair {
 7619                        start: "<".to_string(),
 7620                        end: ">".to_string(),
 7621                        close: false,
 7622                        surround: true,
 7623                        newline: true,
 7624                    },
 7625                ],
 7626                ..Default::default()
 7627            },
 7628            autoclose_before: "})]".to_string(),
 7629            ..Default::default()
 7630        },
 7631        Some(tree_sitter_rust::LANGUAGE.into()),
 7632    );
 7633    let language = Arc::new(language);
 7634
 7635    cx.language_registry().add(language.clone());
 7636    cx.update_buffer(|buffer, cx| {
 7637        buffer.set_language(Some(language), cx);
 7638    });
 7639
 7640    // Ensure that signature_help is not called when no signature help is enabled.
 7641    cx.set_state(
 7642        &r#"
 7643            fn main() {
 7644                sampleˇ
 7645            }
 7646        "#
 7647        .unindent(),
 7648    );
 7649    cx.update_editor(|view, cx| {
 7650        view.handle_input("(", cx);
 7651    });
 7652    cx.assert_editor_state(
 7653        &"
 7654            fn main() {
 7655                sample(ˇ)
 7656            }
 7657        "
 7658        .unindent(),
 7659    );
 7660    cx.editor(|editor, _| {
 7661        assert!(editor.signature_help_state.task().is_none());
 7662    });
 7663
 7664    let mocked_response = lsp::SignatureHelp {
 7665        signatures: vec![lsp::SignatureInformation {
 7666            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7667            documentation: None,
 7668            parameters: Some(vec![
 7669                lsp::ParameterInformation {
 7670                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7671                    documentation: None,
 7672                },
 7673                lsp::ParameterInformation {
 7674                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7675                    documentation: None,
 7676                },
 7677            ]),
 7678            active_parameter: None,
 7679        }],
 7680        active_signature: Some(0),
 7681        active_parameter: Some(0),
 7682    };
 7683
 7684    // Ensure that signature_help is called when enabled afte edits
 7685    cx.update(|cx| {
 7686        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7687            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7688                settings.auto_signature_help = Some(false);
 7689                settings.show_signature_help_after_edits = Some(true);
 7690            });
 7691        });
 7692    });
 7693    cx.set_state(
 7694        &r#"
 7695            fn main() {
 7696                sampleˇ
 7697            }
 7698        "#
 7699        .unindent(),
 7700    );
 7701    cx.update_editor(|view, cx| {
 7702        view.handle_input("(", cx);
 7703    });
 7704    cx.assert_editor_state(
 7705        &"
 7706            fn main() {
 7707                sample(ˇ)
 7708            }
 7709        "
 7710        .unindent(),
 7711    );
 7712    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7713    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7714        .await;
 7715    cx.update_editor(|editor, _| {
 7716        let signature_help_state = editor.signature_help_state.popover().cloned();
 7717        assert!(signature_help_state.is_some());
 7718        let ParsedMarkdown {
 7719            text, highlights, ..
 7720        } = signature_help_state.unwrap().parsed_content;
 7721        assert_eq!(text, "param1: u8, param2: u8");
 7722        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7723        editor.signature_help_state = SignatureHelpState::default();
 7724    });
 7725
 7726    // Ensure that signature_help is called when auto signature help override is enabled
 7727    cx.update(|cx| {
 7728        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7729            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7730                settings.auto_signature_help = Some(true);
 7731                settings.show_signature_help_after_edits = Some(false);
 7732            });
 7733        });
 7734    });
 7735    cx.set_state(
 7736        &r#"
 7737            fn main() {
 7738                sampleˇ
 7739            }
 7740        "#
 7741        .unindent(),
 7742    );
 7743    cx.update_editor(|view, cx| {
 7744        view.handle_input("(", cx);
 7745    });
 7746    cx.assert_editor_state(
 7747        &"
 7748            fn main() {
 7749                sample(ˇ)
 7750            }
 7751        "
 7752        .unindent(),
 7753    );
 7754    handle_signature_help_request(&mut cx, mocked_response).await;
 7755    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7756        .await;
 7757    cx.editor(|editor, _| {
 7758        let signature_help_state = editor.signature_help_state.popover().cloned();
 7759        assert!(signature_help_state.is_some());
 7760        let ParsedMarkdown {
 7761            text, highlights, ..
 7762        } = signature_help_state.unwrap().parsed_content;
 7763        assert_eq!(text, "param1: u8, param2: u8");
 7764        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7765    });
 7766}
 7767
 7768#[gpui::test]
 7769async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7770    init_test(cx, |_| {});
 7771    cx.update(|cx| {
 7772        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7773            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7774                settings.auto_signature_help = Some(true);
 7775            });
 7776        });
 7777    });
 7778
 7779    let mut cx = EditorLspTestContext::new_rust(
 7780        lsp::ServerCapabilities {
 7781            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7782                ..Default::default()
 7783            }),
 7784            ..Default::default()
 7785        },
 7786        cx,
 7787    )
 7788    .await;
 7789
 7790    // A test that directly calls `show_signature_help`
 7791    cx.update_editor(|editor, cx| {
 7792        editor.show_signature_help(&ShowSignatureHelp, cx);
 7793    });
 7794
 7795    let mocked_response = lsp::SignatureHelp {
 7796        signatures: vec![lsp::SignatureInformation {
 7797            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7798            documentation: None,
 7799            parameters: Some(vec![
 7800                lsp::ParameterInformation {
 7801                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7802                    documentation: None,
 7803                },
 7804                lsp::ParameterInformation {
 7805                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7806                    documentation: None,
 7807                },
 7808            ]),
 7809            active_parameter: None,
 7810        }],
 7811        active_signature: Some(0),
 7812        active_parameter: Some(0),
 7813    };
 7814    handle_signature_help_request(&mut cx, mocked_response).await;
 7815
 7816    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7817        .await;
 7818
 7819    cx.editor(|editor, _| {
 7820        let signature_help_state = editor.signature_help_state.popover().cloned();
 7821        assert!(signature_help_state.is_some());
 7822        let ParsedMarkdown {
 7823            text, highlights, ..
 7824        } = signature_help_state.unwrap().parsed_content;
 7825        assert_eq!(text, "param1: u8, param2: u8");
 7826        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7827    });
 7828
 7829    // When exiting outside from inside the brackets, `signature_help` is closed.
 7830    cx.set_state(indoc! {"
 7831        fn main() {
 7832            sample(ˇ);
 7833        }
 7834
 7835        fn sample(param1: u8, param2: u8) {}
 7836    "});
 7837
 7838    cx.update_editor(|editor, cx| {
 7839        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7840    });
 7841
 7842    let mocked_response = lsp::SignatureHelp {
 7843        signatures: Vec::new(),
 7844        active_signature: None,
 7845        active_parameter: None,
 7846    };
 7847    handle_signature_help_request(&mut cx, mocked_response).await;
 7848
 7849    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7850        .await;
 7851
 7852    cx.editor(|editor, _| {
 7853        assert!(!editor.signature_help_state.is_shown());
 7854    });
 7855
 7856    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7857    cx.set_state(indoc! {"
 7858        fn main() {
 7859            sample(ˇ);
 7860        }
 7861
 7862        fn sample(param1: u8, param2: u8) {}
 7863    "});
 7864
 7865    let mocked_response = lsp::SignatureHelp {
 7866        signatures: vec![lsp::SignatureInformation {
 7867            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7868            documentation: None,
 7869            parameters: Some(vec![
 7870                lsp::ParameterInformation {
 7871                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7872                    documentation: None,
 7873                },
 7874                lsp::ParameterInformation {
 7875                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7876                    documentation: None,
 7877                },
 7878            ]),
 7879            active_parameter: None,
 7880        }],
 7881        active_signature: Some(0),
 7882        active_parameter: Some(0),
 7883    };
 7884    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7885    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7886        .await;
 7887    cx.editor(|editor, _| {
 7888        assert!(editor.signature_help_state.is_shown());
 7889    });
 7890
 7891    // Restore the popover with more parameter input
 7892    cx.set_state(indoc! {"
 7893        fn main() {
 7894            sample(param1, param2ˇ);
 7895        }
 7896
 7897        fn sample(param1: u8, param2: u8) {}
 7898    "});
 7899
 7900    let mocked_response = lsp::SignatureHelp {
 7901        signatures: vec![lsp::SignatureInformation {
 7902            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7903            documentation: None,
 7904            parameters: Some(vec![
 7905                lsp::ParameterInformation {
 7906                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7907                    documentation: None,
 7908                },
 7909                lsp::ParameterInformation {
 7910                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7911                    documentation: None,
 7912                },
 7913            ]),
 7914            active_parameter: None,
 7915        }],
 7916        active_signature: Some(0),
 7917        active_parameter: Some(1),
 7918    };
 7919    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7920    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7921        .await;
 7922
 7923    // When selecting a range, the popover is gone.
 7924    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7925    cx.update_editor(|editor, cx| {
 7926        editor.change_selections(None, cx, |s| {
 7927            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7928        })
 7929    });
 7930    cx.assert_editor_state(indoc! {"
 7931        fn main() {
 7932            sample(param1, «ˇparam2»);
 7933        }
 7934
 7935        fn sample(param1: u8, param2: u8) {}
 7936    "});
 7937    cx.editor(|editor, _| {
 7938        assert!(!editor.signature_help_state.is_shown());
 7939    });
 7940
 7941    // When unselecting again, the popover is back if within the brackets.
 7942    cx.update_editor(|editor, cx| {
 7943        editor.change_selections(None, cx, |s| {
 7944            s.select_ranges(Some(Point::new(1, 19)..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    handle_signature_help_request(&mut cx, mocked_response).await;
 7955    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7956        .await;
 7957    cx.editor(|editor, _| {
 7958        assert!(editor.signature_help_state.is_shown());
 7959    });
 7960
 7961    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7962    cx.update_editor(|editor, cx| {
 7963        editor.change_selections(None, cx, |s| {
 7964            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7965            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7966        })
 7967    });
 7968    cx.assert_editor_state(indoc! {"
 7969        fn main() {
 7970            sample(param1, ˇparam2);
 7971        }
 7972
 7973        fn sample(param1: u8, param2: u8) {}
 7974    "});
 7975
 7976    let mocked_response = lsp::SignatureHelp {
 7977        signatures: vec![lsp::SignatureInformation {
 7978            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7979            documentation: None,
 7980            parameters: Some(vec![
 7981                lsp::ParameterInformation {
 7982                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7983                    documentation: None,
 7984                },
 7985                lsp::ParameterInformation {
 7986                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7987                    documentation: None,
 7988                },
 7989            ]),
 7990            active_parameter: None,
 7991        }],
 7992        active_signature: Some(0),
 7993        active_parameter: Some(1),
 7994    };
 7995    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7996    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7997        .await;
 7998    cx.update_editor(|editor, cx| {
 7999        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8000    });
 8001    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8002        .await;
 8003    cx.update_editor(|editor, cx| {
 8004        editor.change_selections(None, cx, |s| {
 8005            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8006        })
 8007    });
 8008    cx.assert_editor_state(indoc! {"
 8009        fn main() {
 8010            sample(param1, «ˇparam2»);
 8011        }
 8012
 8013        fn sample(param1: u8, param2: u8) {}
 8014    "});
 8015    cx.update_editor(|editor, cx| {
 8016        editor.change_selections(None, cx, |s| {
 8017            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8018        })
 8019    });
 8020    cx.assert_editor_state(indoc! {"
 8021        fn main() {
 8022            sample(param1, ˇparam2);
 8023        }
 8024
 8025        fn sample(param1: u8, param2: u8) {}
 8026    "});
 8027    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8028        .await;
 8029}
 8030
 8031#[gpui::test]
 8032async fn test_completion(cx: &mut gpui::TestAppContext) {
 8033    init_test(cx, |_| {});
 8034
 8035    let mut cx = EditorLspTestContext::new_rust(
 8036        lsp::ServerCapabilities {
 8037            completion_provider: Some(lsp::CompletionOptions {
 8038                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8039                resolve_provider: Some(true),
 8040                ..Default::default()
 8041            }),
 8042            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8043            ..Default::default()
 8044        },
 8045        cx,
 8046    )
 8047    .await;
 8048    let counter = Arc::new(AtomicUsize::new(0));
 8049
 8050    cx.set_state(indoc! {"
 8051        oneˇ
 8052        two
 8053        three
 8054    "});
 8055    cx.simulate_keystroke(".");
 8056    handle_completion_request(
 8057        &mut cx,
 8058        indoc! {"
 8059            one.|<>
 8060            two
 8061            three
 8062        "},
 8063        vec!["first_completion", "second_completion"],
 8064        counter.clone(),
 8065    )
 8066    .await;
 8067    cx.condition(|editor, _| editor.context_menu_visible())
 8068        .await;
 8069    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8070
 8071    let _handler = handle_signature_help_request(
 8072        &mut cx,
 8073        lsp::SignatureHelp {
 8074            signatures: vec![lsp::SignatureInformation {
 8075                label: "test signature".to_string(),
 8076                documentation: None,
 8077                parameters: Some(vec![lsp::ParameterInformation {
 8078                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8079                    documentation: None,
 8080                }]),
 8081                active_parameter: None,
 8082            }],
 8083            active_signature: None,
 8084            active_parameter: None,
 8085        },
 8086    );
 8087    cx.update_editor(|editor, cx| {
 8088        assert!(
 8089            !editor.signature_help_state.is_shown(),
 8090            "No signature help was called for"
 8091        );
 8092        editor.show_signature_help(&ShowSignatureHelp, cx);
 8093    });
 8094    cx.run_until_parked();
 8095    cx.update_editor(|editor, _| {
 8096        assert!(
 8097            !editor.signature_help_state.is_shown(),
 8098            "No signature help should be shown when completions menu is open"
 8099        );
 8100    });
 8101
 8102    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8103        editor.context_menu_next(&Default::default(), cx);
 8104        editor
 8105            .confirm_completion(&ConfirmCompletion::default(), cx)
 8106            .unwrap()
 8107    });
 8108    cx.assert_editor_state(indoc! {"
 8109        one.second_completionˇ
 8110        two
 8111        three
 8112    "});
 8113
 8114    handle_resolve_completion_request(
 8115        &mut cx,
 8116        Some(vec![
 8117            (
 8118                //This overlaps with the primary completion edit which is
 8119                //misbehavior from the LSP spec, test that we filter it out
 8120                indoc! {"
 8121                    one.second_ˇcompletion
 8122                    two
 8123                    threeˇ
 8124                "},
 8125                "overlapping additional edit",
 8126            ),
 8127            (
 8128                indoc! {"
 8129                    one.second_completion
 8130                    two
 8131                    threeˇ
 8132                "},
 8133                "\nadditional edit",
 8134            ),
 8135        ]),
 8136    )
 8137    .await;
 8138    apply_additional_edits.await.unwrap();
 8139    cx.assert_editor_state(indoc! {"
 8140        one.second_completionˇ
 8141        two
 8142        three
 8143        additional edit
 8144    "});
 8145
 8146    cx.set_state(indoc! {"
 8147        one.second_completion
 8148        twoˇ
 8149        threeˇ
 8150        additional edit
 8151    "});
 8152    cx.simulate_keystroke(" ");
 8153    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8154    cx.simulate_keystroke("s");
 8155    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8156
 8157    cx.assert_editor_state(indoc! {"
 8158        one.second_completion
 8159        two sˇ
 8160        three sˇ
 8161        additional edit
 8162    "});
 8163    handle_completion_request(
 8164        &mut cx,
 8165        indoc! {"
 8166            one.second_completion
 8167            two s
 8168            three <s|>
 8169            additional edit
 8170        "},
 8171        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8172        counter.clone(),
 8173    )
 8174    .await;
 8175    cx.condition(|editor, _| editor.context_menu_visible())
 8176        .await;
 8177    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8178
 8179    cx.simulate_keystroke("i");
 8180
 8181    handle_completion_request(
 8182        &mut cx,
 8183        indoc! {"
 8184            one.second_completion
 8185            two si
 8186            three <si|>
 8187            additional edit
 8188        "},
 8189        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8190        counter.clone(),
 8191    )
 8192    .await;
 8193    cx.condition(|editor, _| editor.context_menu_visible())
 8194        .await;
 8195    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8196
 8197    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8198        editor
 8199            .confirm_completion(&ConfirmCompletion::default(), cx)
 8200            .unwrap()
 8201    });
 8202    cx.assert_editor_state(indoc! {"
 8203        one.second_completion
 8204        two sixth_completionˇ
 8205        three sixth_completionˇ
 8206        additional edit
 8207    "});
 8208
 8209    handle_resolve_completion_request(&mut cx, None).await;
 8210    apply_additional_edits.await.unwrap();
 8211
 8212    cx.update(|cx| {
 8213        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8214            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8215                settings.show_completions_on_input = Some(false);
 8216            });
 8217        })
 8218    });
 8219    cx.set_state("editorˇ");
 8220    cx.simulate_keystroke(".");
 8221    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8222    cx.simulate_keystroke("c");
 8223    cx.simulate_keystroke("l");
 8224    cx.simulate_keystroke("o");
 8225    cx.assert_editor_state("editor.cloˇ");
 8226    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8227    cx.update_editor(|editor, cx| {
 8228        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8229    });
 8230    handle_completion_request(
 8231        &mut cx,
 8232        "editor.<clo|>",
 8233        vec!["close", "clobber"],
 8234        counter.clone(),
 8235    )
 8236    .await;
 8237    cx.condition(|editor, _| editor.context_menu_visible())
 8238        .await;
 8239    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8240
 8241    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8242        editor
 8243            .confirm_completion(&ConfirmCompletion::default(), cx)
 8244            .unwrap()
 8245    });
 8246    cx.assert_editor_state("editor.closeˇ");
 8247    handle_resolve_completion_request(&mut cx, None).await;
 8248    apply_additional_edits.await.unwrap();
 8249}
 8250
 8251#[gpui::test]
 8252async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8253    init_test(cx, |_| {});
 8254    let mut cx = EditorLspTestContext::new_rust(
 8255        lsp::ServerCapabilities {
 8256            completion_provider: Some(lsp::CompletionOptions {
 8257                trigger_characters: Some(vec![".".to_string()]),
 8258                ..Default::default()
 8259            }),
 8260            ..Default::default()
 8261        },
 8262        cx,
 8263    )
 8264    .await;
 8265    cx.lsp
 8266        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8267            Ok(Some(lsp::CompletionResponse::Array(vec![
 8268                lsp::CompletionItem {
 8269                    label: "first".into(),
 8270                    ..Default::default()
 8271                },
 8272                lsp::CompletionItem {
 8273                    label: "last".into(),
 8274                    ..Default::default()
 8275                },
 8276            ])))
 8277        });
 8278    cx.set_state("variableˇ");
 8279    cx.simulate_keystroke(".");
 8280    cx.executor().run_until_parked();
 8281
 8282    cx.update_editor(|editor, _| {
 8283        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8284            assert_eq!(
 8285                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8286                &["first", "last"]
 8287            );
 8288        } else {
 8289            panic!("expected completion menu to be open");
 8290        }
 8291    });
 8292
 8293    cx.update_editor(|editor, cx| {
 8294        editor.move_page_down(&MovePageDown::default(), cx);
 8295        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8296            assert!(
 8297                menu.selected_item == 1,
 8298                "expected PageDown to select the last item from the context menu"
 8299            );
 8300        } else {
 8301            panic!("expected completion menu to stay open after PageDown");
 8302        }
 8303    });
 8304
 8305    cx.update_editor(|editor, cx| {
 8306        editor.move_page_up(&MovePageUp::default(), cx);
 8307        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8308            assert!(
 8309                menu.selected_item == 0,
 8310                "expected PageUp to select the first item from the context menu"
 8311            );
 8312        } else {
 8313            panic!("expected completion menu to stay open after PageUp");
 8314        }
 8315    });
 8316}
 8317
 8318#[gpui::test]
 8319async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8320    init_test(cx, |_| {});
 8321
 8322    let mut cx = EditorLspTestContext::new_rust(
 8323        lsp::ServerCapabilities {
 8324            completion_provider: Some(lsp::CompletionOptions {
 8325                trigger_characters: Some(vec![".".to_string()]),
 8326                resolve_provider: Some(true),
 8327                ..Default::default()
 8328            }),
 8329            ..Default::default()
 8330        },
 8331        cx,
 8332    )
 8333    .await;
 8334
 8335    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8336    cx.simulate_keystroke(".");
 8337    let completion_item = lsp::CompletionItem {
 8338        label: "Some".into(),
 8339        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8340        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8341        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8342            kind: lsp::MarkupKind::Markdown,
 8343            value: "```rust\nSome(2)\n```".to_string(),
 8344        })),
 8345        deprecated: Some(false),
 8346        sort_text: Some("Some".to_string()),
 8347        filter_text: Some("Some".to_string()),
 8348        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8349        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8350            range: lsp::Range {
 8351                start: lsp::Position {
 8352                    line: 0,
 8353                    character: 22,
 8354                },
 8355                end: lsp::Position {
 8356                    line: 0,
 8357                    character: 22,
 8358                },
 8359            },
 8360            new_text: "Some(2)".to_string(),
 8361        })),
 8362        additional_text_edits: Some(vec![lsp::TextEdit {
 8363            range: lsp::Range {
 8364                start: lsp::Position {
 8365                    line: 0,
 8366                    character: 20,
 8367                },
 8368                end: lsp::Position {
 8369                    line: 0,
 8370                    character: 22,
 8371                },
 8372            },
 8373            new_text: "".to_string(),
 8374        }]),
 8375        ..Default::default()
 8376    };
 8377
 8378    let closure_completion_item = completion_item.clone();
 8379    let counter = Arc::new(AtomicUsize::new(0));
 8380    let counter_clone = counter.clone();
 8381    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8382        let task_completion_item = closure_completion_item.clone();
 8383        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8384        async move {
 8385            Ok(Some(lsp::CompletionResponse::Array(vec![
 8386                task_completion_item,
 8387            ])))
 8388        }
 8389    });
 8390
 8391    cx.condition(|editor, _| editor.context_menu_visible())
 8392        .await;
 8393    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8394    assert!(request.next().await.is_some());
 8395    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8396
 8397    cx.simulate_keystroke("S");
 8398    cx.simulate_keystroke("o");
 8399    cx.simulate_keystroke("m");
 8400    cx.condition(|editor, _| editor.context_menu_visible())
 8401        .await;
 8402    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8403    assert!(request.next().await.is_some());
 8404    assert!(request.next().await.is_some());
 8405    assert!(request.next().await.is_some());
 8406    request.close();
 8407    assert!(request.next().await.is_none());
 8408    assert_eq!(
 8409        counter.load(atomic::Ordering::Acquire),
 8410        4,
 8411        "With the completions menu open, only one LSP request should happen per input"
 8412    );
 8413}
 8414
 8415#[gpui::test]
 8416async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8417    init_test(cx, |_| {});
 8418    let mut cx = EditorTestContext::new(cx).await;
 8419    let language = Arc::new(Language::new(
 8420        LanguageConfig {
 8421            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8422            ..Default::default()
 8423        },
 8424        Some(tree_sitter_rust::LANGUAGE.into()),
 8425    ));
 8426    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8427
 8428    // If multiple selections intersect a line, the line is only toggled once.
 8429    cx.set_state(indoc! {"
 8430        fn a() {
 8431            «//b();
 8432            ˇ»// «c();
 8433            //ˇ»  d();
 8434        }
 8435    "});
 8436
 8437    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8438
 8439    cx.assert_editor_state(indoc! {"
 8440        fn a() {
 8441            «b();
 8442            c();
 8443            ˇ» d();
 8444        }
 8445    "});
 8446
 8447    // The comment prefix is inserted at the same column for every line in a
 8448    // selection.
 8449    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8450
 8451    cx.assert_editor_state(indoc! {"
 8452        fn a() {
 8453            // «b();
 8454            // c();
 8455            ˇ»//  d();
 8456        }
 8457    "});
 8458
 8459    // If a selection ends at the beginning of a line, that line is not toggled.
 8460    cx.set_selections_state(indoc! {"
 8461        fn a() {
 8462            // b();
 8463            «// c();
 8464        ˇ»    //  d();
 8465        }
 8466    "});
 8467
 8468    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8469
 8470    cx.assert_editor_state(indoc! {"
 8471        fn a() {
 8472            // b();
 8473            «c();
 8474        ˇ»    //  d();
 8475        }
 8476    "});
 8477
 8478    // If a selection span a single line and is empty, the line is toggled.
 8479    cx.set_state(indoc! {"
 8480        fn a() {
 8481            a();
 8482            b();
 8483        ˇ
 8484        }
 8485    "});
 8486
 8487    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8488
 8489    cx.assert_editor_state(indoc! {"
 8490        fn a() {
 8491            a();
 8492            b();
 8493        //•ˇ
 8494        }
 8495    "});
 8496
 8497    // If a selection span multiple lines, empty lines are not toggled.
 8498    cx.set_state(indoc! {"
 8499        fn a() {
 8500            «a();
 8501
 8502            c();ˇ»
 8503        }
 8504    "});
 8505
 8506    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8507
 8508    cx.assert_editor_state(indoc! {"
 8509        fn a() {
 8510            // «a();
 8511
 8512            // c();ˇ»
 8513        }
 8514    "});
 8515
 8516    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8517    cx.set_state(indoc! {"
 8518        fn a() {
 8519            «// a();
 8520            /// b();
 8521            //! c();ˇ»
 8522        }
 8523    "});
 8524
 8525    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8526
 8527    cx.assert_editor_state(indoc! {"
 8528        fn a() {
 8529            «a();
 8530            b();
 8531            c();ˇ»
 8532        }
 8533    "});
 8534}
 8535
 8536#[gpui::test]
 8537async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 8538    init_test(cx, |_| {});
 8539    let mut cx = EditorTestContext::new(cx).await;
 8540    let language = Arc::new(Language::new(
 8541        LanguageConfig {
 8542            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8543            ..Default::default()
 8544        },
 8545        Some(tree_sitter_rust::LANGUAGE.into()),
 8546    ));
 8547    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8548
 8549    let toggle_comments = &ToggleComments {
 8550        advance_downwards: false,
 8551        ignore_indent: true,
 8552    };
 8553
 8554    // If multiple selections intersect a line, the line is only toggled once.
 8555    cx.set_state(indoc! {"
 8556        fn a() {
 8557        //    «b();
 8558        //    c();
 8559        //    ˇ» d();
 8560        }
 8561    "});
 8562
 8563    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8564
 8565    cx.assert_editor_state(indoc! {"
 8566        fn a() {
 8567            «b();
 8568            c();
 8569            ˇ» d();
 8570        }
 8571    "});
 8572
 8573    // The comment prefix is inserted at the beginning of each line
 8574    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8575
 8576    cx.assert_editor_state(indoc! {"
 8577        fn a() {
 8578        //    «b();
 8579        //    c();
 8580        //    ˇ» d();
 8581        }
 8582    "});
 8583
 8584    // If a selection ends at the beginning of a line, that line is not toggled.
 8585    cx.set_selections_state(indoc! {"
 8586        fn a() {
 8587        //    b();
 8588        //    «c();
 8589        ˇ»//     d();
 8590        }
 8591    "});
 8592
 8593    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8594
 8595    cx.assert_editor_state(indoc! {"
 8596        fn a() {
 8597        //    b();
 8598            «c();
 8599        ˇ»//     d();
 8600        }
 8601    "});
 8602
 8603    // If a selection span a single line and is empty, the line is toggled.
 8604    cx.set_state(indoc! {"
 8605        fn a() {
 8606            a();
 8607            b();
 8608        ˇ
 8609        }
 8610    "});
 8611
 8612    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8613
 8614    cx.assert_editor_state(indoc! {"
 8615        fn a() {
 8616            a();
 8617            b();
 8618        //ˇ
 8619        }
 8620    "});
 8621
 8622    // If a selection span multiple lines, empty lines are not toggled.
 8623    cx.set_state(indoc! {"
 8624        fn a() {
 8625            «a();
 8626
 8627            c();ˇ»
 8628        }
 8629    "});
 8630
 8631    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8632
 8633    cx.assert_editor_state(indoc! {"
 8634        fn a() {
 8635        //    «a();
 8636
 8637        //    c();ˇ»
 8638        }
 8639    "});
 8640
 8641    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8642    cx.set_state(indoc! {"
 8643        fn a() {
 8644        //    «a();
 8645        ///    b();
 8646        //!    c();ˇ»
 8647        }
 8648    "});
 8649
 8650    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 8651
 8652    cx.assert_editor_state(indoc! {"
 8653        fn a() {
 8654            «a();
 8655            b();
 8656            c();ˇ»
 8657        }
 8658    "});
 8659}
 8660
 8661#[gpui::test]
 8662async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8663    init_test(cx, |_| {});
 8664
 8665    let language = Arc::new(Language::new(
 8666        LanguageConfig {
 8667            line_comments: vec!["// ".into()],
 8668            ..Default::default()
 8669        },
 8670        Some(tree_sitter_rust::LANGUAGE.into()),
 8671    ));
 8672
 8673    let mut cx = EditorTestContext::new(cx).await;
 8674
 8675    cx.language_registry().add(language.clone());
 8676    cx.update_buffer(|buffer, cx| {
 8677        buffer.set_language(Some(language), cx);
 8678    });
 8679
 8680    let toggle_comments = &ToggleComments {
 8681        advance_downwards: true,
 8682        ignore_indent: false,
 8683    };
 8684
 8685    // Single cursor on one line -> advance
 8686    // Cursor moves horizontally 3 characters as well on non-blank line
 8687    cx.set_state(indoc!(
 8688        "fn a() {
 8689             ˇdog();
 8690             cat();
 8691        }"
 8692    ));
 8693    cx.update_editor(|editor, cx| {
 8694        editor.toggle_comments(toggle_comments, cx);
 8695    });
 8696    cx.assert_editor_state(indoc!(
 8697        "fn a() {
 8698             // dog();
 8699             catˇ();
 8700        }"
 8701    ));
 8702
 8703    // Single selection on one line -> don't advance
 8704    cx.set_state(indoc!(
 8705        "fn a() {
 8706             «dog()ˇ»;
 8707             cat();
 8708        }"
 8709    ));
 8710    cx.update_editor(|editor, cx| {
 8711        editor.toggle_comments(toggle_comments, cx);
 8712    });
 8713    cx.assert_editor_state(indoc!(
 8714        "fn a() {
 8715             // «dog()ˇ»;
 8716             cat();
 8717        }"
 8718    ));
 8719
 8720    // Multiple cursors on one line -> advance
 8721    cx.set_state(indoc!(
 8722        "fn a() {
 8723             ˇdˇog();
 8724             cat();
 8725        }"
 8726    ));
 8727    cx.update_editor(|editor, cx| {
 8728        editor.toggle_comments(toggle_comments, cx);
 8729    });
 8730    cx.assert_editor_state(indoc!(
 8731        "fn a() {
 8732             // dog();
 8733             catˇ(ˇ);
 8734        }"
 8735    ));
 8736
 8737    // Multiple cursors on one line, with selection -> don't advance
 8738    cx.set_state(indoc!(
 8739        "fn a() {
 8740             ˇdˇog«()ˇ»;
 8741             cat();
 8742        }"
 8743    ));
 8744    cx.update_editor(|editor, cx| {
 8745        editor.toggle_comments(toggle_comments, cx);
 8746    });
 8747    cx.assert_editor_state(indoc!(
 8748        "fn a() {
 8749             // ˇdˇog«()ˇ»;
 8750             cat();
 8751        }"
 8752    ));
 8753
 8754    // Single cursor on one line -> advance
 8755    // Cursor moves to column 0 on blank line
 8756    cx.set_state(indoc!(
 8757        "fn a() {
 8758             ˇdog();
 8759
 8760             cat();
 8761        }"
 8762    ));
 8763    cx.update_editor(|editor, cx| {
 8764        editor.toggle_comments(toggle_comments, cx);
 8765    });
 8766    cx.assert_editor_state(indoc!(
 8767        "fn a() {
 8768             // dog();
 8769        ˇ
 8770             cat();
 8771        }"
 8772    ));
 8773
 8774    // Single cursor on one line -> advance
 8775    // Cursor starts and ends at column 0
 8776    cx.set_state(indoc!(
 8777        "fn a() {
 8778         ˇ    dog();
 8779             cat();
 8780        }"
 8781    ));
 8782    cx.update_editor(|editor, cx| {
 8783        editor.toggle_comments(toggle_comments, cx);
 8784    });
 8785    cx.assert_editor_state(indoc!(
 8786        "fn a() {
 8787             // dog();
 8788         ˇ    cat();
 8789        }"
 8790    ));
 8791}
 8792
 8793#[gpui::test]
 8794async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8795    init_test(cx, |_| {});
 8796
 8797    let mut cx = EditorTestContext::new(cx).await;
 8798
 8799    let html_language = Arc::new(
 8800        Language::new(
 8801            LanguageConfig {
 8802                name: "HTML".into(),
 8803                block_comment: Some(("<!-- ".into(), " -->".into())),
 8804                ..Default::default()
 8805            },
 8806            Some(tree_sitter_html::language()),
 8807        )
 8808        .with_injection_query(
 8809            r#"
 8810            (script_element
 8811                (raw_text) @content
 8812                (#set! "language" "javascript"))
 8813            "#,
 8814        )
 8815        .unwrap(),
 8816    );
 8817
 8818    let javascript_language = Arc::new(Language::new(
 8819        LanguageConfig {
 8820            name: "JavaScript".into(),
 8821            line_comments: vec!["// ".into()],
 8822            ..Default::default()
 8823        },
 8824        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8825    ));
 8826
 8827    cx.language_registry().add(html_language.clone());
 8828    cx.language_registry().add(javascript_language.clone());
 8829    cx.update_buffer(|buffer, cx| {
 8830        buffer.set_language(Some(html_language), cx);
 8831    });
 8832
 8833    // Toggle comments for empty selections
 8834    cx.set_state(
 8835        &r#"
 8836            <p>A</p>ˇ
 8837            <p>B</p>ˇ
 8838            <p>C</p>ˇ
 8839        "#
 8840        .unindent(),
 8841    );
 8842    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8843    cx.assert_editor_state(
 8844        &r#"
 8845            <!-- <p>A</p>ˇ -->
 8846            <!-- <p>B</p>ˇ -->
 8847            <!-- <p>C</p>ˇ -->
 8848        "#
 8849        .unindent(),
 8850    );
 8851    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8852    cx.assert_editor_state(
 8853        &r#"
 8854            <p>A</p>ˇ
 8855            <p>B</p>ˇ
 8856            <p>C</p>ˇ
 8857        "#
 8858        .unindent(),
 8859    );
 8860
 8861    // Toggle comments for mixture of empty and non-empty selections, where
 8862    // multiple selections occupy a given line.
 8863    cx.set_state(
 8864        &r#"
 8865            <p>A«</p>
 8866            <p>ˇ»B</p>ˇ
 8867            <p>C«</p>
 8868            <p>ˇ»D</p>ˇ
 8869        "#
 8870        .unindent(),
 8871    );
 8872
 8873    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8874    cx.assert_editor_state(
 8875        &r#"
 8876            <!-- <p>A«</p>
 8877            <p>ˇ»B</p>ˇ -->
 8878            <!-- <p>C«</p>
 8879            <p>ˇ»D</p>ˇ -->
 8880        "#
 8881        .unindent(),
 8882    );
 8883    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8884    cx.assert_editor_state(
 8885        &r#"
 8886            <p>A«</p>
 8887            <p>ˇ»B</p>ˇ
 8888            <p>C«</p>
 8889            <p>ˇ»D</p>ˇ
 8890        "#
 8891        .unindent(),
 8892    );
 8893
 8894    // Toggle comments when different languages are active for different
 8895    // selections.
 8896    cx.set_state(
 8897        &r#"
 8898            ˇ<script>
 8899                ˇvar x = new Y();
 8900            ˇ</script>
 8901        "#
 8902        .unindent(),
 8903    );
 8904    cx.executor().run_until_parked();
 8905    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8906    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8907    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8908    cx.assert_editor_state(
 8909        &r#"
 8910            <!-- ˇ<script> -->
 8911                // ˇvar x = new Y();
 8912            // ˇ</script>
 8913        "#
 8914        .unindent(),
 8915    );
 8916}
 8917
 8918#[gpui::test]
 8919fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8920    init_test(cx, |_| {});
 8921
 8922    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8923    let multibuffer = cx.new_model(|cx| {
 8924        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8925        multibuffer.push_excerpts(
 8926            buffer.clone(),
 8927            [
 8928                ExcerptRange {
 8929                    context: Point::new(0, 0)..Point::new(0, 4),
 8930                    primary: None,
 8931                },
 8932                ExcerptRange {
 8933                    context: Point::new(1, 0)..Point::new(1, 4),
 8934                    primary: None,
 8935                },
 8936            ],
 8937            cx,
 8938        );
 8939        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8940        multibuffer
 8941    });
 8942
 8943    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8944    view.update(cx, |view, cx| {
 8945        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8946        view.change_selections(None, cx, |s| {
 8947            s.select_ranges([
 8948                Point::new(0, 0)..Point::new(0, 0),
 8949                Point::new(1, 0)..Point::new(1, 0),
 8950            ])
 8951        });
 8952
 8953        view.handle_input("X", cx);
 8954        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8955        assert_eq!(
 8956            view.selections.ranges(cx),
 8957            [
 8958                Point::new(0, 1)..Point::new(0, 1),
 8959                Point::new(1, 1)..Point::new(1, 1),
 8960            ]
 8961        );
 8962
 8963        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8964        view.change_selections(None, cx, |s| {
 8965            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8966        });
 8967        view.backspace(&Default::default(), cx);
 8968        assert_eq!(view.text(cx), "Xa\nbbb");
 8969        assert_eq!(
 8970            view.selections.ranges(cx),
 8971            [Point::new(1, 0)..Point::new(1, 0)]
 8972        );
 8973
 8974        view.change_selections(None, cx, |s| {
 8975            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8976        });
 8977        view.backspace(&Default::default(), cx);
 8978        assert_eq!(view.text(cx), "X\nbb");
 8979        assert_eq!(
 8980            view.selections.ranges(cx),
 8981            [Point::new(0, 1)..Point::new(0, 1)]
 8982        );
 8983    });
 8984}
 8985
 8986#[gpui::test]
 8987fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8988    init_test(cx, |_| {});
 8989
 8990    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8991    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8992        indoc! {"
 8993            [aaaa
 8994            (bbbb]
 8995            cccc)",
 8996        },
 8997        markers.clone(),
 8998    );
 8999    let excerpt_ranges = markers.into_iter().map(|marker| {
 9000        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9001        ExcerptRange {
 9002            context,
 9003            primary: None,
 9004        }
 9005    });
 9006    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9007    let multibuffer = cx.new_model(|cx| {
 9008        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9009        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9010        multibuffer
 9011    });
 9012
 9013    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9014    view.update(cx, |view, cx| {
 9015        let (expected_text, selection_ranges) = marked_text_ranges(
 9016            indoc! {"
 9017                aaaa
 9018                bˇbbb
 9019                bˇbbˇb
 9020                cccc"
 9021            },
 9022            true,
 9023        );
 9024        assert_eq!(view.text(cx), expected_text);
 9025        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9026
 9027        view.handle_input("X", cx);
 9028
 9029        let (expected_text, expected_selections) = marked_text_ranges(
 9030            indoc! {"
 9031                aaaa
 9032                bXˇbbXb
 9033                bXˇbbXˇb
 9034                cccc"
 9035            },
 9036            false,
 9037        );
 9038        assert_eq!(view.text(cx), expected_text);
 9039        assert_eq!(view.selections.ranges(cx), expected_selections);
 9040
 9041        view.newline(&Newline, cx);
 9042        let (expected_text, expected_selections) = marked_text_ranges(
 9043            indoc! {"
 9044                aaaa
 9045                bX
 9046                ˇbbX
 9047                b
 9048                bX
 9049                ˇbbX
 9050                ˇb
 9051                cccc"
 9052            },
 9053            false,
 9054        );
 9055        assert_eq!(view.text(cx), expected_text);
 9056        assert_eq!(view.selections.ranges(cx), expected_selections);
 9057    });
 9058}
 9059
 9060#[gpui::test]
 9061fn test_refresh_selections(cx: &mut TestAppContext) {
 9062    init_test(cx, |_| {});
 9063
 9064    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9065    let mut excerpt1_id = None;
 9066    let multibuffer = cx.new_model(|cx| {
 9067        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9068        excerpt1_id = multibuffer
 9069            .push_excerpts(
 9070                buffer.clone(),
 9071                [
 9072                    ExcerptRange {
 9073                        context: Point::new(0, 0)..Point::new(1, 4),
 9074                        primary: None,
 9075                    },
 9076                    ExcerptRange {
 9077                        context: Point::new(1, 0)..Point::new(2, 4),
 9078                        primary: None,
 9079                    },
 9080                ],
 9081                cx,
 9082            )
 9083            .into_iter()
 9084            .next();
 9085        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9086        multibuffer
 9087    });
 9088
 9089    let editor = cx.add_window(|cx| {
 9090        let mut editor = build_editor(multibuffer.clone(), cx);
 9091        let snapshot = editor.snapshot(cx);
 9092        editor.change_selections(None, cx, |s| {
 9093            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9094        });
 9095        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9096        assert_eq!(
 9097            editor.selections.ranges(cx),
 9098            [
 9099                Point::new(1, 3)..Point::new(1, 3),
 9100                Point::new(2, 1)..Point::new(2, 1),
 9101            ]
 9102        );
 9103        editor
 9104    });
 9105
 9106    // Refreshing selections is a no-op when excerpts haven't changed.
 9107    _ = editor.update(cx, |editor, cx| {
 9108        editor.change_selections(None, cx, |s| s.refresh());
 9109        assert_eq!(
 9110            editor.selections.ranges(cx),
 9111            [
 9112                Point::new(1, 3)..Point::new(1, 3),
 9113                Point::new(2, 1)..Point::new(2, 1),
 9114            ]
 9115        );
 9116    });
 9117
 9118    multibuffer.update(cx, |multibuffer, cx| {
 9119        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9120    });
 9121    _ = editor.update(cx, |editor, cx| {
 9122        // Removing an excerpt causes the first selection to become degenerate.
 9123        assert_eq!(
 9124            editor.selections.ranges(cx),
 9125            [
 9126                Point::new(0, 0)..Point::new(0, 0),
 9127                Point::new(0, 1)..Point::new(0, 1)
 9128            ]
 9129        );
 9130
 9131        // Refreshing selections will relocate the first selection to the original buffer
 9132        // location.
 9133        editor.change_selections(None, cx, |s| s.refresh());
 9134        assert_eq!(
 9135            editor.selections.ranges(cx),
 9136            [
 9137                Point::new(0, 1)..Point::new(0, 1),
 9138                Point::new(0, 3)..Point::new(0, 3)
 9139            ]
 9140        );
 9141        assert!(editor.selections.pending_anchor().is_some());
 9142    });
 9143}
 9144
 9145#[gpui::test]
 9146fn test_refresh_selections_while_selecting_with_mouse(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.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9178        assert_eq!(
 9179            editor.selections.ranges(cx),
 9180            [Point::new(1, 3)..Point::new(1, 3)]
 9181        );
 9182        editor
 9183    });
 9184
 9185    multibuffer.update(cx, |multibuffer, cx| {
 9186        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9187    });
 9188    _ = editor.update(cx, |editor, cx| {
 9189        assert_eq!(
 9190            editor.selections.ranges(cx),
 9191            [Point::new(0, 0)..Point::new(0, 0)]
 9192        );
 9193
 9194        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9195        editor.change_selections(None, cx, |s| s.refresh());
 9196        assert_eq!(
 9197            editor.selections.ranges(cx),
 9198            [Point::new(0, 3)..Point::new(0, 3)]
 9199        );
 9200        assert!(editor.selections.pending_anchor().is_some());
 9201    });
 9202}
 9203
 9204#[gpui::test]
 9205async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9206    init_test(cx, |_| {});
 9207
 9208    let language = Arc::new(
 9209        Language::new(
 9210            LanguageConfig {
 9211                brackets: BracketPairConfig {
 9212                    pairs: vec![
 9213                        BracketPair {
 9214                            start: "{".to_string(),
 9215                            end: "}".to_string(),
 9216                            close: true,
 9217                            surround: true,
 9218                            newline: true,
 9219                        },
 9220                        BracketPair {
 9221                            start: "/* ".to_string(),
 9222                            end: " */".to_string(),
 9223                            close: true,
 9224                            surround: true,
 9225                            newline: true,
 9226                        },
 9227                    ],
 9228                    ..Default::default()
 9229                },
 9230                ..Default::default()
 9231            },
 9232            Some(tree_sitter_rust::LANGUAGE.into()),
 9233        )
 9234        .with_indents_query("")
 9235        .unwrap(),
 9236    );
 9237
 9238    let text = concat!(
 9239        "{   }\n",     //
 9240        "  x\n",       //
 9241        "  /*   */\n", //
 9242        "x\n",         //
 9243        "{{} }\n",     //
 9244    );
 9245
 9246    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9247    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9248    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9249    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9250        .await;
 9251
 9252    view.update(cx, |view, cx| {
 9253        view.change_selections(None, cx, |s| {
 9254            s.select_display_ranges([
 9255                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9256                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9257                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9258            ])
 9259        });
 9260        view.newline(&Newline, cx);
 9261
 9262        assert_eq!(
 9263            view.buffer().read(cx).read(cx).text(),
 9264            concat!(
 9265                "{ \n",    // Suppress rustfmt
 9266                "\n",      //
 9267                "}\n",     //
 9268                "  x\n",   //
 9269                "  /* \n", //
 9270                "  \n",    //
 9271                "  */\n",  //
 9272                "x\n",     //
 9273                "{{} \n",  //
 9274                "}\n",     //
 9275            )
 9276        );
 9277    });
 9278}
 9279
 9280#[gpui::test]
 9281fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9282    init_test(cx, |_| {});
 9283
 9284    let editor = cx.add_window(|cx| {
 9285        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9286        build_editor(buffer.clone(), cx)
 9287    });
 9288
 9289    _ = editor.update(cx, |editor, cx| {
 9290        struct Type1;
 9291        struct Type2;
 9292
 9293        let buffer = editor.buffer.read(cx).snapshot(cx);
 9294
 9295        let anchor_range =
 9296            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9297
 9298        editor.highlight_background::<Type1>(
 9299            &[
 9300                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9301                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9302                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9303                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9304            ],
 9305            |_| Hsla::red(),
 9306            cx,
 9307        );
 9308        editor.highlight_background::<Type2>(
 9309            &[
 9310                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9311                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9312                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9313                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9314            ],
 9315            |_| Hsla::green(),
 9316            cx,
 9317        );
 9318
 9319        let snapshot = editor.snapshot(cx);
 9320        let mut highlighted_ranges = editor.background_highlights_in_range(
 9321            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9322            &snapshot,
 9323            cx.theme().colors(),
 9324        );
 9325        // Enforce a consistent ordering based on color without relying on the ordering of the
 9326        // highlight's `TypeId` which is non-executor.
 9327        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9328        assert_eq!(
 9329            highlighted_ranges,
 9330            &[
 9331                (
 9332                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9333                    Hsla::red(),
 9334                ),
 9335                (
 9336                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9337                    Hsla::red(),
 9338                ),
 9339                (
 9340                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9341                    Hsla::green(),
 9342                ),
 9343                (
 9344                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9345                    Hsla::green(),
 9346                ),
 9347            ]
 9348        );
 9349        assert_eq!(
 9350            editor.background_highlights_in_range(
 9351                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9352                &snapshot,
 9353                cx.theme().colors(),
 9354            ),
 9355            &[(
 9356                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9357                Hsla::red(),
 9358            )]
 9359        );
 9360    });
 9361}
 9362
 9363#[gpui::test]
 9364async fn test_following(cx: &mut gpui::TestAppContext) {
 9365    init_test(cx, |_| {});
 9366
 9367    let fs = FakeFs::new(cx.executor());
 9368    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9369
 9370    let buffer = project.update(cx, |project, cx| {
 9371        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9372        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9373    });
 9374    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9375    let follower = cx.update(|cx| {
 9376        cx.open_window(
 9377            WindowOptions {
 9378                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9379                    gpui::Point::new(px(0.), px(0.)),
 9380                    gpui::Point::new(px(10.), px(80.)),
 9381                ))),
 9382                ..Default::default()
 9383            },
 9384            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9385        )
 9386        .unwrap()
 9387    });
 9388
 9389    let is_still_following = Rc::new(RefCell::new(true));
 9390    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9391    let pending_update = Rc::new(RefCell::new(None));
 9392    _ = follower.update(cx, {
 9393        let update = pending_update.clone();
 9394        let is_still_following = is_still_following.clone();
 9395        let follower_edit_event_count = follower_edit_event_count.clone();
 9396        |_, cx| {
 9397            cx.subscribe(
 9398                &leader.root_view(cx).unwrap(),
 9399                move |_, leader, event, cx| {
 9400                    leader
 9401                        .read(cx)
 9402                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9403                },
 9404            )
 9405            .detach();
 9406
 9407            cx.subscribe(
 9408                &follower.root_view(cx).unwrap(),
 9409                move |_, _, event: &EditorEvent, _cx| {
 9410                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9411                        *is_still_following.borrow_mut() = false;
 9412                    }
 9413
 9414                    if let EditorEvent::BufferEdited = event {
 9415                        *follower_edit_event_count.borrow_mut() += 1;
 9416                    }
 9417                },
 9418            )
 9419            .detach();
 9420        }
 9421    });
 9422
 9423    // Update the selections only
 9424    _ = leader.update(cx, |leader, cx| {
 9425        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9426    });
 9427    follower
 9428        .update(cx, |follower, cx| {
 9429            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9430        })
 9431        .unwrap()
 9432        .await
 9433        .unwrap();
 9434    _ = follower.update(cx, |follower, cx| {
 9435        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9436    });
 9437    assert!(*is_still_following.borrow());
 9438    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9439
 9440    // Update the scroll position only
 9441    _ = leader.update(cx, |leader, cx| {
 9442        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9443    });
 9444    follower
 9445        .update(cx, |follower, cx| {
 9446            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9447        })
 9448        .unwrap()
 9449        .await
 9450        .unwrap();
 9451    assert_eq!(
 9452        follower
 9453            .update(cx, |follower, cx| follower.scroll_position(cx))
 9454            .unwrap(),
 9455        gpui::Point::new(1.5, 3.5)
 9456    );
 9457    assert!(*is_still_following.borrow());
 9458    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9459
 9460    // Update the selections and scroll position. The follower's scroll position is updated
 9461    // via autoscroll, not via the leader's exact scroll position.
 9462    _ = leader.update(cx, |leader, cx| {
 9463        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9464        leader.request_autoscroll(Autoscroll::newest(), cx);
 9465        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9466    });
 9467    follower
 9468        .update(cx, |follower, cx| {
 9469            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9470        })
 9471        .unwrap()
 9472        .await
 9473        .unwrap();
 9474    _ = follower.update(cx, |follower, cx| {
 9475        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9476        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9477    });
 9478    assert!(*is_still_following.borrow());
 9479
 9480    // Creating a pending selection that precedes another selection
 9481    _ = leader.update(cx, |leader, cx| {
 9482        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9483        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9484    });
 9485    follower
 9486        .update(cx, |follower, cx| {
 9487            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9488        })
 9489        .unwrap()
 9490        .await
 9491        .unwrap();
 9492    _ = follower.update(cx, |follower, cx| {
 9493        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9494    });
 9495    assert!(*is_still_following.borrow());
 9496
 9497    // Extend the pending selection so that it surrounds another selection
 9498    _ = leader.update(cx, |leader, cx| {
 9499        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9500    });
 9501    follower
 9502        .update(cx, |follower, cx| {
 9503            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9504        })
 9505        .unwrap()
 9506        .await
 9507        .unwrap();
 9508    _ = follower.update(cx, |follower, cx| {
 9509        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9510    });
 9511
 9512    // Scrolling locally breaks the follow
 9513    _ = follower.update(cx, |follower, cx| {
 9514        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9515        follower.set_scroll_anchor(
 9516            ScrollAnchor {
 9517                anchor: top_anchor,
 9518                offset: gpui::Point::new(0.0, 0.5),
 9519            },
 9520            cx,
 9521        );
 9522    });
 9523    assert!(!(*is_still_following.borrow()));
 9524}
 9525
 9526#[gpui::test]
 9527async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9528    init_test(cx, |_| {});
 9529
 9530    let fs = FakeFs::new(cx.executor());
 9531    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9532    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9533    let pane = workspace
 9534        .update(cx, |workspace, _| workspace.active_pane().clone())
 9535        .unwrap();
 9536
 9537    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9538
 9539    let leader = pane.update(cx, |_, cx| {
 9540        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9541        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9542    });
 9543
 9544    // Start following the editor when it has no excerpts.
 9545    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9546    let follower_1 = cx
 9547        .update_window(*workspace.deref(), |_, cx| {
 9548            Editor::from_state_proto(
 9549                workspace.root_view(cx).unwrap(),
 9550                ViewId {
 9551                    creator: Default::default(),
 9552                    id: 0,
 9553                },
 9554                &mut state_message,
 9555                cx,
 9556            )
 9557        })
 9558        .unwrap()
 9559        .unwrap()
 9560        .await
 9561        .unwrap();
 9562
 9563    let update_message = Rc::new(RefCell::new(None));
 9564    follower_1.update(cx, {
 9565        let update = update_message.clone();
 9566        |_, cx| {
 9567            cx.subscribe(&leader, move |_, leader, event, cx| {
 9568                leader
 9569                    .read(cx)
 9570                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9571            })
 9572            .detach();
 9573        }
 9574    });
 9575
 9576    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9577        (
 9578            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9579            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9580        )
 9581    });
 9582
 9583    // Insert some excerpts.
 9584    leader.update(cx, |leader, cx| {
 9585        leader.buffer.update(cx, |multibuffer, cx| {
 9586            let excerpt_ids = multibuffer.push_excerpts(
 9587                buffer_1.clone(),
 9588                [
 9589                    ExcerptRange {
 9590                        context: 1..6,
 9591                        primary: None,
 9592                    },
 9593                    ExcerptRange {
 9594                        context: 12..15,
 9595                        primary: None,
 9596                    },
 9597                    ExcerptRange {
 9598                        context: 0..3,
 9599                        primary: None,
 9600                    },
 9601                ],
 9602                cx,
 9603            );
 9604            multibuffer.insert_excerpts_after(
 9605                excerpt_ids[0],
 9606                buffer_2.clone(),
 9607                [
 9608                    ExcerptRange {
 9609                        context: 8..12,
 9610                        primary: None,
 9611                    },
 9612                    ExcerptRange {
 9613                        context: 0..6,
 9614                        primary: None,
 9615                    },
 9616                ],
 9617                cx,
 9618            );
 9619        });
 9620    });
 9621
 9622    // Apply the update of adding the excerpts.
 9623    follower_1
 9624        .update(cx, |follower, cx| {
 9625            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9626        })
 9627        .await
 9628        .unwrap();
 9629    assert_eq!(
 9630        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9631        leader.update(cx, |editor, cx| editor.text(cx))
 9632    );
 9633    update_message.borrow_mut().take();
 9634
 9635    // Start following separately after it already has excerpts.
 9636    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9637    let follower_2 = cx
 9638        .update_window(*workspace.deref(), |_, cx| {
 9639            Editor::from_state_proto(
 9640                workspace.root_view(cx).unwrap().clone(),
 9641                ViewId {
 9642                    creator: Default::default(),
 9643                    id: 0,
 9644                },
 9645                &mut state_message,
 9646                cx,
 9647            )
 9648        })
 9649        .unwrap()
 9650        .unwrap()
 9651        .await
 9652        .unwrap();
 9653    assert_eq!(
 9654        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9655        leader.update(cx, |editor, cx| editor.text(cx))
 9656    );
 9657
 9658    // Remove some excerpts.
 9659    leader.update(cx, |leader, cx| {
 9660        leader.buffer.update(cx, |multibuffer, cx| {
 9661            let excerpt_ids = multibuffer.excerpt_ids();
 9662            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9663            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9664        });
 9665    });
 9666
 9667    // Apply the update of removing the excerpts.
 9668    follower_1
 9669        .update(cx, |follower, cx| {
 9670            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9671        })
 9672        .await
 9673        .unwrap();
 9674    follower_2
 9675        .update(cx, |follower, cx| {
 9676            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9677        })
 9678        .await
 9679        .unwrap();
 9680    update_message.borrow_mut().take();
 9681    assert_eq!(
 9682        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9683        leader.update(cx, |editor, cx| editor.text(cx))
 9684    );
 9685}
 9686
 9687#[gpui::test]
 9688async fn go_to_prev_overlapping_diagnostic(
 9689    executor: BackgroundExecutor,
 9690    cx: &mut gpui::TestAppContext,
 9691) {
 9692    init_test(cx, |_| {});
 9693
 9694    let mut cx = EditorTestContext::new(cx).await;
 9695    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9696
 9697    cx.set_state(indoc! {"
 9698        ˇfn func(abc def: i32) -> u32 {
 9699        }
 9700    "});
 9701
 9702    cx.update(|cx| {
 9703        project.update(cx, |project, cx| {
 9704            project
 9705                .update_diagnostics(
 9706                    LanguageServerId(0),
 9707                    lsp::PublishDiagnosticsParams {
 9708                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9709                        version: None,
 9710                        diagnostics: vec![
 9711                            lsp::Diagnostic {
 9712                                range: lsp::Range::new(
 9713                                    lsp::Position::new(0, 11),
 9714                                    lsp::Position::new(0, 12),
 9715                                ),
 9716                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9717                                ..Default::default()
 9718                            },
 9719                            lsp::Diagnostic {
 9720                                range: lsp::Range::new(
 9721                                    lsp::Position::new(0, 12),
 9722                                    lsp::Position::new(0, 15),
 9723                                ),
 9724                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9725                                ..Default::default()
 9726                            },
 9727                            lsp::Diagnostic {
 9728                                range: lsp::Range::new(
 9729                                    lsp::Position::new(0, 25),
 9730                                    lsp::Position::new(0, 28),
 9731                                ),
 9732                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9733                                ..Default::default()
 9734                            },
 9735                        ],
 9736                    },
 9737                    &[],
 9738                    cx,
 9739                )
 9740                .unwrap()
 9741        });
 9742    });
 9743
 9744    executor.run_until_parked();
 9745
 9746    cx.update_editor(|editor, cx| {
 9747        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9748    });
 9749
 9750    cx.assert_editor_state(indoc! {"
 9751        fn func(abc def: i32) -> ˇu32 {
 9752        }
 9753    "});
 9754
 9755    cx.update_editor(|editor, cx| {
 9756        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9757    });
 9758
 9759    cx.assert_editor_state(indoc! {"
 9760        fn func(abc ˇdef: i32) -> u32 {
 9761        }
 9762    "});
 9763
 9764    cx.update_editor(|editor, cx| {
 9765        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9766    });
 9767
 9768    cx.assert_editor_state(indoc! {"
 9769        fn func(abcˇ def: i32) -> u32 {
 9770        }
 9771    "});
 9772
 9773    cx.update_editor(|editor, cx| {
 9774        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9775    });
 9776
 9777    cx.assert_editor_state(indoc! {"
 9778        fn func(abc def: i32) -> ˇu32 {
 9779        }
 9780    "});
 9781}
 9782
 9783#[gpui::test]
 9784async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9785    init_test(cx, |_| {});
 9786
 9787    let mut cx = EditorTestContext::new(cx).await;
 9788
 9789    cx.set_state(indoc! {"
 9790        fn func(abˇc def: i32) -> u32 {
 9791        }
 9792    "});
 9793    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9794
 9795    cx.update(|cx| {
 9796        project.update(cx, |project, cx| {
 9797            project.update_diagnostics(
 9798                LanguageServerId(0),
 9799                lsp::PublishDiagnosticsParams {
 9800                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9801                    version: None,
 9802                    diagnostics: vec![lsp::Diagnostic {
 9803                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9804                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9805                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9806                        ..Default::default()
 9807                    }],
 9808                },
 9809                &[],
 9810                cx,
 9811            )
 9812        })
 9813    }).unwrap();
 9814    cx.run_until_parked();
 9815    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9816    cx.run_until_parked();
 9817    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9818}
 9819
 9820#[gpui::test]
 9821async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9822    init_test(cx, |_| {});
 9823
 9824    let mut cx = EditorTestContext::new(cx).await;
 9825
 9826    let diff_base = r#"
 9827        use some::mod;
 9828
 9829        const A: u32 = 42;
 9830
 9831        fn main() {
 9832            println!("hello");
 9833
 9834            println!("world");
 9835        }
 9836        "#
 9837    .unindent();
 9838
 9839    // Edits are modified, removed, modified, added
 9840    cx.set_state(
 9841        &r#"
 9842        use some::modified;
 9843
 9844        ˇ
 9845        fn main() {
 9846            println!("hello there");
 9847
 9848            println!("around the");
 9849            println!("world");
 9850        }
 9851        "#
 9852        .unindent(),
 9853    );
 9854
 9855    cx.set_diff_base(Some(&diff_base));
 9856    executor.run_until_parked();
 9857
 9858    cx.update_editor(|editor, cx| {
 9859        //Wrap around the bottom of the buffer
 9860        for _ in 0..3 {
 9861            editor.go_to_next_hunk(&GoToHunk, cx);
 9862        }
 9863    });
 9864
 9865    cx.assert_editor_state(
 9866        &r#"
 9867        ˇuse some::modified;
 9868
 9869
 9870        fn main() {
 9871            println!("hello there");
 9872
 9873            println!("around the");
 9874            println!("world");
 9875        }
 9876        "#
 9877        .unindent(),
 9878    );
 9879
 9880    cx.update_editor(|editor, cx| {
 9881        //Wrap around the top of the buffer
 9882        for _ in 0..2 {
 9883            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9884        }
 9885    });
 9886
 9887    cx.assert_editor_state(
 9888        &r#"
 9889        use some::modified;
 9890
 9891
 9892        fn main() {
 9893        ˇ    println!("hello there");
 9894
 9895            println!("around the");
 9896            println!("world");
 9897        }
 9898        "#
 9899        .unindent(),
 9900    );
 9901
 9902    cx.update_editor(|editor, cx| {
 9903        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9904    });
 9905
 9906    cx.assert_editor_state(
 9907        &r#"
 9908        use some::modified;
 9909
 9910        ˇ
 9911        fn main() {
 9912            println!("hello there");
 9913
 9914            println!("around the");
 9915            println!("world");
 9916        }
 9917        "#
 9918        .unindent(),
 9919    );
 9920
 9921    cx.update_editor(|editor, cx| {
 9922        for _ in 0..3 {
 9923            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9924        }
 9925    });
 9926
 9927    cx.assert_editor_state(
 9928        &r#"
 9929        use some::modified;
 9930
 9931
 9932        fn main() {
 9933        ˇ    println!("hello there");
 9934
 9935            println!("around the");
 9936            println!("world");
 9937        }
 9938        "#
 9939        .unindent(),
 9940    );
 9941
 9942    cx.update_editor(|editor, cx| {
 9943        editor.fold(&Fold, cx);
 9944
 9945        //Make sure that the fold only gets one hunk
 9946        for _ in 0..4 {
 9947            editor.go_to_next_hunk(&GoToHunk, cx);
 9948        }
 9949    });
 9950
 9951    cx.assert_editor_state(
 9952        &r#"
 9953        ˇuse some::modified;
 9954
 9955
 9956        fn main() {
 9957            println!("hello there");
 9958
 9959            println!("around the");
 9960            println!("world");
 9961        }
 9962        "#
 9963        .unindent(),
 9964    );
 9965}
 9966
 9967#[test]
 9968fn test_split_words() {
 9969    fn split(text: &str) -> Vec<&str> {
 9970        split_words(text).collect()
 9971    }
 9972
 9973    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9974    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9975    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9976    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9977    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9978    assert_eq!(split("helloworld"), &["helloworld"]);
 9979
 9980    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9981}
 9982
 9983#[gpui::test]
 9984async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9985    init_test(cx, |_| {});
 9986
 9987    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9988    let mut assert = |before, after| {
 9989        let _state_context = cx.set_state(before);
 9990        cx.update_editor(|editor, cx| {
 9991            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9992        });
 9993        cx.assert_editor_state(after);
 9994    };
 9995
 9996    // Outside bracket jumps to outside of matching bracket
 9997    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9998    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9999
10000    // Inside bracket jumps to inside of matching bracket
10001    assert("console.log(ˇvar);", "console.log(varˇ);");
10002    assert("console.log(varˇ);", "console.log(ˇvar);");
10003
10004    // When outside a bracket and inside, favor jumping to the inside bracket
10005    assert(
10006        "console.log('foo', [1, 2, 3]ˇ);",
10007        "console.log(ˇ'foo', [1, 2, 3]);",
10008    );
10009    assert(
10010        "console.log(ˇ'foo', [1, 2, 3]);",
10011        "console.log('foo', [1, 2, 3]ˇ);",
10012    );
10013
10014    // Bias forward if two options are equally likely
10015    assert(
10016        "let result = curried_fun()ˇ();",
10017        "let result = curried_fun()()ˇ;",
10018    );
10019
10020    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10021    assert(
10022        indoc! {"
10023            function test() {
10024                console.log('test')ˇ
10025            }"},
10026        indoc! {"
10027            function test() {
10028                console.logˇ('test')
10029            }"},
10030    );
10031}
10032
10033#[gpui::test]
10034async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10035    init_test(cx, |_| {});
10036
10037    let fs = FakeFs::new(cx.executor());
10038    fs.insert_tree(
10039        "/a",
10040        json!({
10041            "main.rs": "fn main() { let a = 5; }",
10042            "other.rs": "// Test file",
10043        }),
10044    )
10045    .await;
10046    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10047
10048    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10049    language_registry.add(Arc::new(Language::new(
10050        LanguageConfig {
10051            name: "Rust".into(),
10052            matcher: LanguageMatcher {
10053                path_suffixes: vec!["rs".to_string()],
10054                ..Default::default()
10055            },
10056            brackets: BracketPairConfig {
10057                pairs: vec![BracketPair {
10058                    start: "{".to_string(),
10059                    end: "}".to_string(),
10060                    close: true,
10061                    surround: true,
10062                    newline: true,
10063                }],
10064                disabled_scopes_by_bracket_ix: Vec::new(),
10065            },
10066            ..Default::default()
10067        },
10068        Some(tree_sitter_rust::LANGUAGE.into()),
10069    )));
10070    let mut fake_servers = language_registry.register_fake_lsp(
10071        "Rust",
10072        FakeLspAdapter {
10073            capabilities: lsp::ServerCapabilities {
10074                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10075                    first_trigger_character: "{".to_string(),
10076                    more_trigger_character: None,
10077                }),
10078                ..Default::default()
10079            },
10080            ..Default::default()
10081        },
10082    );
10083
10084    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10085
10086    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10087
10088    let worktree_id = workspace
10089        .update(cx, |workspace, cx| {
10090            workspace.project().update(cx, |project, cx| {
10091                project.worktrees(cx).next().unwrap().read(cx).id()
10092            })
10093        })
10094        .unwrap();
10095
10096    let buffer = project
10097        .update(cx, |project, cx| {
10098            project.open_local_buffer("/a/main.rs", cx)
10099        })
10100        .await
10101        .unwrap();
10102    cx.executor().run_until_parked();
10103    cx.executor().start_waiting();
10104    let fake_server = fake_servers.next().await.unwrap();
10105    let editor_handle = workspace
10106        .update(cx, |workspace, cx| {
10107            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10108        })
10109        .unwrap()
10110        .await
10111        .unwrap()
10112        .downcast::<Editor>()
10113        .unwrap();
10114
10115    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10116        assert_eq!(
10117            params.text_document_position.text_document.uri,
10118            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10119        );
10120        assert_eq!(
10121            params.text_document_position.position,
10122            lsp::Position::new(0, 21),
10123        );
10124
10125        Ok(Some(vec![lsp::TextEdit {
10126            new_text: "]".to_string(),
10127            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10128        }]))
10129    });
10130
10131    editor_handle.update(cx, |editor, cx| {
10132        editor.focus(cx);
10133        editor.change_selections(None, cx, |s| {
10134            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10135        });
10136        editor.handle_input("{", cx);
10137    });
10138
10139    cx.executor().run_until_parked();
10140
10141    buffer.update(cx, |buffer, _| {
10142        assert_eq!(
10143            buffer.text(),
10144            "fn main() { let a = {5}; }",
10145            "No extra braces from on type formatting should appear in the buffer"
10146        )
10147    });
10148}
10149
10150#[gpui::test]
10151async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10152    init_test(cx, |_| {});
10153
10154    let fs = FakeFs::new(cx.executor());
10155    fs.insert_tree(
10156        "/a",
10157        json!({
10158            "main.rs": "fn main() { let a = 5; }",
10159            "other.rs": "// Test file",
10160        }),
10161    )
10162    .await;
10163
10164    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10165
10166    let server_restarts = Arc::new(AtomicUsize::new(0));
10167    let closure_restarts = Arc::clone(&server_restarts);
10168    let language_server_name = "test language server";
10169    let language_name: LanguageName = "Rust".into();
10170
10171    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10172    language_registry.add(Arc::new(Language::new(
10173        LanguageConfig {
10174            name: language_name.clone(),
10175            matcher: LanguageMatcher {
10176                path_suffixes: vec!["rs".to_string()],
10177                ..Default::default()
10178            },
10179            ..Default::default()
10180        },
10181        Some(tree_sitter_rust::LANGUAGE.into()),
10182    )));
10183    let mut fake_servers = language_registry.register_fake_lsp(
10184        "Rust",
10185        FakeLspAdapter {
10186            name: language_server_name,
10187            initialization_options: Some(json!({
10188                "testOptionValue": true
10189            })),
10190            initializer: Some(Box::new(move |fake_server| {
10191                let task_restarts = Arc::clone(&closure_restarts);
10192                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10193                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10194                    futures::future::ready(Ok(()))
10195                });
10196            })),
10197            ..Default::default()
10198        },
10199    );
10200
10201    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10202    let _buffer = project
10203        .update(cx, |project, cx| {
10204            project.open_local_buffer("/a/main.rs", cx)
10205        })
10206        .await
10207        .unwrap();
10208    let _fake_server = fake_servers.next().await.unwrap();
10209    update_test_language_settings(cx, |language_settings| {
10210        language_settings.languages.insert(
10211            language_name.clone(),
10212            LanguageSettingsContent {
10213                tab_size: NonZeroU32::new(8),
10214                ..Default::default()
10215            },
10216        );
10217    });
10218    cx.executor().run_until_parked();
10219    assert_eq!(
10220        server_restarts.load(atomic::Ordering::Acquire),
10221        0,
10222        "Should not restart LSP server on an unrelated change"
10223    );
10224
10225    update_test_project_settings(cx, |project_settings| {
10226        project_settings.lsp.insert(
10227            "Some other server name".into(),
10228            LspSettings {
10229                binary: None,
10230                settings: None,
10231                initialization_options: Some(json!({
10232                    "some other init value": false
10233                })),
10234            },
10235        );
10236    });
10237    cx.executor().run_until_parked();
10238    assert_eq!(
10239        server_restarts.load(atomic::Ordering::Acquire),
10240        0,
10241        "Should not restart LSP server on an unrelated LSP settings change"
10242    );
10243
10244    update_test_project_settings(cx, |project_settings| {
10245        project_settings.lsp.insert(
10246            language_server_name.into(),
10247            LspSettings {
10248                binary: None,
10249                settings: None,
10250                initialization_options: Some(json!({
10251                    "anotherInitValue": false
10252                })),
10253            },
10254        );
10255    });
10256    cx.executor().run_until_parked();
10257    assert_eq!(
10258        server_restarts.load(atomic::Ordering::Acquire),
10259        1,
10260        "Should restart LSP server on a related LSP settings change"
10261    );
10262
10263    update_test_project_settings(cx, |project_settings| {
10264        project_settings.lsp.insert(
10265            language_server_name.into(),
10266            LspSettings {
10267                binary: None,
10268                settings: None,
10269                initialization_options: Some(json!({
10270                    "anotherInitValue": false
10271                })),
10272            },
10273        );
10274    });
10275    cx.executor().run_until_parked();
10276    assert_eq!(
10277        server_restarts.load(atomic::Ordering::Acquire),
10278        1,
10279        "Should not restart LSP server on a related LSP settings change that is the same"
10280    );
10281
10282    update_test_project_settings(cx, |project_settings| {
10283        project_settings.lsp.insert(
10284            language_server_name.into(),
10285            LspSettings {
10286                binary: None,
10287                settings: None,
10288                initialization_options: None,
10289            },
10290        );
10291    });
10292    cx.executor().run_until_parked();
10293    assert_eq!(
10294        server_restarts.load(atomic::Ordering::Acquire),
10295        2,
10296        "Should restart LSP server on another related LSP settings change"
10297    );
10298}
10299
10300#[gpui::test]
10301async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10302    init_test(cx, |_| {});
10303
10304    let mut cx = EditorLspTestContext::new_rust(
10305        lsp::ServerCapabilities {
10306            completion_provider: Some(lsp::CompletionOptions {
10307                trigger_characters: Some(vec![".".to_string()]),
10308                resolve_provider: Some(true),
10309                ..Default::default()
10310            }),
10311            ..Default::default()
10312        },
10313        cx,
10314    )
10315    .await;
10316
10317    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10318    cx.simulate_keystroke(".");
10319    let completion_item = lsp::CompletionItem {
10320        label: "some".into(),
10321        kind: Some(lsp::CompletionItemKind::SNIPPET),
10322        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10323        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10324            kind: lsp::MarkupKind::Markdown,
10325            value: "```rust\nSome(2)\n```".to_string(),
10326        })),
10327        deprecated: Some(false),
10328        sort_text: Some("fffffff2".to_string()),
10329        filter_text: Some("some".to_string()),
10330        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10331        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10332            range: lsp::Range {
10333                start: lsp::Position {
10334                    line: 0,
10335                    character: 22,
10336                },
10337                end: lsp::Position {
10338                    line: 0,
10339                    character: 22,
10340                },
10341            },
10342            new_text: "Some(2)".to_string(),
10343        })),
10344        additional_text_edits: Some(vec![lsp::TextEdit {
10345            range: lsp::Range {
10346                start: lsp::Position {
10347                    line: 0,
10348                    character: 20,
10349                },
10350                end: lsp::Position {
10351                    line: 0,
10352                    character: 22,
10353                },
10354            },
10355            new_text: "".to_string(),
10356        }]),
10357        ..Default::default()
10358    };
10359
10360    let closure_completion_item = completion_item.clone();
10361    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10362        let task_completion_item = closure_completion_item.clone();
10363        async move {
10364            Ok(Some(lsp::CompletionResponse::Array(vec![
10365                task_completion_item,
10366            ])))
10367        }
10368    });
10369
10370    request.next().await;
10371
10372    cx.condition(|editor, _| editor.context_menu_visible())
10373        .await;
10374    let apply_additional_edits = cx.update_editor(|editor, cx| {
10375        editor
10376            .confirm_completion(&ConfirmCompletion::default(), cx)
10377            .unwrap()
10378    });
10379    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10380
10381    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10382        let task_completion_item = completion_item.clone();
10383        async move { Ok(task_completion_item) }
10384    })
10385    .next()
10386    .await
10387    .unwrap();
10388    apply_additional_edits.await.unwrap();
10389    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10390}
10391
10392#[gpui::test]
10393async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10394    init_test(cx, |_| {});
10395
10396    let mut cx = EditorLspTestContext::new(
10397        Language::new(
10398            LanguageConfig {
10399                matcher: LanguageMatcher {
10400                    path_suffixes: vec!["jsx".into()],
10401                    ..Default::default()
10402                },
10403                overrides: [(
10404                    "element".into(),
10405                    LanguageConfigOverride {
10406                        word_characters: Override::Set(['-'].into_iter().collect()),
10407                        ..Default::default()
10408                    },
10409                )]
10410                .into_iter()
10411                .collect(),
10412                ..Default::default()
10413            },
10414            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10415        )
10416        .with_override_query("(jsx_self_closing_element) @element")
10417        .unwrap(),
10418        lsp::ServerCapabilities {
10419            completion_provider: Some(lsp::CompletionOptions {
10420                trigger_characters: Some(vec![":".to_string()]),
10421                ..Default::default()
10422            }),
10423            ..Default::default()
10424        },
10425        cx,
10426    )
10427    .await;
10428
10429    cx.lsp
10430        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10431            Ok(Some(lsp::CompletionResponse::Array(vec![
10432                lsp::CompletionItem {
10433                    label: "bg-blue".into(),
10434                    ..Default::default()
10435                },
10436                lsp::CompletionItem {
10437                    label: "bg-red".into(),
10438                    ..Default::default()
10439                },
10440                lsp::CompletionItem {
10441                    label: "bg-yellow".into(),
10442                    ..Default::default()
10443                },
10444            ])))
10445        });
10446
10447    cx.set_state(r#"<p class="bgˇ" />"#);
10448
10449    // Trigger completion when typing a dash, because the dash is an extra
10450    // word character in the 'element' scope, which contains the cursor.
10451    cx.simulate_keystroke("-");
10452    cx.executor().run_until_parked();
10453    cx.update_editor(|editor, _| {
10454        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10455            assert_eq!(
10456                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10457                &["bg-red", "bg-blue", "bg-yellow"]
10458            );
10459        } else {
10460            panic!("expected completion menu to be open");
10461        }
10462    });
10463
10464    cx.simulate_keystroke("l");
10465    cx.executor().run_until_parked();
10466    cx.update_editor(|editor, _| {
10467        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10468            assert_eq!(
10469                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10470                &["bg-blue", "bg-yellow"]
10471            );
10472        } else {
10473            panic!("expected completion menu to be open");
10474        }
10475    });
10476
10477    // When filtering completions, consider the character after the '-' to
10478    // be the start of a subword.
10479    cx.set_state(r#"<p class="yelˇ" />"#);
10480    cx.simulate_keystroke("l");
10481    cx.executor().run_until_parked();
10482    cx.update_editor(|editor, _| {
10483        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10484            assert_eq!(
10485                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10486                &["bg-yellow"]
10487            );
10488        } else {
10489            panic!("expected completion menu to be open");
10490        }
10491    });
10492}
10493
10494#[gpui::test]
10495async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10496    init_test(cx, |settings| {
10497        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10498            FormatterList(vec![Formatter::Prettier].into()),
10499        ))
10500    });
10501
10502    let fs = FakeFs::new(cx.executor());
10503    fs.insert_file("/file.ts", Default::default()).await;
10504
10505    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10506    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10507
10508    language_registry.add(Arc::new(Language::new(
10509        LanguageConfig {
10510            name: "TypeScript".into(),
10511            matcher: LanguageMatcher {
10512                path_suffixes: vec!["ts".to_string()],
10513                ..Default::default()
10514            },
10515            ..Default::default()
10516        },
10517        Some(tree_sitter_rust::LANGUAGE.into()),
10518    )));
10519    update_test_language_settings(cx, |settings| {
10520        settings.defaults.prettier = Some(PrettierSettings {
10521            allowed: true,
10522            ..PrettierSettings::default()
10523        });
10524    });
10525
10526    let test_plugin = "test_plugin";
10527    let _ = language_registry.register_fake_lsp(
10528        "TypeScript",
10529        FakeLspAdapter {
10530            prettier_plugins: vec![test_plugin],
10531            ..Default::default()
10532        },
10533    );
10534
10535    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10536    let buffer = project
10537        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10538        .await
10539        .unwrap();
10540
10541    let buffer_text = "one\ntwo\nthree\n";
10542    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10543    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10544    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10545
10546    editor
10547        .update(cx, |editor, cx| {
10548            editor.perform_format(
10549                project.clone(),
10550                FormatTrigger::Manual,
10551                FormatTarget::Buffer,
10552                cx,
10553            )
10554        })
10555        .unwrap()
10556        .await;
10557    assert_eq!(
10558        editor.update(cx, |editor, cx| editor.text(cx)),
10559        buffer_text.to_string() + prettier_format_suffix,
10560        "Test prettier formatting was not applied to the original buffer text",
10561    );
10562
10563    update_test_language_settings(cx, |settings| {
10564        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10565    });
10566    let format = editor.update(cx, |editor, cx| {
10567        editor.perform_format(
10568            project.clone(),
10569            FormatTrigger::Manual,
10570            FormatTarget::Buffer,
10571            cx,
10572        )
10573    });
10574    format.await.unwrap();
10575    assert_eq!(
10576        editor.update(cx, |editor, cx| editor.text(cx)),
10577        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10578        "Autoformatting (via test prettier) was not applied to the original buffer text",
10579    );
10580}
10581
10582#[gpui::test]
10583async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10584    init_test(cx, |_| {});
10585    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10586    let base_text = indoc! {r#"struct Row;
10587struct Row1;
10588struct Row2;
10589
10590struct Row4;
10591struct Row5;
10592struct Row6;
10593
10594struct Row8;
10595struct Row9;
10596struct Row10;"#};
10597
10598    // When addition hunks are not adjacent to carets, no hunk revert is performed
10599    assert_hunk_revert(
10600        indoc! {r#"struct Row;
10601                   struct Row1;
10602                   struct Row1.1;
10603                   struct Row1.2;
10604                   struct Row2;ˇ
10605
10606                   struct Row4;
10607                   struct Row5;
10608                   struct Row6;
10609
10610                   struct Row8;
10611                   ˇstruct Row9;
10612                   struct Row9.1;
10613                   struct Row9.2;
10614                   struct Row9.3;
10615                   struct Row10;"#},
10616        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10617        indoc! {r#"struct Row;
10618                   struct Row1;
10619                   struct Row1.1;
10620                   struct Row1.2;
10621                   struct Row2;ˇ
10622
10623                   struct Row4;
10624                   struct Row5;
10625                   struct Row6;
10626
10627                   struct Row8;
10628                   ˇstruct Row9;
10629                   struct Row9.1;
10630                   struct Row9.2;
10631                   struct Row9.3;
10632                   struct Row10;"#},
10633        base_text,
10634        &mut cx,
10635    );
10636    // Same for selections
10637    assert_hunk_revert(
10638        indoc! {r#"struct Row;
10639                   struct Row1;
10640                   struct Row2;
10641                   struct Row2.1;
10642                   struct Row2.2;
10643                   «ˇ
10644                   struct Row4;
10645                   struct» Row5;
10646                   «struct Row6;
10647                   ˇ»
10648                   struct Row9.1;
10649                   struct Row9.2;
10650                   struct Row9.3;
10651                   struct Row8;
10652                   struct Row9;
10653                   struct Row10;"#},
10654        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10655        indoc! {r#"struct Row;
10656                   struct Row1;
10657                   struct Row2;
10658                   struct Row2.1;
10659                   struct Row2.2;
10660                   «ˇ
10661                   struct Row4;
10662                   struct» Row5;
10663                   «struct Row6;
10664                   ˇ»
10665                   struct Row9.1;
10666                   struct Row9.2;
10667                   struct Row9.3;
10668                   struct Row8;
10669                   struct Row9;
10670                   struct Row10;"#},
10671        base_text,
10672        &mut cx,
10673    );
10674
10675    // When carets and selections intersect the addition hunks, those are reverted.
10676    // Adjacent carets got merged.
10677    assert_hunk_revert(
10678        indoc! {r#"struct Row;
10679                   ˇ// something on the top
10680                   struct Row1;
10681                   struct Row2;
10682                   struct Roˇw3.1;
10683                   struct Row2.2;
10684                   struct Row2.3;ˇ
10685
10686                   struct Row4;
10687                   struct ˇRow5.1;
10688                   struct Row5.2;
10689                   struct «Rowˇ»5.3;
10690                   struct Row5;
10691                   struct Row6;
10692                   ˇ
10693                   struct Row9.1;
10694                   struct «Rowˇ»9.2;
10695                   struct «ˇRow»9.3;
10696                   struct Row8;
10697                   struct Row9;
10698                   «ˇ// something on bottom»
10699                   struct Row10;"#},
10700        vec![
10701            DiffHunkStatus::Added,
10702            DiffHunkStatus::Added,
10703            DiffHunkStatus::Added,
10704            DiffHunkStatus::Added,
10705            DiffHunkStatus::Added,
10706        ],
10707        indoc! {r#"struct Row;
10708                   ˇstruct Row1;
10709                   struct Row2;
10710                   ˇ
10711                   struct Row4;
10712                   ˇstruct Row5;
10713                   struct Row6;
10714                   ˇ
10715                   ˇstruct Row8;
10716                   struct Row9;
10717                   ˇstruct Row10;"#},
10718        base_text,
10719        &mut cx,
10720    );
10721}
10722
10723#[gpui::test]
10724async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10725    init_test(cx, |_| {});
10726    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10727    let base_text = indoc! {r#"struct Row;
10728struct Row1;
10729struct Row2;
10730
10731struct Row4;
10732struct Row5;
10733struct Row6;
10734
10735struct Row8;
10736struct Row9;
10737struct Row10;"#};
10738
10739    // Modification hunks behave the same as the addition ones.
10740    assert_hunk_revert(
10741        indoc! {r#"struct Row;
10742                   struct Row1;
10743                   struct Row33;
10744                   ˇ
10745                   struct Row4;
10746                   struct Row5;
10747                   struct Row6;
10748                   ˇ
10749                   struct Row99;
10750                   struct Row9;
10751                   struct Row10;"#},
10752        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10753        indoc! {r#"struct Row;
10754                   struct Row1;
10755                   struct Row33;
10756                   ˇ
10757                   struct Row4;
10758                   struct Row5;
10759                   struct Row6;
10760                   ˇ
10761                   struct Row99;
10762                   struct Row9;
10763                   struct Row10;"#},
10764        base_text,
10765        &mut cx,
10766    );
10767    assert_hunk_revert(
10768        indoc! {r#"struct Row;
10769                   struct Row1;
10770                   struct Row33;
10771                   «ˇ
10772                   struct Row4;
10773                   struct» Row5;
10774                   «struct Row6;
10775                   ˇ»
10776                   struct Row99;
10777                   struct Row9;
10778                   struct Row10;"#},
10779        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10780        indoc! {r#"struct Row;
10781                   struct Row1;
10782                   struct Row33;
10783                   «ˇ
10784                   struct Row4;
10785                   struct» Row5;
10786                   «struct Row6;
10787                   ˇ»
10788                   struct Row99;
10789                   struct Row9;
10790                   struct Row10;"#},
10791        base_text,
10792        &mut cx,
10793    );
10794
10795    assert_hunk_revert(
10796        indoc! {r#"ˇstruct Row1.1;
10797                   struct Row1;
10798                   «ˇstr»uct Row22;
10799
10800                   struct ˇRow44;
10801                   struct Row5;
10802                   struct «Rˇ»ow66;ˇ
10803
10804                   «struˇ»ct Row88;
10805                   struct Row9;
10806                   struct Row1011;ˇ"#},
10807        vec![
10808            DiffHunkStatus::Modified,
10809            DiffHunkStatus::Modified,
10810            DiffHunkStatus::Modified,
10811            DiffHunkStatus::Modified,
10812            DiffHunkStatus::Modified,
10813            DiffHunkStatus::Modified,
10814        ],
10815        indoc! {r#"struct Row;
10816                   ˇstruct Row1;
10817                   struct Row2;
10818                   ˇ
10819                   struct Row4;
10820                   ˇstruct Row5;
10821                   struct Row6;
10822                   ˇ
10823                   struct Row8;
10824                   ˇstruct Row9;
10825                   struct Row10;ˇ"#},
10826        base_text,
10827        &mut cx,
10828    );
10829}
10830
10831#[gpui::test]
10832async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10833    init_test(cx, |_| {});
10834    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10835    let base_text = indoc! {r#"struct Row;
10836struct Row1;
10837struct Row2;
10838
10839struct Row4;
10840struct Row5;
10841struct Row6;
10842
10843struct Row8;
10844struct Row9;
10845struct Row10;"#};
10846
10847    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10848    assert_hunk_revert(
10849        indoc! {r#"struct Row;
10850                   struct Row2;
10851
10852                   ˇstruct Row4;
10853                   struct Row5;
10854                   struct Row6;
10855                   ˇ
10856                   struct Row8;
10857                   struct Row10;"#},
10858        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10859        indoc! {r#"struct Row;
10860                   struct Row2;
10861
10862                   ˇstruct Row4;
10863                   struct Row5;
10864                   struct Row6;
10865                   ˇ
10866                   struct Row8;
10867                   struct Row10;"#},
10868        base_text,
10869        &mut cx,
10870    );
10871    assert_hunk_revert(
10872        indoc! {r#"struct Row;
10873                   struct Row2;
10874
10875                   «ˇstruct Row4;
10876                   struct» Row5;
10877                   «struct Row6;
10878                   ˇ»
10879                   struct Row8;
10880                   struct Row10;"#},
10881        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10882        indoc! {r#"struct Row;
10883                   struct Row2;
10884
10885                   «ˇstruct Row4;
10886                   struct» Row5;
10887                   «struct Row6;
10888                   ˇ»
10889                   struct Row8;
10890                   struct Row10;"#},
10891        base_text,
10892        &mut cx,
10893    );
10894
10895    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10896    assert_hunk_revert(
10897        indoc! {r#"struct Row;
10898                   ˇstruct Row2;
10899
10900                   struct Row4;
10901                   struct Row5;
10902                   struct Row6;
10903
10904                   struct Row8;ˇ
10905                   struct Row10;"#},
10906        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10907        indoc! {r#"struct Row;
10908                   struct Row1;
10909                   ˇstruct Row2;
10910
10911                   struct Row4;
10912                   struct Row5;
10913                   struct Row6;
10914
10915                   struct Row8;ˇ
10916                   struct Row9;
10917                   struct Row10;"#},
10918        base_text,
10919        &mut cx,
10920    );
10921    assert_hunk_revert(
10922        indoc! {r#"struct Row;
10923                   struct Row2«ˇ;
10924                   struct Row4;
10925                   struct» Row5;
10926                   «struct Row6;
10927
10928                   struct Row8;ˇ»
10929                   struct Row10;"#},
10930        vec![
10931            DiffHunkStatus::Removed,
10932            DiffHunkStatus::Removed,
10933            DiffHunkStatus::Removed,
10934        ],
10935        indoc! {r#"struct Row;
10936                   struct Row1;
10937                   struct Row2«ˇ;
10938
10939                   struct Row4;
10940                   struct» Row5;
10941                   «struct Row6;
10942
10943                   struct Row8;ˇ»
10944                   struct Row9;
10945                   struct Row10;"#},
10946        base_text,
10947        &mut cx,
10948    );
10949}
10950
10951#[gpui::test]
10952async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10953    init_test(cx, |_| {});
10954
10955    let cols = 4;
10956    let rows = 10;
10957    let sample_text_1 = sample_text(rows, cols, 'a');
10958    assert_eq!(
10959        sample_text_1,
10960        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10961    );
10962    let sample_text_2 = sample_text(rows, cols, 'l');
10963    assert_eq!(
10964        sample_text_2,
10965        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10966    );
10967    let sample_text_3 = sample_text(rows, cols, 'v');
10968    assert_eq!(
10969        sample_text_3,
10970        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10971    );
10972
10973    fn diff_every_buffer_row(
10974        buffer: &Model<Buffer>,
10975        sample_text: String,
10976        cols: usize,
10977        cx: &mut gpui::TestAppContext,
10978    ) {
10979        // revert first character in each row, creating one large diff hunk per buffer
10980        let is_first_char = |offset: usize| offset % cols == 0;
10981        buffer.update(cx, |buffer, cx| {
10982            buffer.set_text(
10983                sample_text
10984                    .chars()
10985                    .enumerate()
10986                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10987                    .collect::<String>(),
10988                cx,
10989            );
10990            buffer.set_diff_base(Some(sample_text), cx);
10991        });
10992        cx.executor().run_until_parked();
10993    }
10994
10995    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10996    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10997
10998    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10999    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11000
11001    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11002    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11003
11004    let multibuffer = cx.new_model(|cx| {
11005        let mut multibuffer = MultiBuffer::new(ReadWrite);
11006        multibuffer.push_excerpts(
11007            buffer_1.clone(),
11008            [
11009                ExcerptRange {
11010                    context: Point::new(0, 0)..Point::new(3, 0),
11011                    primary: None,
11012                },
11013                ExcerptRange {
11014                    context: Point::new(5, 0)..Point::new(7, 0),
11015                    primary: None,
11016                },
11017                ExcerptRange {
11018                    context: Point::new(9, 0)..Point::new(10, 4),
11019                    primary: None,
11020                },
11021            ],
11022            cx,
11023        );
11024        multibuffer.push_excerpts(
11025            buffer_2.clone(),
11026            [
11027                ExcerptRange {
11028                    context: Point::new(0, 0)..Point::new(3, 0),
11029                    primary: None,
11030                },
11031                ExcerptRange {
11032                    context: Point::new(5, 0)..Point::new(7, 0),
11033                    primary: None,
11034                },
11035                ExcerptRange {
11036                    context: Point::new(9, 0)..Point::new(10, 4),
11037                    primary: None,
11038                },
11039            ],
11040            cx,
11041        );
11042        multibuffer.push_excerpts(
11043            buffer_3.clone(),
11044            [
11045                ExcerptRange {
11046                    context: Point::new(0, 0)..Point::new(3, 0),
11047                    primary: None,
11048                },
11049                ExcerptRange {
11050                    context: Point::new(5, 0)..Point::new(7, 0),
11051                    primary: None,
11052                },
11053                ExcerptRange {
11054                    context: Point::new(9, 0)..Point::new(10, 4),
11055                    primary: None,
11056                },
11057            ],
11058            cx,
11059        );
11060        multibuffer
11061    });
11062
11063    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
11064    editor.update(cx, |editor, cx| {
11065        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");
11066        editor.select_all(&SelectAll, cx);
11067        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11068    });
11069    cx.executor().run_until_parked();
11070    // When all ranges are selected, all buffer hunks are reverted.
11071    editor.update(cx, |editor, cx| {
11072        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");
11073    });
11074    buffer_1.update(cx, |buffer, _| {
11075        assert_eq!(buffer.text(), sample_text_1);
11076    });
11077    buffer_2.update(cx, |buffer, _| {
11078        assert_eq!(buffer.text(), sample_text_2);
11079    });
11080    buffer_3.update(cx, |buffer, _| {
11081        assert_eq!(buffer.text(), sample_text_3);
11082    });
11083
11084    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
11085    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
11086    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
11087    editor.update(cx, |editor, cx| {
11088        editor.change_selections(None, cx, |s| {
11089            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
11090        });
11091        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11092    });
11093    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
11094    // but not affect buffer_2 and its related excerpts.
11095    editor.update(cx, |editor, cx| {
11096        assert_eq!(
11097            editor.text(cx),
11098            "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"
11099        );
11100    });
11101    buffer_1.update(cx, |buffer, _| {
11102        assert_eq!(buffer.text(), sample_text_1);
11103    });
11104    buffer_2.update(cx, |buffer, _| {
11105        assert_eq!(
11106            buffer.text(),
11107            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
11108        );
11109    });
11110    buffer_3.update(cx, |buffer, _| {
11111        assert_eq!(
11112            buffer.text(),
11113            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
11114        );
11115    });
11116}
11117
11118#[gpui::test]
11119async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
11120    init_test(cx, |_| {});
11121
11122    let cols = 4;
11123    let rows = 10;
11124    let sample_text_1 = sample_text(rows, cols, 'a');
11125    assert_eq!(
11126        sample_text_1,
11127        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
11128    );
11129    let sample_text_2 = sample_text(rows, cols, 'l');
11130    assert_eq!(
11131        sample_text_2,
11132        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
11133    );
11134    let sample_text_3 = sample_text(rows, cols, 'v');
11135    assert_eq!(
11136        sample_text_3,
11137        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
11138    );
11139
11140    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
11141    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
11142    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
11143
11144    let multi_buffer = cx.new_model(|cx| {
11145        let mut multibuffer = MultiBuffer::new(ReadWrite);
11146        multibuffer.push_excerpts(
11147            buffer_1.clone(),
11148            [
11149                ExcerptRange {
11150                    context: Point::new(0, 0)..Point::new(3, 0),
11151                    primary: None,
11152                },
11153                ExcerptRange {
11154                    context: Point::new(5, 0)..Point::new(7, 0),
11155                    primary: None,
11156                },
11157                ExcerptRange {
11158                    context: Point::new(9, 0)..Point::new(10, 4),
11159                    primary: None,
11160                },
11161            ],
11162            cx,
11163        );
11164        multibuffer.push_excerpts(
11165            buffer_2.clone(),
11166            [
11167                ExcerptRange {
11168                    context: Point::new(0, 0)..Point::new(3, 0),
11169                    primary: None,
11170                },
11171                ExcerptRange {
11172                    context: Point::new(5, 0)..Point::new(7, 0),
11173                    primary: None,
11174                },
11175                ExcerptRange {
11176                    context: Point::new(9, 0)..Point::new(10, 4),
11177                    primary: None,
11178                },
11179            ],
11180            cx,
11181        );
11182        multibuffer.push_excerpts(
11183            buffer_3.clone(),
11184            [
11185                ExcerptRange {
11186                    context: Point::new(0, 0)..Point::new(3, 0),
11187                    primary: None,
11188                },
11189                ExcerptRange {
11190                    context: Point::new(5, 0)..Point::new(7, 0),
11191                    primary: None,
11192                },
11193                ExcerptRange {
11194                    context: Point::new(9, 0)..Point::new(10, 4),
11195                    primary: None,
11196                },
11197            ],
11198            cx,
11199        );
11200        multibuffer
11201    });
11202
11203    let fs = FakeFs::new(cx.executor());
11204    fs.insert_tree(
11205        "/a",
11206        json!({
11207            "main.rs": sample_text_1,
11208            "other.rs": sample_text_2,
11209            "lib.rs": sample_text_3,
11210        }),
11211    )
11212    .await;
11213    let project = Project::test(fs, ["/a".as_ref()], cx).await;
11214    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
11215    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11216    let multi_buffer_editor = cx.new_view(|cx| {
11217        Editor::new(
11218            EditorMode::Full,
11219            multi_buffer,
11220            Some(project.clone()),
11221            true,
11222            cx,
11223        )
11224    });
11225    let multibuffer_item_id = workspace
11226        .update(cx, |workspace, cx| {
11227            assert!(
11228                workspace.active_item(cx).is_none(),
11229                "active item should be None before the first item is added"
11230            );
11231            workspace.add_item_to_active_pane(
11232                Box::new(multi_buffer_editor.clone()),
11233                None,
11234                true,
11235                cx,
11236            );
11237            let active_item = workspace
11238                .active_item(cx)
11239                .expect("should have an active item after adding the multi buffer");
11240            assert!(
11241                !active_item.is_singleton(cx),
11242                "A multi buffer was expected to active after adding"
11243            );
11244            active_item.item_id()
11245        })
11246        .unwrap();
11247    cx.executor().run_until_parked();
11248
11249    multi_buffer_editor.update(cx, |editor, cx| {
11250        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11251        editor.open_excerpts(&OpenExcerpts, cx);
11252    });
11253    cx.executor().run_until_parked();
11254    let first_item_id = workspace
11255        .update(cx, |workspace, cx| {
11256            let active_item = workspace
11257                .active_item(cx)
11258                .expect("should have an active item after navigating into the 1st buffer");
11259            let first_item_id = active_item.item_id();
11260            assert_ne!(
11261                first_item_id, multibuffer_item_id,
11262                "Should navigate into the 1st buffer and activate it"
11263            );
11264            assert!(
11265                active_item.is_singleton(cx),
11266                "New active item should be a singleton buffer"
11267            );
11268            assert_eq!(
11269                active_item
11270                    .act_as::<Editor>(cx)
11271                    .expect("should have navigated into an editor for the 1st buffer")
11272                    .read(cx)
11273                    .text(cx),
11274                sample_text_1
11275            );
11276
11277            workspace
11278                .go_back(workspace.active_pane().downgrade(), cx)
11279                .detach_and_log_err(cx);
11280
11281            first_item_id
11282        })
11283        .unwrap();
11284    cx.executor().run_until_parked();
11285    workspace
11286        .update(cx, |workspace, cx| {
11287            let active_item = workspace
11288                .active_item(cx)
11289                .expect("should have an active item after navigating back");
11290            assert_eq!(
11291                active_item.item_id(),
11292                multibuffer_item_id,
11293                "Should navigate back to the multi buffer"
11294            );
11295            assert!(!active_item.is_singleton(cx));
11296        })
11297        .unwrap();
11298
11299    multi_buffer_editor.update(cx, |editor, cx| {
11300        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11301            s.select_ranges(Some(39..40))
11302        });
11303        editor.open_excerpts(&OpenExcerpts, cx);
11304    });
11305    cx.executor().run_until_parked();
11306    let second_item_id = workspace
11307        .update(cx, |workspace, cx| {
11308            let active_item = workspace
11309                .active_item(cx)
11310                .expect("should have an active item after navigating into the 2nd buffer");
11311            let second_item_id = active_item.item_id();
11312            assert_ne!(
11313                second_item_id, multibuffer_item_id,
11314                "Should navigate away from the multibuffer"
11315            );
11316            assert_ne!(
11317                second_item_id, first_item_id,
11318                "Should navigate into the 2nd buffer and activate it"
11319            );
11320            assert!(
11321                active_item.is_singleton(cx),
11322                "New active item should be a singleton buffer"
11323            );
11324            assert_eq!(
11325                active_item
11326                    .act_as::<Editor>(cx)
11327                    .expect("should have navigated into an editor")
11328                    .read(cx)
11329                    .text(cx),
11330                sample_text_2
11331            );
11332
11333            workspace
11334                .go_back(workspace.active_pane().downgrade(), cx)
11335                .detach_and_log_err(cx);
11336
11337            second_item_id
11338        })
11339        .unwrap();
11340    cx.executor().run_until_parked();
11341    workspace
11342        .update(cx, |workspace, cx| {
11343            let active_item = workspace
11344                .active_item(cx)
11345                .expect("should have an active item after navigating back from the 2nd buffer");
11346            assert_eq!(
11347                active_item.item_id(),
11348                multibuffer_item_id,
11349                "Should navigate back from the 2nd buffer to the multi buffer"
11350            );
11351            assert!(!active_item.is_singleton(cx));
11352        })
11353        .unwrap();
11354
11355    multi_buffer_editor.update(cx, |editor, cx| {
11356        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11357            s.select_ranges(Some(60..70))
11358        });
11359        editor.open_excerpts(&OpenExcerpts, cx);
11360    });
11361    cx.executor().run_until_parked();
11362    workspace
11363        .update(cx, |workspace, cx| {
11364            let active_item = workspace
11365                .active_item(cx)
11366                .expect("should have an active item after navigating into the 3rd buffer");
11367            let third_item_id = active_item.item_id();
11368            assert_ne!(
11369                third_item_id, multibuffer_item_id,
11370                "Should navigate into the 3rd buffer and activate it"
11371            );
11372            assert_ne!(third_item_id, first_item_id);
11373            assert_ne!(third_item_id, second_item_id);
11374            assert!(
11375                active_item.is_singleton(cx),
11376                "New active item should be a singleton buffer"
11377            );
11378            assert_eq!(
11379                active_item
11380                    .act_as::<Editor>(cx)
11381                    .expect("should have navigated into an editor")
11382                    .read(cx)
11383                    .text(cx),
11384                sample_text_3
11385            );
11386
11387            workspace
11388                .go_back(workspace.active_pane().downgrade(), cx)
11389                .detach_and_log_err(cx);
11390        })
11391        .unwrap();
11392    cx.executor().run_until_parked();
11393    workspace
11394        .update(cx, |workspace, cx| {
11395            let active_item = workspace
11396                .active_item(cx)
11397                .expect("should have an active item after navigating back from the 3rd buffer");
11398            assert_eq!(
11399                active_item.item_id(),
11400                multibuffer_item_id,
11401                "Should navigate back from the 3rd buffer to the multi buffer"
11402            );
11403            assert!(!active_item.is_singleton(cx));
11404        })
11405        .unwrap();
11406}
11407
11408#[gpui::test]
11409async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11410    init_test(cx, |_| {});
11411
11412    let mut cx = EditorTestContext::new(cx).await;
11413
11414    let diff_base = r#"
11415        use some::mod;
11416
11417        const A: u32 = 42;
11418
11419        fn main() {
11420            println!("hello");
11421
11422            println!("world");
11423        }
11424        "#
11425    .unindent();
11426
11427    cx.set_state(
11428        &r#"
11429        use some::modified;
11430
11431        ˇ
11432        fn main() {
11433            println!("hello there");
11434
11435            println!("around the");
11436            println!("world");
11437        }
11438        "#
11439        .unindent(),
11440    );
11441
11442    cx.set_diff_base(Some(&diff_base));
11443    executor.run_until_parked();
11444
11445    cx.update_editor(|editor, cx| {
11446        editor.go_to_next_hunk(&GoToHunk, cx);
11447        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11448    });
11449    executor.run_until_parked();
11450    cx.assert_diff_hunks(
11451        r#"
11452          use some::modified;
11453
11454
11455          fn main() {
11456        -     println!("hello");
11457        +     println!("hello there");
11458
11459              println!("around the");
11460              println!("world");
11461          }
11462        "#
11463        .unindent(),
11464    );
11465
11466    cx.update_editor(|editor, cx| {
11467        for _ in 0..3 {
11468            editor.go_to_next_hunk(&GoToHunk, cx);
11469            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11470        }
11471    });
11472    executor.run_until_parked();
11473    cx.assert_editor_state(
11474        &r#"
11475        use some::modified;
11476
11477        ˇ
11478        fn main() {
11479            println!("hello there");
11480
11481            println!("around the");
11482            println!("world");
11483        }
11484        "#
11485        .unindent(),
11486    );
11487
11488    cx.assert_diff_hunks(
11489        r#"
11490        - use some::mod;
11491        + use some::modified;
11492
11493        - const A: u32 = 42;
11494
11495          fn main() {
11496        -     println!("hello");
11497        +     println!("hello there");
11498
11499        +     println!("around the");
11500              println!("world");
11501          }
11502        "#
11503        .unindent(),
11504    );
11505
11506    cx.update_editor(|editor, cx| {
11507        editor.cancel(&Cancel, cx);
11508    });
11509
11510    cx.assert_diff_hunks(
11511        r#"
11512          use some::modified;
11513
11514
11515          fn main() {
11516              println!("hello there");
11517
11518              println!("around the");
11519              println!("world");
11520          }
11521        "#
11522        .unindent(),
11523    );
11524}
11525
11526#[gpui::test]
11527async fn test_diff_base_change_with_expanded_diff_hunks(
11528    executor: BackgroundExecutor,
11529    cx: &mut gpui::TestAppContext,
11530) {
11531    init_test(cx, |_| {});
11532
11533    let mut cx = EditorTestContext::new(cx).await;
11534
11535    let diff_base = r#"
11536        use some::mod1;
11537        use some::mod2;
11538
11539        const A: u32 = 42;
11540        const B: u32 = 42;
11541        const C: u32 = 42;
11542
11543        fn main() {
11544            println!("hello");
11545
11546            println!("world");
11547        }
11548        "#
11549    .unindent();
11550
11551    cx.set_state(
11552        &r#"
11553        use some::mod2;
11554
11555        const A: u32 = 42;
11556        const C: u32 = 42;
11557
11558        fn main(ˇ) {
11559            //println!("hello");
11560
11561            println!("world");
11562            //
11563            //
11564        }
11565        "#
11566        .unindent(),
11567    );
11568
11569    cx.set_diff_base(Some(&diff_base));
11570    executor.run_until_parked();
11571
11572    cx.update_editor(|editor, cx| {
11573        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11574    });
11575    executor.run_until_parked();
11576    cx.assert_diff_hunks(
11577        r#"
11578        - use some::mod1;
11579          use some::mod2;
11580
11581          const A: u32 = 42;
11582        - const B: u32 = 42;
11583          const C: u32 = 42;
11584
11585          fn main() {
11586        -     println!("hello");
11587        +     //println!("hello");
11588
11589              println!("world");
11590        +     //
11591        +     //
11592          }
11593        "#
11594        .unindent(),
11595    );
11596
11597    cx.set_diff_base(Some("new diff base!"));
11598    executor.run_until_parked();
11599    cx.assert_diff_hunks(
11600        r#"
11601          use some::mod2;
11602
11603          const A: u32 = 42;
11604          const C: u32 = 42;
11605
11606          fn main() {
11607              //println!("hello");
11608
11609              println!("world");
11610              //
11611              //
11612          }
11613        "#
11614        .unindent(),
11615    );
11616
11617    cx.update_editor(|editor, cx| {
11618        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11619    });
11620    executor.run_until_parked();
11621    cx.assert_diff_hunks(
11622        r#"
11623        - new diff base!
11624        + use some::mod2;
11625        +
11626        + const A: u32 = 42;
11627        + const C: u32 = 42;
11628        +
11629        + fn main() {
11630        +     //println!("hello");
11631        +
11632        +     println!("world");
11633        +     //
11634        +     //
11635        + }
11636        "#
11637        .unindent(),
11638    );
11639}
11640
11641#[gpui::test]
11642async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11643    init_test(cx, |_| {});
11644
11645    let mut cx = EditorTestContext::new(cx).await;
11646
11647    let diff_base = r#"
11648        use some::mod1;
11649        use some::mod2;
11650
11651        const A: u32 = 42;
11652        const B: u32 = 42;
11653        const C: u32 = 42;
11654
11655        fn main() {
11656            println!("hello");
11657
11658            println!("world");
11659        }
11660
11661        fn another() {
11662            println!("another");
11663        }
11664
11665        fn another2() {
11666            println!("another2");
11667        }
11668        "#
11669    .unindent();
11670
11671    cx.set_state(
11672        &r#"
11673        «use some::mod2;
11674
11675        const A: u32 = 42;
11676        const C: u32 = 42;
11677
11678        fn main() {
11679            //println!("hello");
11680
11681            println!("world");
11682            //
11683            //ˇ»
11684        }
11685
11686        fn another() {
11687            println!("another");
11688            println!("another");
11689        }
11690
11691            println!("another2");
11692        }
11693        "#
11694        .unindent(),
11695    );
11696
11697    cx.set_diff_base(Some(&diff_base));
11698    executor.run_until_parked();
11699
11700    cx.update_editor(|editor, cx| {
11701        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11702    });
11703    executor.run_until_parked();
11704
11705    cx.assert_diff_hunks(
11706        r#"
11707        - use some::mod1;
11708          use some::mod2;
11709
11710          const A: u32 = 42;
11711        - const B: u32 = 42;
11712          const C: u32 = 42;
11713
11714          fn main() {
11715        -     println!("hello");
11716        +     //println!("hello");
11717
11718              println!("world");
11719        +     //
11720        +     //
11721          }
11722
11723          fn another() {
11724              println!("another");
11725        +     println!("another");
11726          }
11727
11728        - fn another2() {
11729              println!("another2");
11730          }
11731        "#
11732        .unindent(),
11733    );
11734
11735    // Fold across some of the diff hunks. They should no longer appear expanded.
11736    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11737    cx.executor().run_until_parked();
11738
11739    // Hunks are not shown if their position is within a fold
11740    cx.assert_diff_hunks(
11741        r#"
11742          use some::mod2;
11743
11744          const A: u32 = 42;
11745          const C: u32 = 42;
11746
11747          fn main() {
11748              //println!("hello");
11749
11750              println!("world");
11751              //
11752              //
11753          }
11754
11755          fn another() {
11756              println!("another");
11757        +     println!("another");
11758          }
11759
11760        - fn another2() {
11761              println!("another2");
11762          }
11763        "#
11764        .unindent(),
11765    );
11766
11767    cx.update_editor(|editor, cx| {
11768        editor.select_all(&SelectAll, cx);
11769        editor.unfold_lines(&UnfoldLines, cx);
11770    });
11771    cx.executor().run_until_parked();
11772
11773    // The deletions reappear when unfolding.
11774    cx.assert_diff_hunks(
11775        r#"
11776        - use some::mod1;
11777          use some::mod2;
11778
11779          const A: u32 = 42;
11780        - const B: u32 = 42;
11781          const C: u32 = 42;
11782
11783          fn main() {
11784        -     println!("hello");
11785        +     //println!("hello");
11786
11787              println!("world");
11788        +     //
11789        +     //
11790          }
11791
11792          fn another() {
11793              println!("another");
11794        +     println!("another");
11795          }
11796
11797        - fn another2() {
11798              println!("another2");
11799          }
11800        "#
11801        .unindent(),
11802    );
11803}
11804
11805#[gpui::test]
11806async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11807    init_test(cx, |_| {});
11808
11809    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11810    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11811    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11812    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11813    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11814    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11815
11816    let buffer_1 = cx.new_model(|cx| {
11817        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11818        buffer.set_diff_base(Some(file_1_old.into()), cx);
11819        buffer
11820    });
11821    let buffer_2 = cx.new_model(|cx| {
11822        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11823        buffer.set_diff_base(Some(file_2_old.into()), cx);
11824        buffer
11825    });
11826    let buffer_3 = cx.new_model(|cx| {
11827        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11828        buffer.set_diff_base(Some(file_3_old.into()), cx);
11829        buffer
11830    });
11831
11832    let multi_buffer = cx.new_model(|cx| {
11833        let mut multibuffer = MultiBuffer::new(ReadWrite);
11834        multibuffer.push_excerpts(
11835            buffer_1.clone(),
11836            [
11837                ExcerptRange {
11838                    context: Point::new(0, 0)..Point::new(3, 0),
11839                    primary: None,
11840                },
11841                ExcerptRange {
11842                    context: Point::new(5, 0)..Point::new(7, 0),
11843                    primary: None,
11844                },
11845                ExcerptRange {
11846                    context: Point::new(9, 0)..Point::new(10, 3),
11847                    primary: None,
11848                },
11849            ],
11850            cx,
11851        );
11852        multibuffer.push_excerpts(
11853            buffer_2.clone(),
11854            [
11855                ExcerptRange {
11856                    context: Point::new(0, 0)..Point::new(3, 0),
11857                    primary: None,
11858                },
11859                ExcerptRange {
11860                    context: Point::new(5, 0)..Point::new(7, 0),
11861                    primary: None,
11862                },
11863                ExcerptRange {
11864                    context: Point::new(9, 0)..Point::new(10, 3),
11865                    primary: None,
11866                },
11867            ],
11868            cx,
11869        );
11870        multibuffer.push_excerpts(
11871            buffer_3.clone(),
11872            [
11873                ExcerptRange {
11874                    context: Point::new(0, 0)..Point::new(3, 0),
11875                    primary: None,
11876                },
11877                ExcerptRange {
11878                    context: Point::new(5, 0)..Point::new(7, 0),
11879                    primary: None,
11880                },
11881                ExcerptRange {
11882                    context: Point::new(9, 0)..Point::new(10, 3),
11883                    primary: None,
11884                },
11885            ],
11886            cx,
11887        );
11888        multibuffer
11889    });
11890
11891    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11892    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11893    cx.run_until_parked();
11894
11895    cx.assert_editor_state(
11896        &"
11897            ˇaaa
11898            ccc
11899            ddd
11900
11901            ggg
11902            hhh
11903
11904
11905            lll
11906            mmm
11907            NNN
11908
11909            qqq
11910            rrr
11911
11912            uuu
11913            111
11914            222
11915            333
11916
11917            666
11918            777
11919
11920            000
11921            !!!"
11922        .unindent(),
11923    );
11924
11925    cx.update_editor(|editor, cx| {
11926        editor.select_all(&SelectAll, cx);
11927        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11928    });
11929    cx.executor().run_until_parked();
11930
11931    cx.assert_diff_hunks(
11932        "
11933            aaa
11934          - bbb
11935            ccc
11936            ddd
11937
11938            ggg
11939            hhh
11940
11941
11942            lll
11943            mmm
11944          - nnn
11945          + NNN
11946
11947            qqq
11948            rrr
11949
11950            uuu
11951            111
11952            222
11953            333
11954
11955          + 666
11956            777
11957
11958            000
11959            !!!"
11960        .unindent(),
11961    );
11962}
11963
11964#[gpui::test]
11965async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
11966    init_test(cx, |_| {});
11967
11968    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
11969    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
11970
11971    let buffer = cx.new_model(|cx| {
11972        let mut buffer = Buffer::local(text.to_string(), cx);
11973        buffer.set_diff_base(Some(base.into()), cx);
11974        buffer
11975    });
11976
11977    let multi_buffer = cx.new_model(|cx| {
11978        let mut multibuffer = MultiBuffer::new(ReadWrite);
11979        multibuffer.push_excerpts(
11980            buffer.clone(),
11981            [
11982                ExcerptRange {
11983                    context: Point::new(0, 0)..Point::new(2, 0),
11984                    primary: None,
11985                },
11986                ExcerptRange {
11987                    context: Point::new(5, 0)..Point::new(7, 0),
11988                    primary: None,
11989                },
11990            ],
11991            cx,
11992        );
11993        multibuffer
11994    });
11995
11996    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11997    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11998    cx.run_until_parked();
11999
12000    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12001    cx.executor().run_until_parked();
12002
12003    cx.assert_diff_hunks(
12004        "
12005            aaa
12006          - bbb
12007          + BBB
12008
12009          - ddd
12010          - eee
12011          + EEE
12012            fff
12013        "
12014        .unindent(),
12015    );
12016}
12017
12018#[gpui::test]
12019async fn test_edits_around_expanded_insertion_hunks(
12020    executor: BackgroundExecutor,
12021    cx: &mut gpui::TestAppContext,
12022) {
12023    init_test(cx, |_| {});
12024
12025    let mut cx = EditorTestContext::new(cx).await;
12026
12027    let diff_base = r#"
12028        use some::mod1;
12029        use some::mod2;
12030
12031        const A: u32 = 42;
12032
12033        fn main() {
12034            println!("hello");
12035
12036            println!("world");
12037        }
12038        "#
12039    .unindent();
12040    executor.run_until_parked();
12041    cx.set_state(
12042        &r#"
12043        use some::mod1;
12044        use some::mod2;
12045
12046        const A: u32 = 42;
12047        const B: u32 = 42;
12048        const C: u32 = 42;
12049        ˇ
12050
12051        fn main() {
12052            println!("hello");
12053
12054            println!("world");
12055        }
12056        "#
12057        .unindent(),
12058    );
12059
12060    cx.set_diff_base(Some(&diff_base));
12061    executor.run_until_parked();
12062
12063    cx.update_editor(|editor, cx| {
12064        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12065    });
12066    executor.run_until_parked();
12067
12068    cx.assert_diff_hunks(
12069        r#"
12070        use some::mod1;
12071        use some::mod2;
12072
12073        const A: u32 = 42;
12074      + const B: u32 = 42;
12075      + const C: u32 = 42;
12076      +
12077
12078        fn main() {
12079            println!("hello");
12080
12081            println!("world");
12082        }
12083        "#
12084        .unindent(),
12085    );
12086
12087    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
12088    executor.run_until_parked();
12089
12090    cx.assert_diff_hunks(
12091        r#"
12092        use some::mod1;
12093        use some::mod2;
12094
12095        const A: u32 = 42;
12096      + const B: u32 = 42;
12097      + const C: u32 = 42;
12098      + const D: u32 = 42;
12099      +
12100
12101        fn main() {
12102            println!("hello");
12103
12104            println!("world");
12105        }
12106        "#
12107        .unindent(),
12108    );
12109
12110    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
12111    executor.run_until_parked();
12112
12113    cx.assert_diff_hunks(
12114        r#"
12115        use some::mod1;
12116        use some::mod2;
12117
12118        const A: u32 = 42;
12119      + const B: u32 = 42;
12120      + const C: u32 = 42;
12121      + const D: u32 = 42;
12122      + const E: u32 = 42;
12123      +
12124
12125        fn main() {
12126            println!("hello");
12127
12128            println!("world");
12129        }
12130        "#
12131        .unindent(),
12132    );
12133
12134    cx.update_editor(|editor, cx| {
12135        editor.delete_line(&DeleteLine, cx);
12136    });
12137    executor.run_until_parked();
12138
12139    cx.assert_diff_hunks(
12140        r#"
12141        use some::mod1;
12142        use some::mod2;
12143
12144        const A: u32 = 42;
12145      + const B: u32 = 42;
12146      + const C: u32 = 42;
12147      + const D: u32 = 42;
12148      + const E: u32 = 42;
12149
12150        fn main() {
12151            println!("hello");
12152
12153            println!("world");
12154        }
12155        "#
12156        .unindent(),
12157    );
12158
12159    cx.update_editor(|editor, cx| {
12160        editor.move_up(&MoveUp, cx);
12161        editor.delete_line(&DeleteLine, cx);
12162        editor.move_up(&MoveUp, cx);
12163        editor.delete_line(&DeleteLine, cx);
12164        editor.move_up(&MoveUp, cx);
12165        editor.delete_line(&DeleteLine, cx);
12166    });
12167    executor.run_until_parked();
12168    cx.assert_editor_state(
12169        &r#"
12170        use some::mod1;
12171        use some::mod2;
12172
12173        const A: u32 = 42;
12174        const B: u32 = 42;
12175        ˇ
12176        fn main() {
12177            println!("hello");
12178
12179            println!("world");
12180        }
12181        "#
12182        .unindent(),
12183    );
12184
12185    cx.assert_diff_hunks(
12186        r#"
12187        use some::mod1;
12188        use some::mod2;
12189
12190        const A: u32 = 42;
12191      + const B: u32 = 42;
12192
12193        fn main() {
12194            println!("hello");
12195
12196            println!("world");
12197        }
12198        "#
12199        .unindent(),
12200    );
12201
12202    cx.update_editor(|editor, cx| {
12203        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
12204        editor.delete_line(&DeleteLine, cx);
12205    });
12206    executor.run_until_parked();
12207    cx.assert_diff_hunks(
12208        r#"
12209        use some::mod1;
12210      - use some::mod2;
12211      -
12212      - const A: u32 = 42;
12213
12214        fn main() {
12215            println!("hello");
12216
12217            println!("world");
12218        }
12219        "#
12220        .unindent(),
12221    );
12222}
12223
12224#[gpui::test]
12225async fn test_edits_around_expanded_deletion_hunks(
12226    executor: BackgroundExecutor,
12227    cx: &mut gpui::TestAppContext,
12228) {
12229    init_test(cx, |_| {});
12230
12231    let mut cx = EditorTestContext::new(cx).await;
12232
12233    let diff_base = r#"
12234        use some::mod1;
12235        use some::mod2;
12236
12237        const A: u32 = 42;
12238        const B: u32 = 42;
12239        const C: u32 = 42;
12240
12241
12242        fn main() {
12243            println!("hello");
12244
12245            println!("world");
12246        }
12247    "#
12248    .unindent();
12249    executor.run_until_parked();
12250    cx.set_state(
12251        &r#"
12252        use some::mod1;
12253        use some::mod2;
12254
12255        ˇconst B: u32 = 42;
12256        const C: u32 = 42;
12257
12258
12259        fn main() {
12260            println!("hello");
12261
12262            println!("world");
12263        }
12264        "#
12265        .unindent(),
12266    );
12267
12268    cx.set_diff_base(Some(&diff_base));
12269    executor.run_until_parked();
12270
12271    cx.update_editor(|editor, cx| {
12272        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12273    });
12274    executor.run_until_parked();
12275
12276    cx.assert_diff_hunks(
12277        r#"
12278        use some::mod1;
12279        use some::mod2;
12280
12281      - const A: u32 = 42;
12282        const B: u32 = 42;
12283        const C: u32 = 42;
12284
12285
12286        fn main() {
12287            println!("hello");
12288
12289            println!("world");
12290        }
12291        "#
12292        .unindent(),
12293    );
12294
12295    cx.update_editor(|editor, cx| {
12296        editor.delete_line(&DeleteLine, cx);
12297    });
12298    executor.run_until_parked();
12299    cx.assert_editor_state(
12300        &r#"
12301        use some::mod1;
12302        use some::mod2;
12303
12304        ˇconst C: u32 = 42;
12305
12306
12307        fn main() {
12308            println!("hello");
12309
12310            println!("world");
12311        }
12312        "#
12313        .unindent(),
12314    );
12315    cx.assert_diff_hunks(
12316        r#"
12317        use some::mod1;
12318        use some::mod2;
12319
12320      - const A: u32 = 42;
12321      - const B: u32 = 42;
12322        const C: u32 = 42;
12323
12324
12325        fn main() {
12326            println!("hello");
12327
12328            println!("world");
12329        }
12330        "#
12331        .unindent(),
12332    );
12333
12334    cx.update_editor(|editor, cx| {
12335        editor.delete_line(&DeleteLine, cx);
12336    });
12337    executor.run_until_parked();
12338    cx.assert_editor_state(
12339        &r#"
12340        use some::mod1;
12341        use some::mod2;
12342
12343        ˇ
12344
12345        fn main() {
12346            println!("hello");
12347
12348            println!("world");
12349        }
12350        "#
12351        .unindent(),
12352    );
12353    cx.assert_diff_hunks(
12354        r#"
12355        use some::mod1;
12356        use some::mod2;
12357
12358      - const A: u32 = 42;
12359      - const B: u32 = 42;
12360      - const C: u32 = 42;
12361
12362
12363        fn main() {
12364            println!("hello");
12365
12366            println!("world");
12367        }
12368        "#
12369        .unindent(),
12370    );
12371
12372    cx.update_editor(|editor, cx| {
12373        editor.handle_input("replacement", cx);
12374    });
12375    executor.run_until_parked();
12376    cx.assert_editor_state(
12377        &r#"
12378        use some::mod1;
12379        use some::mod2;
12380
12381        replacementˇ
12382
12383        fn main() {
12384            println!("hello");
12385
12386            println!("world");
12387        }
12388        "#
12389        .unindent(),
12390    );
12391    cx.assert_diff_hunks(
12392        r#"
12393        use some::mod1;
12394        use some::mod2;
12395
12396      - const A: u32 = 42;
12397      - const B: u32 = 42;
12398      - const C: u32 = 42;
12399      -
12400      + replacement
12401
12402        fn main() {
12403            println!("hello");
12404
12405            println!("world");
12406        }
12407        "#
12408        .unindent(),
12409    );
12410}
12411
12412#[gpui::test]
12413async fn test_edit_after_expanded_modification_hunk(
12414    executor: BackgroundExecutor,
12415    cx: &mut gpui::TestAppContext,
12416) {
12417    init_test(cx, |_| {});
12418
12419    let mut cx = EditorTestContext::new(cx).await;
12420
12421    let diff_base = r#"
12422        use some::mod1;
12423        use some::mod2;
12424
12425        const A: u32 = 42;
12426        const B: u32 = 42;
12427        const C: u32 = 42;
12428        const D: u32 = 42;
12429
12430
12431        fn main() {
12432            println!("hello");
12433
12434            println!("world");
12435        }"#
12436    .unindent();
12437
12438    cx.set_state(
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 = 43ˇ
12446        const D: u32 = 42;
12447
12448
12449        fn main() {
12450            println!("hello");
12451
12452            println!("world");
12453        }"#
12454        .unindent(),
12455    );
12456
12457    cx.set_diff_base(Some(&diff_base));
12458    executor.run_until_parked();
12459    cx.update_editor(|editor, cx| {
12460        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12461    });
12462    executor.run_until_parked();
12463
12464    cx.assert_diff_hunks(
12465        r#"
12466        use some::mod1;
12467        use some::mod2;
12468
12469        const A: u32 = 42;
12470        const B: u32 = 42;
12471      - const C: u32 = 42;
12472      + const C: u32 = 43
12473        const D: u32 = 42;
12474
12475
12476        fn main() {
12477            println!("hello");
12478
12479            println!("world");
12480        }"#
12481        .unindent(),
12482    );
12483
12484    cx.update_editor(|editor, cx| {
12485        editor.handle_input("\nnew_line\n", cx);
12486    });
12487    executor.run_until_parked();
12488
12489    cx.assert_diff_hunks(
12490        r#"
12491        use some::mod1;
12492        use some::mod2;
12493
12494        const A: u32 = 42;
12495        const B: u32 = 42;
12496      - const C: u32 = 42;
12497      + const C: u32 = 43
12498      + new_line
12499      +
12500        const D: u32 = 42;
12501
12502
12503        fn main() {
12504            println!("hello");
12505
12506            println!("world");
12507        }"#
12508        .unindent(),
12509    );
12510}
12511
12512async fn setup_indent_guides_editor(
12513    text: &str,
12514    cx: &mut gpui::TestAppContext,
12515) -> (BufferId, EditorTestContext) {
12516    init_test(cx, |_| {});
12517
12518    let mut cx = EditorTestContext::new(cx).await;
12519
12520    let buffer_id = cx.update_editor(|editor, cx| {
12521        editor.set_text(text, cx);
12522        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12523
12524        buffer_ids[0]
12525    });
12526
12527    (buffer_id, cx)
12528}
12529
12530fn assert_indent_guides(
12531    range: Range<u32>,
12532    expected: Vec<IndentGuide>,
12533    active_indices: Option<Vec<usize>>,
12534    cx: &mut EditorTestContext,
12535) {
12536    let indent_guides = cx.update_editor(|editor, cx| {
12537        let snapshot = editor.snapshot(cx).display_snapshot;
12538        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12539            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12540            true,
12541            &snapshot,
12542            cx,
12543        );
12544
12545        indent_guides.sort_by(|a, b| {
12546            a.depth.cmp(&b.depth).then(
12547                a.start_row
12548                    .cmp(&b.start_row)
12549                    .then(a.end_row.cmp(&b.end_row)),
12550            )
12551        });
12552        indent_guides
12553    });
12554
12555    if let Some(expected) = active_indices {
12556        let active_indices = cx.update_editor(|editor, cx| {
12557            let snapshot = editor.snapshot(cx).display_snapshot;
12558            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12559        });
12560
12561        assert_eq!(
12562            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12563            expected,
12564            "Active indent guide indices do not match"
12565        );
12566    }
12567
12568    let expected: Vec<_> = expected
12569        .into_iter()
12570        .map(|guide| MultiBufferIndentGuide {
12571            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12572            buffer: guide,
12573        })
12574        .collect();
12575
12576    assert_eq!(indent_guides, expected, "Indent guides do not match");
12577}
12578
12579fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12580    IndentGuide {
12581        buffer_id,
12582        start_row,
12583        end_row,
12584        depth,
12585        tab_size: 4,
12586        settings: IndentGuideSettings {
12587            enabled: true,
12588            line_width: 1,
12589            active_line_width: 1,
12590            ..Default::default()
12591        },
12592    }
12593}
12594
12595#[gpui::test]
12596async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12597    let (buffer_id, mut cx) = setup_indent_guides_editor(
12598        &"
12599    fn main() {
12600        let a = 1;
12601    }"
12602        .unindent(),
12603        cx,
12604    )
12605    .await;
12606
12607    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12608}
12609
12610#[gpui::test]
12611async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12612    let (buffer_id, mut cx) = setup_indent_guides_editor(
12613        &"
12614    fn main() {
12615        let a = 1;
12616        let b = 2;
12617    }"
12618        .unindent(),
12619        cx,
12620    )
12621    .await;
12622
12623    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12624}
12625
12626#[gpui::test]
12627async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12628    let (buffer_id, mut cx) = setup_indent_guides_editor(
12629        &"
12630    fn main() {
12631        let a = 1;
12632        if a == 3 {
12633            let b = 2;
12634        } else {
12635            let c = 3;
12636        }
12637    }"
12638        .unindent(),
12639        cx,
12640    )
12641    .await;
12642
12643    assert_indent_guides(
12644        0..8,
12645        vec![
12646            indent_guide(buffer_id, 1, 6, 0),
12647            indent_guide(buffer_id, 3, 3, 1),
12648            indent_guide(buffer_id, 5, 5, 1),
12649        ],
12650        None,
12651        &mut cx,
12652    );
12653}
12654
12655#[gpui::test]
12656async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12657    let (buffer_id, mut cx) = setup_indent_guides_editor(
12658        &"
12659    fn main() {
12660        let a = 1;
12661            let b = 2;
12662        let c = 3;
12663    }"
12664        .unindent(),
12665        cx,
12666    )
12667    .await;
12668
12669    assert_indent_guides(
12670        0..5,
12671        vec![
12672            indent_guide(buffer_id, 1, 3, 0),
12673            indent_guide(buffer_id, 2, 2, 1),
12674        ],
12675        None,
12676        &mut cx,
12677    );
12678}
12679
12680#[gpui::test]
12681async fn test_indent_guide_continues_on_empty_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            let c = 3;
12688        }"
12689        .unindent(),
12690        cx,
12691    )
12692    .await;
12693
12694    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12695}
12696
12697#[gpui::test]
12698async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12699    let (buffer_id, mut cx) = setup_indent_guides_editor(
12700        &"
12701        fn main() {
12702            let a = 1;
12703
12704            let c = 3;
12705
12706            if a == 3 {
12707                let b = 2;
12708            } else {
12709                let c = 3;
12710            }
12711        }"
12712        .unindent(),
12713        cx,
12714    )
12715    .await;
12716
12717    assert_indent_guides(
12718        0..11,
12719        vec![
12720            indent_guide(buffer_id, 1, 9, 0),
12721            indent_guide(buffer_id, 6, 6, 1),
12722            indent_guide(buffer_id, 8, 8, 1),
12723        ],
12724        None,
12725        &mut cx,
12726    );
12727}
12728
12729#[gpui::test]
12730async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12731    let (buffer_id, mut cx) = setup_indent_guides_editor(
12732        &"
12733        fn main() {
12734            let a = 1;
12735
12736            let c = 3;
12737
12738            if a == 3 {
12739                let b = 2;
12740            } else {
12741                let c = 3;
12742            }
12743        }"
12744        .unindent(),
12745        cx,
12746    )
12747    .await;
12748
12749    assert_indent_guides(
12750        1..11,
12751        vec![
12752            indent_guide(buffer_id, 1, 9, 0),
12753            indent_guide(buffer_id, 6, 6, 1),
12754            indent_guide(buffer_id, 8, 8, 1),
12755        ],
12756        None,
12757        &mut cx,
12758    );
12759}
12760
12761#[gpui::test]
12762async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12763    let (buffer_id, mut cx) = setup_indent_guides_editor(
12764        &"
12765        fn main() {
12766            let a = 1;
12767
12768            let c = 3;
12769
12770            if a == 3 {
12771                let b = 2;
12772            } else {
12773                let c = 3;
12774            }
12775        }"
12776        .unindent(),
12777        cx,
12778    )
12779    .await;
12780
12781    assert_indent_guides(
12782        1..10,
12783        vec![
12784            indent_guide(buffer_id, 1, 9, 0),
12785            indent_guide(buffer_id, 6, 6, 1),
12786            indent_guide(buffer_id, 8, 8, 1),
12787        ],
12788        None,
12789        &mut cx,
12790    );
12791}
12792
12793#[gpui::test]
12794async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12795    let (buffer_id, mut cx) = setup_indent_guides_editor(
12796        &"
12797        block1
12798            block2
12799                block3
12800                    block4
12801            block2
12802        block1
12803        block1"
12804            .unindent(),
12805        cx,
12806    )
12807    .await;
12808
12809    assert_indent_guides(
12810        1..10,
12811        vec![
12812            indent_guide(buffer_id, 1, 4, 0),
12813            indent_guide(buffer_id, 2, 3, 1),
12814            indent_guide(buffer_id, 3, 3, 2),
12815        ],
12816        None,
12817        &mut cx,
12818    );
12819}
12820
12821#[gpui::test]
12822async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12823    let (buffer_id, mut cx) = setup_indent_guides_editor(
12824        &"
12825        block1
12826            block2
12827                block3
12828
12829        block1
12830        block1"
12831            .unindent(),
12832        cx,
12833    )
12834    .await;
12835
12836    assert_indent_guides(
12837        0..6,
12838        vec![
12839            indent_guide(buffer_id, 1, 2, 0),
12840            indent_guide(buffer_id, 2, 2, 1),
12841        ],
12842        None,
12843        &mut cx,
12844    );
12845}
12846
12847#[gpui::test]
12848async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12849    let (buffer_id, mut cx) = setup_indent_guides_editor(
12850        &"
12851        block1
12852
12853
12854
12855            block2
12856        "
12857        .unindent(),
12858        cx,
12859    )
12860    .await;
12861
12862    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12863}
12864
12865#[gpui::test]
12866async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12867    let (buffer_id, mut cx) = setup_indent_guides_editor(
12868        &"
12869        def a:
12870        \tb = 3
12871        \tif True:
12872        \t\tc = 4
12873        \t\td = 5
12874        \tprint(b)
12875        "
12876        .unindent(),
12877        cx,
12878    )
12879    .await;
12880
12881    assert_indent_guides(
12882        0..6,
12883        vec![
12884            indent_guide(buffer_id, 1, 6, 0),
12885            indent_guide(buffer_id, 3, 4, 1),
12886        ],
12887        None,
12888        &mut cx,
12889    );
12890}
12891
12892#[gpui::test]
12893async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12894    let (buffer_id, mut cx) = setup_indent_guides_editor(
12895        &"
12896    fn main() {
12897        let a = 1;
12898    }"
12899        .unindent(),
12900        cx,
12901    )
12902    .await;
12903
12904    cx.update_editor(|editor, cx| {
12905        editor.change_selections(None, cx, |s| {
12906            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12907        });
12908    });
12909
12910    assert_indent_guides(
12911        0..3,
12912        vec![indent_guide(buffer_id, 1, 1, 0)],
12913        Some(vec![0]),
12914        &mut cx,
12915    );
12916}
12917
12918#[gpui::test]
12919async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12920    let (buffer_id, mut cx) = setup_indent_guides_editor(
12921        &"
12922    fn main() {
12923        if 1 == 2 {
12924            let a = 1;
12925        }
12926    }"
12927        .unindent(),
12928        cx,
12929    )
12930    .await;
12931
12932    cx.update_editor(|editor, cx| {
12933        editor.change_selections(None, cx, |s| {
12934            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12935        });
12936    });
12937
12938    assert_indent_guides(
12939        0..4,
12940        vec![
12941            indent_guide(buffer_id, 1, 3, 0),
12942            indent_guide(buffer_id, 2, 2, 1),
12943        ],
12944        Some(vec![1]),
12945        &mut cx,
12946    );
12947
12948    cx.update_editor(|editor, cx| {
12949        editor.change_selections(None, cx, |s| {
12950            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12951        });
12952    });
12953
12954    assert_indent_guides(
12955        0..4,
12956        vec![
12957            indent_guide(buffer_id, 1, 3, 0),
12958            indent_guide(buffer_id, 2, 2, 1),
12959        ],
12960        Some(vec![1]),
12961        &mut cx,
12962    );
12963
12964    cx.update_editor(|editor, cx| {
12965        editor.change_selections(None, cx, |s| {
12966            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12967        });
12968    });
12969
12970    assert_indent_guides(
12971        0..4,
12972        vec![
12973            indent_guide(buffer_id, 1, 3, 0),
12974            indent_guide(buffer_id, 2, 2, 1),
12975        ],
12976        Some(vec![0]),
12977        &mut cx,
12978    );
12979}
12980
12981#[gpui::test]
12982async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12983    let (buffer_id, mut cx) = setup_indent_guides_editor(
12984        &"
12985    fn main() {
12986        let a = 1;
12987
12988        let b = 2;
12989    }"
12990        .unindent(),
12991        cx,
12992    )
12993    .await;
12994
12995    cx.update_editor(|editor, cx| {
12996        editor.change_selections(None, cx, |s| {
12997            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12998        });
12999    });
13000
13001    assert_indent_guides(
13002        0..5,
13003        vec![indent_guide(buffer_id, 1, 3, 0)],
13004        Some(vec![0]),
13005        &mut cx,
13006    );
13007}
13008
13009#[gpui::test]
13010async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13011    let (buffer_id, mut cx) = setup_indent_guides_editor(
13012        &"
13013    def m:
13014        a = 1
13015        pass"
13016            .unindent(),
13017        cx,
13018    )
13019    .await;
13020
13021    cx.update_editor(|editor, cx| {
13022        editor.change_selections(None, cx, |s| {
13023            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13024        });
13025    });
13026
13027    assert_indent_guides(
13028        0..3,
13029        vec![indent_guide(buffer_id, 1, 2, 0)],
13030        Some(vec![0]),
13031        &mut cx,
13032    );
13033}
13034
13035#[gpui::test]
13036fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13037    init_test(cx, |_| {});
13038
13039    let editor = cx.add_window(|cx| {
13040        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13041        build_editor(buffer, cx)
13042    });
13043
13044    let render_args = Arc::new(Mutex::new(None));
13045    let snapshot = editor
13046        .update(cx, |editor, cx| {
13047            let snapshot = editor.buffer().read(cx).snapshot(cx);
13048            let range =
13049                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13050
13051            struct RenderArgs {
13052                row: MultiBufferRow,
13053                folded: bool,
13054                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13055            }
13056
13057            let crease = Crease::new(
13058                range,
13059                FoldPlaceholder::test(),
13060                {
13061                    let toggle_callback = render_args.clone();
13062                    move |row, folded, callback, _cx| {
13063                        *toggle_callback.lock() = Some(RenderArgs {
13064                            row,
13065                            folded,
13066                            callback,
13067                        });
13068                        div()
13069                    }
13070                },
13071                |_row, _folded, _cx| div(),
13072            );
13073
13074            editor.insert_creases(Some(crease), cx);
13075            let snapshot = editor.snapshot(cx);
13076            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13077            snapshot
13078        })
13079        .unwrap();
13080
13081    let render_args = render_args.lock().take().unwrap();
13082    assert_eq!(render_args.row, MultiBufferRow(1));
13083    assert!(!render_args.folded);
13084    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13085
13086    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
13087        .unwrap();
13088    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13089    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
13090
13091    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
13092        .unwrap();
13093    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
13094    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13095}
13096
13097#[gpui::test]
13098async fn test_input_text(cx: &mut gpui::TestAppContext) {
13099    init_test(cx, |_| {});
13100    let mut cx = EditorTestContext::new(cx).await;
13101
13102    cx.set_state(
13103        &r#"ˇone
13104        two
13105
13106        three
13107        fourˇ
13108        five
13109
13110        siˇx"#
13111            .unindent(),
13112    );
13113
13114    cx.dispatch_action(HandleInput(String::new()));
13115    cx.assert_editor_state(
13116        &r#"ˇone
13117        two
13118
13119        three
13120        fourˇ
13121        five
13122
13123        siˇx"#
13124            .unindent(),
13125    );
13126
13127    cx.dispatch_action(HandleInput("AAAA".to_string()));
13128    cx.assert_editor_state(
13129        &r#"AAAAˇone
13130        two
13131
13132        three
13133        fourAAAAˇ
13134        five
13135
13136        siAAAAˇx"#
13137            .unindent(),
13138    );
13139}
13140
13141#[gpui::test]
13142async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
13143    init_test(cx, |_| {});
13144
13145    let mut cx = EditorTestContext::new(cx).await;
13146    cx.set_state(
13147        r#"let foo = 1;
13148let foo = 2;
13149let foo = 3;
13150let fooˇ = 4;
13151let foo = 5;
13152let foo = 6;
13153let foo = 7;
13154let foo = 8;
13155let foo = 9;
13156let foo = 10;
13157let foo = 11;
13158let foo = 12;
13159let foo = 13;
13160let foo = 14;
13161let foo = 15;"#,
13162    );
13163
13164    cx.update_editor(|e, cx| {
13165        assert_eq!(
13166            e.next_scroll_position,
13167            NextScrollCursorCenterTopBottom::Center,
13168            "Default next scroll direction is center",
13169        );
13170
13171        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13172        assert_eq!(
13173            e.next_scroll_position,
13174            NextScrollCursorCenterTopBottom::Top,
13175            "After center, next scroll direction should be top",
13176        );
13177
13178        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13179        assert_eq!(
13180            e.next_scroll_position,
13181            NextScrollCursorCenterTopBottom::Bottom,
13182            "After top, next scroll direction should be bottom",
13183        );
13184
13185        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13186        assert_eq!(
13187            e.next_scroll_position,
13188            NextScrollCursorCenterTopBottom::Center,
13189            "After bottom, scrolling should start over",
13190        );
13191
13192        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
13193        assert_eq!(
13194            e.next_scroll_position,
13195            NextScrollCursorCenterTopBottom::Top,
13196            "Scrolling continues if retriggered fast enough"
13197        );
13198    });
13199
13200    cx.executor()
13201        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
13202    cx.executor().run_until_parked();
13203    cx.update_editor(|e, _| {
13204        assert_eq!(
13205            e.next_scroll_position,
13206            NextScrollCursorCenterTopBottom::Center,
13207            "If scrolling is not triggered fast enough, it should reset"
13208        );
13209    });
13210}
13211
13212#[gpui::test]
13213async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
13214    init_test(cx, |_| {});
13215    let mut cx = EditorLspTestContext::new_rust(
13216        lsp::ServerCapabilities {
13217            definition_provider: Some(lsp::OneOf::Left(true)),
13218            references_provider: Some(lsp::OneOf::Left(true)),
13219            ..lsp::ServerCapabilities::default()
13220        },
13221        cx,
13222    )
13223    .await;
13224
13225    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
13226        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
13227            move |params, _| async move {
13228                if empty_go_to_definition {
13229                    Ok(None)
13230                } else {
13231                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13232                        uri: params.text_document_position_params.text_document.uri,
13233                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13234                    })))
13235                }
13236            },
13237        );
13238        let references =
13239            cx.lsp
13240                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13241                    Ok(Some(vec![lsp::Location {
13242                        uri: params.text_document_position.text_document.uri,
13243                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13244                    }]))
13245                });
13246        (go_to_definition, references)
13247    };
13248
13249    cx.set_state(
13250        &r#"fn one() {
13251            let mut a = ˇtwo();
13252        }
13253
13254        fn two() {}"#
13255            .unindent(),
13256    );
13257    set_up_lsp_handlers(false, &mut cx);
13258    let navigated = cx
13259        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13260        .await
13261        .expect("Failed to navigate to definition");
13262    assert_eq!(
13263        navigated,
13264        Navigated::Yes,
13265        "Should have navigated to definition from the GetDefinition response"
13266    );
13267    cx.assert_editor_state(
13268        &r#"fn one() {
13269            let mut a = two();
13270        }
13271
13272        fn «twoˇ»() {}"#
13273            .unindent(),
13274    );
13275
13276    let editors = cx.update_workspace(|workspace, cx| {
13277        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13278    });
13279    cx.update_editor(|_, test_editor_cx| {
13280        assert_eq!(
13281            editors.len(),
13282            1,
13283            "Initially, only one, test, editor should be open in the workspace"
13284        );
13285        assert_eq!(
13286            test_editor_cx.view(),
13287            editors.last().expect("Asserted len is 1")
13288        );
13289    });
13290
13291    set_up_lsp_handlers(true, &mut cx);
13292    let navigated = cx
13293        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13294        .await
13295        .expect("Failed to navigate to lookup references");
13296    assert_eq!(
13297        navigated,
13298        Navigated::Yes,
13299        "Should have navigated to references as a fallback after empty GoToDefinition response"
13300    );
13301    // We should not change the selections in the existing file,
13302    // if opening another milti buffer with the references
13303    cx.assert_editor_state(
13304        &r#"fn one() {
13305            let mut a = two();
13306        }
13307
13308        fn «twoˇ»() {}"#
13309            .unindent(),
13310    );
13311    let editors = cx.update_workspace(|workspace, cx| {
13312        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13313    });
13314    cx.update_editor(|_, test_editor_cx| {
13315        assert_eq!(
13316            editors.len(),
13317            2,
13318            "After falling back to references search, we open a new editor with the results"
13319        );
13320        let references_fallback_text = editors
13321            .into_iter()
13322            .find(|new_editor| new_editor != test_editor_cx.view())
13323            .expect("Should have one non-test editor now")
13324            .read(test_editor_cx)
13325            .text(test_editor_cx);
13326        assert_eq!(
13327            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13328            "Should use the range from the references response and not the GoToDefinition one"
13329        );
13330    });
13331}
13332
13333#[gpui::test]
13334async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
13335    init_test(cx, |_| {});
13336
13337    let language = Arc::new(Language::new(
13338        LanguageConfig::default(),
13339        Some(tree_sitter_rust::LANGUAGE.into()),
13340    ));
13341
13342    let text = r#"
13343        #[cfg(test)]
13344        mod tests() {
13345            #[test]
13346            fn runnable_1() {
13347                let a = 1;
13348            }
13349
13350            #[test]
13351            fn runnable_2() {
13352                let a = 1;
13353                let b = 2;
13354            }
13355        }
13356    "#
13357    .unindent();
13358
13359    let fs = FakeFs::new(cx.executor());
13360    fs.insert_file("/file.rs", Default::default()).await;
13361
13362    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13363    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
13364    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13365    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
13366    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
13367
13368    let editor = cx.new_view(|cx| {
13369        Editor::new(
13370            EditorMode::Full,
13371            multi_buffer,
13372            Some(project.clone()),
13373            true,
13374            cx,
13375        )
13376    });
13377
13378    editor.update(cx, |editor, cx| {
13379        editor.tasks.insert(
13380            (buffer.read(cx).remote_id(), 3),
13381            RunnableTasks {
13382                templates: vec![],
13383                offset: MultiBufferOffset(43),
13384                column: 0,
13385                extra_variables: HashMap::default(),
13386                context_range: BufferOffset(43)..BufferOffset(85),
13387            },
13388        );
13389        editor.tasks.insert(
13390            (buffer.read(cx).remote_id(), 8),
13391            RunnableTasks {
13392                templates: vec![],
13393                offset: MultiBufferOffset(86),
13394                column: 0,
13395                extra_variables: HashMap::default(),
13396                context_range: BufferOffset(86)..BufferOffset(191),
13397            },
13398        );
13399
13400        // Test finding task when cursor is inside function body
13401        editor.change_selections(None, cx, |s| {
13402            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
13403        });
13404        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13405        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
13406
13407        // Test finding task when cursor is on function name
13408        editor.change_selections(None, cx, |s| {
13409            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
13410        });
13411        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
13412        assert_eq!(row, 8, "Should find task when cursor is on function name");
13413    });
13414}
13415
13416fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13417    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13418    point..point
13419}
13420
13421fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13422    let (text, ranges) = marked_text_ranges(marked_text, true);
13423    assert_eq!(view.text(cx), text);
13424    assert_eq!(
13425        view.selections.ranges(cx),
13426        ranges,
13427        "Assert selections are {}",
13428        marked_text
13429    );
13430}
13431
13432pub fn handle_signature_help_request(
13433    cx: &mut EditorLspTestContext,
13434    mocked_response: lsp::SignatureHelp,
13435) -> impl Future<Output = ()> {
13436    let mut request =
13437        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13438            let mocked_response = mocked_response.clone();
13439            async move { Ok(Some(mocked_response)) }
13440        });
13441
13442    async move {
13443        request.next().await;
13444    }
13445}
13446
13447/// Handle completion request passing a marked string specifying where the completion
13448/// should be triggered from using '|' character, what range should be replaced, and what completions
13449/// should be returned using '<' and '>' to delimit the range
13450pub fn handle_completion_request(
13451    cx: &mut EditorLspTestContext,
13452    marked_string: &str,
13453    completions: Vec<&'static str>,
13454    counter: Arc<AtomicUsize>,
13455) -> impl Future<Output = ()> {
13456    let complete_from_marker: TextRangeMarker = '|'.into();
13457    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13458    let (_, mut marked_ranges) = marked_text_ranges_by(
13459        marked_string,
13460        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13461    );
13462
13463    let complete_from_position =
13464        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13465    let replace_range =
13466        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13467
13468    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13469        let completions = completions.clone();
13470        counter.fetch_add(1, atomic::Ordering::Release);
13471        async move {
13472            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13473            assert_eq!(
13474                params.text_document_position.position,
13475                complete_from_position
13476            );
13477            Ok(Some(lsp::CompletionResponse::Array(
13478                completions
13479                    .iter()
13480                    .map(|completion_text| lsp::CompletionItem {
13481                        label: completion_text.to_string(),
13482                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13483                            range: replace_range,
13484                            new_text: completion_text.to_string(),
13485                        })),
13486                        ..Default::default()
13487                    })
13488                    .collect(),
13489            )))
13490        }
13491    });
13492
13493    async move {
13494        request.next().await;
13495    }
13496}
13497
13498fn handle_resolve_completion_request(
13499    cx: &mut EditorLspTestContext,
13500    edits: Option<Vec<(&'static str, &'static str)>>,
13501) -> impl Future<Output = ()> {
13502    let edits = edits.map(|edits| {
13503        edits
13504            .iter()
13505            .map(|(marked_string, new_text)| {
13506                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13507                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13508                lsp::TextEdit::new(replace_range, new_text.to_string())
13509            })
13510            .collect::<Vec<_>>()
13511    });
13512
13513    let mut request =
13514        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13515            let edits = edits.clone();
13516            async move {
13517                Ok(lsp::CompletionItem {
13518                    additional_text_edits: edits,
13519                    ..Default::default()
13520                })
13521            }
13522        });
13523
13524    async move {
13525        request.next().await;
13526    }
13527}
13528
13529pub(crate) fn update_test_language_settings(
13530    cx: &mut TestAppContext,
13531    f: impl Fn(&mut AllLanguageSettingsContent),
13532) {
13533    cx.update(|cx| {
13534        SettingsStore::update_global(cx, |store, cx| {
13535            store.update_user_settings::<AllLanguageSettings>(cx, f);
13536        });
13537    });
13538}
13539
13540pub(crate) fn update_test_project_settings(
13541    cx: &mut TestAppContext,
13542    f: impl Fn(&mut ProjectSettings),
13543) {
13544    cx.update(|cx| {
13545        SettingsStore::update_global(cx, |store, cx| {
13546            store.update_user_settings::<ProjectSettings>(cx, f);
13547        });
13548    });
13549}
13550
13551pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13552    cx.update(|cx| {
13553        assets::Assets.load_test_fonts(cx);
13554        let store = SettingsStore::test(cx);
13555        cx.set_global(store);
13556        theme::init(theme::LoadThemes::JustBase, cx);
13557        release_channel::init(SemanticVersion::default(), cx);
13558        client::init_settings(cx);
13559        language::init(cx);
13560        Project::init_settings(cx);
13561        workspace::init_settings(cx);
13562        crate::init(cx);
13563    });
13564
13565    update_test_language_settings(cx, f);
13566}
13567
13568pub(crate) fn rust_lang() -> Arc<Language> {
13569    Arc::new(Language::new(
13570        LanguageConfig {
13571            name: "Rust".into(),
13572            matcher: LanguageMatcher {
13573                path_suffixes: vec!["rs".to_string()],
13574                ..Default::default()
13575            },
13576            ..Default::default()
13577        },
13578        Some(tree_sitter_rust::LANGUAGE.into()),
13579    ))
13580}
13581
13582#[track_caller]
13583fn assert_hunk_revert(
13584    not_reverted_text_with_selections: &str,
13585    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13586    expected_reverted_text_with_selections: &str,
13587    base_text: &str,
13588    cx: &mut EditorLspTestContext,
13589) {
13590    cx.set_state(not_reverted_text_with_selections);
13591    cx.update_editor(|editor, cx| {
13592        editor
13593            .buffer()
13594            .read(cx)
13595            .as_singleton()
13596            .unwrap()
13597            .update(cx, |buffer, cx| {
13598                buffer.set_diff_base(Some(base_text.into()), cx);
13599            });
13600    });
13601    cx.executor().run_until_parked();
13602
13603    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13604        let snapshot = editor.buffer().read(cx).snapshot(cx);
13605        let reverted_hunk_statuses = snapshot
13606            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13607            .map(|hunk| hunk_status(&hunk))
13608            .collect::<Vec<_>>();
13609
13610        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13611        reverted_hunk_statuses
13612    });
13613    cx.executor().run_until_parked();
13614    cx.assert_editor_state(expected_reverted_text_with_selections);
13615    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13616}