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_move_cursor(cx: &mut TestAppContext) {
 1085    init_test(cx, |_| {});
 1086
 1087    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1088    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1089
 1090    buffer.update(cx, |buffer, cx| {
 1091        buffer.edit(
 1092            vec![
 1093                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1094                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1095            ],
 1096            None,
 1097            cx,
 1098        );
 1099    });
 1100    _ = view.update(cx, |view, cx| {
 1101        assert_eq!(
 1102            view.selections.display_ranges(cx),
 1103            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1104        );
 1105
 1106        view.move_down(&MoveDown, cx);
 1107        assert_eq!(
 1108            view.selections.display_ranges(cx),
 1109            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1110        );
 1111
 1112        view.move_right(&MoveRight, cx);
 1113        assert_eq!(
 1114            view.selections.display_ranges(cx),
 1115            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1116        );
 1117
 1118        view.move_left(&MoveLeft, cx);
 1119        assert_eq!(
 1120            view.selections.display_ranges(cx),
 1121            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1122        );
 1123
 1124        view.move_up(&MoveUp, cx);
 1125        assert_eq!(
 1126            view.selections.display_ranges(cx),
 1127            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1128        );
 1129
 1130        view.move_to_end(&MoveToEnd, cx);
 1131        assert_eq!(
 1132            view.selections.display_ranges(cx),
 1133            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1134        );
 1135
 1136        view.move_to_beginning(&MoveToBeginning, cx);
 1137        assert_eq!(
 1138            view.selections.display_ranges(cx),
 1139            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1140        );
 1141
 1142        view.change_selections(None, cx, |s| {
 1143            s.select_display_ranges([
 1144                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1145            ]);
 1146        });
 1147        view.select_to_beginning(&SelectToBeginning, cx);
 1148        assert_eq!(
 1149            view.selections.display_ranges(cx),
 1150            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1151        );
 1152
 1153        view.select_to_end(&SelectToEnd, cx);
 1154        assert_eq!(
 1155            view.selections.display_ranges(cx),
 1156            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1157        );
 1158    });
 1159}
 1160
 1161// TODO: Re-enable this test
 1162#[cfg(target_os = "macos")]
 1163#[gpui::test]
 1164fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1165    init_test(cx, |_| {});
 1166
 1167    let view = cx.add_window(|cx| {
 1168        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1169        build_editor(buffer.clone(), cx)
 1170    });
 1171
 1172    assert_eq!('ⓐ'.len_utf8(), 3);
 1173    assert_eq!('α'.len_utf8(), 2);
 1174
 1175    _ = view.update(cx, |view, cx| {
 1176        view.fold_ranges(
 1177            vec![
 1178                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1179                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1180                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1181            ],
 1182            true,
 1183            cx,
 1184        );
 1185        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1186
 1187        view.move_right(&MoveRight, cx);
 1188        assert_eq!(
 1189            view.selections.display_ranges(cx),
 1190            &[empty_range(0, "".len())]
 1191        );
 1192        view.move_right(&MoveRight, cx);
 1193        assert_eq!(
 1194            view.selections.display_ranges(cx),
 1195            &[empty_range(0, "ⓐⓑ".len())]
 1196        );
 1197        view.move_right(&MoveRight, cx);
 1198        assert_eq!(
 1199            view.selections.display_ranges(cx),
 1200            &[empty_range(0, "ⓐⓑ⋯".len())]
 1201        );
 1202
 1203        view.move_down(&MoveDown, cx);
 1204        assert_eq!(
 1205            view.selections.display_ranges(cx),
 1206            &[empty_range(1, "ab⋯e".len())]
 1207        );
 1208        view.move_left(&MoveLeft, cx);
 1209        assert_eq!(
 1210            view.selections.display_ranges(cx),
 1211            &[empty_range(1, "ab⋯".len())]
 1212        );
 1213        view.move_left(&MoveLeft, cx);
 1214        assert_eq!(
 1215            view.selections.display_ranges(cx),
 1216            &[empty_range(1, "ab".len())]
 1217        );
 1218        view.move_left(&MoveLeft, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[empty_range(1, "a".len())]
 1222        );
 1223
 1224        view.move_down(&MoveDown, cx);
 1225        assert_eq!(
 1226            view.selections.display_ranges(cx),
 1227            &[empty_range(2, "α".len())]
 1228        );
 1229        view.move_right(&MoveRight, cx);
 1230        assert_eq!(
 1231            view.selections.display_ranges(cx),
 1232            &[empty_range(2, "αβ".len())]
 1233        );
 1234        view.move_right(&MoveRight, cx);
 1235        assert_eq!(
 1236            view.selections.display_ranges(cx),
 1237            &[empty_range(2, "αβ⋯".len())]
 1238        );
 1239        view.move_right(&MoveRight, cx);
 1240        assert_eq!(
 1241            view.selections.display_ranges(cx),
 1242            &[empty_range(2, "αβ⋯ε".len())]
 1243        );
 1244
 1245        view.move_up(&MoveUp, cx);
 1246        assert_eq!(
 1247            view.selections.display_ranges(cx),
 1248            &[empty_range(1, "ab⋯e".len())]
 1249        );
 1250        view.move_down(&MoveDown, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[empty_range(2, "αβ⋯ε".len())]
 1254        );
 1255        view.move_up(&MoveUp, cx);
 1256        assert_eq!(
 1257            view.selections.display_ranges(cx),
 1258            &[empty_range(1, "ab⋯e".len())]
 1259        );
 1260
 1261        view.move_up(&MoveUp, cx);
 1262        assert_eq!(
 1263            view.selections.display_ranges(cx),
 1264            &[empty_range(0, "ⓐⓑ".len())]
 1265        );
 1266        view.move_left(&MoveLeft, cx);
 1267        assert_eq!(
 1268            view.selections.display_ranges(cx),
 1269            &[empty_range(0, "".len())]
 1270        );
 1271        view.move_left(&MoveLeft, cx);
 1272        assert_eq!(
 1273            view.selections.display_ranges(cx),
 1274            &[empty_range(0, "".len())]
 1275        );
 1276    });
 1277}
 1278
 1279#[gpui::test]
 1280fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1281    init_test(cx, |_| {});
 1282
 1283    let view = cx.add_window(|cx| {
 1284        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1285        build_editor(buffer.clone(), cx)
 1286    });
 1287    _ = view.update(cx, |view, cx| {
 1288        view.change_selections(None, cx, |s| {
 1289            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1290        });
 1291        view.move_down(&MoveDown, cx);
 1292        assert_eq!(
 1293            view.selections.display_ranges(cx),
 1294            &[empty_range(1, "abcd".len())]
 1295        );
 1296
 1297        view.move_down(&MoveDown, cx);
 1298        assert_eq!(
 1299            view.selections.display_ranges(cx),
 1300            &[empty_range(2, "αβγ".len())]
 1301        );
 1302
 1303        view.move_down(&MoveDown, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(3, "abcd".len())]
 1307        );
 1308
 1309        view.move_down(&MoveDown, cx);
 1310        assert_eq!(
 1311            view.selections.display_ranges(cx),
 1312            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1313        );
 1314
 1315        view.move_up(&MoveUp, cx);
 1316        assert_eq!(
 1317            view.selections.display_ranges(cx),
 1318            &[empty_range(3, "abcd".len())]
 1319        );
 1320
 1321        view.move_up(&MoveUp, cx);
 1322        assert_eq!(
 1323            view.selections.display_ranges(cx),
 1324            &[empty_range(2, "αβγ".len())]
 1325        );
 1326    });
 1327}
 1328
 1329#[gpui::test]
 1330fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1331    init_test(cx, |_| {});
 1332    let move_to_beg = MoveToBeginningOfLine {
 1333        stop_at_soft_wraps: true,
 1334    };
 1335
 1336    let move_to_end = MoveToEndOfLine {
 1337        stop_at_soft_wraps: true,
 1338    };
 1339
 1340    let view = cx.add_window(|cx| {
 1341        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1342        build_editor(buffer, cx)
 1343    });
 1344    _ = view.update(cx, |view, cx| {
 1345        view.change_selections(None, cx, |s| {
 1346            s.select_display_ranges([
 1347                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1348                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1349            ]);
 1350        });
 1351    });
 1352
 1353    _ = view.update(cx, |view, cx| {
 1354        view.move_to_beginning_of_line(&move_to_beg, cx);
 1355        assert_eq!(
 1356            view.selections.display_ranges(cx),
 1357            &[
 1358                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1359                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1360            ]
 1361        );
 1362    });
 1363
 1364    _ = view.update(cx, |view, cx| {
 1365        view.move_to_beginning_of_line(&move_to_beg, cx);
 1366        assert_eq!(
 1367            view.selections.display_ranges(cx),
 1368            &[
 1369                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1370                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1371            ]
 1372        );
 1373    });
 1374
 1375    _ = view.update(cx, |view, cx| {
 1376        view.move_to_beginning_of_line(&move_to_beg, cx);
 1377        assert_eq!(
 1378            view.selections.display_ranges(cx),
 1379            &[
 1380                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1381                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1382            ]
 1383        );
 1384    });
 1385
 1386    _ = view.update(cx, |view, cx| {
 1387        view.move_to_end_of_line(&move_to_end, cx);
 1388        assert_eq!(
 1389            view.selections.display_ranges(cx),
 1390            &[
 1391                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1392                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1393            ]
 1394        );
 1395    });
 1396
 1397    // Moving to the end of line again is a no-op.
 1398    _ = view.update(cx, |view, cx| {
 1399        view.move_to_end_of_line(&move_to_end, cx);
 1400        assert_eq!(
 1401            view.selections.display_ranges(cx),
 1402            &[
 1403                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1404                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1405            ]
 1406        );
 1407    });
 1408
 1409    _ = view.update(cx, |view, cx| {
 1410        view.move_left(&MoveLeft, cx);
 1411        view.select_to_beginning_of_line(
 1412            &SelectToBeginningOfLine {
 1413                stop_at_soft_wraps: true,
 1414            },
 1415            cx,
 1416        );
 1417        assert_eq!(
 1418            view.selections.display_ranges(cx),
 1419            &[
 1420                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1421                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1422            ]
 1423        );
 1424    });
 1425
 1426    _ = view.update(cx, |view, cx| {
 1427        view.select_to_beginning_of_line(
 1428            &SelectToBeginningOfLine {
 1429                stop_at_soft_wraps: true,
 1430            },
 1431            cx,
 1432        );
 1433        assert_eq!(
 1434            view.selections.display_ranges(cx),
 1435            &[
 1436                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1437                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1438            ]
 1439        );
 1440    });
 1441
 1442    _ = view.update(cx, |view, cx| {
 1443        view.select_to_beginning_of_line(
 1444            &SelectToBeginningOfLine {
 1445                stop_at_soft_wraps: true,
 1446            },
 1447            cx,
 1448        );
 1449        assert_eq!(
 1450            view.selections.display_ranges(cx),
 1451            &[
 1452                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1453                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1454            ]
 1455        );
 1456    });
 1457
 1458    _ = view.update(cx, |view, cx| {
 1459        view.select_to_end_of_line(
 1460            &SelectToEndOfLine {
 1461                stop_at_soft_wraps: true,
 1462            },
 1463            cx,
 1464        );
 1465        assert_eq!(
 1466            view.selections.display_ranges(cx),
 1467            &[
 1468                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1469                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1470            ]
 1471        );
 1472    });
 1473
 1474    _ = view.update(cx, |view, cx| {
 1475        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1476        assert_eq!(view.display_text(cx), "ab\n  de");
 1477        assert_eq!(
 1478            view.selections.display_ranges(cx),
 1479            &[
 1480                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1481                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1482            ]
 1483        );
 1484    });
 1485
 1486    _ = view.update(cx, |view, cx| {
 1487        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1488        assert_eq!(view.display_text(cx), "\n");
 1489        assert_eq!(
 1490            view.selections.display_ranges(cx),
 1491            &[
 1492                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1493                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1494            ]
 1495        );
 1496    });
 1497}
 1498
 1499#[gpui::test]
 1500fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1501    init_test(cx, |_| {});
 1502    let move_to_beg = MoveToBeginningOfLine {
 1503        stop_at_soft_wraps: false,
 1504    };
 1505
 1506    let move_to_end = MoveToEndOfLine {
 1507        stop_at_soft_wraps: false,
 1508    };
 1509
 1510    let view = cx.add_window(|cx| {
 1511        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1512        build_editor(buffer, cx)
 1513    });
 1514
 1515    _ = view.update(cx, |view, cx| {
 1516        view.set_wrap_width(Some(140.0.into()), cx);
 1517
 1518        // We expect the following lines after wrapping
 1519        // ```
 1520        // thequickbrownfox
 1521        // jumpedoverthelazydo
 1522        // gs
 1523        // ```
 1524        // The final `gs` was soft-wrapped onto a new line.
 1525        assert_eq!(
 1526            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1527            view.display_text(cx),
 1528        );
 1529
 1530        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1531        // Start the cursor at the `k` on the first line
 1532        view.change_selections(None, cx, |s| {
 1533            s.select_display_ranges([
 1534                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1535            ]);
 1536        });
 1537
 1538        // Moving to the beginning of the line should put us at the beginning of the line.
 1539        view.move_to_beginning_of_line(&move_to_beg, cx);
 1540        assert_eq!(
 1541            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1542            view.selections.display_ranges(cx)
 1543        );
 1544
 1545        // Moving to the end of the line should put us at the end of the line.
 1546        view.move_to_end_of_line(&move_to_end, cx);
 1547        assert_eq!(
 1548            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1549            view.selections.display_ranges(cx)
 1550        );
 1551
 1552        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1553        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1554        view.change_selections(None, cx, |s| {
 1555            s.select_display_ranges([
 1556                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1557            ]);
 1558        });
 1559
 1560        // Moving to the beginning of the line should put us at the start of the second line of
 1561        // display text, i.e., the `j`.
 1562        view.move_to_beginning_of_line(&move_to_beg, cx);
 1563        assert_eq!(
 1564            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1565            view.selections.display_ranges(cx)
 1566        );
 1567
 1568        // Moving to the beginning of the line again should be a no-op.
 1569        view.move_to_beginning_of_line(&move_to_beg, cx);
 1570        assert_eq!(
 1571            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1572            view.selections.display_ranges(cx)
 1573        );
 1574
 1575        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1576        // next display line.
 1577        view.move_to_end_of_line(&move_to_end, cx);
 1578        assert_eq!(
 1579            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1580            view.selections.display_ranges(cx)
 1581        );
 1582
 1583        // Moving to the end of the line again should be a no-op.
 1584        view.move_to_end_of_line(&move_to_end, cx);
 1585        assert_eq!(
 1586            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1587            view.selections.display_ranges(cx)
 1588        );
 1589    });
 1590}
 1591
 1592#[gpui::test]
 1593fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1594    init_test(cx, |_| {});
 1595
 1596    let view = cx.add_window(|cx| {
 1597        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1598        build_editor(buffer, cx)
 1599    });
 1600    _ = view.update(cx, |view, cx| {
 1601        view.change_selections(None, cx, |s| {
 1602            s.select_display_ranges([
 1603                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1604                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1605            ])
 1606        });
 1607
 1608        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1609        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1610
 1611        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1612        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1613
 1614        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1615        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1616
 1617        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1618        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1619
 1620        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1621        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1622
 1623        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1624        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1625
 1626        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1627        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1628
 1629        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1630        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1631
 1632        view.move_right(&MoveRight, cx);
 1633        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1634        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1635
 1636        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1637        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1638
 1639        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1640        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1641    });
 1642}
 1643
 1644#[gpui::test]
 1645fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1646    init_test(cx, |_| {});
 1647
 1648    let view = cx.add_window(|cx| {
 1649        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1650        build_editor(buffer, cx)
 1651    });
 1652
 1653    _ = view.update(cx, |view, cx| {
 1654        view.set_wrap_width(Some(140.0.into()), cx);
 1655        assert_eq!(
 1656            view.display_text(cx),
 1657            "use one::{\n    two::three::\n    four::five\n};"
 1658        );
 1659
 1660        view.change_selections(None, cx, |s| {
 1661            s.select_display_ranges([
 1662                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1663            ]);
 1664        });
 1665
 1666        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1667        assert_eq!(
 1668            view.selections.display_ranges(cx),
 1669            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1670        );
 1671
 1672        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1673        assert_eq!(
 1674            view.selections.display_ranges(cx),
 1675            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1676        );
 1677
 1678        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1679        assert_eq!(
 1680            view.selections.display_ranges(cx),
 1681            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1682        );
 1683
 1684        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1685        assert_eq!(
 1686            view.selections.display_ranges(cx),
 1687            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1688        );
 1689
 1690        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1691        assert_eq!(
 1692            view.selections.display_ranges(cx),
 1693            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1694        );
 1695
 1696        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1697        assert_eq!(
 1698            view.selections.display_ranges(cx),
 1699            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1700        );
 1701    });
 1702}
 1703
 1704#[gpui::test]
 1705async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1706    init_test(cx, |_| {});
 1707    let mut cx = EditorTestContext::new(cx).await;
 1708
 1709    let line_height = cx.editor(|editor, cx| {
 1710        editor
 1711            .style()
 1712            .unwrap()
 1713            .text
 1714            .line_height_in_pixels(cx.rem_size())
 1715    });
 1716    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1717
 1718    cx.set_state(
 1719        &r#"ˇone
 1720        two
 1721
 1722        three
 1723        fourˇ
 1724        five
 1725
 1726        six"#
 1727            .unindent(),
 1728    );
 1729
 1730    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1731    cx.assert_editor_state(
 1732        &r#"one
 1733        two
 1734        ˇ
 1735        three
 1736        four
 1737        five
 1738        ˇ
 1739        six"#
 1740            .unindent(),
 1741    );
 1742
 1743    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1744    cx.assert_editor_state(
 1745        &r#"one
 1746        two
 1747
 1748        three
 1749        four
 1750        five
 1751        ˇ
 1752        sixˇ"#
 1753            .unindent(),
 1754    );
 1755
 1756    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1757    cx.assert_editor_state(
 1758        &r#"one
 1759        two
 1760
 1761        three
 1762        four
 1763        five
 1764
 1765        sixˇ"#
 1766            .unindent(),
 1767    );
 1768
 1769    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1770    cx.assert_editor_state(
 1771        &r#"one
 1772        two
 1773
 1774        three
 1775        four
 1776        five
 1777        ˇ
 1778        six"#
 1779            .unindent(),
 1780    );
 1781
 1782    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1783    cx.assert_editor_state(
 1784        &r#"one
 1785        two
 1786        ˇ
 1787        three
 1788        four
 1789        five
 1790
 1791        six"#
 1792            .unindent(),
 1793    );
 1794
 1795    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1796    cx.assert_editor_state(
 1797        &r#"ˇone
 1798        two
 1799
 1800        three
 1801        four
 1802        five
 1803
 1804        six"#
 1805            .unindent(),
 1806    );
 1807}
 1808
 1809#[gpui::test]
 1810async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1811    init_test(cx, |_| {});
 1812    let mut cx = EditorTestContext::new(cx).await;
 1813    let line_height = cx.editor(|editor, cx| {
 1814        editor
 1815            .style()
 1816            .unwrap()
 1817            .text
 1818            .line_height_in_pixels(cx.rem_size())
 1819    });
 1820    let window = cx.window;
 1821    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1822
 1823    cx.set_state(
 1824        r#"ˇone
 1825        two
 1826        three
 1827        four
 1828        five
 1829        six
 1830        seven
 1831        eight
 1832        nine
 1833        ten
 1834        "#,
 1835    );
 1836
 1837    cx.update_editor(|editor, cx| {
 1838        assert_eq!(
 1839            editor.snapshot(cx).scroll_position(),
 1840            gpui::Point::new(0., 0.)
 1841        );
 1842        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1843        assert_eq!(
 1844            editor.snapshot(cx).scroll_position(),
 1845            gpui::Point::new(0., 3.)
 1846        );
 1847        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1848        assert_eq!(
 1849            editor.snapshot(cx).scroll_position(),
 1850            gpui::Point::new(0., 6.)
 1851        );
 1852        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1853        assert_eq!(
 1854            editor.snapshot(cx).scroll_position(),
 1855            gpui::Point::new(0., 3.)
 1856        );
 1857
 1858        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1859        assert_eq!(
 1860            editor.snapshot(cx).scroll_position(),
 1861            gpui::Point::new(0., 1.)
 1862        );
 1863        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1864        assert_eq!(
 1865            editor.snapshot(cx).scroll_position(),
 1866            gpui::Point::new(0., 3.)
 1867        );
 1868    });
 1869}
 1870
 1871#[gpui::test]
 1872async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1873    init_test(cx, |_| {});
 1874    let mut cx = EditorTestContext::new(cx).await;
 1875
 1876    let line_height = cx.update_editor(|editor, cx| {
 1877        editor.set_vertical_scroll_margin(2, cx);
 1878        editor
 1879            .style()
 1880            .unwrap()
 1881            .text
 1882            .line_height_in_pixels(cx.rem_size())
 1883    });
 1884    let window = cx.window;
 1885    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1886
 1887    cx.set_state(
 1888        r#"ˇone
 1889            two
 1890            three
 1891            four
 1892            five
 1893            six
 1894            seven
 1895            eight
 1896            nine
 1897            ten
 1898        "#,
 1899    );
 1900    cx.update_editor(|editor, cx| {
 1901        assert_eq!(
 1902            editor.snapshot(cx).scroll_position(),
 1903            gpui::Point::new(0., 0.0)
 1904        );
 1905    });
 1906
 1907    // Add a cursor below the visible area. Since both cursors cannot fit
 1908    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1909    // allows the vertical scroll margin below that cursor.
 1910    cx.update_editor(|editor, cx| {
 1911        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1912            selections.select_ranges([
 1913                Point::new(0, 0)..Point::new(0, 0),
 1914                Point::new(6, 0)..Point::new(6, 0),
 1915            ]);
 1916        })
 1917    });
 1918    cx.update_editor(|editor, cx| {
 1919        assert_eq!(
 1920            editor.snapshot(cx).scroll_position(),
 1921            gpui::Point::new(0., 3.0)
 1922        );
 1923    });
 1924
 1925    // Move down. The editor cursor scrolls down to track the newest cursor.
 1926    cx.update_editor(|editor, cx| {
 1927        editor.move_down(&Default::default(), cx);
 1928    });
 1929    cx.update_editor(|editor, cx| {
 1930        assert_eq!(
 1931            editor.snapshot(cx).scroll_position(),
 1932            gpui::Point::new(0., 4.0)
 1933        );
 1934    });
 1935
 1936    // Add a cursor above the visible area. Since both cursors fit on screen,
 1937    // the editor scrolls to show both.
 1938    cx.update_editor(|editor, cx| {
 1939        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1940            selections.select_ranges([
 1941                Point::new(1, 0)..Point::new(1, 0),
 1942                Point::new(6, 0)..Point::new(6, 0),
 1943            ]);
 1944        })
 1945    });
 1946    cx.update_editor(|editor, cx| {
 1947        assert_eq!(
 1948            editor.snapshot(cx).scroll_position(),
 1949            gpui::Point::new(0., 1.0)
 1950        );
 1951    });
 1952}
 1953
 1954#[gpui::test]
 1955async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1956    init_test(cx, |_| {});
 1957    let mut cx = EditorTestContext::new(cx).await;
 1958
 1959    let line_height = cx.editor(|editor, cx| {
 1960        editor
 1961            .style()
 1962            .unwrap()
 1963            .text
 1964            .line_height_in_pixels(cx.rem_size())
 1965    });
 1966    let window = cx.window;
 1967    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1968    cx.set_state(
 1969        &r#"
 1970        ˇone
 1971        two
 1972        threeˇ
 1973        four
 1974        five
 1975        six
 1976        seven
 1977        eight
 1978        nine
 1979        ten
 1980        "#
 1981        .unindent(),
 1982    );
 1983
 1984    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1985    cx.assert_editor_state(
 1986        &r#"
 1987        one
 1988        two
 1989        three
 1990        ˇfour
 1991        five
 1992        sixˇ
 1993        seven
 1994        eight
 1995        nine
 1996        ten
 1997        "#
 1998        .unindent(),
 1999    );
 2000
 2001    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2002    cx.assert_editor_state(
 2003        &r#"
 2004        one
 2005        two
 2006        three
 2007        four
 2008        five
 2009        six
 2010        ˇseven
 2011        eight
 2012        nineˇ
 2013        ten
 2014        "#
 2015        .unindent(),
 2016    );
 2017
 2018    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2019    cx.assert_editor_state(
 2020        &r#"
 2021        one
 2022        two
 2023        three
 2024        ˇfour
 2025        five
 2026        sixˇ
 2027        seven
 2028        eight
 2029        nine
 2030        ten
 2031        "#
 2032        .unindent(),
 2033    );
 2034
 2035    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2036    cx.assert_editor_state(
 2037        &r#"
 2038        ˇone
 2039        two
 2040        threeˇ
 2041        four
 2042        five
 2043        six
 2044        seven
 2045        eight
 2046        nine
 2047        ten
 2048        "#
 2049        .unindent(),
 2050    );
 2051
 2052    // Test select collapsing
 2053    cx.update_editor(|editor, cx| {
 2054        editor.move_page_down(&MovePageDown::default(), cx);
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057    });
 2058    cx.assert_editor_state(
 2059        &r#"
 2060        one
 2061        two
 2062        three
 2063        four
 2064        five
 2065        six
 2066        seven
 2067        eight
 2068        nine
 2069        ˇten
 2070        ˇ"#
 2071        .unindent(),
 2072    );
 2073}
 2074
 2075#[gpui::test]
 2076async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2077    init_test(cx, |_| {});
 2078    let mut cx = EditorTestContext::new(cx).await;
 2079    cx.set_state("one «two threeˇ» four");
 2080    cx.update_editor(|editor, cx| {
 2081        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2082        assert_eq!(editor.text(cx), " four");
 2083    });
 2084}
 2085
 2086#[gpui::test]
 2087fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2088    init_test(cx, |_| {});
 2089
 2090    let view = cx.add_window(|cx| {
 2091        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2092        build_editor(buffer.clone(), cx)
 2093    });
 2094
 2095    _ = view.update(cx, |view, cx| {
 2096        view.change_selections(None, cx, |s| {
 2097            s.select_display_ranges([
 2098                // an empty selection - the preceding word fragment is deleted
 2099                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2100                // characters selected - they are deleted
 2101                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2102            ])
 2103        });
 2104        view.delete_to_previous_word_start(
 2105            &DeleteToPreviousWordStart {
 2106                ignore_newlines: false,
 2107            },
 2108            cx,
 2109        );
 2110        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2111    });
 2112
 2113    _ = view.update(cx, |view, cx| {
 2114        view.change_selections(None, cx, |s| {
 2115            s.select_display_ranges([
 2116                // an empty selection - the following word fragment is deleted
 2117                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2118                // characters selected - they are deleted
 2119                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2120            ])
 2121        });
 2122        view.delete_to_next_word_end(
 2123            &DeleteToNextWordEnd {
 2124                ignore_newlines: false,
 2125            },
 2126            cx,
 2127        );
 2128        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2129    });
 2130}
 2131
 2132#[gpui::test]
 2133fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135
 2136    let view = cx.add_window(|cx| {
 2137        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2138        build_editor(buffer.clone(), cx)
 2139    });
 2140    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2141        ignore_newlines: false,
 2142    };
 2143    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2144        ignore_newlines: true,
 2145    };
 2146
 2147    _ = view.update(cx, |view, cx| {
 2148        view.change_selections(None, cx, |s| {
 2149            s.select_display_ranges([
 2150                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2151            ])
 2152        });
 2153        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2154        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2155        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2156        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2157        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2158        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2159        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2160        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2161        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2162        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2163        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2164        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2165    });
 2166}
 2167
 2168#[gpui::test]
 2169fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2170    init_test(cx, |_| {});
 2171
 2172    let view = cx.add_window(|cx| {
 2173        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2174        build_editor(buffer.clone(), cx)
 2175    });
 2176    let del_to_next_word_end = DeleteToNextWordEnd {
 2177        ignore_newlines: false,
 2178    };
 2179    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2180        ignore_newlines: true,
 2181    };
 2182
 2183    _ = view.update(cx, |view, cx| {
 2184        view.change_selections(None, cx, |s| {
 2185            s.select_display_ranges([
 2186                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2187            ])
 2188        });
 2189        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2190        assert_eq!(
 2191            view.buffer.read(cx).read(cx).text(),
 2192            "one\n   two\nthree\n   four"
 2193        );
 2194        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2195        assert_eq!(
 2196            view.buffer.read(cx).read(cx).text(),
 2197            "\n   two\nthree\n   four"
 2198        );
 2199        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2200        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2201        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2202        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2203        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2204        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2205        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2206        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2207    });
 2208}
 2209
 2210#[gpui::test]
 2211fn test_newline(cx: &mut TestAppContext) {
 2212    init_test(cx, |_| {});
 2213
 2214    let view = cx.add_window(|cx| {
 2215        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2216        build_editor(buffer.clone(), cx)
 2217    });
 2218
 2219    _ = view.update(cx, |view, cx| {
 2220        view.change_selections(None, cx, |s| {
 2221            s.select_display_ranges([
 2222                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2223                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2224                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2225            ])
 2226        });
 2227
 2228        view.newline(&Newline, cx);
 2229        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2230    });
 2231}
 2232
 2233#[gpui::test]
 2234fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2235    init_test(cx, |_| {});
 2236
 2237    let editor = cx.add_window(|cx| {
 2238        let buffer = MultiBuffer::build_simple(
 2239            "
 2240                a
 2241                b(
 2242                    X
 2243                )
 2244                c(
 2245                    X
 2246                )
 2247            "
 2248            .unindent()
 2249            .as_str(),
 2250            cx,
 2251        );
 2252        let mut editor = build_editor(buffer.clone(), cx);
 2253        editor.change_selections(None, cx, |s| {
 2254            s.select_ranges([
 2255                Point::new(2, 4)..Point::new(2, 5),
 2256                Point::new(5, 4)..Point::new(5, 5),
 2257            ])
 2258        });
 2259        editor
 2260    });
 2261
 2262    _ = editor.update(cx, |editor, cx| {
 2263        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2264        editor.buffer.update(cx, |buffer, cx| {
 2265            buffer.edit(
 2266                [
 2267                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2268                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2269                ],
 2270                None,
 2271                cx,
 2272            );
 2273            assert_eq!(
 2274                buffer.read(cx).text(),
 2275                "
 2276                    a
 2277                    b()
 2278                    c()
 2279                "
 2280                .unindent()
 2281            );
 2282        });
 2283        assert_eq!(
 2284            editor.selections.ranges(cx),
 2285            &[
 2286                Point::new(1, 2)..Point::new(1, 2),
 2287                Point::new(2, 2)..Point::new(2, 2),
 2288            ],
 2289        );
 2290
 2291        editor.newline(&Newline, cx);
 2292        assert_eq!(
 2293            editor.text(cx),
 2294            "
 2295                a
 2296                b(
 2297                )
 2298                c(
 2299                )
 2300            "
 2301            .unindent()
 2302        );
 2303
 2304        // The selections are moved after the inserted newlines
 2305        assert_eq!(
 2306            editor.selections.ranges(cx),
 2307            &[
 2308                Point::new(2, 0)..Point::new(2, 0),
 2309                Point::new(4, 0)..Point::new(4, 0),
 2310            ],
 2311        );
 2312    });
 2313}
 2314
 2315#[gpui::test]
 2316async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2317    init_test(cx, |settings| {
 2318        settings.defaults.tab_size = NonZeroU32::new(4)
 2319    });
 2320
 2321    let language = Arc::new(
 2322        Language::new(
 2323            LanguageConfig::default(),
 2324            Some(tree_sitter_rust::LANGUAGE.into()),
 2325        )
 2326        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2327        .unwrap(),
 2328    );
 2329
 2330    let mut cx = EditorTestContext::new(cx).await;
 2331    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2332    cx.set_state(indoc! {"
 2333        const a: ˇA = (
 2334 2335                «const_functionˇ»(ˇ),
 2336                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2337 2338        ˇ);ˇ
 2339    "});
 2340
 2341    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2342    cx.assert_editor_state(indoc! {"
 2343        ˇ
 2344        const a: A = (
 2345            ˇ
 2346            (
 2347                ˇ
 2348                ˇ
 2349                const_function(),
 2350                ˇ
 2351                ˇ
 2352                ˇ
 2353                ˇ
 2354                something_else,
 2355                ˇ
 2356            )
 2357            ˇ
 2358            ˇ
 2359        );
 2360    "});
 2361}
 2362
 2363#[gpui::test]
 2364async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2365    init_test(cx, |settings| {
 2366        settings.defaults.tab_size = NonZeroU32::new(4)
 2367    });
 2368
 2369    let language = Arc::new(
 2370        Language::new(
 2371            LanguageConfig::default(),
 2372            Some(tree_sitter_rust::LANGUAGE.into()),
 2373        )
 2374        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2375        .unwrap(),
 2376    );
 2377
 2378    let mut cx = EditorTestContext::new(cx).await;
 2379    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2380    cx.set_state(indoc! {"
 2381        const a: ˇA = (
 2382 2383                «const_functionˇ»(ˇ),
 2384                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2385 2386        ˇ);ˇ
 2387    "});
 2388
 2389    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2390    cx.assert_editor_state(indoc! {"
 2391        const a: A = (
 2392            ˇ
 2393            (
 2394                ˇ
 2395                const_function(),
 2396                ˇ
 2397                ˇ
 2398                something_else,
 2399                ˇ
 2400                ˇ
 2401                ˇ
 2402                ˇ
 2403            )
 2404            ˇ
 2405        );
 2406        ˇ
 2407        ˇ
 2408    "});
 2409}
 2410
 2411#[gpui::test]
 2412async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2413    init_test(cx, |settings| {
 2414        settings.defaults.tab_size = NonZeroU32::new(4)
 2415    });
 2416
 2417    let language = Arc::new(Language::new(
 2418        LanguageConfig {
 2419            line_comments: vec!["//".into()],
 2420            ..LanguageConfig::default()
 2421        },
 2422        None,
 2423    ));
 2424    {
 2425        let mut cx = EditorTestContext::new(cx).await;
 2426        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2427        cx.set_state(indoc! {"
 2428        // Fooˇ
 2429    "});
 2430
 2431        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2432        cx.assert_editor_state(indoc! {"
 2433        // Foo
 2434        //ˇ
 2435    "});
 2436        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2437        cx.set_state(indoc! {"
 2438        ˇ// Foo
 2439    "});
 2440        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2441        cx.assert_editor_state(indoc! {"
 2442
 2443        ˇ// Foo
 2444    "});
 2445    }
 2446    // Ensure that comment continuations can be disabled.
 2447    update_test_language_settings(cx, |settings| {
 2448        settings.defaults.extend_comment_on_newline = Some(false);
 2449    });
 2450    let mut cx = EditorTestContext::new(cx).await;
 2451    cx.set_state(indoc! {"
 2452        // Fooˇ
 2453    "});
 2454    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2455    cx.assert_editor_state(indoc! {"
 2456        // Foo
 2457        ˇ
 2458    "});
 2459}
 2460
 2461#[gpui::test]
 2462fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2463    init_test(cx, |_| {});
 2464
 2465    let editor = cx.add_window(|cx| {
 2466        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2467        let mut editor = build_editor(buffer.clone(), cx);
 2468        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2469        editor
 2470    });
 2471
 2472    _ = editor.update(cx, |editor, cx| {
 2473        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2474        editor.buffer.update(cx, |buffer, cx| {
 2475            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2476            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2477        });
 2478        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2479
 2480        editor.insert("Z", cx);
 2481        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2482
 2483        // The selections are moved after the inserted characters
 2484        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2485    });
 2486}
 2487
 2488#[gpui::test]
 2489async fn test_tab(cx: &mut gpui::TestAppContext) {
 2490    init_test(cx, |settings| {
 2491        settings.defaults.tab_size = NonZeroU32::new(3)
 2492    });
 2493
 2494    let mut cx = EditorTestContext::new(cx).await;
 2495    cx.set_state(indoc! {"
 2496        ˇabˇc
 2497        ˇ🏀ˇ🏀ˇefg
 2498 2499    "});
 2500    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2501    cx.assert_editor_state(indoc! {"
 2502           ˇab ˇc
 2503           ˇ🏀  ˇ🏀  ˇefg
 2504        d  ˇ
 2505    "});
 2506
 2507    cx.set_state(indoc! {"
 2508        a
 2509        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2510    "});
 2511    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2512    cx.assert_editor_state(indoc! {"
 2513        a
 2514           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2515    "});
 2516}
 2517
 2518#[gpui::test]
 2519async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2520    init_test(cx, |_| {});
 2521
 2522    let mut cx = EditorTestContext::new(cx).await;
 2523    let language = Arc::new(
 2524        Language::new(
 2525            LanguageConfig::default(),
 2526            Some(tree_sitter_rust::LANGUAGE.into()),
 2527        )
 2528        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2529        .unwrap(),
 2530    );
 2531    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2532
 2533    // cursors that are already at the suggested indent level insert
 2534    // a soft tab. cursors that are to the left of the suggested indent
 2535    // auto-indent their line.
 2536    cx.set_state(indoc! {"
 2537        ˇ
 2538        const a: B = (
 2539            c(
 2540                d(
 2541        ˇ
 2542                )
 2543        ˇ
 2544        ˇ    )
 2545        );
 2546    "});
 2547    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2548    cx.assert_editor_state(indoc! {"
 2549            ˇ
 2550        const a: B = (
 2551            c(
 2552                d(
 2553                    ˇ
 2554                )
 2555                ˇ
 2556            ˇ)
 2557        );
 2558    "});
 2559
 2560    // handle auto-indent when there are multiple cursors on the same line
 2561    cx.set_state(indoc! {"
 2562        const a: B = (
 2563            c(
 2564        ˇ    ˇ
 2565        ˇ    )
 2566        );
 2567    "});
 2568    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2569    cx.assert_editor_state(indoc! {"
 2570        const a: B = (
 2571            c(
 2572                ˇ
 2573            ˇ)
 2574        );
 2575    "});
 2576}
 2577
 2578#[gpui::test]
 2579async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2580    init_test(cx, |settings| {
 2581        settings.defaults.tab_size = NonZeroU32::new(4)
 2582    });
 2583
 2584    let language = Arc::new(
 2585        Language::new(
 2586            LanguageConfig::default(),
 2587            Some(tree_sitter_rust::LANGUAGE.into()),
 2588        )
 2589        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2590        .unwrap(),
 2591    );
 2592
 2593    let mut cx = EditorTestContext::new(cx).await;
 2594    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2595    cx.set_state(indoc! {"
 2596        fn a() {
 2597            if b {
 2598        \t ˇc
 2599            }
 2600        }
 2601    "});
 2602
 2603    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2604    cx.assert_editor_state(indoc! {"
 2605        fn a() {
 2606            if b {
 2607                ˇc
 2608            }
 2609        }
 2610    "});
 2611}
 2612
 2613#[gpui::test]
 2614async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2615    init_test(cx, |settings| {
 2616        settings.defaults.tab_size = NonZeroU32::new(4);
 2617    });
 2618
 2619    let mut cx = EditorTestContext::new(cx).await;
 2620
 2621    cx.set_state(indoc! {"
 2622          «oneˇ» «twoˇ»
 2623        three
 2624         four
 2625    "});
 2626    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2627    cx.assert_editor_state(indoc! {"
 2628            «oneˇ» «twoˇ»
 2629        three
 2630         four
 2631    "});
 2632
 2633    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2634    cx.assert_editor_state(indoc! {"
 2635        «oneˇ» «twoˇ»
 2636        three
 2637         four
 2638    "});
 2639
 2640    // select across line ending
 2641    cx.set_state(indoc! {"
 2642        one two
 2643        t«hree
 2644        ˇ» four
 2645    "});
 2646    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2647    cx.assert_editor_state(indoc! {"
 2648        one two
 2649            t«hree
 2650        ˇ» four
 2651    "});
 2652
 2653    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655        one two
 2656        t«hree
 2657        ˇ» four
 2658    "});
 2659
 2660    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2661    cx.set_state(indoc! {"
 2662        one two
 2663        ˇthree
 2664            four
 2665    "});
 2666    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2667    cx.assert_editor_state(indoc! {"
 2668        one two
 2669            ˇthree
 2670            four
 2671    "});
 2672
 2673    cx.set_state(indoc! {"
 2674        one two
 2675        ˇ    three
 2676            four
 2677    "});
 2678    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2679    cx.assert_editor_state(indoc! {"
 2680        one two
 2681        ˇthree
 2682            four
 2683    "});
 2684}
 2685
 2686#[gpui::test]
 2687async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2688    init_test(cx, |settings| {
 2689        settings.defaults.hard_tabs = Some(true);
 2690    });
 2691
 2692    let mut cx = EditorTestContext::new(cx).await;
 2693
 2694    // select two ranges on one line
 2695    cx.set_state(indoc! {"
 2696        «oneˇ» «twoˇ»
 2697        three
 2698        four
 2699    "});
 2700    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2701    cx.assert_editor_state(indoc! {"
 2702        \t«oneˇ» «twoˇ»
 2703        three
 2704        four
 2705    "});
 2706    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2707    cx.assert_editor_state(indoc! {"
 2708        \t\t«oneˇ» «twoˇ»
 2709        three
 2710        four
 2711    "});
 2712    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2713    cx.assert_editor_state(indoc! {"
 2714        \t«oneˇ» «twoˇ»
 2715        three
 2716        four
 2717    "});
 2718    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2719    cx.assert_editor_state(indoc! {"
 2720        «oneˇ» «twoˇ»
 2721        three
 2722        four
 2723    "});
 2724
 2725    // select across a line ending
 2726    cx.set_state(indoc! {"
 2727        one two
 2728        t«hree
 2729        ˇ»four
 2730    "});
 2731    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2732    cx.assert_editor_state(indoc! {"
 2733        one two
 2734        \tt«hree
 2735        ˇ»four
 2736    "});
 2737    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2738    cx.assert_editor_state(indoc! {"
 2739        one two
 2740        \t\tt«hree
 2741        ˇ»four
 2742    "});
 2743    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2744    cx.assert_editor_state(indoc! {"
 2745        one two
 2746        \tt«hree
 2747        ˇ»four
 2748    "});
 2749    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2750    cx.assert_editor_state(indoc! {"
 2751        one two
 2752        t«hree
 2753        ˇ»four
 2754    "});
 2755
 2756    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2757    cx.set_state(indoc! {"
 2758        one two
 2759        ˇthree
 2760        four
 2761    "});
 2762    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2763    cx.assert_editor_state(indoc! {"
 2764        one two
 2765        ˇthree
 2766        four
 2767    "});
 2768    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770        one two
 2771        \tˇthree
 2772        four
 2773    "});
 2774    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2775    cx.assert_editor_state(indoc! {"
 2776        one two
 2777        ˇthree
 2778        four
 2779    "});
 2780}
 2781
 2782#[gpui::test]
 2783fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2784    init_test(cx, |settings| {
 2785        settings.languages.extend([
 2786            (
 2787                "TOML".into(),
 2788                LanguageSettingsContent {
 2789                    tab_size: NonZeroU32::new(2),
 2790                    ..Default::default()
 2791                },
 2792            ),
 2793            (
 2794                "Rust".into(),
 2795                LanguageSettingsContent {
 2796                    tab_size: NonZeroU32::new(4),
 2797                    ..Default::default()
 2798                },
 2799            ),
 2800        ]);
 2801    });
 2802
 2803    let toml_language = Arc::new(Language::new(
 2804        LanguageConfig {
 2805            name: "TOML".into(),
 2806            ..Default::default()
 2807        },
 2808        None,
 2809    ));
 2810    let rust_language = Arc::new(Language::new(
 2811        LanguageConfig {
 2812            name: "Rust".into(),
 2813            ..Default::default()
 2814        },
 2815        None,
 2816    ));
 2817
 2818    let toml_buffer =
 2819        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2820    let rust_buffer = cx.new_model(|cx| {
 2821        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2822    });
 2823    let multibuffer = cx.new_model(|cx| {
 2824        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2825        multibuffer.push_excerpts(
 2826            toml_buffer.clone(),
 2827            [ExcerptRange {
 2828                context: Point::new(0, 0)..Point::new(2, 0),
 2829                primary: None,
 2830            }],
 2831            cx,
 2832        );
 2833        multibuffer.push_excerpts(
 2834            rust_buffer.clone(),
 2835            [ExcerptRange {
 2836                context: Point::new(0, 0)..Point::new(1, 0),
 2837                primary: None,
 2838            }],
 2839            cx,
 2840        );
 2841        multibuffer
 2842    });
 2843
 2844    cx.add_window(|cx| {
 2845        let mut editor = build_editor(multibuffer, cx);
 2846
 2847        assert_eq!(
 2848            editor.text(cx),
 2849            indoc! {"
 2850                a = 1
 2851                b = 2
 2852
 2853                const c: usize = 3;
 2854            "}
 2855        );
 2856
 2857        select_ranges(
 2858            &mut editor,
 2859            indoc! {"
 2860                «aˇ» = 1
 2861                b = 2
 2862
 2863                «const c:ˇ» usize = 3;
 2864            "},
 2865            cx,
 2866        );
 2867
 2868        editor.tab(&Tab, cx);
 2869        assert_text_with_selections(
 2870            &mut editor,
 2871            indoc! {"
 2872                  «aˇ» = 1
 2873                b = 2
 2874
 2875                    «const c:ˇ» usize = 3;
 2876            "},
 2877            cx,
 2878        );
 2879        editor.tab_prev(&TabPrev, cx);
 2880        assert_text_with_selections(
 2881            &mut editor,
 2882            indoc! {"
 2883                «aˇ» = 1
 2884                b = 2
 2885
 2886                «const c:ˇ» usize = 3;
 2887            "},
 2888            cx,
 2889        );
 2890
 2891        editor
 2892    });
 2893}
 2894
 2895#[gpui::test]
 2896async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2897    init_test(cx, |_| {});
 2898
 2899    let mut cx = EditorTestContext::new(cx).await;
 2900
 2901    // Basic backspace
 2902    cx.set_state(indoc! {"
 2903        onˇe two three
 2904        fou«rˇ» five six
 2905        seven «ˇeight nine
 2906        »ten
 2907    "});
 2908    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        oˇe two three
 2911        fouˇ five six
 2912        seven ˇten
 2913    "});
 2914
 2915    // Test backspace inside and around indents
 2916    cx.set_state(indoc! {"
 2917        zero
 2918            ˇone
 2919                ˇtwo
 2920            ˇ ˇ ˇ  three
 2921        ˇ  ˇ  four
 2922    "});
 2923    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2924    cx.assert_editor_state(indoc! {"
 2925        zero
 2926        ˇone
 2927            ˇtwo
 2928        ˇ  threeˇ  four
 2929    "});
 2930
 2931    // Test backspace with line_mode set to true
 2932    cx.update_editor(|e, _| e.selections.line_mode = true);
 2933    cx.set_state(indoc! {"
 2934        The ˇquick ˇbrown
 2935        fox jumps over
 2936        the lazy dog
 2937        ˇThe qu«ick bˇ»rown"});
 2938    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2939    cx.assert_editor_state(indoc! {"
 2940        ˇfox jumps over
 2941        the lazy dogˇ"});
 2942}
 2943
 2944#[gpui::test]
 2945async fn test_delete(cx: &mut gpui::TestAppContext) {
 2946    init_test(cx, |_| {});
 2947
 2948    let mut cx = EditorTestContext::new(cx).await;
 2949    cx.set_state(indoc! {"
 2950        onˇe two three
 2951        fou«rˇ» five six
 2952        seven «ˇeight nine
 2953        »ten
 2954    "});
 2955    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        onˇ two three
 2958        fouˇ five six
 2959        seven ˇten
 2960    "});
 2961
 2962    // Test backspace with line_mode set to true
 2963    cx.update_editor(|e, _| e.selections.line_mode = true);
 2964    cx.set_state(indoc! {"
 2965        The ˇquick ˇbrown
 2966        fox «ˇjum»ps over
 2967        the lazy dog
 2968        ˇThe qu«ick bˇ»rown"});
 2969    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2970    cx.assert_editor_state("ˇthe lazy dogˇ");
 2971}
 2972
 2973#[gpui::test]
 2974fn test_delete_line(cx: &mut TestAppContext) {
 2975    init_test(cx, |_| {});
 2976
 2977    let view = cx.add_window(|cx| {
 2978        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2979        build_editor(buffer, cx)
 2980    });
 2981    _ = view.update(cx, |view, cx| {
 2982        view.change_selections(None, cx, |s| {
 2983            s.select_display_ranges([
 2984                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2985                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2986                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2987            ])
 2988        });
 2989        view.delete_line(&DeleteLine, cx);
 2990        assert_eq!(view.display_text(cx), "ghi");
 2991        assert_eq!(
 2992            view.selections.display_ranges(cx),
 2993            vec![
 2994                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2995                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2996            ]
 2997        );
 2998    });
 2999
 3000    let view = cx.add_window(|cx| {
 3001        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3002        build_editor(buffer, cx)
 3003    });
 3004    _ = view.update(cx, |view, cx| {
 3005        view.change_selections(None, cx, |s| {
 3006            s.select_display_ranges([
 3007                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3008            ])
 3009        });
 3010        view.delete_line(&DeleteLine, cx);
 3011        assert_eq!(view.display_text(cx), "ghi\n");
 3012        assert_eq!(
 3013            view.selections.display_ranges(cx),
 3014            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3015        );
 3016    });
 3017}
 3018
 3019#[gpui::test]
 3020fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3021    init_test(cx, |_| {});
 3022
 3023    cx.add_window(|cx| {
 3024        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3025        let mut editor = build_editor(buffer.clone(), cx);
 3026        let buffer = buffer.read(cx).as_singleton().unwrap();
 3027
 3028        assert_eq!(
 3029            editor.selections.ranges::<Point>(cx),
 3030            &[Point::new(0, 0)..Point::new(0, 0)]
 3031        );
 3032
 3033        // When on single line, replace newline at end by space
 3034        editor.join_lines(&JoinLines, cx);
 3035        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3036        assert_eq!(
 3037            editor.selections.ranges::<Point>(cx),
 3038            &[Point::new(0, 3)..Point::new(0, 3)]
 3039        );
 3040
 3041        // When multiple lines are selected, remove newlines that are spanned by the selection
 3042        editor.change_selections(None, cx, |s| {
 3043            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3044        });
 3045        editor.join_lines(&JoinLines, cx);
 3046        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3047        assert_eq!(
 3048            editor.selections.ranges::<Point>(cx),
 3049            &[Point::new(0, 11)..Point::new(0, 11)]
 3050        );
 3051
 3052        // Undo should be transactional
 3053        editor.undo(&Undo, cx);
 3054        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3055        assert_eq!(
 3056            editor.selections.ranges::<Point>(cx),
 3057            &[Point::new(0, 5)..Point::new(2, 2)]
 3058        );
 3059
 3060        // When joining an empty line don't insert a space
 3061        editor.change_selections(None, cx, |s| {
 3062            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3063        });
 3064        editor.join_lines(&JoinLines, cx);
 3065        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3066        assert_eq!(
 3067            editor.selections.ranges::<Point>(cx),
 3068            [Point::new(2, 3)..Point::new(2, 3)]
 3069        );
 3070
 3071        // We can remove trailing newlines
 3072        editor.join_lines(&JoinLines, cx);
 3073        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3074        assert_eq!(
 3075            editor.selections.ranges::<Point>(cx),
 3076            [Point::new(2, 3)..Point::new(2, 3)]
 3077        );
 3078
 3079        // We don't blow up on the last line
 3080        editor.join_lines(&JoinLines, cx);
 3081        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3082        assert_eq!(
 3083            editor.selections.ranges::<Point>(cx),
 3084            [Point::new(2, 3)..Point::new(2, 3)]
 3085        );
 3086
 3087        // reset to test indentation
 3088        editor.buffer.update(cx, |buffer, cx| {
 3089            buffer.edit(
 3090                [
 3091                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3092                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3093                ],
 3094                None,
 3095                cx,
 3096            )
 3097        });
 3098
 3099        // We remove any leading spaces
 3100        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3101        editor.change_selections(None, cx, |s| {
 3102            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3103        });
 3104        editor.join_lines(&JoinLines, cx);
 3105        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3106
 3107        // We don't insert a space for a line containing only spaces
 3108        editor.join_lines(&JoinLines, cx);
 3109        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3110
 3111        // We ignore any leading tabs
 3112        editor.join_lines(&JoinLines, cx);
 3113        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3114
 3115        editor
 3116    });
 3117}
 3118
 3119#[gpui::test]
 3120fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3121    init_test(cx, |_| {});
 3122
 3123    cx.add_window(|cx| {
 3124        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3125        let mut editor = build_editor(buffer.clone(), cx);
 3126        let buffer = buffer.read(cx).as_singleton().unwrap();
 3127
 3128        editor.change_selections(None, cx, |s| {
 3129            s.select_ranges([
 3130                Point::new(0, 2)..Point::new(1, 1),
 3131                Point::new(1, 2)..Point::new(1, 2),
 3132                Point::new(3, 1)..Point::new(3, 2),
 3133            ])
 3134        });
 3135
 3136        editor.join_lines(&JoinLines, cx);
 3137        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3138
 3139        assert_eq!(
 3140            editor.selections.ranges::<Point>(cx),
 3141            [
 3142                Point::new(0, 7)..Point::new(0, 7),
 3143                Point::new(1, 3)..Point::new(1, 3)
 3144            ]
 3145        );
 3146        editor
 3147    });
 3148}
 3149
 3150#[gpui::test]
 3151async fn test_join_lines_with_git_diff_base(
 3152    executor: BackgroundExecutor,
 3153    cx: &mut gpui::TestAppContext,
 3154) {
 3155    init_test(cx, |_| {});
 3156
 3157    let mut cx = EditorTestContext::new(cx).await;
 3158
 3159    let diff_base = r#"
 3160        Line 0
 3161        Line 1
 3162        Line 2
 3163        Line 3
 3164        "#
 3165    .unindent();
 3166
 3167    cx.set_state(
 3168        &r#"
 3169        ˇLine 0
 3170        Line 1
 3171        Line 2
 3172        Line 3
 3173        "#
 3174        .unindent(),
 3175    );
 3176
 3177    cx.set_diff_base(Some(&diff_base));
 3178    executor.run_until_parked();
 3179
 3180    // Join lines
 3181    cx.update_editor(|editor, cx| {
 3182        editor.join_lines(&JoinLines, cx);
 3183    });
 3184    executor.run_until_parked();
 3185
 3186    cx.assert_editor_state(
 3187        &r#"
 3188        Line 0ˇ Line 1
 3189        Line 2
 3190        Line 3
 3191        "#
 3192        .unindent(),
 3193    );
 3194    // Join again
 3195    cx.update_editor(|editor, cx| {
 3196        editor.join_lines(&JoinLines, cx);
 3197    });
 3198    executor.run_until_parked();
 3199
 3200    cx.assert_editor_state(
 3201        &r#"
 3202        Line 0 Line 1ˇ Line 2
 3203        Line 3
 3204        "#
 3205        .unindent(),
 3206    );
 3207}
 3208
 3209#[gpui::test]
 3210async fn test_custom_newlines_cause_no_false_positive_diffs(
 3211    executor: BackgroundExecutor,
 3212    cx: &mut gpui::TestAppContext,
 3213) {
 3214    init_test(cx, |_| {});
 3215    let mut cx = EditorTestContext::new(cx).await;
 3216    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3217    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3218    executor.run_until_parked();
 3219
 3220    cx.update_editor(|editor, cx| {
 3221        assert_eq!(
 3222            editor
 3223                .buffer()
 3224                .read(cx)
 3225                .snapshot(cx)
 3226                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3227                .collect::<Vec<_>>(),
 3228            Vec::new(),
 3229            "Should not have any diffs for files with custom newlines"
 3230        );
 3231    });
 3232}
 3233
 3234#[gpui::test]
 3235async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3236    init_test(cx, |_| {});
 3237
 3238    let mut cx = EditorTestContext::new(cx).await;
 3239
 3240    // Test sort_lines_case_insensitive()
 3241    cx.set_state(indoc! {"
 3242        «z
 3243        y
 3244        x
 3245        Z
 3246        Y
 3247        Xˇ»
 3248    "});
 3249    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3250    cx.assert_editor_state(indoc! {"
 3251        «x
 3252        X
 3253        y
 3254        Y
 3255        z
 3256        Zˇ»
 3257    "});
 3258
 3259    // Test reverse_lines()
 3260    cx.set_state(indoc! {"
 3261        «5
 3262        4
 3263        3
 3264        2
 3265        1ˇ»
 3266    "});
 3267    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        «1
 3270        2
 3271        3
 3272        4
 3273        5ˇ»
 3274    "});
 3275
 3276    // Skip testing shuffle_line()
 3277
 3278    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3279    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3280
 3281    // Don't manipulate when cursor is on single line, but expand the selection
 3282    cx.set_state(indoc! {"
 3283        ddˇdd
 3284        ccc
 3285        bb
 3286        a
 3287    "});
 3288    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3289    cx.assert_editor_state(indoc! {"
 3290        «ddddˇ»
 3291        ccc
 3292        bb
 3293        a
 3294    "});
 3295
 3296    // Basic manipulate case
 3297    // Start selection moves to column 0
 3298    // End of selection shrinks to fit shorter line
 3299    cx.set_state(indoc! {"
 3300        dd«d
 3301        ccc
 3302        bb
 3303        aaaaaˇ»
 3304    "});
 3305    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3306    cx.assert_editor_state(indoc! {"
 3307        «aaaaa
 3308        bb
 3309        ccc
 3310        dddˇ»
 3311    "});
 3312
 3313    // Manipulate case with newlines
 3314    cx.set_state(indoc! {"
 3315        dd«d
 3316        ccc
 3317
 3318        bb
 3319        aaaaa
 3320
 3321        ˇ»
 3322    "});
 3323    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3324    cx.assert_editor_state(indoc! {"
 3325        «
 3326
 3327        aaaaa
 3328        bb
 3329        ccc
 3330        dddˇ»
 3331
 3332    "});
 3333
 3334    // Adding new line
 3335    cx.set_state(indoc! {"
 3336        aa«a
 3337        bbˇ»b
 3338    "});
 3339    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3340    cx.assert_editor_state(indoc! {"
 3341        «aaa
 3342        bbb
 3343        added_lineˇ»
 3344    "});
 3345
 3346    // Removing line
 3347    cx.set_state(indoc! {"
 3348        aa«a
 3349        bbbˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| {
 3352        e.manipulate_lines(cx, |lines| {
 3353            lines.pop();
 3354        })
 3355    });
 3356    cx.assert_editor_state(indoc! {"
 3357        «aaaˇ»
 3358    "});
 3359
 3360    // Removing all lines
 3361    cx.set_state(indoc! {"
 3362        aa«a
 3363        bbbˇ»
 3364    "});
 3365    cx.update_editor(|e, cx| {
 3366        e.manipulate_lines(cx, |lines| {
 3367            lines.drain(..);
 3368        })
 3369    });
 3370    cx.assert_editor_state(indoc! {"
 3371        ˇ
 3372    "});
 3373}
 3374
 3375#[gpui::test]
 3376async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3377    init_test(cx, |_| {});
 3378
 3379    let mut cx = EditorTestContext::new(cx).await;
 3380
 3381    // Consider continuous selection as single selection
 3382    cx.set_state(indoc! {"
 3383        Aaa«aa
 3384        cˇ»c«c
 3385        bb
 3386        aaaˇ»aa
 3387    "});
 3388    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3389    cx.assert_editor_state(indoc! {"
 3390        «Aaaaa
 3391        ccc
 3392        bb
 3393        aaaaaˇ»
 3394    "});
 3395
 3396    cx.set_state(indoc! {"
 3397        Aaa«aa
 3398        cˇ»c«c
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «Aaaaa
 3405        ccc
 3406        bbˇ»
 3407    "});
 3408
 3409    // Consider non continuous selection as distinct dedup operations
 3410    cx.set_state(indoc! {"
 3411        «aaaaa
 3412        bb
 3413        aaaaa
 3414        aaaaaˇ»
 3415
 3416        aaa«aaˇ»
 3417    "});
 3418    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3419    cx.assert_editor_state(indoc! {"
 3420        «aaaaa
 3421        bbˇ»
 3422
 3423        «aaaaaˇ»
 3424    "});
 3425}
 3426
 3427#[gpui::test]
 3428async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3429    init_test(cx, |_| {});
 3430
 3431    let mut cx = EditorTestContext::new(cx).await;
 3432
 3433    cx.set_state(indoc! {"
 3434        «Aaa
 3435        aAa
 3436        Aaaˇ»
 3437    "});
 3438    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3439    cx.assert_editor_state(indoc! {"
 3440        «Aaa
 3441        aAaˇ»
 3442    "});
 3443
 3444    cx.set_state(indoc! {"
 3445        «Aaa
 3446        aAa
 3447        aaAˇ»
 3448    "});
 3449    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3450    cx.assert_editor_state(indoc! {"
 3451        «Aaaˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Manipulate with multiple selections on a single line
 3462    cx.set_state(indoc! {"
 3463        dd«dd
 3464        cˇ»c«c
 3465        bb
 3466        aaaˇ»aa
 3467    "});
 3468    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3469    cx.assert_editor_state(indoc! {"
 3470        «aaaaa
 3471        bb
 3472        ccc
 3473        ddddˇ»
 3474    "});
 3475
 3476    // Manipulate with multiple disjoin selections
 3477    cx.set_state(indoc! {"
 3478 3479        4
 3480        3
 3481        2
 3482        1ˇ»
 3483
 3484        dd«dd
 3485        ccc
 3486        bb
 3487        aaaˇ»aa
 3488    "});
 3489    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        «1
 3492        2
 3493        3
 3494        4
 3495        5ˇ»
 3496
 3497        «aaaaa
 3498        bb
 3499        ccc
 3500        ddddˇ»
 3501    "});
 3502
 3503    // Adding lines on each selection
 3504    cx.set_state(indoc! {"
 3505 3506        1ˇ»
 3507
 3508        bb«bb
 3509        aaaˇ»aa
 3510    "});
 3511    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3512    cx.assert_editor_state(indoc! {"
 3513        «2
 3514        1
 3515        added lineˇ»
 3516
 3517        «bbbb
 3518        aaaaa
 3519        added lineˇ»
 3520    "});
 3521
 3522    // Removing lines on each selection
 3523    cx.set_state(indoc! {"
 3524 3525        1ˇ»
 3526
 3527        bb«bb
 3528        aaaˇ»aa
 3529    "});
 3530    cx.update_editor(|e, cx| {
 3531        e.manipulate_lines(cx, |lines| {
 3532            lines.pop();
 3533        })
 3534    });
 3535    cx.assert_editor_state(indoc! {"
 3536        «2ˇ»
 3537
 3538        «bbbbˇ»
 3539    "});
 3540}
 3541
 3542#[gpui::test]
 3543async fn test_manipulate_text(cx: &mut TestAppContext) {
 3544    init_test(cx, |_| {});
 3545
 3546    let mut cx = EditorTestContext::new(cx).await;
 3547
 3548    // Test convert_to_upper_case()
 3549    cx.set_state(indoc! {"
 3550        «hello worldˇ»
 3551    "});
 3552    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3553    cx.assert_editor_state(indoc! {"
 3554        «HELLO WORLDˇ»
 3555    "});
 3556
 3557    // Test convert_to_lower_case()
 3558    cx.set_state(indoc! {"
 3559        «HELLO WORLDˇ»
 3560    "});
 3561    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3562    cx.assert_editor_state(indoc! {"
 3563        «hello worldˇ»
 3564    "});
 3565
 3566    // Test multiple line, single selection case
 3567    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3568    cx.set_state(indoc! {"
 3569        «The quick brown
 3570        fox jumps over
 3571        the lazy dogˇ»
 3572    "});
 3573    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3574    cx.assert_editor_state(indoc! {"
 3575        «The Quick Brown
 3576        Fox Jumps Over
 3577        The Lazy Dogˇ»
 3578    "});
 3579
 3580    // Test multiple line, single selection case
 3581    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3582    cx.set_state(indoc! {"
 3583        «The quick brown
 3584        fox jumps over
 3585        the lazy dogˇ»
 3586    "});
 3587    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3588    cx.assert_editor_state(indoc! {"
 3589        «TheQuickBrown
 3590        FoxJumpsOver
 3591        TheLazyDogˇ»
 3592    "});
 3593
 3594    // From here on out, test more complex cases of manipulate_text()
 3595
 3596    // Test no selection case - should affect words cursors are in
 3597    // Cursor at beginning, middle, and end of word
 3598    cx.set_state(indoc! {"
 3599        ˇhello big beauˇtiful worldˇ
 3600    "});
 3601    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3602    cx.assert_editor_state(indoc! {"
 3603        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3604    "});
 3605
 3606    // Test multiple selections on a single line and across multiple lines
 3607    cx.set_state(indoc! {"
 3608        «Theˇ» quick «brown
 3609        foxˇ» jumps «overˇ»
 3610        the «lazyˇ» dog
 3611    "});
 3612    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3613    cx.assert_editor_state(indoc! {"
 3614        «THEˇ» quick «BROWN
 3615        FOXˇ» jumps «OVERˇ»
 3616        the «LAZYˇ» dog
 3617    "});
 3618
 3619    // Test case where text length grows
 3620    cx.set_state(indoc! {"
 3621        «tschüߡ»
 3622    "});
 3623    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3624    cx.assert_editor_state(indoc! {"
 3625        «TSCHÜSSˇ»
 3626    "});
 3627
 3628    // Test to make sure we don't crash when text shrinks
 3629    cx.set_state(indoc! {"
 3630        aaa_bbbˇ
 3631    "});
 3632    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3633    cx.assert_editor_state(indoc! {"
 3634        «aaaBbbˇ»
 3635    "});
 3636
 3637    // Test to make sure we all aware of the fact that each word can grow and shrink
 3638    // Final selections should be aware of this fact
 3639    cx.set_state(indoc! {"
 3640        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3641    "});
 3642    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3643    cx.assert_editor_state(indoc! {"
 3644        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3645    "});
 3646
 3647    cx.set_state(indoc! {"
 3648        «hElLo, WoRld!ˇ»
 3649    "});
 3650    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3651    cx.assert_editor_state(indoc! {"
 3652        «HeLlO, wOrLD!ˇ»
 3653    "});
 3654}
 3655
 3656#[gpui::test]
 3657fn test_duplicate_line(cx: &mut TestAppContext) {
 3658    init_test(cx, |_| {});
 3659
 3660    let view = cx.add_window(|cx| {
 3661        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3662        build_editor(buffer, cx)
 3663    });
 3664    _ = view.update(cx, |view, cx| {
 3665        view.change_selections(None, cx, |s| {
 3666            s.select_display_ranges([
 3667                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3669                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3670                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3671            ])
 3672        });
 3673        view.duplicate_line_down(&DuplicateLineDown, cx);
 3674        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3675        assert_eq!(
 3676            view.selections.display_ranges(cx),
 3677            vec![
 3678                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3679                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3680                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3681                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3682            ]
 3683        );
 3684    });
 3685
 3686    let view = cx.add_window(|cx| {
 3687        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3688        build_editor(buffer, cx)
 3689    });
 3690    _ = view.update(cx, |view, cx| {
 3691        view.change_selections(None, cx, |s| {
 3692            s.select_display_ranges([
 3693                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3694                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3695            ])
 3696        });
 3697        view.duplicate_line_down(&DuplicateLineDown, cx);
 3698        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3699        assert_eq!(
 3700            view.selections.display_ranges(cx),
 3701            vec![
 3702                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3703                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3704            ]
 3705        );
 3706    });
 3707
 3708    // With `move_upwards` the selections stay in place, except for
 3709    // the lines inserted above them
 3710    let view = cx.add_window(|cx| {
 3711        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3712        build_editor(buffer, cx)
 3713    });
 3714    _ = view.update(cx, |view, cx| {
 3715        view.change_selections(None, cx, |s| {
 3716            s.select_display_ranges([
 3717                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3718                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3719                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3720                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3721            ])
 3722        });
 3723        view.duplicate_line_up(&DuplicateLineUp, cx);
 3724        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3730                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3731                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3732            ]
 3733        );
 3734    });
 3735
 3736    let view = cx.add_window(|cx| {
 3737        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3738        build_editor(buffer, cx)
 3739    });
 3740    _ = view.update(cx, |view, cx| {
 3741        view.change_selections(None, cx, |s| {
 3742            s.select_display_ranges([
 3743                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3744                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3745            ])
 3746        });
 3747        view.duplicate_line_up(&DuplicateLineUp, cx);
 3748        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3749        assert_eq!(
 3750            view.selections.display_ranges(cx),
 3751            vec![
 3752                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3753                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3754            ]
 3755        );
 3756    });
 3757}
 3758
 3759#[gpui::test]
 3760fn test_move_line_up_down(cx: &mut TestAppContext) {
 3761    init_test(cx, |_| {});
 3762
 3763    let view = cx.add_window(|cx| {
 3764        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3765        build_editor(buffer, cx)
 3766    });
 3767    _ = view.update(cx, |view, cx| {
 3768        view.fold_ranges(
 3769            vec![
 3770                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3771                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3772                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3773            ],
 3774            true,
 3775            cx,
 3776        );
 3777        view.change_selections(None, cx, |s| {
 3778            s.select_display_ranges([
 3779                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3780                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3781                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3782                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3783            ])
 3784        });
 3785        assert_eq!(
 3786            view.display_text(cx),
 3787            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3788        );
 3789
 3790        view.move_line_up(&MoveLineUp, cx);
 3791        assert_eq!(
 3792            view.display_text(cx),
 3793            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3794        );
 3795        assert_eq!(
 3796            view.selections.display_ranges(cx),
 3797            vec![
 3798                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3799                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3800                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3801                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3802            ]
 3803        );
 3804    });
 3805
 3806    _ = view.update(cx, |view, cx| {
 3807        view.move_line_down(&MoveLineDown, cx);
 3808        assert_eq!(
 3809            view.display_text(cx),
 3810            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3811        );
 3812        assert_eq!(
 3813            view.selections.display_ranges(cx),
 3814            vec![
 3815                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3816                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3817                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3818                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3819            ]
 3820        );
 3821    });
 3822
 3823    _ = view.update(cx, |view, cx| {
 3824        view.move_line_down(&MoveLineDown, cx);
 3825        assert_eq!(
 3826            view.display_text(cx),
 3827            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3828        );
 3829        assert_eq!(
 3830            view.selections.display_ranges(cx),
 3831            vec![
 3832                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3833                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3834                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3835                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3836            ]
 3837        );
 3838    });
 3839
 3840    _ = view.update(cx, |view, cx| {
 3841        view.move_line_up(&MoveLineUp, cx);
 3842        assert_eq!(
 3843            view.display_text(cx),
 3844            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3845        );
 3846        assert_eq!(
 3847            view.selections.display_ranges(cx),
 3848            vec![
 3849                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3850                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3851                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3852                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3853            ]
 3854        );
 3855    });
 3856}
 3857
 3858#[gpui::test]
 3859fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3860    init_test(cx, |_| {});
 3861
 3862    let editor = cx.add_window(|cx| {
 3863        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3864        build_editor(buffer, cx)
 3865    });
 3866    _ = editor.update(cx, |editor, cx| {
 3867        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3868        editor.insert_blocks(
 3869            [BlockProperties {
 3870                style: BlockStyle::Fixed,
 3871                position: snapshot.anchor_after(Point::new(2, 0)),
 3872                disposition: BlockDisposition::Below,
 3873                height: 1,
 3874                render: Box::new(|_| div().into_any()),
 3875                priority: 0,
 3876            }],
 3877            Some(Autoscroll::fit()),
 3878            cx,
 3879        );
 3880        editor.change_selections(None, cx, |s| {
 3881            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3882        });
 3883        editor.move_line_down(&MoveLineDown, cx);
 3884    });
 3885}
 3886
 3887#[gpui::test]
 3888fn test_transpose(cx: &mut TestAppContext) {
 3889    init_test(cx, |_| {});
 3890
 3891    _ = cx.add_window(|cx| {
 3892        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3893        editor.set_style(EditorStyle::default(), cx);
 3894        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3895        editor.transpose(&Default::default(), cx);
 3896        assert_eq!(editor.text(cx), "bac");
 3897        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3898
 3899        editor.transpose(&Default::default(), cx);
 3900        assert_eq!(editor.text(cx), "bca");
 3901        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3902
 3903        editor.transpose(&Default::default(), cx);
 3904        assert_eq!(editor.text(cx), "bac");
 3905        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3906
 3907        editor
 3908    });
 3909
 3910    _ = cx.add_window(|cx| {
 3911        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3912        editor.set_style(EditorStyle::default(), cx);
 3913        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3914        editor.transpose(&Default::default(), cx);
 3915        assert_eq!(editor.text(cx), "acb\nde");
 3916        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3917
 3918        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3919        editor.transpose(&Default::default(), cx);
 3920        assert_eq!(editor.text(cx), "acbd\ne");
 3921        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3922
 3923        editor.transpose(&Default::default(), cx);
 3924        assert_eq!(editor.text(cx), "acbde\n");
 3925        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3926
 3927        editor.transpose(&Default::default(), cx);
 3928        assert_eq!(editor.text(cx), "acbd\ne");
 3929        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3930
 3931        editor
 3932    });
 3933
 3934    _ = cx.add_window(|cx| {
 3935        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3936        editor.set_style(EditorStyle::default(), cx);
 3937        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3938        editor.transpose(&Default::default(), cx);
 3939        assert_eq!(editor.text(cx), "bacd\ne");
 3940        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3941
 3942        editor.transpose(&Default::default(), cx);
 3943        assert_eq!(editor.text(cx), "bcade\n");
 3944        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3945
 3946        editor.transpose(&Default::default(), cx);
 3947        assert_eq!(editor.text(cx), "bcda\ne");
 3948        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3949
 3950        editor.transpose(&Default::default(), cx);
 3951        assert_eq!(editor.text(cx), "bcade\n");
 3952        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3953
 3954        editor.transpose(&Default::default(), cx);
 3955        assert_eq!(editor.text(cx), "bcaed\n");
 3956        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3957
 3958        editor
 3959    });
 3960
 3961    _ = cx.add_window(|cx| {
 3962        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3963        editor.set_style(EditorStyle::default(), cx);
 3964        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3965        editor.transpose(&Default::default(), cx);
 3966        assert_eq!(editor.text(cx), "🏀🍐✋");
 3967        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3968
 3969        editor.transpose(&Default::default(), cx);
 3970        assert_eq!(editor.text(cx), "🏀✋🍐");
 3971        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3972
 3973        editor.transpose(&Default::default(), cx);
 3974        assert_eq!(editor.text(cx), "🏀🍐✋");
 3975        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3976
 3977        editor
 3978    });
 3979}
 3980
 3981#[gpui::test]
 3982async fn test_rewrap(cx: &mut TestAppContext) {
 3983    init_test(cx, |_| {});
 3984
 3985    let mut cx = EditorTestContext::new(cx).await;
 3986
 3987    {
 3988        let language = Arc::new(Language::new(
 3989            LanguageConfig {
 3990                line_comments: vec!["// ".into()],
 3991                ..LanguageConfig::default()
 3992            },
 3993            None,
 3994        ));
 3995        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3996
 3997        let unwrapped_text = indoc! {"
 3998            // ˇ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.
 3999        "};
 4000
 4001        let wrapped_text = indoc! {"
 4002            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4003            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4004            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4005            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4006            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4007            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4008            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4009            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4010            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4011            // porttitor id. Aliquam id accumsan eros.ˇ
 4012        "};
 4013
 4014        cx.set_state(unwrapped_text);
 4015        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4016        cx.assert_editor_state(wrapped_text);
 4017    }
 4018
 4019    // Test that rewrapping works inside of a selection
 4020    {
 4021        let language = Arc::new(Language::new(
 4022            LanguageConfig {
 4023                line_comments: vec!["// ".into()],
 4024                ..LanguageConfig::default()
 4025            },
 4026            None,
 4027        ));
 4028        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4029
 4030        let unwrapped_text = indoc! {"
 4031            «// 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.ˇ»
 4032        "};
 4033
 4034        let wrapped_text = indoc! {"
 4035            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4036            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4037            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4038            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4039            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4040            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4041            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4042            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4043            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4044            // porttitor id. Aliquam id accumsan eros.ˇ
 4045        "};
 4046
 4047        cx.set_state(unwrapped_text);
 4048        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4049        cx.assert_editor_state(wrapped_text);
 4050    }
 4051
 4052    // Test that cursors that expand to the same region are collapsed.
 4053    {
 4054        let language = Arc::new(Language::new(
 4055            LanguageConfig {
 4056                line_comments: vec!["// ".into()],
 4057                ..LanguageConfig::default()
 4058            },
 4059            None,
 4060        ));
 4061        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4062
 4063        let unwrapped_text = indoc! {"
 4064            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4065            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4066            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4067            // ˇ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.
 4068        "};
 4069
 4070        let wrapped_text = indoc! {"
 4071            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4072            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4073            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4074            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4075            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4076            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4077            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4078            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4079            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4080            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4081        "};
 4082
 4083        cx.set_state(unwrapped_text);
 4084        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4085        cx.assert_editor_state(wrapped_text);
 4086    }
 4087
 4088    // Test that non-contiguous selections are treated separately.
 4089    {
 4090        let language = Arc::new(Language::new(
 4091            LanguageConfig {
 4092                line_comments: vec!["// ".into()],
 4093                ..LanguageConfig::default()
 4094            },
 4095            None,
 4096        ));
 4097        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4098
 4099        let unwrapped_text = indoc! {"
 4100            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4101            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4102            //
 4103            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4104            // ˇ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.
 4105        "};
 4106
 4107        let wrapped_text = indoc! {"
 4108            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4109            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4110            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4111            //
 4112            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4113            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4114            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4115            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4116            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4117            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4118            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4119        "};
 4120
 4121        cx.set_state(unwrapped_text);
 4122        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4123        cx.assert_editor_state(wrapped_text);
 4124    }
 4125
 4126    // Test that different comment prefixes are supported.
 4127    {
 4128        let language = Arc::new(Language::new(
 4129            LanguageConfig {
 4130                line_comments: vec!["# ".into()],
 4131                ..LanguageConfig::default()
 4132            },
 4133            None,
 4134        ));
 4135        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4136
 4137        let unwrapped_text = indoc! {"
 4138            # ˇ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.
 4139        "};
 4140
 4141        let wrapped_text = indoc! {"
 4142            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4143            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4144            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4145            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4146            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4147            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4148            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4149            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4150            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4151            # accumsan eros.ˇ
 4152        "};
 4153
 4154        cx.set_state(unwrapped_text);
 4155        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4156        cx.assert_editor_state(wrapped_text);
 4157    }
 4158
 4159    // Test that rewrapping is ignored outside of comments in most languages.
 4160    {
 4161        let language = Arc::new(Language::new(
 4162            LanguageConfig {
 4163                line_comments: vec!["// ".into(), "/// ".into()],
 4164                ..LanguageConfig::default()
 4165            },
 4166            Some(tree_sitter_rust::LANGUAGE.into()),
 4167        ));
 4168        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4169
 4170        let unwrapped_text = indoc! {"
 4171            /// Adds two numbers.
 4172            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4173            fn add(a: u32, b: u32) -> u32 {
 4174                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ˇ
 4175            }
 4176        "};
 4177
 4178        let wrapped_text = indoc! {"
 4179            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4180            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4181            fn add(a: u32, b: u32) -> u32 {
 4182                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ˇ
 4183            }
 4184        "};
 4185
 4186        cx.set_state(unwrapped_text);
 4187        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4188        cx.assert_editor_state(wrapped_text);
 4189    }
 4190
 4191    // Test that rewrapping works in Markdown and Plain Text languages.
 4192    {
 4193        let markdown_language = Arc::new(Language::new(
 4194            LanguageConfig {
 4195                name: "Markdown".into(),
 4196                ..LanguageConfig::default()
 4197            },
 4198            None,
 4199        ));
 4200        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4201
 4202        let unwrapped_text = indoc! {"
 4203            # Hello
 4204
 4205            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4206        "};
 4207
 4208        let wrapped_text = indoc! {"
 4209            # Hello
 4210
 4211            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4212            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4213            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4214            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4215            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4216            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4217            Integer sit amet scelerisque nisi.ˇ
 4218        "};
 4219
 4220        cx.set_state(unwrapped_text);
 4221        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4222        cx.assert_editor_state(wrapped_text);
 4223
 4224        let plaintext_language = Arc::new(Language::new(
 4225            LanguageConfig {
 4226                name: "Plain Text".into(),
 4227                ..LanguageConfig::default()
 4228            },
 4229            None,
 4230        ));
 4231        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4232
 4233        let unwrapped_text = indoc! {"
 4234            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.
 4235        "};
 4236
 4237        let wrapped_text = indoc! {"
 4238            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4239            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4240            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4241            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4242            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4243            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4244            Integer sit amet scelerisque nisi.ˇ
 4245        "};
 4246
 4247        cx.set_state(unwrapped_text);
 4248        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4249        cx.assert_editor_state(wrapped_text);
 4250    }
 4251
 4252    // Test rewrapping unaligned comments in a selection.
 4253    {
 4254        let language = Arc::new(Language::new(
 4255            LanguageConfig {
 4256                line_comments: vec!["// ".into(), "/// ".into()],
 4257                ..LanguageConfig::default()
 4258            },
 4259            Some(tree_sitter_rust::LANGUAGE.into()),
 4260        ));
 4261        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4262
 4263        let unwrapped_text = indoc! {"
 4264            fn foo() {
 4265                if true {
 4266            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4267            // Praesent semper egestas tellus id dignissim.ˇ»
 4268                    do_something();
 4269                } else {
 4270                    //
 4271                }
 4272            }
 4273        "};
 4274
 4275        let wrapped_text = indoc! {"
 4276            fn foo() {
 4277                if true {
 4278                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4279                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4280                    // egestas tellus id dignissim.ˇ
 4281                    do_something();
 4282                } else {
 4283                    //
 4284                }
 4285            }
 4286        "};
 4287
 4288        cx.set_state(unwrapped_text);
 4289        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4290        cx.assert_editor_state(wrapped_text);
 4291
 4292        let unwrapped_text = indoc! {"
 4293            fn foo() {
 4294                if true {
 4295            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4296            // Praesent semper egestas tellus id dignissim.»
 4297                    do_something();
 4298                } else {
 4299                    //
 4300                }
 4301
 4302            }
 4303        "};
 4304
 4305        let wrapped_text = indoc! {"
 4306            fn foo() {
 4307                if true {
 4308                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4309                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4310                    // egestas tellus id dignissim.ˇ
 4311                    do_something();
 4312                } else {
 4313                    //
 4314                }
 4315
 4316            }
 4317        "};
 4318
 4319        cx.set_state(unwrapped_text);
 4320        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4321        cx.assert_editor_state(wrapped_text);
 4322    }
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4327    init_test(cx, |_| {});
 4328
 4329    let mut cx = EditorTestContext::new(cx).await;
 4330
 4331    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4332    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4333    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4334
 4335    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4336    cx.set_state("two ˇfour ˇsix ˇ");
 4337    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4338    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4339
 4340    // Paste again but with only two cursors. Since the number of cursors doesn't
 4341    // match the number of slices in the clipboard, the entire clipboard text
 4342    // is pasted at each cursor.
 4343    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4344    cx.update_editor(|e, cx| {
 4345        e.handle_input("( ", cx);
 4346        e.paste(&Paste, cx);
 4347        e.handle_input(") ", cx);
 4348    });
 4349    cx.assert_editor_state(
 4350        &([
 4351            "( one✅ ",
 4352            "three ",
 4353            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4354            "three ",
 4355            "five ) ˇ",
 4356        ]
 4357        .join("\n")),
 4358    );
 4359
 4360    // Cut with three selections, one of which is full-line.
 4361    cx.set_state(indoc! {"
 4362        1«2ˇ»3
 4363        4ˇ567
 4364        «8ˇ»9"});
 4365    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4366    cx.assert_editor_state(indoc! {"
 4367        1ˇ3
 4368        ˇ9"});
 4369
 4370    // Paste with three selections, noticing how the copied selection that was full-line
 4371    // gets inserted before the second cursor.
 4372    cx.set_state(indoc! {"
 4373        1ˇ3
 4374 4375        «oˇ»ne"});
 4376    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4377    cx.assert_editor_state(indoc! {"
 4378        12ˇ3
 4379        4567
 4380 4381        8ˇne"});
 4382
 4383    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4384    cx.set_state(indoc! {"
 4385        The quick brown
 4386        fox juˇmps over
 4387        the lazy dog"});
 4388    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4389    assert_eq!(
 4390        cx.read_from_clipboard()
 4391            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4392        Some("fox jumps over\n".to_string())
 4393    );
 4394
 4395    // Paste with three selections, noticing how the copied full-line selection is inserted
 4396    // before the empty selections but replaces the selection that is non-empty.
 4397    cx.set_state(indoc! {"
 4398        Tˇhe quick brown
 4399        «foˇ»x jumps over
 4400        tˇhe lazy dog"});
 4401    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4402    cx.assert_editor_state(indoc! {"
 4403        fox jumps over
 4404        Tˇhe quick brown
 4405        fox jumps over
 4406        ˇx jumps over
 4407        fox jumps over
 4408        tˇhe lazy dog"});
 4409}
 4410
 4411#[gpui::test]
 4412async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4413    init_test(cx, |_| {});
 4414
 4415    let mut cx = EditorTestContext::new(cx).await;
 4416    let language = Arc::new(Language::new(
 4417        LanguageConfig::default(),
 4418        Some(tree_sitter_rust::LANGUAGE.into()),
 4419    ));
 4420    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4421
 4422    // Cut an indented block, without the leading whitespace.
 4423    cx.set_state(indoc! {"
 4424        const a: B = (
 4425            c(),
 4426            «d(
 4427                e,
 4428                f
 4429            )ˇ»
 4430        );
 4431    "});
 4432    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4433    cx.assert_editor_state(indoc! {"
 4434        const a: B = (
 4435            c(),
 4436            ˇ
 4437        );
 4438    "});
 4439
 4440    // Paste it at the same position.
 4441    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4442    cx.assert_editor_state(indoc! {"
 4443        const a: B = (
 4444            c(),
 4445            d(
 4446                e,
 4447                f
 4448 4449        );
 4450    "});
 4451
 4452    // Paste it at a line with a lower indent level.
 4453    cx.set_state(indoc! {"
 4454        ˇ
 4455        const a: B = (
 4456            c(),
 4457        );
 4458    "});
 4459    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4460    cx.assert_editor_state(indoc! {"
 4461        d(
 4462            e,
 4463            f
 4464 4465        const a: B = (
 4466            c(),
 4467        );
 4468    "});
 4469
 4470    // Cut an indented block, with the leading whitespace.
 4471    cx.set_state(indoc! {"
 4472        const a: B = (
 4473            c(),
 4474        «    d(
 4475                e,
 4476                f
 4477            )
 4478        ˇ»);
 4479    "});
 4480    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4481    cx.assert_editor_state(indoc! {"
 4482        const a: B = (
 4483            c(),
 4484        ˇ);
 4485    "});
 4486
 4487    // Paste it at the same position.
 4488    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4489    cx.assert_editor_state(indoc! {"
 4490        const a: B = (
 4491            c(),
 4492            d(
 4493                e,
 4494                f
 4495            )
 4496        ˇ);
 4497    "});
 4498
 4499    // Paste it at a line with a higher indent level.
 4500    cx.set_state(indoc! {"
 4501        const a: B = (
 4502            c(),
 4503            d(
 4504                e,
 4505 4506            )
 4507        );
 4508    "});
 4509    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4510    cx.assert_editor_state(indoc! {"
 4511        const a: B = (
 4512            c(),
 4513            d(
 4514                e,
 4515                f    d(
 4516                    e,
 4517                    f
 4518                )
 4519        ˇ
 4520            )
 4521        );
 4522    "});
 4523}
 4524
 4525#[gpui::test]
 4526fn test_select_all(cx: &mut TestAppContext) {
 4527    init_test(cx, |_| {});
 4528
 4529    let view = cx.add_window(|cx| {
 4530        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4531        build_editor(buffer, cx)
 4532    });
 4533    _ = view.update(cx, |view, cx| {
 4534        view.select_all(&SelectAll, cx);
 4535        assert_eq!(
 4536            view.selections.display_ranges(cx),
 4537            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4538        );
 4539    });
 4540}
 4541
 4542#[gpui::test]
 4543fn test_select_line(cx: &mut TestAppContext) {
 4544    init_test(cx, |_| {});
 4545
 4546    let view = cx.add_window(|cx| {
 4547        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4548        build_editor(buffer, cx)
 4549    });
 4550    _ = view.update(cx, |view, cx| {
 4551        view.change_selections(None, cx, |s| {
 4552            s.select_display_ranges([
 4553                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4554                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4555                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4556                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4557            ])
 4558        });
 4559        view.select_line(&SelectLine, cx);
 4560        assert_eq!(
 4561            view.selections.display_ranges(cx),
 4562            vec![
 4563                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4564                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4565            ]
 4566        );
 4567    });
 4568
 4569    _ = view.update(cx, |view, cx| {
 4570        view.select_line(&SelectLine, cx);
 4571        assert_eq!(
 4572            view.selections.display_ranges(cx),
 4573            vec![
 4574                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4575                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4576            ]
 4577        );
 4578    });
 4579
 4580    _ = view.update(cx, |view, cx| {
 4581        view.select_line(&SelectLine, cx);
 4582        assert_eq!(
 4583            view.selections.display_ranges(cx),
 4584            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4585        );
 4586    });
 4587}
 4588
 4589#[gpui::test]
 4590fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4591    init_test(cx, |_| {});
 4592
 4593    let view = cx.add_window(|cx| {
 4594        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4595        build_editor(buffer, cx)
 4596    });
 4597    _ = view.update(cx, |view, cx| {
 4598        view.fold_ranges(
 4599            vec![
 4600                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4601                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4602                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4603            ],
 4604            true,
 4605            cx,
 4606        );
 4607        view.change_selections(None, cx, |s| {
 4608            s.select_display_ranges([
 4609                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4610                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4611                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4612                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4613            ])
 4614        });
 4615        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4616    });
 4617
 4618    _ = view.update(cx, |view, cx| {
 4619        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4620        assert_eq!(
 4621            view.display_text(cx),
 4622            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4623        );
 4624        assert_eq!(
 4625            view.selections.display_ranges(cx),
 4626            [
 4627                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4628                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4629                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4630                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4631            ]
 4632        );
 4633    });
 4634
 4635    _ = view.update(cx, |view, cx| {
 4636        view.change_selections(None, cx, |s| {
 4637            s.select_display_ranges([
 4638                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4639            ])
 4640        });
 4641        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4642        assert_eq!(
 4643            view.display_text(cx),
 4644            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4645        );
 4646        assert_eq!(
 4647            view.selections.display_ranges(cx),
 4648            [
 4649                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4650                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4651                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4652                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4653                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4654                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4655                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4656                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4657            ]
 4658        );
 4659    });
 4660}
 4661
 4662#[gpui::test]
 4663async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4664    init_test(cx, |_| {});
 4665
 4666    let mut cx = EditorTestContext::new(cx).await;
 4667
 4668    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4669    cx.set_state(indoc!(
 4670        r#"abc
 4671           defˇghi
 4672
 4673           jk
 4674           nlmo
 4675           "#
 4676    ));
 4677
 4678    cx.update_editor(|editor, cx| {
 4679        editor.add_selection_above(&Default::default(), cx);
 4680    });
 4681
 4682    cx.assert_editor_state(indoc!(
 4683        r#"abcˇ
 4684           defˇghi
 4685
 4686           jk
 4687           nlmo
 4688           "#
 4689    ));
 4690
 4691    cx.update_editor(|editor, cx| {
 4692        editor.add_selection_above(&Default::default(), cx);
 4693    });
 4694
 4695    cx.assert_editor_state(indoc!(
 4696        r#"abcˇ
 4697            defˇghi
 4698
 4699            jk
 4700            nlmo
 4701            "#
 4702    ));
 4703
 4704    cx.update_editor(|view, cx| {
 4705        view.add_selection_below(&Default::default(), cx);
 4706    });
 4707
 4708    cx.assert_editor_state(indoc!(
 4709        r#"abc
 4710           defˇghi
 4711
 4712           jk
 4713           nlmo
 4714           "#
 4715    ));
 4716
 4717    cx.update_editor(|view, cx| {
 4718        view.undo_selection(&Default::default(), cx);
 4719    });
 4720
 4721    cx.assert_editor_state(indoc!(
 4722        r#"abcˇ
 4723           defˇghi
 4724
 4725           jk
 4726           nlmo
 4727           "#
 4728    ));
 4729
 4730    cx.update_editor(|view, cx| {
 4731        view.redo_selection(&Default::default(), cx);
 4732    });
 4733
 4734    cx.assert_editor_state(indoc!(
 4735        r#"abc
 4736           defˇghi
 4737
 4738           jk
 4739           nlmo
 4740           "#
 4741    ));
 4742
 4743    cx.update_editor(|view, cx| {
 4744        view.add_selection_below(&Default::default(), cx);
 4745    });
 4746
 4747    cx.assert_editor_state(indoc!(
 4748        r#"abc
 4749           defˇghi
 4750
 4751           jk
 4752           nlmˇo
 4753           "#
 4754    ));
 4755
 4756    cx.update_editor(|view, cx| {
 4757        view.add_selection_below(&Default::default(), cx);
 4758    });
 4759
 4760    cx.assert_editor_state(indoc!(
 4761        r#"abc
 4762           defˇghi
 4763
 4764           jk
 4765           nlmˇo
 4766           "#
 4767    ));
 4768
 4769    // change selections
 4770    cx.set_state(indoc!(
 4771        r#"abc
 4772           def«ˇg»hi
 4773
 4774           jk
 4775           nlmo
 4776           "#
 4777    ));
 4778
 4779    cx.update_editor(|view, cx| {
 4780        view.add_selection_below(&Default::default(), cx);
 4781    });
 4782
 4783    cx.assert_editor_state(indoc!(
 4784        r#"abc
 4785           def«ˇg»hi
 4786
 4787           jk
 4788           nlm«ˇo»
 4789           "#
 4790    ));
 4791
 4792    cx.update_editor(|view, cx| {
 4793        view.add_selection_below(&Default::default(), cx);
 4794    });
 4795
 4796    cx.assert_editor_state(indoc!(
 4797        r#"abc
 4798           def«ˇg»hi
 4799
 4800           jk
 4801           nlm«ˇo»
 4802           "#
 4803    ));
 4804
 4805    cx.update_editor(|view, cx| {
 4806        view.add_selection_above(&Default::default(), cx);
 4807    });
 4808
 4809    cx.assert_editor_state(indoc!(
 4810        r#"abc
 4811           def«ˇg»hi
 4812
 4813           jk
 4814           nlmo
 4815           "#
 4816    ));
 4817
 4818    cx.update_editor(|view, cx| {
 4819        view.add_selection_above(&Default::default(), cx);
 4820    });
 4821
 4822    cx.assert_editor_state(indoc!(
 4823        r#"abc
 4824           def«ˇg»hi
 4825
 4826           jk
 4827           nlmo
 4828           "#
 4829    ));
 4830
 4831    // Change selections again
 4832    cx.set_state(indoc!(
 4833        r#"a«bc
 4834           defgˇ»hi
 4835
 4836           jk
 4837           nlmo
 4838           "#
 4839    ));
 4840
 4841    cx.update_editor(|view, cx| {
 4842        view.add_selection_below(&Default::default(), cx);
 4843    });
 4844
 4845    cx.assert_editor_state(indoc!(
 4846        r#"a«bcˇ»
 4847           d«efgˇ»hi
 4848
 4849           j«kˇ»
 4850           nlmo
 4851           "#
 4852    ));
 4853
 4854    cx.update_editor(|view, cx| {
 4855        view.add_selection_below(&Default::default(), cx);
 4856    });
 4857    cx.assert_editor_state(indoc!(
 4858        r#"a«bcˇ»
 4859           d«efgˇ»hi
 4860
 4861           j«kˇ»
 4862           n«lmoˇ»
 4863           "#
 4864    ));
 4865    cx.update_editor(|view, cx| {
 4866        view.add_selection_above(&Default::default(), cx);
 4867    });
 4868
 4869    cx.assert_editor_state(indoc!(
 4870        r#"a«bcˇ»
 4871           d«efgˇ»hi
 4872
 4873           j«kˇ»
 4874           nlmo
 4875           "#
 4876    ));
 4877
 4878    // Change selections again
 4879    cx.set_state(indoc!(
 4880        r#"abc
 4881           d«ˇefghi
 4882
 4883           jk
 4884           nlm»o
 4885           "#
 4886    ));
 4887
 4888    cx.update_editor(|view, cx| {
 4889        view.add_selection_above(&Default::default(), cx);
 4890    });
 4891
 4892    cx.assert_editor_state(indoc!(
 4893        r#"a«ˇbc»
 4894           d«ˇef»ghi
 4895
 4896           j«ˇk»
 4897           n«ˇlm»o
 4898           "#
 4899    ));
 4900
 4901    cx.update_editor(|view, cx| {
 4902        view.add_selection_below(&Default::default(), cx);
 4903    });
 4904
 4905    cx.assert_editor_state(indoc!(
 4906        r#"abc
 4907           d«ˇef»ghi
 4908
 4909           j«ˇk»
 4910           n«ˇlm»o
 4911           "#
 4912    ));
 4913}
 4914
 4915#[gpui::test]
 4916async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4917    init_test(cx, |_| {});
 4918
 4919    let mut cx = EditorTestContext::new(cx).await;
 4920    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4921
 4922    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4923        .unwrap();
 4924    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4925
 4926    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4927        .unwrap();
 4928    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4929
 4930    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4931    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4932
 4933    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4934    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4935
 4936    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4937        .unwrap();
 4938    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4939
 4940    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4941        .unwrap();
 4942    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4943}
 4944
 4945#[gpui::test]
 4946async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4947    init_test(cx, |_| {});
 4948
 4949    let mut cx = EditorTestContext::new(cx).await;
 4950    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4951
 4952    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4953        .unwrap();
 4954    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4955}
 4956
 4957#[gpui::test]
 4958async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4959    init_test(cx, |_| {});
 4960
 4961    let mut cx = EditorTestContext::new(cx).await;
 4962    cx.set_state(
 4963        r#"let foo = 2;
 4964lˇet foo = 2;
 4965let fooˇ = 2;
 4966let foo = 2;
 4967let foo = ˇ2;"#,
 4968    );
 4969
 4970    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4971        .unwrap();
 4972    cx.assert_editor_state(
 4973        r#"let foo = 2;
 4974«letˇ» foo = 2;
 4975let «fooˇ» = 2;
 4976let foo = 2;
 4977let foo = «2ˇ»;"#,
 4978    );
 4979
 4980    // noop for multiple selections with different contents
 4981    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4982        .unwrap();
 4983    cx.assert_editor_state(
 4984        r#"let foo = 2;
 4985«letˇ» foo = 2;
 4986let «fooˇ» = 2;
 4987let foo = 2;
 4988let foo = «2ˇ»;"#,
 4989    );
 4990}
 4991
 4992#[gpui::test]
 4993async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4994    init_test(cx, |_| {});
 4995
 4996    let mut cx =
 4997        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 4998
 4999    cx.assert_editor_state(indoc! {"
 5000        ˇbbb
 5001        ccc
 5002
 5003        bbb
 5004        ccc
 5005        "});
 5006    cx.dispatch_action(SelectPrevious::default());
 5007    cx.assert_editor_state(indoc! {"
 5008                «bbbˇ»
 5009                ccc
 5010
 5011                bbb
 5012                ccc
 5013                "});
 5014    cx.dispatch_action(SelectPrevious::default());
 5015    cx.assert_editor_state(indoc! {"
 5016                «bbbˇ»
 5017                ccc
 5018
 5019                «bbbˇ»
 5020                ccc
 5021                "});
 5022}
 5023
 5024#[gpui::test]
 5025async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5026    init_test(cx, |_| {});
 5027
 5028    let mut cx = EditorTestContext::new(cx).await;
 5029    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5030
 5031    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5032        .unwrap();
 5033    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5034
 5035    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5036        .unwrap();
 5037    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5038
 5039    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5040    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5041
 5042    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5043    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5044
 5045    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5046        .unwrap();
 5047    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5048
 5049    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5050        .unwrap();
 5051    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5052
 5053    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5054        .unwrap();
 5055    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5056}
 5057
 5058#[gpui::test]
 5059async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5060    init_test(cx, |_| {});
 5061
 5062    let mut cx = EditorTestContext::new(cx).await;
 5063    cx.set_state(
 5064        r#"let foo = 2;
 5065lˇet foo = 2;
 5066let fooˇ = 2;
 5067let foo = 2;
 5068let foo = ˇ2;"#,
 5069    );
 5070
 5071    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5072        .unwrap();
 5073    cx.assert_editor_state(
 5074        r#"let foo = 2;
 5075«letˇ» foo = 2;
 5076let «fooˇ» = 2;
 5077let foo = 2;
 5078let foo = «2ˇ»;"#,
 5079    );
 5080
 5081    // noop for multiple selections with different contents
 5082    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5083        .unwrap();
 5084    cx.assert_editor_state(
 5085        r#"let foo = 2;
 5086«letˇ» foo = 2;
 5087let «fooˇ» = 2;
 5088let foo = 2;
 5089let foo = «2ˇ»;"#,
 5090    );
 5091}
 5092
 5093#[gpui::test]
 5094async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5095    init_test(cx, |_| {});
 5096
 5097    let mut cx = EditorTestContext::new(cx).await;
 5098    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5099
 5100    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5101        .unwrap();
 5102    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5103
 5104    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5105        .unwrap();
 5106    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5107
 5108    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5109    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5110
 5111    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5112    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5113
 5114    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5115        .unwrap();
 5116    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5117
 5118    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5119        .unwrap();
 5120    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5121}
 5122
 5123#[gpui::test]
 5124async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5125    init_test(cx, |_| {});
 5126
 5127    let language = Arc::new(Language::new(
 5128        LanguageConfig::default(),
 5129        Some(tree_sitter_rust::LANGUAGE.into()),
 5130    ));
 5131
 5132    let text = r#"
 5133        use mod1::mod2::{mod3, mod4};
 5134
 5135        fn fn_1(param1: bool, param2: &str) {
 5136            let var1 = "text";
 5137        }
 5138    "#
 5139    .unindent();
 5140
 5141    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5142    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5143    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5144
 5145    editor
 5146        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5147        .await;
 5148
 5149    editor.update(cx, |view, cx| {
 5150        view.change_selections(None, cx, |s| {
 5151            s.select_display_ranges([
 5152                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5153                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5154                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5155            ]);
 5156        });
 5157        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5158    });
 5159    editor.update(cx, |editor, cx| {
 5160        assert_text_with_selections(
 5161            editor,
 5162            indoc! {r#"
 5163                use mod1::mod2::{mod3, «mod4ˇ»};
 5164
 5165                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5166                    let var1 = "«textˇ»";
 5167                }
 5168            "#},
 5169            cx,
 5170        );
 5171    });
 5172
 5173    editor.update(cx, |view, cx| {
 5174        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5175    });
 5176    editor.update(cx, |editor, cx| {
 5177        assert_text_with_selections(
 5178            editor,
 5179            indoc! {r#"
 5180                use mod1::mod2::«{mod3, mod4}ˇ»;
 5181
 5182                «ˇfn fn_1(param1: bool, param2: &str) {
 5183                    let var1 = "text";
 5184 5185            "#},
 5186            cx,
 5187        );
 5188    });
 5189
 5190    editor.update(cx, |view, cx| {
 5191        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5192    });
 5193    assert_eq!(
 5194        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5195        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5196    );
 5197
 5198    // Trying to expand the selected syntax node one more time has no effect.
 5199    editor.update(cx, |view, cx| {
 5200        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5201    });
 5202    assert_eq!(
 5203        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5204        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5205    );
 5206
 5207    editor.update(cx, |view, cx| {
 5208        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5209    });
 5210    editor.update(cx, |editor, cx| {
 5211        assert_text_with_selections(
 5212            editor,
 5213            indoc! {r#"
 5214                use mod1::mod2::«{mod3, mod4}ˇ»;
 5215
 5216                «ˇfn fn_1(param1: bool, param2: &str) {
 5217                    let var1 = "text";
 5218 5219            "#},
 5220            cx,
 5221        );
 5222    });
 5223
 5224    editor.update(cx, |view, cx| {
 5225        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5226    });
 5227    editor.update(cx, |editor, cx| {
 5228        assert_text_with_selections(
 5229            editor,
 5230            indoc! {r#"
 5231                use mod1::mod2::{mod3, «mod4ˇ»};
 5232
 5233                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5234                    let var1 = "«textˇ»";
 5235                }
 5236            "#},
 5237            cx,
 5238        );
 5239    });
 5240
 5241    editor.update(cx, |view, cx| {
 5242        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5243    });
 5244    editor.update(cx, |editor, cx| {
 5245        assert_text_with_selections(
 5246            editor,
 5247            indoc! {r#"
 5248                use mod1::mod2::{mod3, mo«ˇ»d4};
 5249
 5250                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5251                    let var1 = "te«ˇ»xt";
 5252                }
 5253            "#},
 5254            cx,
 5255        );
 5256    });
 5257
 5258    // Trying to shrink the selected syntax node one more time has no effect.
 5259    editor.update(cx, |view, cx| {
 5260        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5261    });
 5262    editor.update(cx, |editor, cx| {
 5263        assert_text_with_selections(
 5264            editor,
 5265            indoc! {r#"
 5266                use mod1::mod2::{mod3, mo«ˇ»d4};
 5267
 5268                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5269                    let var1 = "te«ˇ»xt";
 5270                }
 5271            "#},
 5272            cx,
 5273        );
 5274    });
 5275
 5276    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5277    // a fold.
 5278    editor.update(cx, |view, cx| {
 5279        view.fold_ranges(
 5280            vec![
 5281                (
 5282                    Point::new(0, 21)..Point::new(0, 24),
 5283                    FoldPlaceholder::test(),
 5284                ),
 5285                (
 5286                    Point::new(3, 20)..Point::new(3, 22),
 5287                    FoldPlaceholder::test(),
 5288                ),
 5289            ],
 5290            true,
 5291            cx,
 5292        );
 5293        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5294    });
 5295    editor.update(cx, |editor, cx| {
 5296        assert_text_with_selections(
 5297            editor,
 5298            indoc! {r#"
 5299                use mod1::mod2::«{mod3, mod4}ˇ»;
 5300
 5301                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5302                    «let var1 = "text";ˇ»
 5303                }
 5304            "#},
 5305            cx,
 5306        );
 5307    });
 5308}
 5309
 5310#[gpui::test]
 5311async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5312    init_test(cx, |_| {});
 5313
 5314    let language = Arc::new(
 5315        Language::new(
 5316            LanguageConfig {
 5317                brackets: BracketPairConfig {
 5318                    pairs: vec![
 5319                        BracketPair {
 5320                            start: "{".to_string(),
 5321                            end: "}".to_string(),
 5322                            close: false,
 5323                            surround: false,
 5324                            newline: true,
 5325                        },
 5326                        BracketPair {
 5327                            start: "(".to_string(),
 5328                            end: ")".to_string(),
 5329                            close: false,
 5330                            surround: false,
 5331                            newline: true,
 5332                        },
 5333                    ],
 5334                    ..Default::default()
 5335                },
 5336                ..Default::default()
 5337            },
 5338            Some(tree_sitter_rust::LANGUAGE.into()),
 5339        )
 5340        .with_indents_query(
 5341            r#"
 5342                (_ "(" ")" @end) @indent
 5343                (_ "{" "}" @end) @indent
 5344            "#,
 5345        )
 5346        .unwrap(),
 5347    );
 5348
 5349    let text = "fn a() {}";
 5350
 5351    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5352    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5353    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5354    editor
 5355        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5356        .await;
 5357
 5358    editor.update(cx, |editor, cx| {
 5359        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5360        editor.newline(&Newline, cx);
 5361        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5362        assert_eq!(
 5363            editor.selections.ranges(cx),
 5364            &[
 5365                Point::new(1, 4)..Point::new(1, 4),
 5366                Point::new(3, 4)..Point::new(3, 4),
 5367                Point::new(5, 0)..Point::new(5, 0)
 5368            ]
 5369        );
 5370    });
 5371}
 5372
 5373#[gpui::test]
 5374async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5375    init_test(cx, |_| {});
 5376
 5377    let mut cx = EditorTestContext::new(cx).await;
 5378
 5379    let language = Arc::new(Language::new(
 5380        LanguageConfig {
 5381            brackets: BracketPairConfig {
 5382                pairs: vec![
 5383                    BracketPair {
 5384                        start: "{".to_string(),
 5385                        end: "}".to_string(),
 5386                        close: true,
 5387                        surround: true,
 5388                        newline: true,
 5389                    },
 5390                    BracketPair {
 5391                        start: "(".to_string(),
 5392                        end: ")".to_string(),
 5393                        close: true,
 5394                        surround: true,
 5395                        newline: true,
 5396                    },
 5397                    BracketPair {
 5398                        start: "/*".to_string(),
 5399                        end: " */".to_string(),
 5400                        close: true,
 5401                        surround: true,
 5402                        newline: true,
 5403                    },
 5404                    BracketPair {
 5405                        start: "[".to_string(),
 5406                        end: "]".to_string(),
 5407                        close: false,
 5408                        surround: false,
 5409                        newline: true,
 5410                    },
 5411                    BracketPair {
 5412                        start: "\"".to_string(),
 5413                        end: "\"".to_string(),
 5414                        close: true,
 5415                        surround: true,
 5416                        newline: false,
 5417                    },
 5418                    BracketPair {
 5419                        start: "<".to_string(),
 5420                        end: ">".to_string(),
 5421                        close: false,
 5422                        surround: true,
 5423                        newline: true,
 5424                    },
 5425                ],
 5426                ..Default::default()
 5427            },
 5428            autoclose_before: "})]".to_string(),
 5429            ..Default::default()
 5430        },
 5431        Some(tree_sitter_rust::LANGUAGE.into()),
 5432    ));
 5433
 5434    cx.language_registry().add(language.clone());
 5435    cx.update_buffer(|buffer, cx| {
 5436        buffer.set_language(Some(language), cx);
 5437    });
 5438
 5439    cx.set_state(
 5440        &r#"
 5441            🏀ˇ
 5442            εˇ
 5443            ❤️ˇ
 5444        "#
 5445        .unindent(),
 5446    );
 5447
 5448    // autoclose multiple nested brackets at multiple cursors
 5449    cx.update_editor(|view, cx| {
 5450        view.handle_input("{", cx);
 5451        view.handle_input("{", cx);
 5452        view.handle_input("{", cx);
 5453    });
 5454    cx.assert_editor_state(
 5455        &"
 5456            🏀{{{ˇ}}}
 5457            ε{{{ˇ}}}
 5458            ❤️{{{ˇ}}}
 5459        "
 5460        .unindent(),
 5461    );
 5462
 5463    // insert a different closing bracket
 5464    cx.update_editor(|view, cx| {
 5465        view.handle_input(")", cx);
 5466    });
 5467    cx.assert_editor_state(
 5468        &"
 5469            🏀{{{)ˇ}}}
 5470            ε{{{)ˇ}}}
 5471            ❤️{{{)ˇ}}}
 5472        "
 5473        .unindent(),
 5474    );
 5475
 5476    // skip over the auto-closed brackets when typing a closing bracket
 5477    cx.update_editor(|view, cx| {
 5478        view.move_right(&MoveRight, cx);
 5479        view.handle_input("}", cx);
 5480        view.handle_input("}", cx);
 5481        view.handle_input("}", cx);
 5482    });
 5483    cx.assert_editor_state(
 5484        &"
 5485            🏀{{{)}}}}ˇ
 5486            ε{{{)}}}}ˇ
 5487            ❤️{{{)}}}}ˇ
 5488        "
 5489        .unindent(),
 5490    );
 5491
 5492    // autoclose multi-character pairs
 5493    cx.set_state(
 5494        &"
 5495            ˇ
 5496            ˇ
 5497        "
 5498        .unindent(),
 5499    );
 5500    cx.update_editor(|view, cx| {
 5501        view.handle_input("/", cx);
 5502        view.handle_input("*", cx);
 5503    });
 5504    cx.assert_editor_state(
 5505        &"
 5506            /*ˇ */
 5507            /*ˇ */
 5508        "
 5509        .unindent(),
 5510    );
 5511
 5512    // one cursor autocloses a multi-character pair, one cursor
 5513    // does not autoclose.
 5514    cx.set_state(
 5515        &"
 5516 5517            ˇ
 5518        "
 5519        .unindent(),
 5520    );
 5521    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5522    cx.assert_editor_state(
 5523        &"
 5524            /*ˇ */
 5525 5526        "
 5527        .unindent(),
 5528    );
 5529
 5530    // Don't autoclose if the next character isn't whitespace and isn't
 5531    // listed in the language's "autoclose_before" section.
 5532    cx.set_state("ˇa b");
 5533    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5534    cx.assert_editor_state("{ˇa b");
 5535
 5536    // Don't autoclose if `close` is false for the bracket pair
 5537    cx.set_state("ˇ");
 5538    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5539    cx.assert_editor_state("");
 5540
 5541    // Surround with brackets if text is selected
 5542    cx.set_state("«aˇ» b");
 5543    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5544    cx.assert_editor_state("{«aˇ»} b");
 5545
 5546    // Autclose pair where the start and end characters are the same
 5547    cx.set_state("");
 5548    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5549    cx.assert_editor_state("a\"ˇ\"");
 5550    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5551    cx.assert_editor_state("a\"\"ˇ");
 5552
 5553    // Don't autoclose pair if autoclose is disabled
 5554    cx.set_state("ˇ");
 5555    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5556    cx.assert_editor_state("");
 5557
 5558    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5559    cx.set_state("«aˇ» b");
 5560    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5561    cx.assert_editor_state("<«aˇ»> b");
 5562}
 5563
 5564#[gpui::test]
 5565async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5566    init_test(cx, |settings| {
 5567        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5568    });
 5569
 5570    let mut cx = EditorTestContext::new(cx).await;
 5571
 5572    let language = Arc::new(Language::new(
 5573        LanguageConfig {
 5574            brackets: BracketPairConfig {
 5575                pairs: vec![
 5576                    BracketPair {
 5577                        start: "{".to_string(),
 5578                        end: "}".to_string(),
 5579                        close: true,
 5580                        surround: true,
 5581                        newline: true,
 5582                    },
 5583                    BracketPair {
 5584                        start: "(".to_string(),
 5585                        end: ")".to_string(),
 5586                        close: true,
 5587                        surround: true,
 5588                        newline: true,
 5589                    },
 5590                    BracketPair {
 5591                        start: "[".to_string(),
 5592                        end: "]".to_string(),
 5593                        close: false,
 5594                        surround: false,
 5595                        newline: true,
 5596                    },
 5597                ],
 5598                ..Default::default()
 5599            },
 5600            autoclose_before: "})]".to_string(),
 5601            ..Default::default()
 5602        },
 5603        Some(tree_sitter_rust::LANGUAGE.into()),
 5604    ));
 5605
 5606    cx.language_registry().add(language.clone());
 5607    cx.update_buffer(|buffer, cx| {
 5608        buffer.set_language(Some(language), cx);
 5609    });
 5610
 5611    cx.set_state(
 5612        &"
 5613            ˇ
 5614            ˇ
 5615            ˇ
 5616        "
 5617        .unindent(),
 5618    );
 5619
 5620    // ensure only matching closing brackets are skipped over
 5621    cx.update_editor(|view, cx| {
 5622        view.handle_input("}", cx);
 5623        view.move_left(&MoveLeft, cx);
 5624        view.handle_input(")", cx);
 5625        view.move_left(&MoveLeft, cx);
 5626    });
 5627    cx.assert_editor_state(
 5628        &"
 5629            ˇ)}
 5630            ˇ)}
 5631            ˇ)}
 5632        "
 5633        .unindent(),
 5634    );
 5635
 5636    // skip-over closing brackets at multiple cursors
 5637    cx.update_editor(|view, cx| {
 5638        view.handle_input(")", cx);
 5639        view.handle_input("}", cx);
 5640    });
 5641    cx.assert_editor_state(
 5642        &"
 5643            )}ˇ
 5644            )}ˇ
 5645            )}ˇ
 5646        "
 5647        .unindent(),
 5648    );
 5649
 5650    // ignore non-close brackets
 5651    cx.update_editor(|view, cx| {
 5652        view.handle_input("]", cx);
 5653        view.move_left(&MoveLeft, cx);
 5654        view.handle_input("]", cx);
 5655    });
 5656    cx.assert_editor_state(
 5657        &"
 5658            )}]ˇ]
 5659            )}]ˇ]
 5660            )}]ˇ]
 5661        "
 5662        .unindent(),
 5663    );
 5664}
 5665
 5666#[gpui::test]
 5667async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5668    init_test(cx, |_| {});
 5669
 5670    let mut cx = EditorTestContext::new(cx).await;
 5671
 5672    let html_language = Arc::new(
 5673        Language::new(
 5674            LanguageConfig {
 5675                name: "HTML".into(),
 5676                brackets: BracketPairConfig {
 5677                    pairs: vec![
 5678                        BracketPair {
 5679                            start: "<".into(),
 5680                            end: ">".into(),
 5681                            close: true,
 5682                            ..Default::default()
 5683                        },
 5684                        BracketPair {
 5685                            start: "{".into(),
 5686                            end: "}".into(),
 5687                            close: true,
 5688                            ..Default::default()
 5689                        },
 5690                        BracketPair {
 5691                            start: "(".into(),
 5692                            end: ")".into(),
 5693                            close: true,
 5694                            ..Default::default()
 5695                        },
 5696                    ],
 5697                    ..Default::default()
 5698                },
 5699                autoclose_before: "})]>".into(),
 5700                ..Default::default()
 5701            },
 5702            Some(tree_sitter_html::language()),
 5703        )
 5704        .with_injection_query(
 5705            r#"
 5706            (script_element
 5707                (raw_text) @content
 5708                (#set! "language" "javascript"))
 5709            "#,
 5710        )
 5711        .unwrap(),
 5712    );
 5713
 5714    let javascript_language = Arc::new(Language::new(
 5715        LanguageConfig {
 5716            name: "JavaScript".into(),
 5717            brackets: BracketPairConfig {
 5718                pairs: vec![
 5719                    BracketPair {
 5720                        start: "/*".into(),
 5721                        end: " */".into(),
 5722                        close: true,
 5723                        ..Default::default()
 5724                    },
 5725                    BracketPair {
 5726                        start: "{".into(),
 5727                        end: "}".into(),
 5728                        close: true,
 5729                        ..Default::default()
 5730                    },
 5731                    BracketPair {
 5732                        start: "(".into(),
 5733                        end: ")".into(),
 5734                        close: true,
 5735                        ..Default::default()
 5736                    },
 5737                ],
 5738                ..Default::default()
 5739            },
 5740            autoclose_before: "})]>".into(),
 5741            ..Default::default()
 5742        },
 5743        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5744    ));
 5745
 5746    cx.language_registry().add(html_language.clone());
 5747    cx.language_registry().add(javascript_language.clone());
 5748
 5749    cx.update_buffer(|buffer, cx| {
 5750        buffer.set_language(Some(html_language), cx);
 5751    });
 5752
 5753    cx.set_state(
 5754        &r#"
 5755            <body>ˇ
 5756                <script>
 5757                    var x = 1;ˇ
 5758                </script>
 5759            </body>ˇ
 5760        "#
 5761        .unindent(),
 5762    );
 5763
 5764    // Precondition: different languages are active at different locations.
 5765    cx.update_editor(|editor, cx| {
 5766        let snapshot = editor.snapshot(cx);
 5767        let cursors = editor.selections.ranges::<usize>(cx);
 5768        let languages = cursors
 5769            .iter()
 5770            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5771            .collect::<Vec<_>>();
 5772        assert_eq!(
 5773            languages,
 5774            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5775        );
 5776    });
 5777
 5778    // Angle brackets autoclose in HTML, but not JavaScript.
 5779    cx.update_editor(|editor, cx| {
 5780        editor.handle_input("<", cx);
 5781        editor.handle_input("a", cx);
 5782    });
 5783    cx.assert_editor_state(
 5784        &r#"
 5785            <body><aˇ>
 5786                <script>
 5787                    var x = 1;<aˇ
 5788                </script>
 5789            </body><aˇ>
 5790        "#
 5791        .unindent(),
 5792    );
 5793
 5794    // Curly braces and parens autoclose in both HTML and JavaScript.
 5795    cx.update_editor(|editor, cx| {
 5796        editor.handle_input(" b=", cx);
 5797        editor.handle_input("{", cx);
 5798        editor.handle_input("c", cx);
 5799        editor.handle_input("(", cx);
 5800    });
 5801    cx.assert_editor_state(
 5802        &r#"
 5803            <body><a b={c(ˇ)}>
 5804                <script>
 5805                    var x = 1;<a b={c(ˇ)}
 5806                </script>
 5807            </body><a b={c(ˇ)}>
 5808        "#
 5809        .unindent(),
 5810    );
 5811
 5812    // Brackets that were already autoclosed are skipped.
 5813    cx.update_editor(|editor, cx| {
 5814        editor.handle_input(")", cx);
 5815        editor.handle_input("d", cx);
 5816        editor.handle_input("}", cx);
 5817    });
 5818    cx.assert_editor_state(
 5819        &r#"
 5820            <body><a b={c()d}ˇ>
 5821                <script>
 5822                    var x = 1;<a b={c()d}ˇ
 5823                </script>
 5824            </body><a b={c()d}ˇ>
 5825        "#
 5826        .unindent(),
 5827    );
 5828    cx.update_editor(|editor, cx| {
 5829        editor.handle_input(">", cx);
 5830    });
 5831    cx.assert_editor_state(
 5832        &r#"
 5833            <body><a b={c()d}>ˇ
 5834                <script>
 5835                    var x = 1;<a b={c()d}>ˇ
 5836                </script>
 5837            </body><a b={c()d}>ˇ
 5838        "#
 5839        .unindent(),
 5840    );
 5841
 5842    // Reset
 5843    cx.set_state(
 5844        &r#"
 5845            <body>ˇ
 5846                <script>
 5847                    var x = 1;ˇ
 5848                </script>
 5849            </body>ˇ
 5850        "#
 5851        .unindent(),
 5852    );
 5853
 5854    cx.update_editor(|editor, cx| {
 5855        editor.handle_input("<", cx);
 5856    });
 5857    cx.assert_editor_state(
 5858        &r#"
 5859            <body><ˇ>
 5860                <script>
 5861                    var x = 1;<ˇ
 5862                </script>
 5863            </body><ˇ>
 5864        "#
 5865        .unindent(),
 5866    );
 5867
 5868    // When backspacing, the closing angle brackets are removed.
 5869    cx.update_editor(|editor, cx| {
 5870        editor.backspace(&Backspace, cx);
 5871    });
 5872    cx.assert_editor_state(
 5873        &r#"
 5874            <body>ˇ
 5875                <script>
 5876                    var x = 1;ˇ
 5877                </script>
 5878            </body>ˇ
 5879        "#
 5880        .unindent(),
 5881    );
 5882
 5883    // Block comments autoclose in JavaScript, but not HTML.
 5884    cx.update_editor(|editor, cx| {
 5885        editor.handle_input("/", cx);
 5886        editor.handle_input("*", cx);
 5887    });
 5888    cx.assert_editor_state(
 5889        &r#"
 5890            <body>/*ˇ
 5891                <script>
 5892                    var x = 1;/*ˇ */
 5893                </script>
 5894            </body>/*ˇ
 5895        "#
 5896        .unindent(),
 5897    );
 5898}
 5899
 5900#[gpui::test]
 5901async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5902    init_test(cx, |_| {});
 5903
 5904    let mut cx = EditorTestContext::new(cx).await;
 5905
 5906    let rust_language = Arc::new(
 5907        Language::new(
 5908            LanguageConfig {
 5909                name: "Rust".into(),
 5910                brackets: serde_json::from_value(json!([
 5911                    { "start": "{", "end": "}", "close": true, "newline": true },
 5912                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5913                ]))
 5914                .unwrap(),
 5915                autoclose_before: "})]>".into(),
 5916                ..Default::default()
 5917            },
 5918            Some(tree_sitter_rust::LANGUAGE.into()),
 5919        )
 5920        .with_override_query("(string_literal) @string")
 5921        .unwrap(),
 5922    );
 5923
 5924    cx.language_registry().add(rust_language.clone());
 5925    cx.update_buffer(|buffer, cx| {
 5926        buffer.set_language(Some(rust_language), cx);
 5927    });
 5928
 5929    cx.set_state(
 5930        &r#"
 5931            let x = ˇ
 5932        "#
 5933        .unindent(),
 5934    );
 5935
 5936    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5937    cx.update_editor(|editor, cx| {
 5938        editor.handle_input("\"", cx);
 5939    });
 5940    cx.assert_editor_state(
 5941        &r#"
 5942            let x = "ˇ"
 5943        "#
 5944        .unindent(),
 5945    );
 5946
 5947    // Inserting another quotation mark. The cursor moves across the existing
 5948    // automatically-inserted quotation mark.
 5949    cx.update_editor(|editor, cx| {
 5950        editor.handle_input("\"", cx);
 5951    });
 5952    cx.assert_editor_state(
 5953        &r#"
 5954            let x = ""ˇ
 5955        "#
 5956        .unindent(),
 5957    );
 5958
 5959    // Reset
 5960    cx.set_state(
 5961        &r#"
 5962            let x = ˇ
 5963        "#
 5964        .unindent(),
 5965    );
 5966
 5967    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5968    cx.update_editor(|editor, cx| {
 5969        editor.handle_input("\"", cx);
 5970        editor.handle_input(" ", cx);
 5971        editor.move_left(&Default::default(), cx);
 5972        editor.handle_input("\\", cx);
 5973        editor.handle_input("\"", cx);
 5974    });
 5975    cx.assert_editor_state(
 5976        &r#"
 5977            let x = "\"ˇ "
 5978        "#
 5979        .unindent(),
 5980    );
 5981
 5982    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5983    // mark. Nothing is inserted.
 5984    cx.update_editor(|editor, cx| {
 5985        editor.move_right(&Default::default(), cx);
 5986        editor.handle_input("\"", cx);
 5987    });
 5988    cx.assert_editor_state(
 5989        &r#"
 5990            let x = "\" "ˇ
 5991        "#
 5992        .unindent(),
 5993    );
 5994}
 5995
 5996#[gpui::test]
 5997async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5998    init_test(cx, |_| {});
 5999
 6000    let language = Arc::new(Language::new(
 6001        LanguageConfig {
 6002            brackets: BracketPairConfig {
 6003                pairs: vec![
 6004                    BracketPair {
 6005                        start: "{".to_string(),
 6006                        end: "}".to_string(),
 6007                        close: true,
 6008                        surround: true,
 6009                        newline: true,
 6010                    },
 6011                    BracketPair {
 6012                        start: "/* ".to_string(),
 6013                        end: "*/".to_string(),
 6014                        close: true,
 6015                        surround: true,
 6016                        ..Default::default()
 6017                    },
 6018                ],
 6019                ..Default::default()
 6020            },
 6021            ..Default::default()
 6022        },
 6023        Some(tree_sitter_rust::LANGUAGE.into()),
 6024    ));
 6025
 6026    let text = r#"
 6027        a
 6028        b
 6029        c
 6030    "#
 6031    .unindent();
 6032
 6033    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6034    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6035    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6036    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6037        .await;
 6038
 6039    view.update(cx, |view, cx| {
 6040        view.change_selections(None, cx, |s| {
 6041            s.select_display_ranges([
 6042                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6043                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6044                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6045            ])
 6046        });
 6047
 6048        view.handle_input("{", cx);
 6049        view.handle_input("{", cx);
 6050        view.handle_input("{", cx);
 6051        assert_eq!(
 6052            view.text(cx),
 6053            "
 6054                {{{a}}}
 6055                {{{b}}}
 6056                {{{c}}}
 6057            "
 6058            .unindent()
 6059        );
 6060        assert_eq!(
 6061            view.selections.display_ranges(cx),
 6062            [
 6063                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6064                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6065                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6066            ]
 6067        );
 6068
 6069        view.undo(&Undo, cx);
 6070        view.undo(&Undo, cx);
 6071        view.undo(&Undo, cx);
 6072        assert_eq!(
 6073            view.text(cx),
 6074            "
 6075                a
 6076                b
 6077                c
 6078            "
 6079            .unindent()
 6080        );
 6081        assert_eq!(
 6082            view.selections.display_ranges(cx),
 6083            [
 6084                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6085                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6086                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6087            ]
 6088        );
 6089
 6090        // Ensure inserting the first character of a multi-byte bracket pair
 6091        // doesn't surround the selections with the bracket.
 6092        view.handle_input("/", cx);
 6093        assert_eq!(
 6094            view.text(cx),
 6095            "
 6096                /
 6097                /
 6098                /
 6099            "
 6100            .unindent()
 6101        );
 6102        assert_eq!(
 6103            view.selections.display_ranges(cx),
 6104            [
 6105                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6106                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6107                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6108            ]
 6109        );
 6110
 6111        view.undo(&Undo, cx);
 6112        assert_eq!(
 6113            view.text(cx),
 6114            "
 6115                a
 6116                b
 6117                c
 6118            "
 6119            .unindent()
 6120        );
 6121        assert_eq!(
 6122            view.selections.display_ranges(cx),
 6123            [
 6124                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6125                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6126                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6127            ]
 6128        );
 6129
 6130        // Ensure inserting the last character of a multi-byte bracket pair
 6131        // doesn't surround the selections with the bracket.
 6132        view.handle_input("*", cx);
 6133        assert_eq!(
 6134            view.text(cx),
 6135            "
 6136                *
 6137                *
 6138                *
 6139            "
 6140            .unindent()
 6141        );
 6142        assert_eq!(
 6143            view.selections.display_ranges(cx),
 6144            [
 6145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6146                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6147                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6148            ]
 6149        );
 6150    });
 6151}
 6152
 6153#[gpui::test]
 6154async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6155    init_test(cx, |_| {});
 6156
 6157    let language = Arc::new(Language::new(
 6158        LanguageConfig {
 6159            brackets: BracketPairConfig {
 6160                pairs: vec![BracketPair {
 6161                    start: "{".to_string(),
 6162                    end: "}".to_string(),
 6163                    close: true,
 6164                    surround: true,
 6165                    newline: true,
 6166                }],
 6167                ..Default::default()
 6168            },
 6169            autoclose_before: "}".to_string(),
 6170            ..Default::default()
 6171        },
 6172        Some(tree_sitter_rust::LANGUAGE.into()),
 6173    ));
 6174
 6175    let text = r#"
 6176        a
 6177        b
 6178        c
 6179    "#
 6180    .unindent();
 6181
 6182    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6183    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6184    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6185    editor
 6186        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6187        .await;
 6188
 6189    editor.update(cx, |editor, cx| {
 6190        editor.change_selections(None, cx, |s| {
 6191            s.select_ranges([
 6192                Point::new(0, 1)..Point::new(0, 1),
 6193                Point::new(1, 1)..Point::new(1, 1),
 6194                Point::new(2, 1)..Point::new(2, 1),
 6195            ])
 6196        });
 6197
 6198        editor.handle_input("{", cx);
 6199        editor.handle_input("{", cx);
 6200        editor.handle_input("_", cx);
 6201        assert_eq!(
 6202            editor.text(cx),
 6203            "
 6204                a{{_}}
 6205                b{{_}}
 6206                c{{_}}
 6207            "
 6208            .unindent()
 6209        );
 6210        assert_eq!(
 6211            editor.selections.ranges::<Point>(cx),
 6212            [
 6213                Point::new(0, 4)..Point::new(0, 4),
 6214                Point::new(1, 4)..Point::new(1, 4),
 6215                Point::new(2, 4)..Point::new(2, 4)
 6216            ]
 6217        );
 6218
 6219        editor.backspace(&Default::default(), cx);
 6220        editor.backspace(&Default::default(), cx);
 6221        assert_eq!(
 6222            editor.text(cx),
 6223            "
 6224                a{}
 6225                b{}
 6226                c{}
 6227            "
 6228            .unindent()
 6229        );
 6230        assert_eq!(
 6231            editor.selections.ranges::<Point>(cx),
 6232            [
 6233                Point::new(0, 2)..Point::new(0, 2),
 6234                Point::new(1, 2)..Point::new(1, 2),
 6235                Point::new(2, 2)..Point::new(2, 2)
 6236            ]
 6237        );
 6238
 6239        editor.delete_to_previous_word_start(&Default::default(), cx);
 6240        assert_eq!(
 6241            editor.text(cx),
 6242            "
 6243                a
 6244                b
 6245                c
 6246            "
 6247            .unindent()
 6248        );
 6249        assert_eq!(
 6250            editor.selections.ranges::<Point>(cx),
 6251            [
 6252                Point::new(0, 1)..Point::new(0, 1),
 6253                Point::new(1, 1)..Point::new(1, 1),
 6254                Point::new(2, 1)..Point::new(2, 1)
 6255            ]
 6256        );
 6257    });
 6258}
 6259
 6260#[gpui::test]
 6261async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6262    init_test(cx, |settings| {
 6263        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6264    });
 6265
 6266    let mut cx = EditorTestContext::new(cx).await;
 6267
 6268    let language = Arc::new(Language::new(
 6269        LanguageConfig {
 6270            brackets: BracketPairConfig {
 6271                pairs: vec![
 6272                    BracketPair {
 6273                        start: "{".to_string(),
 6274                        end: "}".to_string(),
 6275                        close: true,
 6276                        surround: true,
 6277                        newline: true,
 6278                    },
 6279                    BracketPair {
 6280                        start: "(".to_string(),
 6281                        end: ")".to_string(),
 6282                        close: true,
 6283                        surround: true,
 6284                        newline: true,
 6285                    },
 6286                    BracketPair {
 6287                        start: "[".to_string(),
 6288                        end: "]".to_string(),
 6289                        close: false,
 6290                        surround: true,
 6291                        newline: true,
 6292                    },
 6293                ],
 6294                ..Default::default()
 6295            },
 6296            autoclose_before: "})]".to_string(),
 6297            ..Default::default()
 6298        },
 6299        Some(tree_sitter_rust::LANGUAGE.into()),
 6300    ));
 6301
 6302    cx.language_registry().add(language.clone());
 6303    cx.update_buffer(|buffer, cx| {
 6304        buffer.set_language(Some(language), cx);
 6305    });
 6306
 6307    cx.set_state(
 6308        &"
 6309            {(ˇ)}
 6310            [[ˇ]]
 6311            {(ˇ)}
 6312        "
 6313        .unindent(),
 6314    );
 6315
 6316    cx.update_editor(|view, cx| {
 6317        view.backspace(&Default::default(), cx);
 6318        view.backspace(&Default::default(), cx);
 6319    });
 6320
 6321    cx.assert_editor_state(
 6322        &"
 6323            ˇ
 6324            ˇ]]
 6325            ˇ
 6326        "
 6327        .unindent(),
 6328    );
 6329
 6330    cx.update_editor(|view, cx| {
 6331        view.handle_input("{", cx);
 6332        view.handle_input("{", cx);
 6333        view.move_right(&MoveRight, cx);
 6334        view.move_right(&MoveRight, cx);
 6335        view.move_left(&MoveLeft, cx);
 6336        view.move_left(&MoveLeft, cx);
 6337        view.backspace(&Default::default(), cx);
 6338    });
 6339
 6340    cx.assert_editor_state(
 6341        &"
 6342            {ˇ}
 6343            {ˇ}]]
 6344            {ˇ}
 6345        "
 6346        .unindent(),
 6347    );
 6348
 6349    cx.update_editor(|view, cx| {
 6350        view.backspace(&Default::default(), cx);
 6351    });
 6352
 6353    cx.assert_editor_state(
 6354        &"
 6355            ˇ
 6356            ˇ]]
 6357            ˇ
 6358        "
 6359        .unindent(),
 6360    );
 6361}
 6362
 6363#[gpui::test]
 6364async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6365    init_test(cx, |_| {});
 6366
 6367    let language = Arc::new(Language::new(
 6368        LanguageConfig::default(),
 6369        Some(tree_sitter_rust::LANGUAGE.into()),
 6370    ));
 6371
 6372    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6373    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6374    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6375    editor
 6376        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6377        .await;
 6378
 6379    editor.update(cx, |editor, cx| {
 6380        editor.set_auto_replace_emoji_shortcode(true);
 6381
 6382        editor.handle_input("Hello ", cx);
 6383        editor.handle_input(":wave", cx);
 6384        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6385
 6386        editor.handle_input(":", cx);
 6387        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6388
 6389        editor.handle_input(" :smile", cx);
 6390        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6391
 6392        editor.handle_input(":", cx);
 6393        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6394
 6395        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6396        editor.handle_input(":wave", cx);
 6397        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6398
 6399        editor.handle_input(":", cx);
 6400        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6401
 6402        editor.handle_input(":1", cx);
 6403        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6404
 6405        editor.handle_input(":", cx);
 6406        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6407
 6408        // Ensure shortcode does not get replaced when it is part of a word
 6409        editor.handle_input(" Test:wave", cx);
 6410        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6411
 6412        editor.handle_input(":", cx);
 6413        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6414
 6415        editor.set_auto_replace_emoji_shortcode(false);
 6416
 6417        // Ensure shortcode does not get replaced when auto replace is off
 6418        editor.handle_input(" :wave", cx);
 6419        assert_eq!(
 6420            editor.text(cx),
 6421            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6422        );
 6423
 6424        editor.handle_input(":", cx);
 6425        assert_eq!(
 6426            editor.text(cx),
 6427            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6428        );
 6429    });
 6430}
 6431
 6432#[gpui::test]
 6433async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6434    init_test(cx, |_| {});
 6435
 6436    let (text, insertion_ranges) = marked_text_ranges(
 6437        indoc! {"
 6438            a.ˇ b
 6439            a.ˇ b
 6440            a.ˇ b
 6441        "},
 6442        false,
 6443    );
 6444
 6445    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6446    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6447
 6448    editor.update(cx, |editor, cx| {
 6449        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6450
 6451        editor
 6452            .insert_snippet(&insertion_ranges, snippet, cx)
 6453            .unwrap();
 6454
 6455        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6456            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6457            assert_eq!(editor.text(cx), expected_text);
 6458            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6459        }
 6460
 6461        assert(
 6462            editor,
 6463            cx,
 6464            indoc! {"
 6465                a.f(«one», two, «three») b
 6466                a.f(«one», two, «three») b
 6467                a.f(«one», two, «three») b
 6468            "},
 6469        );
 6470
 6471        // Can't move earlier than the first tab stop
 6472        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6473        assert(
 6474            editor,
 6475            cx,
 6476            indoc! {"
 6477                a.f(«one», two, «three») b
 6478                a.f(«one», two, «three») b
 6479                a.f(«one», two, «three») b
 6480            "},
 6481        );
 6482
 6483        assert!(editor.move_to_next_snippet_tabstop(cx));
 6484        assert(
 6485            editor,
 6486            cx,
 6487            indoc! {"
 6488                a.f(one, «two», three) b
 6489                a.f(one, «two», three) b
 6490                a.f(one, «two», three) b
 6491            "},
 6492        );
 6493
 6494        editor.move_to_prev_snippet_tabstop(cx);
 6495        assert(
 6496            editor,
 6497            cx,
 6498            indoc! {"
 6499                a.f(«one», two, «three») b
 6500                a.f(«one», two, «three») b
 6501                a.f(«one», two, «three») b
 6502            "},
 6503        );
 6504
 6505        assert!(editor.move_to_next_snippet_tabstop(cx));
 6506        assert(
 6507            editor,
 6508            cx,
 6509            indoc! {"
 6510                a.f(one, «two», three) b
 6511                a.f(one, «two», three) b
 6512                a.f(one, «two», three) b
 6513            "},
 6514        );
 6515        assert!(editor.move_to_next_snippet_tabstop(cx));
 6516        assert(
 6517            editor,
 6518            cx,
 6519            indoc! {"
 6520                a.f(one, two, three)ˇ b
 6521                a.f(one, two, three)ˇ b
 6522                a.f(one, two, three)ˇ b
 6523            "},
 6524        );
 6525
 6526        // As soon as the last tab stop is reached, snippet state is gone
 6527        editor.move_to_prev_snippet_tabstop(cx);
 6528        assert(
 6529            editor,
 6530            cx,
 6531            indoc! {"
 6532                a.f(one, two, three)ˇ b
 6533                a.f(one, two, three)ˇ b
 6534                a.f(one, two, three)ˇ b
 6535            "},
 6536        );
 6537    });
 6538}
 6539
 6540#[gpui::test]
 6541async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6542    init_test(cx, |_| {});
 6543
 6544    let fs = FakeFs::new(cx.executor());
 6545    fs.insert_file("/file.rs", Default::default()).await;
 6546
 6547    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6548
 6549    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6550    language_registry.add(rust_lang());
 6551    let mut fake_servers = language_registry.register_fake_lsp(
 6552        "Rust",
 6553        FakeLspAdapter {
 6554            capabilities: lsp::ServerCapabilities {
 6555                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6556                ..Default::default()
 6557            },
 6558            ..Default::default()
 6559        },
 6560    );
 6561
 6562    let buffer = project
 6563        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6564        .await
 6565        .unwrap();
 6566
 6567    cx.executor().start_waiting();
 6568    let fake_server = fake_servers.next().await.unwrap();
 6569
 6570    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6571    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6572    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6573    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6574
 6575    let save = editor
 6576        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6577        .unwrap();
 6578    fake_server
 6579        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6580            assert_eq!(
 6581                params.text_document.uri,
 6582                lsp::Url::from_file_path("/file.rs").unwrap()
 6583            );
 6584            assert_eq!(params.options.tab_size, 4);
 6585            Ok(Some(vec![lsp::TextEdit::new(
 6586                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6587                ", ".to_string(),
 6588            )]))
 6589        })
 6590        .next()
 6591        .await;
 6592    cx.executor().start_waiting();
 6593    save.await;
 6594
 6595    assert_eq!(
 6596        editor.update(cx, |editor, cx| editor.text(cx)),
 6597        "one, two\nthree\n"
 6598    );
 6599    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6600
 6601    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6602    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6603
 6604    // Ensure we can still save even if formatting hangs.
 6605    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6606        assert_eq!(
 6607            params.text_document.uri,
 6608            lsp::Url::from_file_path("/file.rs").unwrap()
 6609        );
 6610        futures::future::pending::<()>().await;
 6611        unreachable!()
 6612    });
 6613    let save = editor
 6614        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6615        .unwrap();
 6616    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6617    cx.executor().start_waiting();
 6618    save.await;
 6619    assert_eq!(
 6620        editor.update(cx, |editor, cx| editor.text(cx)),
 6621        "one\ntwo\nthree\n"
 6622    );
 6623    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6624
 6625    // For non-dirty buffer, no formatting request should be sent
 6626    let save = editor
 6627        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6628        .unwrap();
 6629    let _pending_format_request = fake_server
 6630        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6631            panic!("Should not be invoked on non-dirty buffer");
 6632        })
 6633        .next();
 6634    cx.executor().start_waiting();
 6635    save.await;
 6636
 6637    // Set rust language override and assert overridden tabsize is sent to language server
 6638    update_test_language_settings(cx, |settings| {
 6639        settings.languages.insert(
 6640            "Rust".into(),
 6641            LanguageSettingsContent {
 6642                tab_size: NonZeroU32::new(8),
 6643                ..Default::default()
 6644            },
 6645        );
 6646    });
 6647
 6648    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6649    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6650    let save = editor
 6651        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6652        .unwrap();
 6653    fake_server
 6654        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6655            assert_eq!(
 6656                params.text_document.uri,
 6657                lsp::Url::from_file_path("/file.rs").unwrap()
 6658            );
 6659            assert_eq!(params.options.tab_size, 8);
 6660            Ok(Some(vec![]))
 6661        })
 6662        .next()
 6663        .await;
 6664    cx.executor().start_waiting();
 6665    save.await;
 6666}
 6667
 6668#[gpui::test]
 6669async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6670    init_test(cx, |_| {});
 6671
 6672    let cols = 4;
 6673    let rows = 10;
 6674    let sample_text_1 = sample_text(rows, cols, 'a');
 6675    assert_eq!(
 6676        sample_text_1,
 6677        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6678    );
 6679    let sample_text_2 = sample_text(rows, cols, 'l');
 6680    assert_eq!(
 6681        sample_text_2,
 6682        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6683    );
 6684    let sample_text_3 = sample_text(rows, cols, 'v');
 6685    assert_eq!(
 6686        sample_text_3,
 6687        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6688    );
 6689
 6690    let fs = FakeFs::new(cx.executor());
 6691    fs.insert_tree(
 6692        "/a",
 6693        json!({
 6694            "main.rs": sample_text_1,
 6695            "other.rs": sample_text_2,
 6696            "lib.rs": sample_text_3,
 6697        }),
 6698    )
 6699    .await;
 6700
 6701    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6702    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6703    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6704
 6705    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6706    language_registry.add(rust_lang());
 6707    let mut fake_servers = language_registry.register_fake_lsp(
 6708        "Rust",
 6709        FakeLspAdapter {
 6710            capabilities: lsp::ServerCapabilities {
 6711                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6712                ..Default::default()
 6713            },
 6714            ..Default::default()
 6715        },
 6716    );
 6717
 6718    let worktree = project.update(cx, |project, cx| {
 6719        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6720        assert_eq!(worktrees.len(), 1);
 6721        worktrees.pop().unwrap()
 6722    });
 6723    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6724
 6725    let buffer_1 = project
 6726        .update(cx, |project, cx| {
 6727            project.open_buffer((worktree_id, "main.rs"), cx)
 6728        })
 6729        .await
 6730        .unwrap();
 6731    let buffer_2 = project
 6732        .update(cx, |project, cx| {
 6733            project.open_buffer((worktree_id, "other.rs"), cx)
 6734        })
 6735        .await
 6736        .unwrap();
 6737    let buffer_3 = project
 6738        .update(cx, |project, cx| {
 6739            project.open_buffer((worktree_id, "lib.rs"), cx)
 6740        })
 6741        .await
 6742        .unwrap();
 6743
 6744    let multi_buffer = cx.new_model(|cx| {
 6745        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6746        multi_buffer.push_excerpts(
 6747            buffer_1.clone(),
 6748            [
 6749                ExcerptRange {
 6750                    context: Point::new(0, 0)..Point::new(3, 0),
 6751                    primary: None,
 6752                },
 6753                ExcerptRange {
 6754                    context: Point::new(5, 0)..Point::new(7, 0),
 6755                    primary: None,
 6756                },
 6757                ExcerptRange {
 6758                    context: Point::new(9, 0)..Point::new(10, 4),
 6759                    primary: None,
 6760                },
 6761            ],
 6762            cx,
 6763        );
 6764        multi_buffer.push_excerpts(
 6765            buffer_2.clone(),
 6766            [
 6767                ExcerptRange {
 6768                    context: Point::new(0, 0)..Point::new(3, 0),
 6769                    primary: None,
 6770                },
 6771                ExcerptRange {
 6772                    context: Point::new(5, 0)..Point::new(7, 0),
 6773                    primary: None,
 6774                },
 6775                ExcerptRange {
 6776                    context: Point::new(9, 0)..Point::new(10, 4),
 6777                    primary: None,
 6778                },
 6779            ],
 6780            cx,
 6781        );
 6782        multi_buffer.push_excerpts(
 6783            buffer_3.clone(),
 6784            [
 6785                ExcerptRange {
 6786                    context: Point::new(0, 0)..Point::new(3, 0),
 6787                    primary: None,
 6788                },
 6789                ExcerptRange {
 6790                    context: Point::new(5, 0)..Point::new(7, 0),
 6791                    primary: None,
 6792                },
 6793                ExcerptRange {
 6794                    context: Point::new(9, 0)..Point::new(10, 4),
 6795                    primary: None,
 6796                },
 6797            ],
 6798            cx,
 6799        );
 6800        multi_buffer
 6801    });
 6802    let multi_buffer_editor = cx.new_view(|cx| {
 6803        Editor::new(
 6804            EditorMode::Full,
 6805            multi_buffer,
 6806            Some(project.clone()),
 6807            true,
 6808            cx,
 6809        )
 6810    });
 6811
 6812    multi_buffer_editor.update(cx, |editor, cx| {
 6813        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6814        editor.insert("|one|two|three|", cx);
 6815    });
 6816    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6817    multi_buffer_editor.update(cx, |editor, cx| {
 6818        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6819            s.select_ranges(Some(60..70))
 6820        });
 6821        editor.insert("|four|five|six|", cx);
 6822    });
 6823    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6824
 6825    // First two buffers should be edited, but not the third one.
 6826    assert_eq!(
 6827        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6828        "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}",
 6829    );
 6830    buffer_1.update(cx, |buffer, _| {
 6831        assert!(buffer.is_dirty());
 6832        assert_eq!(
 6833            buffer.text(),
 6834            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6835        )
 6836    });
 6837    buffer_2.update(cx, |buffer, _| {
 6838        assert!(buffer.is_dirty());
 6839        assert_eq!(
 6840            buffer.text(),
 6841            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6842        )
 6843    });
 6844    buffer_3.update(cx, |buffer, _| {
 6845        assert!(!buffer.is_dirty());
 6846        assert_eq!(buffer.text(), sample_text_3,)
 6847    });
 6848
 6849    cx.executor().start_waiting();
 6850    let save = multi_buffer_editor
 6851        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6852        .unwrap();
 6853
 6854    let fake_server = fake_servers.next().await.unwrap();
 6855    fake_server
 6856        .server
 6857        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6858            Ok(Some(vec![lsp::TextEdit::new(
 6859                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6860                format!("[{} formatted]", params.text_document.uri),
 6861            )]))
 6862        })
 6863        .detach();
 6864    save.await;
 6865
 6866    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6867    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6868    assert_eq!(
 6869        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6870        "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}",
 6871    );
 6872    buffer_1.update(cx, |buffer, _| {
 6873        assert!(!buffer.is_dirty());
 6874        assert_eq!(
 6875            buffer.text(),
 6876            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6877        )
 6878    });
 6879    buffer_2.update(cx, |buffer, _| {
 6880        assert!(!buffer.is_dirty());
 6881        assert_eq!(
 6882            buffer.text(),
 6883            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6884        )
 6885    });
 6886    buffer_3.update(cx, |buffer, _| {
 6887        assert!(!buffer.is_dirty());
 6888        assert_eq!(buffer.text(), sample_text_3,)
 6889    });
 6890}
 6891
 6892#[gpui::test]
 6893async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6894    init_test(cx, |_| {});
 6895
 6896    let fs = FakeFs::new(cx.executor());
 6897    fs.insert_file("/file.rs", Default::default()).await;
 6898
 6899    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6900
 6901    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6902    language_registry.add(rust_lang());
 6903    let mut fake_servers = language_registry.register_fake_lsp(
 6904        "Rust",
 6905        FakeLspAdapter {
 6906            capabilities: lsp::ServerCapabilities {
 6907                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6908                ..Default::default()
 6909            },
 6910            ..Default::default()
 6911        },
 6912    );
 6913
 6914    let buffer = project
 6915        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6916        .await
 6917        .unwrap();
 6918
 6919    cx.executor().start_waiting();
 6920    let fake_server = fake_servers.next().await.unwrap();
 6921
 6922    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6923    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6924    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6925    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6926
 6927    let save = editor
 6928        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6929        .unwrap();
 6930    fake_server
 6931        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6932            assert_eq!(
 6933                params.text_document.uri,
 6934                lsp::Url::from_file_path("/file.rs").unwrap()
 6935            );
 6936            assert_eq!(params.options.tab_size, 4);
 6937            Ok(Some(vec![lsp::TextEdit::new(
 6938                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6939                ", ".to_string(),
 6940            )]))
 6941        })
 6942        .next()
 6943        .await;
 6944    cx.executor().start_waiting();
 6945    save.await;
 6946    assert_eq!(
 6947        editor.update(cx, |editor, cx| editor.text(cx)),
 6948        "one, two\nthree\n"
 6949    );
 6950    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6951
 6952    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6953    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6954
 6955    // Ensure we can still save even if formatting hangs.
 6956    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6957        move |params, _| async move {
 6958            assert_eq!(
 6959                params.text_document.uri,
 6960                lsp::Url::from_file_path("/file.rs").unwrap()
 6961            );
 6962            futures::future::pending::<()>().await;
 6963            unreachable!()
 6964        },
 6965    );
 6966    let save = editor
 6967        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6968        .unwrap();
 6969    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6970    cx.executor().start_waiting();
 6971    save.await;
 6972    assert_eq!(
 6973        editor.update(cx, |editor, cx| editor.text(cx)),
 6974        "one\ntwo\nthree\n"
 6975    );
 6976    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6977
 6978    // For non-dirty buffer, no formatting request should be sent
 6979    let save = editor
 6980        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6981        .unwrap();
 6982    let _pending_format_request = fake_server
 6983        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6984            panic!("Should not be invoked on non-dirty buffer");
 6985        })
 6986        .next();
 6987    cx.executor().start_waiting();
 6988    save.await;
 6989
 6990    // Set Rust language override and assert overridden tabsize is sent to language server
 6991    update_test_language_settings(cx, |settings| {
 6992        settings.languages.insert(
 6993            "Rust".into(),
 6994            LanguageSettingsContent {
 6995                tab_size: NonZeroU32::new(8),
 6996                ..Default::default()
 6997            },
 6998        );
 6999    });
 7000
 7001    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7002    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7003    let save = editor
 7004        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7005        .unwrap();
 7006    fake_server
 7007        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7008            assert_eq!(
 7009                params.text_document.uri,
 7010                lsp::Url::from_file_path("/file.rs").unwrap()
 7011            );
 7012            assert_eq!(params.options.tab_size, 8);
 7013            Ok(Some(vec![]))
 7014        })
 7015        .next()
 7016        .await;
 7017    cx.executor().start_waiting();
 7018    save.await;
 7019}
 7020
 7021#[gpui::test]
 7022async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7023    init_test(cx, |settings| {
 7024        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7025            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7026        ))
 7027    });
 7028
 7029    let fs = FakeFs::new(cx.executor());
 7030    fs.insert_file("/file.rs", Default::default()).await;
 7031
 7032    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7033
 7034    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7035    language_registry.add(Arc::new(Language::new(
 7036        LanguageConfig {
 7037            name: "Rust".into(),
 7038            matcher: LanguageMatcher {
 7039                path_suffixes: vec!["rs".to_string()],
 7040                ..Default::default()
 7041            },
 7042            ..LanguageConfig::default()
 7043        },
 7044        Some(tree_sitter_rust::LANGUAGE.into()),
 7045    )));
 7046    update_test_language_settings(cx, |settings| {
 7047        // Enable Prettier formatting for the same buffer, and ensure
 7048        // LSP is called instead of Prettier.
 7049        settings.defaults.prettier = Some(PrettierSettings {
 7050            allowed: true,
 7051            ..PrettierSettings::default()
 7052        });
 7053    });
 7054    let mut fake_servers = language_registry.register_fake_lsp(
 7055        "Rust",
 7056        FakeLspAdapter {
 7057            capabilities: lsp::ServerCapabilities {
 7058                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7059                ..Default::default()
 7060            },
 7061            ..Default::default()
 7062        },
 7063    );
 7064
 7065    let buffer = project
 7066        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7067        .await
 7068        .unwrap();
 7069
 7070    cx.executor().start_waiting();
 7071    let fake_server = fake_servers.next().await.unwrap();
 7072
 7073    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7074    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7075    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7076
 7077    let format = editor
 7078        .update(cx, |editor, cx| {
 7079            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 7080        })
 7081        .unwrap();
 7082    fake_server
 7083        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7084            assert_eq!(
 7085                params.text_document.uri,
 7086                lsp::Url::from_file_path("/file.rs").unwrap()
 7087            );
 7088            assert_eq!(params.options.tab_size, 4);
 7089            Ok(Some(vec![lsp::TextEdit::new(
 7090                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7091                ", ".to_string(),
 7092            )]))
 7093        })
 7094        .next()
 7095        .await;
 7096    cx.executor().start_waiting();
 7097    format.await;
 7098    assert_eq!(
 7099        editor.update(cx, |editor, cx| editor.text(cx)),
 7100        "one, two\nthree\n"
 7101    );
 7102
 7103    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7104    // Ensure we don't lock if formatting hangs.
 7105    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7106        assert_eq!(
 7107            params.text_document.uri,
 7108            lsp::Url::from_file_path("/file.rs").unwrap()
 7109        );
 7110        futures::future::pending::<()>().await;
 7111        unreachable!()
 7112    });
 7113    let format = editor
 7114        .update(cx, |editor, cx| {
 7115            editor.perform_format(project, FormatTrigger::Manual, cx)
 7116        })
 7117        .unwrap();
 7118    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7119    cx.executor().start_waiting();
 7120    format.await;
 7121    assert_eq!(
 7122        editor.update(cx, |editor, cx| editor.text(cx)),
 7123        "one\ntwo\nthree\n"
 7124    );
 7125}
 7126
 7127#[gpui::test]
 7128async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7129    init_test(cx, |_| {});
 7130
 7131    let mut cx = EditorLspTestContext::new_rust(
 7132        lsp::ServerCapabilities {
 7133            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7134            ..Default::default()
 7135        },
 7136        cx,
 7137    )
 7138    .await;
 7139
 7140    cx.set_state(indoc! {"
 7141        one.twoˇ
 7142    "});
 7143
 7144    // The format request takes a long time. When it completes, it inserts
 7145    // a newline and an indent before the `.`
 7146    cx.lsp
 7147        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7148            let executor = cx.background_executor().clone();
 7149            async move {
 7150                executor.timer(Duration::from_millis(100)).await;
 7151                Ok(Some(vec![lsp::TextEdit {
 7152                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7153                    new_text: "\n    ".into(),
 7154                }]))
 7155            }
 7156        });
 7157
 7158    // Submit a format request.
 7159    let format_1 = cx
 7160        .update_editor(|editor, cx| editor.format(&Format, cx))
 7161        .unwrap();
 7162    cx.executor().run_until_parked();
 7163
 7164    // Submit a second format request.
 7165    let format_2 = cx
 7166        .update_editor(|editor, cx| editor.format(&Format, cx))
 7167        .unwrap();
 7168    cx.executor().run_until_parked();
 7169
 7170    // Wait for both format requests to complete
 7171    cx.executor().advance_clock(Duration::from_millis(200));
 7172    cx.executor().start_waiting();
 7173    format_1.await.unwrap();
 7174    cx.executor().start_waiting();
 7175    format_2.await.unwrap();
 7176
 7177    // The formatting edits only happens once.
 7178    cx.assert_editor_state(indoc! {"
 7179        one
 7180            .twoˇ
 7181    "});
 7182}
 7183
 7184#[gpui::test]
 7185async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7186    init_test(cx, |settings| {
 7187        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7188    });
 7189
 7190    let mut cx = EditorLspTestContext::new_rust(
 7191        lsp::ServerCapabilities {
 7192            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7193            ..Default::default()
 7194        },
 7195        cx,
 7196    )
 7197    .await;
 7198
 7199    // Set up a buffer white some trailing whitespace and no trailing newline.
 7200    cx.set_state(
 7201        &[
 7202            "one ",   //
 7203            "twoˇ",   //
 7204            "three ", //
 7205            "four",   //
 7206        ]
 7207        .join("\n"),
 7208    );
 7209
 7210    // Submit a format request.
 7211    let format = cx
 7212        .update_editor(|editor, cx| editor.format(&Format, cx))
 7213        .unwrap();
 7214
 7215    // Record which buffer changes have been sent to the language server
 7216    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7217    cx.lsp
 7218        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7219            let buffer_changes = buffer_changes.clone();
 7220            move |params, _| {
 7221                buffer_changes.lock().extend(
 7222                    params
 7223                        .content_changes
 7224                        .into_iter()
 7225                        .map(|e| (e.range.unwrap(), e.text)),
 7226                );
 7227            }
 7228        });
 7229
 7230    // Handle formatting requests to the language server.
 7231    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7232        let buffer_changes = buffer_changes.clone();
 7233        move |_, _| {
 7234            // When formatting is requested, trailing whitespace has already been stripped,
 7235            // and the trailing newline has already been added.
 7236            assert_eq!(
 7237                &buffer_changes.lock()[1..],
 7238                &[
 7239                    (
 7240                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7241                        "".into()
 7242                    ),
 7243                    (
 7244                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7245                        "".into()
 7246                    ),
 7247                    (
 7248                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7249                        "\n".into()
 7250                    ),
 7251                ]
 7252            );
 7253
 7254            // Insert blank lines between each line of the buffer.
 7255            async move {
 7256                Ok(Some(vec![
 7257                    lsp::TextEdit {
 7258                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7259                        new_text: "\n".into(),
 7260                    },
 7261                    lsp::TextEdit {
 7262                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7263                        new_text: "\n".into(),
 7264                    },
 7265                ]))
 7266            }
 7267        }
 7268    });
 7269
 7270    // After formatting the buffer, the trailing whitespace is stripped,
 7271    // a newline is appended, and the edits provided by the language server
 7272    // have been applied.
 7273    format.await.unwrap();
 7274    cx.assert_editor_state(
 7275        &[
 7276            "one",   //
 7277            "",      //
 7278            "twoˇ",  //
 7279            "",      //
 7280            "three", //
 7281            "four",  //
 7282            "",      //
 7283        ]
 7284        .join("\n"),
 7285    );
 7286
 7287    // Undoing the formatting undoes the trailing whitespace removal, the
 7288    // trailing newline, and the LSP edits.
 7289    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7290    cx.assert_editor_state(
 7291        &[
 7292            "one ",   //
 7293            "twoˇ",   //
 7294            "three ", //
 7295            "four",   //
 7296        ]
 7297        .join("\n"),
 7298    );
 7299}
 7300
 7301#[gpui::test]
 7302async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7303    cx: &mut gpui::TestAppContext,
 7304) {
 7305    init_test(cx, |_| {});
 7306
 7307    cx.update(|cx| {
 7308        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7309            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7310                settings.auto_signature_help = Some(true);
 7311            });
 7312        });
 7313    });
 7314
 7315    let mut cx = EditorLspTestContext::new_rust(
 7316        lsp::ServerCapabilities {
 7317            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7318                ..Default::default()
 7319            }),
 7320            ..Default::default()
 7321        },
 7322        cx,
 7323    )
 7324    .await;
 7325
 7326    let language = Language::new(
 7327        LanguageConfig {
 7328            name: "Rust".into(),
 7329            brackets: BracketPairConfig {
 7330                pairs: vec![
 7331                    BracketPair {
 7332                        start: "{".to_string(),
 7333                        end: "}".to_string(),
 7334                        close: true,
 7335                        surround: true,
 7336                        newline: true,
 7337                    },
 7338                    BracketPair {
 7339                        start: "(".to_string(),
 7340                        end: ")".to_string(),
 7341                        close: true,
 7342                        surround: true,
 7343                        newline: true,
 7344                    },
 7345                    BracketPair {
 7346                        start: "/*".to_string(),
 7347                        end: " */".to_string(),
 7348                        close: true,
 7349                        surround: true,
 7350                        newline: true,
 7351                    },
 7352                    BracketPair {
 7353                        start: "[".to_string(),
 7354                        end: "]".to_string(),
 7355                        close: false,
 7356                        surround: false,
 7357                        newline: true,
 7358                    },
 7359                    BracketPair {
 7360                        start: "\"".to_string(),
 7361                        end: "\"".to_string(),
 7362                        close: true,
 7363                        surround: true,
 7364                        newline: false,
 7365                    },
 7366                    BracketPair {
 7367                        start: "<".to_string(),
 7368                        end: ">".to_string(),
 7369                        close: false,
 7370                        surround: true,
 7371                        newline: true,
 7372                    },
 7373                ],
 7374                ..Default::default()
 7375            },
 7376            autoclose_before: "})]".to_string(),
 7377            ..Default::default()
 7378        },
 7379        Some(tree_sitter_rust::LANGUAGE.into()),
 7380    );
 7381    let language = Arc::new(language);
 7382
 7383    cx.language_registry().add(language.clone());
 7384    cx.update_buffer(|buffer, cx| {
 7385        buffer.set_language(Some(language), cx);
 7386    });
 7387
 7388    cx.set_state(
 7389        &r#"
 7390            fn main() {
 7391                sampleˇ
 7392            }
 7393        "#
 7394        .unindent(),
 7395    );
 7396
 7397    cx.update_editor(|view, cx| {
 7398        view.handle_input("(", cx);
 7399    });
 7400    cx.assert_editor_state(
 7401        &"
 7402            fn main() {
 7403                sample(ˇ)
 7404            }
 7405        "
 7406        .unindent(),
 7407    );
 7408
 7409    let mocked_response = lsp::SignatureHelp {
 7410        signatures: vec![lsp::SignatureInformation {
 7411            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7412            documentation: None,
 7413            parameters: Some(vec![
 7414                lsp::ParameterInformation {
 7415                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7416                    documentation: None,
 7417                },
 7418                lsp::ParameterInformation {
 7419                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7420                    documentation: None,
 7421                },
 7422            ]),
 7423            active_parameter: None,
 7424        }],
 7425        active_signature: Some(0),
 7426        active_parameter: Some(0),
 7427    };
 7428    handle_signature_help_request(&mut cx, mocked_response).await;
 7429
 7430    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7431        .await;
 7432
 7433    cx.editor(|editor, _| {
 7434        let signature_help_state = editor.signature_help_state.popover().cloned();
 7435        assert!(signature_help_state.is_some());
 7436        let ParsedMarkdown {
 7437            text, highlights, ..
 7438        } = signature_help_state.unwrap().parsed_content;
 7439        assert_eq!(text, "param1: u8, param2: u8");
 7440        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7441    });
 7442}
 7443
 7444#[gpui::test]
 7445async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7446    init_test(cx, |_| {});
 7447
 7448    cx.update(|cx| {
 7449        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7450            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7451                settings.auto_signature_help = Some(false);
 7452                settings.show_signature_help_after_edits = Some(false);
 7453            });
 7454        });
 7455    });
 7456
 7457    let mut cx = EditorLspTestContext::new_rust(
 7458        lsp::ServerCapabilities {
 7459            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7460                ..Default::default()
 7461            }),
 7462            ..Default::default()
 7463        },
 7464        cx,
 7465    )
 7466    .await;
 7467
 7468    let language = Language::new(
 7469        LanguageConfig {
 7470            name: "Rust".into(),
 7471            brackets: BracketPairConfig {
 7472                pairs: vec![
 7473                    BracketPair {
 7474                        start: "{".to_string(),
 7475                        end: "}".to_string(),
 7476                        close: true,
 7477                        surround: true,
 7478                        newline: true,
 7479                    },
 7480                    BracketPair {
 7481                        start: "(".to_string(),
 7482                        end: ")".to_string(),
 7483                        close: true,
 7484                        surround: true,
 7485                        newline: true,
 7486                    },
 7487                    BracketPair {
 7488                        start: "/*".to_string(),
 7489                        end: " */".to_string(),
 7490                        close: true,
 7491                        surround: true,
 7492                        newline: true,
 7493                    },
 7494                    BracketPair {
 7495                        start: "[".to_string(),
 7496                        end: "]".to_string(),
 7497                        close: false,
 7498                        surround: false,
 7499                        newline: true,
 7500                    },
 7501                    BracketPair {
 7502                        start: "\"".to_string(),
 7503                        end: "\"".to_string(),
 7504                        close: true,
 7505                        surround: true,
 7506                        newline: false,
 7507                    },
 7508                    BracketPair {
 7509                        start: "<".to_string(),
 7510                        end: ">".to_string(),
 7511                        close: false,
 7512                        surround: true,
 7513                        newline: true,
 7514                    },
 7515                ],
 7516                ..Default::default()
 7517            },
 7518            autoclose_before: "})]".to_string(),
 7519            ..Default::default()
 7520        },
 7521        Some(tree_sitter_rust::LANGUAGE.into()),
 7522    );
 7523    let language = Arc::new(language);
 7524
 7525    cx.language_registry().add(language.clone());
 7526    cx.update_buffer(|buffer, cx| {
 7527        buffer.set_language(Some(language), cx);
 7528    });
 7529
 7530    // Ensure that signature_help is not called when no signature help is enabled.
 7531    cx.set_state(
 7532        &r#"
 7533            fn main() {
 7534                sampleˇ
 7535            }
 7536        "#
 7537        .unindent(),
 7538    );
 7539    cx.update_editor(|view, cx| {
 7540        view.handle_input("(", cx);
 7541    });
 7542    cx.assert_editor_state(
 7543        &"
 7544            fn main() {
 7545                sample(ˇ)
 7546            }
 7547        "
 7548        .unindent(),
 7549    );
 7550    cx.editor(|editor, _| {
 7551        assert!(editor.signature_help_state.task().is_none());
 7552    });
 7553
 7554    let mocked_response = lsp::SignatureHelp {
 7555        signatures: vec![lsp::SignatureInformation {
 7556            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7557            documentation: None,
 7558            parameters: Some(vec![
 7559                lsp::ParameterInformation {
 7560                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7561                    documentation: None,
 7562                },
 7563                lsp::ParameterInformation {
 7564                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7565                    documentation: None,
 7566                },
 7567            ]),
 7568            active_parameter: None,
 7569        }],
 7570        active_signature: Some(0),
 7571        active_parameter: Some(0),
 7572    };
 7573
 7574    // Ensure that signature_help is called when enabled afte edits
 7575    cx.update(|cx| {
 7576        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7577            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7578                settings.auto_signature_help = Some(false);
 7579                settings.show_signature_help_after_edits = Some(true);
 7580            });
 7581        });
 7582    });
 7583    cx.set_state(
 7584        &r#"
 7585            fn main() {
 7586                sampleˇ
 7587            }
 7588        "#
 7589        .unindent(),
 7590    );
 7591    cx.update_editor(|view, cx| {
 7592        view.handle_input("(", cx);
 7593    });
 7594    cx.assert_editor_state(
 7595        &"
 7596            fn main() {
 7597                sample(ˇ)
 7598            }
 7599        "
 7600        .unindent(),
 7601    );
 7602    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7603    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7604        .await;
 7605    cx.update_editor(|editor, _| {
 7606        let signature_help_state = editor.signature_help_state.popover().cloned();
 7607        assert!(signature_help_state.is_some());
 7608        let ParsedMarkdown {
 7609            text, highlights, ..
 7610        } = signature_help_state.unwrap().parsed_content;
 7611        assert_eq!(text, "param1: u8, param2: u8");
 7612        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7613        editor.signature_help_state = SignatureHelpState::default();
 7614    });
 7615
 7616    // Ensure that signature_help is called when auto signature help override is enabled
 7617    cx.update(|cx| {
 7618        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7619            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7620                settings.auto_signature_help = Some(true);
 7621                settings.show_signature_help_after_edits = Some(false);
 7622            });
 7623        });
 7624    });
 7625    cx.set_state(
 7626        &r#"
 7627            fn main() {
 7628                sampleˇ
 7629            }
 7630        "#
 7631        .unindent(),
 7632    );
 7633    cx.update_editor(|view, cx| {
 7634        view.handle_input("(", cx);
 7635    });
 7636    cx.assert_editor_state(
 7637        &"
 7638            fn main() {
 7639                sample(ˇ)
 7640            }
 7641        "
 7642        .unindent(),
 7643    );
 7644    handle_signature_help_request(&mut cx, mocked_response).await;
 7645    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7646        .await;
 7647    cx.editor(|editor, _| {
 7648        let signature_help_state = editor.signature_help_state.popover().cloned();
 7649        assert!(signature_help_state.is_some());
 7650        let ParsedMarkdown {
 7651            text, highlights, ..
 7652        } = signature_help_state.unwrap().parsed_content;
 7653        assert_eq!(text, "param1: u8, param2: u8");
 7654        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7655    });
 7656}
 7657
 7658#[gpui::test]
 7659async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7660    init_test(cx, |_| {});
 7661    cx.update(|cx| {
 7662        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7663            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7664                settings.auto_signature_help = Some(true);
 7665            });
 7666        });
 7667    });
 7668
 7669    let mut cx = EditorLspTestContext::new_rust(
 7670        lsp::ServerCapabilities {
 7671            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7672                ..Default::default()
 7673            }),
 7674            ..Default::default()
 7675        },
 7676        cx,
 7677    )
 7678    .await;
 7679
 7680    // A test that directly calls `show_signature_help`
 7681    cx.update_editor(|editor, cx| {
 7682        editor.show_signature_help(&ShowSignatureHelp, cx);
 7683    });
 7684
 7685    let mocked_response = lsp::SignatureHelp {
 7686        signatures: vec![lsp::SignatureInformation {
 7687            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7688            documentation: None,
 7689            parameters: Some(vec![
 7690                lsp::ParameterInformation {
 7691                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7692                    documentation: None,
 7693                },
 7694                lsp::ParameterInformation {
 7695                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7696                    documentation: None,
 7697                },
 7698            ]),
 7699            active_parameter: None,
 7700        }],
 7701        active_signature: Some(0),
 7702        active_parameter: Some(0),
 7703    };
 7704    handle_signature_help_request(&mut cx, mocked_response).await;
 7705
 7706    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7707        .await;
 7708
 7709    cx.editor(|editor, _| {
 7710        let signature_help_state = editor.signature_help_state.popover().cloned();
 7711        assert!(signature_help_state.is_some());
 7712        let ParsedMarkdown {
 7713            text, highlights, ..
 7714        } = signature_help_state.unwrap().parsed_content;
 7715        assert_eq!(text, "param1: u8, param2: u8");
 7716        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7717    });
 7718
 7719    // When exiting outside from inside the brackets, `signature_help` is closed.
 7720    cx.set_state(indoc! {"
 7721        fn main() {
 7722            sample(ˇ);
 7723        }
 7724
 7725        fn sample(param1: u8, param2: u8) {}
 7726    "});
 7727
 7728    cx.update_editor(|editor, cx| {
 7729        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7730    });
 7731
 7732    let mocked_response = lsp::SignatureHelp {
 7733        signatures: Vec::new(),
 7734        active_signature: None,
 7735        active_parameter: None,
 7736    };
 7737    handle_signature_help_request(&mut cx, mocked_response).await;
 7738
 7739    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7740        .await;
 7741
 7742    cx.editor(|editor, _| {
 7743        assert!(!editor.signature_help_state.is_shown());
 7744    });
 7745
 7746    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7747    cx.set_state(indoc! {"
 7748        fn main() {
 7749            sample(ˇ);
 7750        }
 7751
 7752        fn sample(param1: u8, param2: u8) {}
 7753    "});
 7754
 7755    let mocked_response = lsp::SignatureHelp {
 7756        signatures: vec![lsp::SignatureInformation {
 7757            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7758            documentation: None,
 7759            parameters: Some(vec![
 7760                lsp::ParameterInformation {
 7761                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7762                    documentation: None,
 7763                },
 7764                lsp::ParameterInformation {
 7765                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7766                    documentation: None,
 7767                },
 7768            ]),
 7769            active_parameter: None,
 7770        }],
 7771        active_signature: Some(0),
 7772        active_parameter: Some(0),
 7773    };
 7774    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7775    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7776        .await;
 7777    cx.editor(|editor, _| {
 7778        assert!(editor.signature_help_state.is_shown());
 7779    });
 7780
 7781    // Restore the popover with more parameter input
 7782    cx.set_state(indoc! {"
 7783        fn main() {
 7784            sample(param1, param2ˇ);
 7785        }
 7786
 7787        fn sample(param1: u8, param2: u8) {}
 7788    "});
 7789
 7790    let mocked_response = lsp::SignatureHelp {
 7791        signatures: vec![lsp::SignatureInformation {
 7792            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7793            documentation: None,
 7794            parameters: Some(vec![
 7795                lsp::ParameterInformation {
 7796                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7797                    documentation: None,
 7798                },
 7799                lsp::ParameterInformation {
 7800                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7801                    documentation: None,
 7802                },
 7803            ]),
 7804            active_parameter: None,
 7805        }],
 7806        active_signature: Some(0),
 7807        active_parameter: Some(1),
 7808    };
 7809    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7810    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7811        .await;
 7812
 7813    // When selecting a range, the popover is gone.
 7814    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7815    cx.update_editor(|editor, cx| {
 7816        editor.change_selections(None, cx, |s| {
 7817            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7818        })
 7819    });
 7820    cx.assert_editor_state(indoc! {"
 7821        fn main() {
 7822            sample(param1, «ˇparam2»);
 7823        }
 7824
 7825        fn sample(param1: u8, param2: u8) {}
 7826    "});
 7827    cx.editor(|editor, _| {
 7828        assert!(!editor.signature_help_state.is_shown());
 7829    });
 7830
 7831    // When unselecting again, the popover is back if within the brackets.
 7832    cx.update_editor(|editor, cx| {
 7833        editor.change_selections(None, cx, |s| {
 7834            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7835        })
 7836    });
 7837    cx.assert_editor_state(indoc! {"
 7838        fn main() {
 7839            sample(param1, ˇparam2);
 7840        }
 7841
 7842        fn sample(param1: u8, param2: u8) {}
 7843    "});
 7844    handle_signature_help_request(&mut cx, mocked_response).await;
 7845    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7846        .await;
 7847    cx.editor(|editor, _| {
 7848        assert!(editor.signature_help_state.is_shown());
 7849    });
 7850
 7851    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7852    cx.update_editor(|editor, cx| {
 7853        editor.change_selections(None, cx, |s| {
 7854            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7855            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7856        })
 7857    });
 7858    cx.assert_editor_state(indoc! {"
 7859        fn main() {
 7860            sample(param1, ˇparam2);
 7861        }
 7862
 7863        fn sample(param1: u8, param2: u8) {}
 7864    "});
 7865
 7866    let mocked_response = lsp::SignatureHelp {
 7867        signatures: vec![lsp::SignatureInformation {
 7868            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7869            documentation: None,
 7870            parameters: Some(vec![
 7871                lsp::ParameterInformation {
 7872                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7873                    documentation: None,
 7874                },
 7875                lsp::ParameterInformation {
 7876                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7877                    documentation: None,
 7878                },
 7879            ]),
 7880            active_parameter: None,
 7881        }],
 7882        active_signature: Some(0),
 7883        active_parameter: Some(1),
 7884    };
 7885    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7887        .await;
 7888    cx.update_editor(|editor, cx| {
 7889        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7890    });
 7891    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7892        .await;
 7893    cx.update_editor(|editor, cx| {
 7894        editor.change_selections(None, cx, |s| {
 7895            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7896        })
 7897    });
 7898    cx.assert_editor_state(indoc! {"
 7899        fn main() {
 7900            sample(param1, «ˇparam2»);
 7901        }
 7902
 7903        fn sample(param1: u8, param2: u8) {}
 7904    "});
 7905    cx.update_editor(|editor, cx| {
 7906        editor.change_selections(None, cx, |s| {
 7907            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7908        })
 7909    });
 7910    cx.assert_editor_state(indoc! {"
 7911        fn main() {
 7912            sample(param1, ˇparam2);
 7913        }
 7914
 7915        fn sample(param1: u8, param2: u8) {}
 7916    "});
 7917    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7918        .await;
 7919}
 7920
 7921#[gpui::test]
 7922async fn test_completion(cx: &mut gpui::TestAppContext) {
 7923    init_test(cx, |_| {});
 7924
 7925    let mut cx = EditorLspTestContext::new_rust(
 7926        lsp::ServerCapabilities {
 7927            completion_provider: Some(lsp::CompletionOptions {
 7928                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7929                resolve_provider: Some(true),
 7930                ..Default::default()
 7931            }),
 7932            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7933            ..Default::default()
 7934        },
 7935        cx,
 7936    )
 7937    .await;
 7938    let counter = Arc::new(AtomicUsize::new(0));
 7939
 7940    cx.set_state(indoc! {"
 7941        oneˇ
 7942        two
 7943        three
 7944    "});
 7945    cx.simulate_keystroke(".");
 7946    handle_completion_request(
 7947        &mut cx,
 7948        indoc! {"
 7949            one.|<>
 7950            two
 7951            three
 7952        "},
 7953        vec!["first_completion", "second_completion"],
 7954        counter.clone(),
 7955    )
 7956    .await;
 7957    cx.condition(|editor, _| editor.context_menu_visible())
 7958        .await;
 7959    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7960
 7961    let _handler = handle_signature_help_request(
 7962        &mut cx,
 7963        lsp::SignatureHelp {
 7964            signatures: vec![lsp::SignatureInformation {
 7965                label: "test signature".to_string(),
 7966                documentation: None,
 7967                parameters: Some(vec![lsp::ParameterInformation {
 7968                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7969                    documentation: None,
 7970                }]),
 7971                active_parameter: None,
 7972            }],
 7973            active_signature: None,
 7974            active_parameter: None,
 7975        },
 7976    );
 7977    cx.update_editor(|editor, cx| {
 7978        assert!(
 7979            !editor.signature_help_state.is_shown(),
 7980            "No signature help was called for"
 7981        );
 7982        editor.show_signature_help(&ShowSignatureHelp, cx);
 7983    });
 7984    cx.run_until_parked();
 7985    cx.update_editor(|editor, _| {
 7986        assert!(
 7987            !editor.signature_help_state.is_shown(),
 7988            "No signature help should be shown when completions menu is open"
 7989        );
 7990    });
 7991
 7992    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7993        editor.context_menu_next(&Default::default(), cx);
 7994        editor
 7995            .confirm_completion(&ConfirmCompletion::default(), cx)
 7996            .unwrap()
 7997    });
 7998    cx.assert_editor_state(indoc! {"
 7999        one.ˇ
 8000        two
 8001        three
 8002    "});
 8003
 8004    handle_resolve_completion_request(
 8005        &mut cx,
 8006        Some(vec![
 8007            (
 8008                //This overlaps with the primary completion edit which is
 8009                //misbehavior from the LSP spec, test that we filter it out
 8010                indoc! {"
 8011                    one.second_ˇcompletion
 8012                    two
 8013                    threeˇ
 8014                "},
 8015                "overlapping additional edit",
 8016            ),
 8017            (
 8018                indoc! {"
 8019                    one.second_completion
 8020                    two
 8021                    threeˇ
 8022                "},
 8023                "\nadditional edit",
 8024            ),
 8025        ]),
 8026    )
 8027    .await;
 8028    apply_additional_edits.await.unwrap();
 8029    cx.assert_editor_state(indoc! {"
 8030        one.second_completionˇ
 8031        two
 8032        thoverlapping additional editree
 8033
 8034        additional edit"});
 8035
 8036    cx.set_state(indoc! {"
 8037        one.second_completion
 8038        twoˇ
 8039        threeˇ
 8040        additional edit
 8041    "});
 8042    cx.simulate_keystroke(" ");
 8043    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8044    cx.simulate_keystroke("s");
 8045    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8046
 8047    cx.assert_editor_state(indoc! {"
 8048        one.second_completion
 8049        two sˇ
 8050        three sˇ
 8051        additional edit
 8052    "});
 8053    handle_completion_request(
 8054        &mut cx,
 8055        indoc! {"
 8056            one.second_completion
 8057            two s
 8058            three <s|>
 8059            additional edit
 8060        "},
 8061        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8062        counter.clone(),
 8063    )
 8064    .await;
 8065    cx.condition(|editor, _| editor.context_menu_visible())
 8066        .await;
 8067    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8068
 8069    cx.simulate_keystroke("i");
 8070
 8071    handle_completion_request(
 8072        &mut cx,
 8073        indoc! {"
 8074            one.second_completion
 8075            two si
 8076            three <si|>
 8077            additional edit
 8078        "},
 8079        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8080        counter.clone(),
 8081    )
 8082    .await;
 8083    cx.condition(|editor, _| editor.context_menu_visible())
 8084        .await;
 8085    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8086
 8087    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8088        editor
 8089            .confirm_completion(&ConfirmCompletion::default(), cx)
 8090            .unwrap()
 8091    });
 8092    cx.assert_editor_state(indoc! {"
 8093        one.second_completion
 8094        two siˇ
 8095        three siˇ
 8096        additional edit
 8097    "});
 8098
 8099    handle_resolve_completion_request(&mut cx, None).await;
 8100    apply_additional_edits.await.unwrap();
 8101
 8102    cx.update(|cx| {
 8103        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8104            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8105                settings.show_completions_on_input = Some(false);
 8106            });
 8107        })
 8108    });
 8109    cx.set_state("editorˇ");
 8110    cx.simulate_keystroke(".");
 8111    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8112    cx.simulate_keystroke("c");
 8113    cx.simulate_keystroke("l");
 8114    cx.simulate_keystroke("o");
 8115    cx.assert_editor_state("editor.cloˇ");
 8116    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8117    cx.update_editor(|editor, cx| {
 8118        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8119    });
 8120    handle_completion_request(
 8121        &mut cx,
 8122        "editor.<clo|>",
 8123        vec!["close", "clobber"],
 8124        counter.clone(),
 8125    )
 8126    .await;
 8127    cx.condition(|editor, _| editor.context_menu_visible())
 8128        .await;
 8129    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8130
 8131    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8132        editor
 8133            .confirm_completion(&ConfirmCompletion::default(), cx)
 8134            .unwrap()
 8135    });
 8136    cx.assert_editor_state("editor.cloˇ");
 8137    handle_resolve_completion_request(&mut cx, None).await;
 8138    apply_additional_edits.await.unwrap();
 8139    cx.assert_editor_state(indoc! {"
 8140    editor.closeˇ"});
 8141}
 8142
 8143#[gpui::test]
 8144async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8145    init_test(cx, |_| {});
 8146    let mut cx = EditorLspTestContext::new_rust(
 8147        lsp::ServerCapabilities {
 8148            completion_provider: Some(lsp::CompletionOptions {
 8149                trigger_characters: Some(vec![".".to_string()]),
 8150                ..Default::default()
 8151            }),
 8152            ..Default::default()
 8153        },
 8154        cx,
 8155    )
 8156    .await;
 8157    cx.lsp
 8158        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8159            Ok(Some(lsp::CompletionResponse::Array(vec![
 8160                lsp::CompletionItem {
 8161                    label: "first".into(),
 8162                    ..Default::default()
 8163                },
 8164                lsp::CompletionItem {
 8165                    label: "last".into(),
 8166                    ..Default::default()
 8167                },
 8168            ])))
 8169        });
 8170    cx.set_state("variableˇ");
 8171    cx.simulate_keystroke(".");
 8172    cx.executor().run_until_parked();
 8173
 8174    cx.update_editor(|editor, _| {
 8175        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8176            assert_eq!(
 8177                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8178                &["first", "last"]
 8179            );
 8180        } else {
 8181            panic!("expected completion menu to be open");
 8182        }
 8183    });
 8184
 8185    cx.update_editor(|editor, cx| {
 8186        editor.move_page_down(&MovePageDown::default(), cx);
 8187        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8188            assert!(
 8189                menu.selected_item == 1,
 8190                "expected PageDown to select the last item from the context menu"
 8191            );
 8192        } else {
 8193            panic!("expected completion menu to stay open after PageDown");
 8194        }
 8195    });
 8196
 8197    cx.update_editor(|editor, cx| {
 8198        editor.move_page_up(&MovePageUp::default(), cx);
 8199        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8200            assert!(
 8201                menu.selected_item == 0,
 8202                "expected PageUp to select the first item from the context menu"
 8203            );
 8204        } else {
 8205            panic!("expected completion menu to stay open after PageUp");
 8206        }
 8207    });
 8208}
 8209
 8210#[gpui::test]
 8211async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8212    init_test(cx, |_| {});
 8213
 8214    let mut cx = EditorLspTestContext::new_rust(
 8215        lsp::ServerCapabilities {
 8216            completion_provider: Some(lsp::CompletionOptions {
 8217                trigger_characters: Some(vec![".".to_string()]),
 8218                resolve_provider: Some(true),
 8219                ..Default::default()
 8220            }),
 8221            ..Default::default()
 8222        },
 8223        cx,
 8224    )
 8225    .await;
 8226
 8227    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8228    cx.simulate_keystroke(".");
 8229    let completion_item = lsp::CompletionItem {
 8230        label: "Some".into(),
 8231        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8232        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8233        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8234            kind: lsp::MarkupKind::Markdown,
 8235            value: "```rust\nSome(2)\n```".to_string(),
 8236        })),
 8237        deprecated: Some(false),
 8238        sort_text: Some("Some".to_string()),
 8239        filter_text: Some("Some".to_string()),
 8240        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8241        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8242            range: lsp::Range {
 8243                start: lsp::Position {
 8244                    line: 0,
 8245                    character: 22,
 8246                },
 8247                end: lsp::Position {
 8248                    line: 0,
 8249                    character: 22,
 8250                },
 8251            },
 8252            new_text: "Some(2)".to_string(),
 8253        })),
 8254        additional_text_edits: Some(vec![lsp::TextEdit {
 8255            range: lsp::Range {
 8256                start: lsp::Position {
 8257                    line: 0,
 8258                    character: 20,
 8259                },
 8260                end: lsp::Position {
 8261                    line: 0,
 8262                    character: 22,
 8263                },
 8264            },
 8265            new_text: "".to_string(),
 8266        }]),
 8267        ..Default::default()
 8268    };
 8269
 8270    let closure_completion_item = completion_item.clone();
 8271    let counter = Arc::new(AtomicUsize::new(0));
 8272    let counter_clone = counter.clone();
 8273    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8274        let task_completion_item = closure_completion_item.clone();
 8275        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8276        async move {
 8277            Ok(Some(lsp::CompletionResponse::Array(vec![
 8278                task_completion_item,
 8279            ])))
 8280        }
 8281    });
 8282
 8283    cx.condition(|editor, _| editor.context_menu_visible())
 8284        .await;
 8285    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8286    assert!(request.next().await.is_some());
 8287    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8288
 8289    cx.simulate_keystroke("S");
 8290    cx.simulate_keystroke("o");
 8291    cx.simulate_keystroke("m");
 8292    cx.condition(|editor, _| editor.context_menu_visible())
 8293        .await;
 8294    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8295    assert!(request.next().await.is_some());
 8296    assert!(request.next().await.is_some());
 8297    assert!(request.next().await.is_some());
 8298    request.close();
 8299    assert!(request.next().await.is_none());
 8300    assert_eq!(
 8301        counter.load(atomic::Ordering::Acquire),
 8302        4,
 8303        "With the completions menu open, only one LSP request should happen per input"
 8304    );
 8305}
 8306
 8307#[gpui::test]
 8308async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8309    init_test(cx, |_| {});
 8310    let mut cx = EditorTestContext::new(cx).await;
 8311    let language = Arc::new(Language::new(
 8312        LanguageConfig {
 8313            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8314            ..Default::default()
 8315        },
 8316        Some(tree_sitter_rust::LANGUAGE.into()),
 8317    ));
 8318    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8319
 8320    // If multiple selections intersect a line, the line is only toggled once.
 8321    cx.set_state(indoc! {"
 8322        fn a() {
 8323            «//b();
 8324            ˇ»// «c();
 8325            //ˇ»  d();
 8326        }
 8327    "});
 8328
 8329    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8330
 8331    cx.assert_editor_state(indoc! {"
 8332        fn a() {
 8333            «b();
 8334            c();
 8335            ˇ» d();
 8336        }
 8337    "});
 8338
 8339    // The comment prefix is inserted at the same column for every line in a
 8340    // selection.
 8341    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8342
 8343    cx.assert_editor_state(indoc! {"
 8344        fn a() {
 8345            // «b();
 8346            // c();
 8347            ˇ»//  d();
 8348        }
 8349    "});
 8350
 8351    // If a selection ends at the beginning of a line, that line is not toggled.
 8352    cx.set_selections_state(indoc! {"
 8353        fn a() {
 8354            // b();
 8355            «// c();
 8356        ˇ»    //  d();
 8357        }
 8358    "});
 8359
 8360    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8361
 8362    cx.assert_editor_state(indoc! {"
 8363        fn a() {
 8364            // b();
 8365            «c();
 8366        ˇ»    //  d();
 8367        }
 8368    "});
 8369
 8370    // If a selection span a single line and is empty, the line is toggled.
 8371    cx.set_state(indoc! {"
 8372        fn a() {
 8373            a();
 8374            b();
 8375        ˇ
 8376        }
 8377    "});
 8378
 8379    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8380
 8381    cx.assert_editor_state(indoc! {"
 8382        fn a() {
 8383            a();
 8384            b();
 8385        //•ˇ
 8386        }
 8387    "});
 8388
 8389    // If a selection span multiple lines, empty lines are not toggled.
 8390    cx.set_state(indoc! {"
 8391        fn a() {
 8392            «a();
 8393
 8394            c();ˇ»
 8395        }
 8396    "});
 8397
 8398    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8399
 8400    cx.assert_editor_state(indoc! {"
 8401        fn a() {
 8402            // «a();
 8403
 8404            // c();ˇ»
 8405        }
 8406    "});
 8407
 8408    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8409    cx.set_state(indoc! {"
 8410        fn a() {
 8411            «// a();
 8412            /// b();
 8413            //! c();ˇ»
 8414        }
 8415    "});
 8416
 8417    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8418
 8419    cx.assert_editor_state(indoc! {"
 8420        fn a() {
 8421            «a();
 8422            b();
 8423            c();ˇ»
 8424        }
 8425    "});
 8426}
 8427
 8428#[gpui::test]
 8429async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8430    init_test(cx, |_| {});
 8431
 8432    let language = Arc::new(Language::new(
 8433        LanguageConfig {
 8434            line_comments: vec!["// ".into()],
 8435            ..Default::default()
 8436        },
 8437        Some(tree_sitter_rust::LANGUAGE.into()),
 8438    ));
 8439
 8440    let mut cx = EditorTestContext::new(cx).await;
 8441
 8442    cx.language_registry().add(language.clone());
 8443    cx.update_buffer(|buffer, cx| {
 8444        buffer.set_language(Some(language), cx);
 8445    });
 8446
 8447    let toggle_comments = &ToggleComments {
 8448        advance_downwards: true,
 8449    };
 8450
 8451    // Single cursor on one line -> advance
 8452    // Cursor moves horizontally 3 characters as well on non-blank line
 8453    cx.set_state(indoc!(
 8454        "fn a() {
 8455             ˇdog();
 8456             cat();
 8457        }"
 8458    ));
 8459    cx.update_editor(|editor, cx| {
 8460        editor.toggle_comments(toggle_comments, cx);
 8461    });
 8462    cx.assert_editor_state(indoc!(
 8463        "fn a() {
 8464             // dog();
 8465             catˇ();
 8466        }"
 8467    ));
 8468
 8469    // Single selection on one line -> don't advance
 8470    cx.set_state(indoc!(
 8471        "fn a() {
 8472             «dog()ˇ»;
 8473             cat();
 8474        }"
 8475    ));
 8476    cx.update_editor(|editor, cx| {
 8477        editor.toggle_comments(toggle_comments, cx);
 8478    });
 8479    cx.assert_editor_state(indoc!(
 8480        "fn a() {
 8481             // «dog()ˇ»;
 8482             cat();
 8483        }"
 8484    ));
 8485
 8486    // Multiple cursors on one line -> advance
 8487    cx.set_state(indoc!(
 8488        "fn a() {
 8489             ˇdˇog();
 8490             cat();
 8491        }"
 8492    ));
 8493    cx.update_editor(|editor, cx| {
 8494        editor.toggle_comments(toggle_comments, cx);
 8495    });
 8496    cx.assert_editor_state(indoc!(
 8497        "fn a() {
 8498             // dog();
 8499             catˇ(ˇ);
 8500        }"
 8501    ));
 8502
 8503    // Multiple cursors on one line, with selection -> don't advance
 8504    cx.set_state(indoc!(
 8505        "fn a() {
 8506             ˇdˇog«()ˇ»;
 8507             cat();
 8508        }"
 8509    ));
 8510    cx.update_editor(|editor, cx| {
 8511        editor.toggle_comments(toggle_comments, cx);
 8512    });
 8513    cx.assert_editor_state(indoc!(
 8514        "fn a() {
 8515             // ˇdˇog«()ˇ»;
 8516             cat();
 8517        }"
 8518    ));
 8519
 8520    // Single cursor on one line -> advance
 8521    // Cursor moves to column 0 on blank line
 8522    cx.set_state(indoc!(
 8523        "fn a() {
 8524             ˇdog();
 8525
 8526             cat();
 8527        }"
 8528    ));
 8529    cx.update_editor(|editor, cx| {
 8530        editor.toggle_comments(toggle_comments, cx);
 8531    });
 8532    cx.assert_editor_state(indoc!(
 8533        "fn a() {
 8534             // dog();
 8535        ˇ
 8536             cat();
 8537        }"
 8538    ));
 8539
 8540    // Single cursor on one line -> advance
 8541    // Cursor starts and ends at column 0
 8542    cx.set_state(indoc!(
 8543        "fn a() {
 8544         ˇ    dog();
 8545             cat();
 8546        }"
 8547    ));
 8548    cx.update_editor(|editor, cx| {
 8549        editor.toggle_comments(toggle_comments, cx);
 8550    });
 8551    cx.assert_editor_state(indoc!(
 8552        "fn a() {
 8553             // dog();
 8554         ˇ    cat();
 8555        }"
 8556    ));
 8557}
 8558
 8559#[gpui::test]
 8560async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8561    init_test(cx, |_| {});
 8562
 8563    let mut cx = EditorTestContext::new(cx).await;
 8564
 8565    let html_language = Arc::new(
 8566        Language::new(
 8567            LanguageConfig {
 8568                name: "HTML".into(),
 8569                block_comment: Some(("<!-- ".into(), " -->".into())),
 8570                ..Default::default()
 8571            },
 8572            Some(tree_sitter_html::language()),
 8573        )
 8574        .with_injection_query(
 8575            r#"
 8576            (script_element
 8577                (raw_text) @content
 8578                (#set! "language" "javascript"))
 8579            "#,
 8580        )
 8581        .unwrap(),
 8582    );
 8583
 8584    let javascript_language = Arc::new(Language::new(
 8585        LanguageConfig {
 8586            name: "JavaScript".into(),
 8587            line_comments: vec!["// ".into()],
 8588            ..Default::default()
 8589        },
 8590        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8591    ));
 8592
 8593    cx.language_registry().add(html_language.clone());
 8594    cx.language_registry().add(javascript_language.clone());
 8595    cx.update_buffer(|buffer, cx| {
 8596        buffer.set_language(Some(html_language), cx);
 8597    });
 8598
 8599    // Toggle comments for empty selections
 8600    cx.set_state(
 8601        &r#"
 8602            <p>A</p>ˇ
 8603            <p>B</p>ˇ
 8604            <p>C</p>ˇ
 8605        "#
 8606        .unindent(),
 8607    );
 8608    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8609    cx.assert_editor_state(
 8610        &r#"
 8611            <!-- <p>A</p>ˇ -->
 8612            <!-- <p>B</p>ˇ -->
 8613            <!-- <p>C</p>ˇ -->
 8614        "#
 8615        .unindent(),
 8616    );
 8617    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8618    cx.assert_editor_state(
 8619        &r#"
 8620            <p>A</p>ˇ
 8621            <p>B</p>ˇ
 8622            <p>C</p>ˇ
 8623        "#
 8624        .unindent(),
 8625    );
 8626
 8627    // Toggle comments for mixture of empty and non-empty selections, where
 8628    // multiple selections occupy a given line.
 8629    cx.set_state(
 8630        &r#"
 8631            <p>A«</p>
 8632            <p>ˇ»B</p>ˇ
 8633            <p>C«</p>
 8634            <p>ˇ»D</p>ˇ
 8635        "#
 8636        .unindent(),
 8637    );
 8638
 8639    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8640    cx.assert_editor_state(
 8641        &r#"
 8642            <!-- <p>A«</p>
 8643            <p>ˇ»B</p>ˇ -->
 8644            <!-- <p>C«</p>
 8645            <p>ˇ»D</p>ˇ -->
 8646        "#
 8647        .unindent(),
 8648    );
 8649    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8650    cx.assert_editor_state(
 8651        &r#"
 8652            <p>A«</p>
 8653            <p>ˇ»B</p>ˇ
 8654            <p>C«</p>
 8655            <p>ˇ»D</p>ˇ
 8656        "#
 8657        .unindent(),
 8658    );
 8659
 8660    // Toggle comments when different languages are active for different
 8661    // selections.
 8662    cx.set_state(
 8663        &r#"
 8664            ˇ<script>
 8665                ˇvar x = new Y();
 8666            ˇ</script>
 8667        "#
 8668        .unindent(),
 8669    );
 8670    cx.executor().run_until_parked();
 8671    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8672    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8673    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8674    cx.assert_editor_state(
 8675        &r#"
 8676            <!-- ˇ<script> -->
 8677                // ˇvar x = new Y();
 8678            // ˇ</script>
 8679        "#
 8680        .unindent(),
 8681    );
 8682}
 8683
 8684#[gpui::test]
 8685fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8686    init_test(cx, |_| {});
 8687
 8688    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8689    let multibuffer = cx.new_model(|cx| {
 8690        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8691        multibuffer.push_excerpts(
 8692            buffer.clone(),
 8693            [
 8694                ExcerptRange {
 8695                    context: Point::new(0, 0)..Point::new(0, 4),
 8696                    primary: None,
 8697                },
 8698                ExcerptRange {
 8699                    context: Point::new(1, 0)..Point::new(1, 4),
 8700                    primary: None,
 8701                },
 8702            ],
 8703            cx,
 8704        );
 8705        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8706        multibuffer
 8707    });
 8708
 8709    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8710    view.update(cx, |view, cx| {
 8711        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8712        view.change_selections(None, cx, |s| {
 8713            s.select_ranges([
 8714                Point::new(0, 0)..Point::new(0, 0),
 8715                Point::new(1, 0)..Point::new(1, 0),
 8716            ])
 8717        });
 8718
 8719        view.handle_input("X", cx);
 8720        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8721        assert_eq!(
 8722            view.selections.ranges(cx),
 8723            [
 8724                Point::new(0, 1)..Point::new(0, 1),
 8725                Point::new(1, 1)..Point::new(1, 1),
 8726            ]
 8727        );
 8728
 8729        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8730        view.change_selections(None, cx, |s| {
 8731            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8732        });
 8733        view.backspace(&Default::default(), cx);
 8734        assert_eq!(view.text(cx), "Xa\nbbb");
 8735        assert_eq!(
 8736            view.selections.ranges(cx),
 8737            [Point::new(1, 0)..Point::new(1, 0)]
 8738        );
 8739
 8740        view.change_selections(None, cx, |s| {
 8741            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8742        });
 8743        view.backspace(&Default::default(), cx);
 8744        assert_eq!(view.text(cx), "X\nbb");
 8745        assert_eq!(
 8746            view.selections.ranges(cx),
 8747            [Point::new(0, 1)..Point::new(0, 1)]
 8748        );
 8749    });
 8750}
 8751
 8752#[gpui::test]
 8753fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8754    init_test(cx, |_| {});
 8755
 8756    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8757    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8758        indoc! {"
 8759            [aaaa
 8760            (bbbb]
 8761            cccc)",
 8762        },
 8763        markers.clone(),
 8764    );
 8765    let excerpt_ranges = markers.into_iter().map(|marker| {
 8766        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8767        ExcerptRange {
 8768            context,
 8769            primary: None,
 8770        }
 8771    });
 8772    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8773    let multibuffer = cx.new_model(|cx| {
 8774        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8775        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8776        multibuffer
 8777    });
 8778
 8779    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8780    view.update(cx, |view, cx| {
 8781        let (expected_text, selection_ranges) = marked_text_ranges(
 8782            indoc! {"
 8783                aaaa
 8784                bˇbbb
 8785                bˇbbˇb
 8786                cccc"
 8787            },
 8788            true,
 8789        );
 8790        assert_eq!(view.text(cx), expected_text);
 8791        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8792
 8793        view.handle_input("X", cx);
 8794
 8795        let (expected_text, expected_selections) = marked_text_ranges(
 8796            indoc! {"
 8797                aaaa
 8798                bXˇbbXb
 8799                bXˇbbXˇb
 8800                cccc"
 8801            },
 8802            false,
 8803        );
 8804        assert_eq!(view.text(cx), expected_text);
 8805        assert_eq!(view.selections.ranges(cx), expected_selections);
 8806
 8807        view.newline(&Newline, cx);
 8808        let (expected_text, expected_selections) = marked_text_ranges(
 8809            indoc! {"
 8810                aaaa
 8811                bX
 8812                ˇbbX
 8813                b
 8814                bX
 8815                ˇbbX
 8816                ˇb
 8817                cccc"
 8818            },
 8819            false,
 8820        );
 8821        assert_eq!(view.text(cx), expected_text);
 8822        assert_eq!(view.selections.ranges(cx), expected_selections);
 8823    });
 8824}
 8825
 8826#[gpui::test]
 8827fn test_refresh_selections(cx: &mut TestAppContext) {
 8828    init_test(cx, |_| {});
 8829
 8830    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8831    let mut excerpt1_id = None;
 8832    let multibuffer = cx.new_model(|cx| {
 8833        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8834        excerpt1_id = multibuffer
 8835            .push_excerpts(
 8836                buffer.clone(),
 8837                [
 8838                    ExcerptRange {
 8839                        context: Point::new(0, 0)..Point::new(1, 4),
 8840                        primary: None,
 8841                    },
 8842                    ExcerptRange {
 8843                        context: Point::new(1, 0)..Point::new(2, 4),
 8844                        primary: None,
 8845                    },
 8846                ],
 8847                cx,
 8848            )
 8849            .into_iter()
 8850            .next();
 8851        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8852        multibuffer
 8853    });
 8854
 8855    let editor = cx.add_window(|cx| {
 8856        let mut editor = build_editor(multibuffer.clone(), cx);
 8857        let snapshot = editor.snapshot(cx);
 8858        editor.change_selections(None, cx, |s| {
 8859            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8860        });
 8861        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8862        assert_eq!(
 8863            editor.selections.ranges(cx),
 8864            [
 8865                Point::new(1, 3)..Point::new(1, 3),
 8866                Point::new(2, 1)..Point::new(2, 1),
 8867            ]
 8868        );
 8869        editor
 8870    });
 8871
 8872    // Refreshing selections is a no-op when excerpts haven't changed.
 8873    _ = editor.update(cx, |editor, cx| {
 8874        editor.change_selections(None, cx, |s| s.refresh());
 8875        assert_eq!(
 8876            editor.selections.ranges(cx),
 8877            [
 8878                Point::new(1, 3)..Point::new(1, 3),
 8879                Point::new(2, 1)..Point::new(2, 1),
 8880            ]
 8881        );
 8882    });
 8883
 8884    multibuffer.update(cx, |multibuffer, cx| {
 8885        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8886    });
 8887    _ = editor.update(cx, |editor, cx| {
 8888        // Removing an excerpt causes the first selection to become degenerate.
 8889        assert_eq!(
 8890            editor.selections.ranges(cx),
 8891            [
 8892                Point::new(0, 0)..Point::new(0, 0),
 8893                Point::new(0, 1)..Point::new(0, 1)
 8894            ]
 8895        );
 8896
 8897        // Refreshing selections will relocate the first selection to the original buffer
 8898        // location.
 8899        editor.change_selections(None, cx, |s| s.refresh());
 8900        assert_eq!(
 8901            editor.selections.ranges(cx),
 8902            [
 8903                Point::new(0, 1)..Point::new(0, 1),
 8904                Point::new(0, 3)..Point::new(0, 3)
 8905            ]
 8906        );
 8907        assert!(editor.selections.pending_anchor().is_some());
 8908    });
 8909}
 8910
 8911#[gpui::test]
 8912fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8913    init_test(cx, |_| {});
 8914
 8915    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8916    let mut excerpt1_id = None;
 8917    let multibuffer = cx.new_model(|cx| {
 8918        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8919        excerpt1_id = multibuffer
 8920            .push_excerpts(
 8921                buffer.clone(),
 8922                [
 8923                    ExcerptRange {
 8924                        context: Point::new(0, 0)..Point::new(1, 4),
 8925                        primary: None,
 8926                    },
 8927                    ExcerptRange {
 8928                        context: Point::new(1, 0)..Point::new(2, 4),
 8929                        primary: None,
 8930                    },
 8931                ],
 8932                cx,
 8933            )
 8934            .into_iter()
 8935            .next();
 8936        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8937        multibuffer
 8938    });
 8939
 8940    let editor = cx.add_window(|cx| {
 8941        let mut editor = build_editor(multibuffer.clone(), cx);
 8942        let snapshot = editor.snapshot(cx);
 8943        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8944        assert_eq!(
 8945            editor.selections.ranges(cx),
 8946            [Point::new(1, 3)..Point::new(1, 3)]
 8947        );
 8948        editor
 8949    });
 8950
 8951    multibuffer.update(cx, |multibuffer, cx| {
 8952        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8953    });
 8954    _ = editor.update(cx, |editor, cx| {
 8955        assert_eq!(
 8956            editor.selections.ranges(cx),
 8957            [Point::new(0, 0)..Point::new(0, 0)]
 8958        );
 8959
 8960        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8961        editor.change_selections(None, cx, |s| s.refresh());
 8962        assert_eq!(
 8963            editor.selections.ranges(cx),
 8964            [Point::new(0, 3)..Point::new(0, 3)]
 8965        );
 8966        assert!(editor.selections.pending_anchor().is_some());
 8967    });
 8968}
 8969
 8970#[gpui::test]
 8971async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8972    init_test(cx, |_| {});
 8973
 8974    let language = Arc::new(
 8975        Language::new(
 8976            LanguageConfig {
 8977                brackets: BracketPairConfig {
 8978                    pairs: vec![
 8979                        BracketPair {
 8980                            start: "{".to_string(),
 8981                            end: "}".to_string(),
 8982                            close: true,
 8983                            surround: true,
 8984                            newline: true,
 8985                        },
 8986                        BracketPair {
 8987                            start: "/* ".to_string(),
 8988                            end: " */".to_string(),
 8989                            close: true,
 8990                            surround: true,
 8991                            newline: true,
 8992                        },
 8993                    ],
 8994                    ..Default::default()
 8995                },
 8996                ..Default::default()
 8997            },
 8998            Some(tree_sitter_rust::LANGUAGE.into()),
 8999        )
 9000        .with_indents_query("")
 9001        .unwrap(),
 9002    );
 9003
 9004    let text = concat!(
 9005        "{   }\n",     //
 9006        "  x\n",       //
 9007        "  /*   */\n", //
 9008        "x\n",         //
 9009        "{{} }\n",     //
 9010    );
 9011
 9012    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9013    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9014    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9015    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9016        .await;
 9017
 9018    view.update(cx, |view, cx| {
 9019        view.change_selections(None, cx, |s| {
 9020            s.select_display_ranges([
 9021                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9022                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9023                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9024            ])
 9025        });
 9026        view.newline(&Newline, cx);
 9027
 9028        assert_eq!(
 9029            view.buffer().read(cx).read(cx).text(),
 9030            concat!(
 9031                "{ \n",    // Suppress rustfmt
 9032                "\n",      //
 9033                "}\n",     //
 9034                "  x\n",   //
 9035                "  /* \n", //
 9036                "  \n",    //
 9037                "  */\n",  //
 9038                "x\n",     //
 9039                "{{} \n",  //
 9040                "}\n",     //
 9041            )
 9042        );
 9043    });
 9044}
 9045
 9046#[gpui::test]
 9047fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9048    init_test(cx, |_| {});
 9049
 9050    let editor = cx.add_window(|cx| {
 9051        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9052        build_editor(buffer.clone(), cx)
 9053    });
 9054
 9055    _ = editor.update(cx, |editor, cx| {
 9056        struct Type1;
 9057        struct Type2;
 9058
 9059        let buffer = editor.buffer.read(cx).snapshot(cx);
 9060
 9061        let anchor_range =
 9062            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9063
 9064        editor.highlight_background::<Type1>(
 9065            &[
 9066                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9067                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9068                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9069                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9070            ],
 9071            |_| Hsla::red(),
 9072            cx,
 9073        );
 9074        editor.highlight_background::<Type2>(
 9075            &[
 9076                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9077                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9078                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9079                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9080            ],
 9081            |_| Hsla::green(),
 9082            cx,
 9083        );
 9084
 9085        let snapshot = editor.snapshot(cx);
 9086        let mut highlighted_ranges = editor.background_highlights_in_range(
 9087            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9088            &snapshot,
 9089            cx.theme().colors(),
 9090        );
 9091        // Enforce a consistent ordering based on color without relying on the ordering of the
 9092        // highlight's `TypeId` which is non-executor.
 9093        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9094        assert_eq!(
 9095            highlighted_ranges,
 9096            &[
 9097                (
 9098                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9099                    Hsla::red(),
 9100                ),
 9101                (
 9102                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9103                    Hsla::red(),
 9104                ),
 9105                (
 9106                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9107                    Hsla::green(),
 9108                ),
 9109                (
 9110                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9111                    Hsla::green(),
 9112                ),
 9113            ]
 9114        );
 9115        assert_eq!(
 9116            editor.background_highlights_in_range(
 9117                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9118                &snapshot,
 9119                cx.theme().colors(),
 9120            ),
 9121            &[(
 9122                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9123                Hsla::red(),
 9124            )]
 9125        );
 9126    });
 9127}
 9128
 9129#[gpui::test]
 9130async fn test_following(cx: &mut gpui::TestAppContext) {
 9131    init_test(cx, |_| {});
 9132
 9133    let fs = FakeFs::new(cx.executor());
 9134    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9135
 9136    let buffer = project.update(cx, |project, cx| {
 9137        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9138        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9139    });
 9140    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9141    let follower = cx.update(|cx| {
 9142        cx.open_window(
 9143            WindowOptions {
 9144                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9145                    gpui::Point::new(px(0.), px(0.)),
 9146                    gpui::Point::new(px(10.), px(80.)),
 9147                ))),
 9148                ..Default::default()
 9149            },
 9150            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9151        )
 9152        .unwrap()
 9153    });
 9154
 9155    let is_still_following = Rc::new(RefCell::new(true));
 9156    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9157    let pending_update = Rc::new(RefCell::new(None));
 9158    _ = follower.update(cx, {
 9159        let update = pending_update.clone();
 9160        let is_still_following = is_still_following.clone();
 9161        let follower_edit_event_count = follower_edit_event_count.clone();
 9162        |_, cx| {
 9163            cx.subscribe(
 9164                &leader.root_view(cx).unwrap(),
 9165                move |_, leader, event, cx| {
 9166                    leader
 9167                        .read(cx)
 9168                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9169                },
 9170            )
 9171            .detach();
 9172
 9173            cx.subscribe(
 9174                &follower.root_view(cx).unwrap(),
 9175                move |_, _, event: &EditorEvent, _cx| {
 9176                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9177                        *is_still_following.borrow_mut() = false;
 9178                    }
 9179
 9180                    if let EditorEvent::BufferEdited = event {
 9181                        *follower_edit_event_count.borrow_mut() += 1;
 9182                    }
 9183                },
 9184            )
 9185            .detach();
 9186        }
 9187    });
 9188
 9189    // Update the selections only
 9190    _ = leader.update(cx, |leader, cx| {
 9191        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9192    });
 9193    follower
 9194        .update(cx, |follower, cx| {
 9195            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9196        })
 9197        .unwrap()
 9198        .await
 9199        .unwrap();
 9200    _ = follower.update(cx, |follower, cx| {
 9201        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9202    });
 9203    assert!(*is_still_following.borrow());
 9204    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9205
 9206    // Update the scroll position only
 9207    _ = leader.update(cx, |leader, cx| {
 9208        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9209    });
 9210    follower
 9211        .update(cx, |follower, cx| {
 9212            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9213        })
 9214        .unwrap()
 9215        .await
 9216        .unwrap();
 9217    assert_eq!(
 9218        follower
 9219            .update(cx, |follower, cx| follower.scroll_position(cx))
 9220            .unwrap(),
 9221        gpui::Point::new(1.5, 3.5)
 9222    );
 9223    assert!(*is_still_following.borrow());
 9224    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9225
 9226    // Update the selections and scroll position. The follower's scroll position is updated
 9227    // via autoscroll, not via the leader's exact scroll position.
 9228    _ = leader.update(cx, |leader, cx| {
 9229        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9230        leader.request_autoscroll(Autoscroll::newest(), cx);
 9231        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9232    });
 9233    follower
 9234        .update(cx, |follower, cx| {
 9235            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9236        })
 9237        .unwrap()
 9238        .await
 9239        .unwrap();
 9240    _ = follower.update(cx, |follower, cx| {
 9241        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9242        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9243    });
 9244    assert!(*is_still_following.borrow());
 9245
 9246    // Creating a pending selection that precedes another selection
 9247    _ = leader.update(cx, |leader, cx| {
 9248        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9249        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9250    });
 9251    follower
 9252        .update(cx, |follower, cx| {
 9253            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9254        })
 9255        .unwrap()
 9256        .await
 9257        .unwrap();
 9258    _ = follower.update(cx, |follower, cx| {
 9259        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9260    });
 9261    assert!(*is_still_following.borrow());
 9262
 9263    // Extend the pending selection so that it surrounds another selection
 9264    _ = leader.update(cx, |leader, cx| {
 9265        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9266    });
 9267    follower
 9268        .update(cx, |follower, cx| {
 9269            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9270        })
 9271        .unwrap()
 9272        .await
 9273        .unwrap();
 9274    _ = follower.update(cx, |follower, cx| {
 9275        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9276    });
 9277
 9278    // Scrolling locally breaks the follow
 9279    _ = follower.update(cx, |follower, cx| {
 9280        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9281        follower.set_scroll_anchor(
 9282            ScrollAnchor {
 9283                anchor: top_anchor,
 9284                offset: gpui::Point::new(0.0, 0.5),
 9285            },
 9286            cx,
 9287        );
 9288    });
 9289    assert!(!(*is_still_following.borrow()));
 9290}
 9291
 9292#[gpui::test]
 9293async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9294    init_test(cx, |_| {});
 9295
 9296    let fs = FakeFs::new(cx.executor());
 9297    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9298    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9299    let pane = workspace
 9300        .update(cx, |workspace, _| workspace.active_pane().clone())
 9301        .unwrap();
 9302
 9303    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9304
 9305    let leader = pane.update(cx, |_, cx| {
 9306        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9307        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9308    });
 9309
 9310    // Start following the editor when it has no excerpts.
 9311    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9312    let follower_1 = cx
 9313        .update_window(*workspace.deref(), |_, cx| {
 9314            Editor::from_state_proto(
 9315                workspace.root_view(cx).unwrap(),
 9316                ViewId {
 9317                    creator: Default::default(),
 9318                    id: 0,
 9319                },
 9320                &mut state_message,
 9321                cx,
 9322            )
 9323        })
 9324        .unwrap()
 9325        .unwrap()
 9326        .await
 9327        .unwrap();
 9328
 9329    let update_message = Rc::new(RefCell::new(None));
 9330    follower_1.update(cx, {
 9331        let update = update_message.clone();
 9332        |_, cx| {
 9333            cx.subscribe(&leader, move |_, leader, event, cx| {
 9334                leader
 9335                    .read(cx)
 9336                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9337            })
 9338            .detach();
 9339        }
 9340    });
 9341
 9342    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9343        (
 9344            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9345            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9346        )
 9347    });
 9348
 9349    // Insert some excerpts.
 9350    leader.update(cx, |leader, cx| {
 9351        leader.buffer.update(cx, |multibuffer, cx| {
 9352            let excerpt_ids = multibuffer.push_excerpts(
 9353                buffer_1.clone(),
 9354                [
 9355                    ExcerptRange {
 9356                        context: 1..6,
 9357                        primary: None,
 9358                    },
 9359                    ExcerptRange {
 9360                        context: 12..15,
 9361                        primary: None,
 9362                    },
 9363                    ExcerptRange {
 9364                        context: 0..3,
 9365                        primary: None,
 9366                    },
 9367                ],
 9368                cx,
 9369            );
 9370            multibuffer.insert_excerpts_after(
 9371                excerpt_ids[0],
 9372                buffer_2.clone(),
 9373                [
 9374                    ExcerptRange {
 9375                        context: 8..12,
 9376                        primary: None,
 9377                    },
 9378                    ExcerptRange {
 9379                        context: 0..6,
 9380                        primary: None,
 9381                    },
 9382                ],
 9383                cx,
 9384            );
 9385        });
 9386    });
 9387
 9388    // Apply the update of adding the excerpts.
 9389    follower_1
 9390        .update(cx, |follower, cx| {
 9391            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9392        })
 9393        .await
 9394        .unwrap();
 9395    assert_eq!(
 9396        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9397        leader.update(cx, |editor, cx| editor.text(cx))
 9398    );
 9399    update_message.borrow_mut().take();
 9400
 9401    // Start following separately after it already has excerpts.
 9402    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9403    let follower_2 = cx
 9404        .update_window(*workspace.deref(), |_, cx| {
 9405            Editor::from_state_proto(
 9406                workspace.root_view(cx).unwrap().clone(),
 9407                ViewId {
 9408                    creator: Default::default(),
 9409                    id: 0,
 9410                },
 9411                &mut state_message,
 9412                cx,
 9413            )
 9414        })
 9415        .unwrap()
 9416        .unwrap()
 9417        .await
 9418        .unwrap();
 9419    assert_eq!(
 9420        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9421        leader.update(cx, |editor, cx| editor.text(cx))
 9422    );
 9423
 9424    // Remove some excerpts.
 9425    leader.update(cx, |leader, cx| {
 9426        leader.buffer.update(cx, |multibuffer, cx| {
 9427            let excerpt_ids = multibuffer.excerpt_ids();
 9428            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9429            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9430        });
 9431    });
 9432
 9433    // Apply the update of removing the excerpts.
 9434    follower_1
 9435        .update(cx, |follower, cx| {
 9436            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9437        })
 9438        .await
 9439        .unwrap();
 9440    follower_2
 9441        .update(cx, |follower, cx| {
 9442            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9443        })
 9444        .await
 9445        .unwrap();
 9446    update_message.borrow_mut().take();
 9447    assert_eq!(
 9448        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9449        leader.update(cx, |editor, cx| editor.text(cx))
 9450    );
 9451}
 9452
 9453#[gpui::test]
 9454async fn go_to_prev_overlapping_diagnostic(
 9455    executor: BackgroundExecutor,
 9456    cx: &mut gpui::TestAppContext,
 9457) {
 9458    init_test(cx, |_| {});
 9459
 9460    let mut cx = EditorTestContext::new(cx).await;
 9461    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9462
 9463    cx.set_state(indoc! {"
 9464        ˇfn func(abc def: i32) -> u32 {
 9465        }
 9466    "});
 9467
 9468    cx.update(|cx| {
 9469        project.update(cx, |project, cx| {
 9470            project
 9471                .update_diagnostics(
 9472                    LanguageServerId(0),
 9473                    lsp::PublishDiagnosticsParams {
 9474                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9475                        version: None,
 9476                        diagnostics: vec![
 9477                            lsp::Diagnostic {
 9478                                range: lsp::Range::new(
 9479                                    lsp::Position::new(0, 11),
 9480                                    lsp::Position::new(0, 12),
 9481                                ),
 9482                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9483                                ..Default::default()
 9484                            },
 9485                            lsp::Diagnostic {
 9486                                range: lsp::Range::new(
 9487                                    lsp::Position::new(0, 12),
 9488                                    lsp::Position::new(0, 15),
 9489                                ),
 9490                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9491                                ..Default::default()
 9492                            },
 9493                            lsp::Diagnostic {
 9494                                range: lsp::Range::new(
 9495                                    lsp::Position::new(0, 25),
 9496                                    lsp::Position::new(0, 28),
 9497                                ),
 9498                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9499                                ..Default::default()
 9500                            },
 9501                        ],
 9502                    },
 9503                    &[],
 9504                    cx,
 9505                )
 9506                .unwrap()
 9507        });
 9508    });
 9509
 9510    executor.run_until_parked();
 9511
 9512    cx.update_editor(|editor, cx| {
 9513        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9514    });
 9515
 9516    cx.assert_editor_state(indoc! {"
 9517        fn func(abc def: i32) -> ˇu32 {
 9518        }
 9519    "});
 9520
 9521    cx.update_editor(|editor, cx| {
 9522        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9523    });
 9524
 9525    cx.assert_editor_state(indoc! {"
 9526        fn func(abc ˇdef: i32) -> u32 {
 9527        }
 9528    "});
 9529
 9530    cx.update_editor(|editor, cx| {
 9531        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9532    });
 9533
 9534    cx.assert_editor_state(indoc! {"
 9535        fn func(abcˇ def: i32) -> u32 {
 9536        }
 9537    "});
 9538
 9539    cx.update_editor(|editor, cx| {
 9540        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9541    });
 9542
 9543    cx.assert_editor_state(indoc! {"
 9544        fn func(abc def: i32) -> ˇu32 {
 9545        }
 9546    "});
 9547}
 9548
 9549#[gpui::test]
 9550async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9551    init_test(cx, |_| {});
 9552
 9553    let mut cx = EditorTestContext::new(cx).await;
 9554
 9555    cx.set_state(indoc! {"
 9556        fn func(abˇc def: i32) -> u32 {
 9557        }
 9558    "});
 9559    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9560
 9561    cx.update(|cx| {
 9562        project.update(cx, |project, cx| {
 9563            project.update_diagnostics(
 9564                LanguageServerId(0),
 9565                lsp::PublishDiagnosticsParams {
 9566                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9567                    version: None,
 9568                    diagnostics: vec![lsp::Diagnostic {
 9569                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9570                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9571                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9572                        ..Default::default()
 9573                    }],
 9574                },
 9575                &[],
 9576                cx,
 9577            )
 9578        })
 9579    }).unwrap();
 9580    cx.run_until_parked();
 9581    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9582    cx.run_until_parked();
 9583    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9584}
 9585
 9586#[gpui::test]
 9587async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9588    init_test(cx, |_| {});
 9589
 9590    let mut cx = EditorTestContext::new(cx).await;
 9591
 9592    let diff_base = r#"
 9593        use some::mod;
 9594
 9595        const A: u32 = 42;
 9596
 9597        fn main() {
 9598            println!("hello");
 9599
 9600            println!("world");
 9601        }
 9602        "#
 9603    .unindent();
 9604
 9605    // Edits are modified, removed, modified, added
 9606    cx.set_state(
 9607        &r#"
 9608        use some::modified;
 9609
 9610        ˇ
 9611        fn main() {
 9612            println!("hello there");
 9613
 9614            println!("around the");
 9615            println!("world");
 9616        }
 9617        "#
 9618        .unindent(),
 9619    );
 9620
 9621    cx.set_diff_base(Some(&diff_base));
 9622    executor.run_until_parked();
 9623
 9624    cx.update_editor(|editor, cx| {
 9625        //Wrap around the bottom of the buffer
 9626        for _ in 0..3 {
 9627            editor.go_to_next_hunk(&GoToHunk, cx);
 9628        }
 9629    });
 9630
 9631    cx.assert_editor_state(
 9632        &r#"
 9633        ˇuse some::modified;
 9634
 9635
 9636        fn main() {
 9637            println!("hello there");
 9638
 9639            println!("around the");
 9640            println!("world");
 9641        }
 9642        "#
 9643        .unindent(),
 9644    );
 9645
 9646    cx.update_editor(|editor, cx| {
 9647        //Wrap around the top of the buffer
 9648        for _ in 0..2 {
 9649            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9650        }
 9651    });
 9652
 9653    cx.assert_editor_state(
 9654        &r#"
 9655        use some::modified;
 9656
 9657
 9658        fn main() {
 9659        ˇ    println!("hello there");
 9660
 9661            println!("around the");
 9662            println!("world");
 9663        }
 9664        "#
 9665        .unindent(),
 9666    );
 9667
 9668    cx.update_editor(|editor, cx| {
 9669        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9670    });
 9671
 9672    cx.assert_editor_state(
 9673        &r#"
 9674        use some::modified;
 9675
 9676        ˇ
 9677        fn main() {
 9678            println!("hello there");
 9679
 9680            println!("around the");
 9681            println!("world");
 9682        }
 9683        "#
 9684        .unindent(),
 9685    );
 9686
 9687    cx.update_editor(|editor, cx| {
 9688        for _ in 0..3 {
 9689            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9690        }
 9691    });
 9692
 9693    cx.assert_editor_state(
 9694        &r#"
 9695        use some::modified;
 9696
 9697
 9698        fn main() {
 9699        ˇ    println!("hello there");
 9700
 9701            println!("around the");
 9702            println!("world");
 9703        }
 9704        "#
 9705        .unindent(),
 9706    );
 9707
 9708    cx.update_editor(|editor, cx| {
 9709        editor.fold(&Fold, cx);
 9710
 9711        //Make sure that the fold only gets one hunk
 9712        for _ in 0..4 {
 9713            editor.go_to_next_hunk(&GoToHunk, cx);
 9714        }
 9715    });
 9716
 9717    cx.assert_editor_state(
 9718        &r#"
 9719        ˇuse some::modified;
 9720
 9721
 9722        fn main() {
 9723            println!("hello there");
 9724
 9725            println!("around the");
 9726            println!("world");
 9727        }
 9728        "#
 9729        .unindent(),
 9730    );
 9731}
 9732
 9733#[test]
 9734fn test_split_words() {
 9735    fn split(text: &str) -> Vec<&str> {
 9736        split_words(text).collect()
 9737    }
 9738
 9739    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9740    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9741    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9742    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9743    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9744    assert_eq!(split("helloworld"), &["helloworld"]);
 9745
 9746    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9747}
 9748
 9749#[gpui::test]
 9750async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9751    init_test(cx, |_| {});
 9752
 9753    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9754    let mut assert = |before, after| {
 9755        let _state_context = cx.set_state(before);
 9756        cx.update_editor(|editor, cx| {
 9757            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9758        });
 9759        cx.assert_editor_state(after);
 9760    };
 9761
 9762    // Outside bracket jumps to outside of matching bracket
 9763    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9764    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9765
 9766    // Inside bracket jumps to inside of matching bracket
 9767    assert("console.log(ˇvar);", "console.log(varˇ);");
 9768    assert("console.log(varˇ);", "console.log(ˇvar);");
 9769
 9770    // When outside a bracket and inside, favor jumping to the inside bracket
 9771    assert(
 9772        "console.log('foo', [1, 2, 3]ˇ);",
 9773        "console.log(ˇ'foo', [1, 2, 3]);",
 9774    );
 9775    assert(
 9776        "console.log(ˇ'foo', [1, 2, 3]);",
 9777        "console.log('foo', [1, 2, 3]ˇ);",
 9778    );
 9779
 9780    // Bias forward if two options are equally likely
 9781    assert(
 9782        "let result = curried_fun()ˇ();",
 9783        "let result = curried_fun()()ˇ;",
 9784    );
 9785
 9786    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9787    assert(
 9788        indoc! {"
 9789            function test() {
 9790                console.log('test')ˇ
 9791            }"},
 9792        indoc! {"
 9793            function test() {
 9794                console.logˇ('test')
 9795            }"},
 9796    );
 9797}
 9798
 9799#[gpui::test]
 9800async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9801    init_test(cx, |_| {});
 9802
 9803    let fs = FakeFs::new(cx.executor());
 9804    fs.insert_tree(
 9805        "/a",
 9806        json!({
 9807            "main.rs": "fn main() { let a = 5; }",
 9808            "other.rs": "// Test file",
 9809        }),
 9810    )
 9811    .await;
 9812    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9813
 9814    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9815    language_registry.add(Arc::new(Language::new(
 9816        LanguageConfig {
 9817            name: "Rust".into(),
 9818            matcher: LanguageMatcher {
 9819                path_suffixes: vec!["rs".to_string()],
 9820                ..Default::default()
 9821            },
 9822            brackets: BracketPairConfig {
 9823                pairs: vec![BracketPair {
 9824                    start: "{".to_string(),
 9825                    end: "}".to_string(),
 9826                    close: true,
 9827                    surround: true,
 9828                    newline: true,
 9829                }],
 9830                disabled_scopes_by_bracket_ix: Vec::new(),
 9831            },
 9832            ..Default::default()
 9833        },
 9834        Some(tree_sitter_rust::LANGUAGE.into()),
 9835    )));
 9836    let mut fake_servers = language_registry.register_fake_lsp(
 9837        "Rust",
 9838        FakeLspAdapter {
 9839            capabilities: lsp::ServerCapabilities {
 9840                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9841                    first_trigger_character: "{".to_string(),
 9842                    more_trigger_character: None,
 9843                }),
 9844                ..Default::default()
 9845            },
 9846            ..Default::default()
 9847        },
 9848    );
 9849
 9850    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9851
 9852    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9853
 9854    let worktree_id = workspace
 9855        .update(cx, |workspace, cx| {
 9856            workspace.project().update(cx, |project, cx| {
 9857                project.worktrees(cx).next().unwrap().read(cx).id()
 9858            })
 9859        })
 9860        .unwrap();
 9861
 9862    let buffer = project
 9863        .update(cx, |project, cx| {
 9864            project.open_local_buffer("/a/main.rs", cx)
 9865        })
 9866        .await
 9867        .unwrap();
 9868    cx.executor().run_until_parked();
 9869    cx.executor().start_waiting();
 9870    let fake_server = fake_servers.next().await.unwrap();
 9871    let editor_handle = workspace
 9872        .update(cx, |workspace, cx| {
 9873            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9874        })
 9875        .unwrap()
 9876        .await
 9877        .unwrap()
 9878        .downcast::<Editor>()
 9879        .unwrap();
 9880
 9881    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9882        assert_eq!(
 9883            params.text_document_position.text_document.uri,
 9884            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9885        );
 9886        assert_eq!(
 9887            params.text_document_position.position,
 9888            lsp::Position::new(0, 21),
 9889        );
 9890
 9891        Ok(Some(vec![lsp::TextEdit {
 9892            new_text: "]".to_string(),
 9893            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9894        }]))
 9895    });
 9896
 9897    editor_handle.update(cx, |editor, cx| {
 9898        editor.focus(cx);
 9899        editor.change_selections(None, cx, |s| {
 9900            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9901        });
 9902        editor.handle_input("{", cx);
 9903    });
 9904
 9905    cx.executor().run_until_parked();
 9906
 9907    buffer.update(cx, |buffer, _| {
 9908        assert_eq!(
 9909            buffer.text(),
 9910            "fn main() { let a = {5}; }",
 9911            "No extra braces from on type formatting should appear in the buffer"
 9912        )
 9913    });
 9914}
 9915
 9916#[gpui::test]
 9917async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9918    init_test(cx, |_| {});
 9919
 9920    let fs = FakeFs::new(cx.executor());
 9921    fs.insert_tree(
 9922        "/a",
 9923        json!({
 9924            "main.rs": "fn main() { let a = 5; }",
 9925            "other.rs": "// Test file",
 9926        }),
 9927    )
 9928    .await;
 9929
 9930    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9931
 9932    let server_restarts = Arc::new(AtomicUsize::new(0));
 9933    let closure_restarts = Arc::clone(&server_restarts);
 9934    let language_server_name = "test language server";
 9935    let language_name: LanguageName = "Rust".into();
 9936
 9937    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9938    language_registry.add(Arc::new(Language::new(
 9939        LanguageConfig {
 9940            name: language_name.clone(),
 9941            matcher: LanguageMatcher {
 9942                path_suffixes: vec!["rs".to_string()],
 9943                ..Default::default()
 9944            },
 9945            ..Default::default()
 9946        },
 9947        Some(tree_sitter_rust::LANGUAGE.into()),
 9948    )));
 9949    let mut fake_servers = language_registry.register_fake_lsp(
 9950        "Rust",
 9951        FakeLspAdapter {
 9952            name: language_server_name,
 9953            initialization_options: Some(json!({
 9954                "testOptionValue": true
 9955            })),
 9956            initializer: Some(Box::new(move |fake_server| {
 9957                let task_restarts = Arc::clone(&closure_restarts);
 9958                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9959                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9960                    futures::future::ready(Ok(()))
 9961                });
 9962            })),
 9963            ..Default::default()
 9964        },
 9965    );
 9966
 9967    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9968    let _buffer = project
 9969        .update(cx, |project, cx| {
 9970            project.open_local_buffer("/a/main.rs", cx)
 9971        })
 9972        .await
 9973        .unwrap();
 9974    let _fake_server = fake_servers.next().await.unwrap();
 9975    update_test_language_settings(cx, |language_settings| {
 9976        language_settings.languages.insert(
 9977            language_name.clone(),
 9978            LanguageSettingsContent {
 9979                tab_size: NonZeroU32::new(8),
 9980                ..Default::default()
 9981            },
 9982        );
 9983    });
 9984    cx.executor().run_until_parked();
 9985    assert_eq!(
 9986        server_restarts.load(atomic::Ordering::Acquire),
 9987        0,
 9988        "Should not restart LSP server on an unrelated change"
 9989    );
 9990
 9991    update_test_project_settings(cx, |project_settings| {
 9992        project_settings.lsp.insert(
 9993            "Some other server name".into(),
 9994            LspSettings {
 9995                binary: None,
 9996                settings: None,
 9997                initialization_options: Some(json!({
 9998                    "some other init value": false
 9999                })),
10000            },
10001        );
10002    });
10003    cx.executor().run_until_parked();
10004    assert_eq!(
10005        server_restarts.load(atomic::Ordering::Acquire),
10006        0,
10007        "Should not restart LSP server on an unrelated LSP settings change"
10008    );
10009
10010    update_test_project_settings(cx, |project_settings| {
10011        project_settings.lsp.insert(
10012            language_server_name.into(),
10013            LspSettings {
10014                binary: None,
10015                settings: None,
10016                initialization_options: Some(json!({
10017                    "anotherInitValue": false
10018                })),
10019            },
10020        );
10021    });
10022    cx.executor().run_until_parked();
10023    assert_eq!(
10024        server_restarts.load(atomic::Ordering::Acquire),
10025        1,
10026        "Should restart LSP server on a related LSP settings change"
10027    );
10028
10029    update_test_project_settings(cx, |project_settings| {
10030        project_settings.lsp.insert(
10031            language_server_name.into(),
10032            LspSettings {
10033                binary: None,
10034                settings: None,
10035                initialization_options: Some(json!({
10036                    "anotherInitValue": false
10037                })),
10038            },
10039        );
10040    });
10041    cx.executor().run_until_parked();
10042    assert_eq!(
10043        server_restarts.load(atomic::Ordering::Acquire),
10044        1,
10045        "Should not restart LSP server on a related LSP settings change that is the same"
10046    );
10047
10048    update_test_project_settings(cx, |project_settings| {
10049        project_settings.lsp.insert(
10050            language_server_name.into(),
10051            LspSettings {
10052                binary: None,
10053                settings: None,
10054                initialization_options: None,
10055            },
10056        );
10057    });
10058    cx.executor().run_until_parked();
10059    assert_eq!(
10060        server_restarts.load(atomic::Ordering::Acquire),
10061        2,
10062        "Should restart LSP server on another related LSP settings change"
10063    );
10064}
10065
10066#[gpui::test]
10067async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10068    init_test(cx, |_| {});
10069
10070    let mut cx = EditorLspTestContext::new_rust(
10071        lsp::ServerCapabilities {
10072            completion_provider: Some(lsp::CompletionOptions {
10073                trigger_characters: Some(vec![".".to_string()]),
10074                resolve_provider: Some(true),
10075                ..Default::default()
10076            }),
10077            ..Default::default()
10078        },
10079        cx,
10080    )
10081    .await;
10082
10083    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10084    cx.simulate_keystroke(".");
10085    let completion_item = lsp::CompletionItem {
10086        label: "some".into(),
10087        kind: Some(lsp::CompletionItemKind::SNIPPET),
10088        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10089        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10090            kind: lsp::MarkupKind::Markdown,
10091            value: "```rust\nSome(2)\n```".to_string(),
10092        })),
10093        deprecated: Some(false),
10094        sort_text: Some("fffffff2".to_string()),
10095        filter_text: Some("some".to_string()),
10096        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10097        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10098            range: lsp::Range {
10099                start: lsp::Position {
10100                    line: 0,
10101                    character: 22,
10102                },
10103                end: lsp::Position {
10104                    line: 0,
10105                    character: 22,
10106                },
10107            },
10108            new_text: "Some(2)".to_string(),
10109        })),
10110        additional_text_edits: Some(vec![lsp::TextEdit {
10111            range: lsp::Range {
10112                start: lsp::Position {
10113                    line: 0,
10114                    character: 20,
10115                },
10116                end: lsp::Position {
10117                    line: 0,
10118                    character: 22,
10119                },
10120            },
10121            new_text: "".to_string(),
10122        }]),
10123        ..Default::default()
10124    };
10125
10126    let closure_completion_item = completion_item.clone();
10127    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10128        let task_completion_item = closure_completion_item.clone();
10129        async move {
10130            Ok(Some(lsp::CompletionResponse::Array(vec![
10131                task_completion_item,
10132            ])))
10133        }
10134    });
10135
10136    request.next().await;
10137
10138    cx.condition(|editor, _| editor.context_menu_visible())
10139        .await;
10140    let apply_additional_edits = cx.update_editor(|editor, cx| {
10141        editor
10142            .confirm_completion(&ConfirmCompletion::default(), cx)
10143            .unwrap()
10144    });
10145    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
10146
10147    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10148        let task_completion_item = completion_item.clone();
10149        async move { Ok(task_completion_item) }
10150    })
10151    .next()
10152    .await
10153    .unwrap();
10154    apply_additional_edits.await.unwrap();
10155    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10156}
10157
10158#[gpui::test]
10159async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10160    init_test(cx, |_| {});
10161
10162    let mut cx = EditorLspTestContext::new(
10163        Language::new(
10164            LanguageConfig {
10165                matcher: LanguageMatcher {
10166                    path_suffixes: vec!["jsx".into()],
10167                    ..Default::default()
10168                },
10169                overrides: [(
10170                    "element".into(),
10171                    LanguageConfigOverride {
10172                        word_characters: Override::Set(['-'].into_iter().collect()),
10173                        ..Default::default()
10174                    },
10175                )]
10176                .into_iter()
10177                .collect(),
10178                ..Default::default()
10179            },
10180            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10181        )
10182        .with_override_query("(jsx_self_closing_element) @element")
10183        .unwrap(),
10184        lsp::ServerCapabilities {
10185            completion_provider: Some(lsp::CompletionOptions {
10186                trigger_characters: Some(vec![":".to_string()]),
10187                ..Default::default()
10188            }),
10189            ..Default::default()
10190        },
10191        cx,
10192    )
10193    .await;
10194
10195    cx.lsp
10196        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10197            Ok(Some(lsp::CompletionResponse::Array(vec![
10198                lsp::CompletionItem {
10199                    label: "bg-blue".into(),
10200                    ..Default::default()
10201                },
10202                lsp::CompletionItem {
10203                    label: "bg-red".into(),
10204                    ..Default::default()
10205                },
10206                lsp::CompletionItem {
10207                    label: "bg-yellow".into(),
10208                    ..Default::default()
10209                },
10210            ])))
10211        });
10212
10213    cx.set_state(r#"<p class="bgˇ" />"#);
10214
10215    // Trigger completion when typing a dash, because the dash is an extra
10216    // word character in the 'element' scope, which contains the cursor.
10217    cx.simulate_keystroke("-");
10218    cx.executor().run_until_parked();
10219    cx.update_editor(|editor, _| {
10220        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10221            assert_eq!(
10222                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10223                &["bg-red", "bg-blue", "bg-yellow"]
10224            );
10225        } else {
10226            panic!("expected completion menu to be open");
10227        }
10228    });
10229
10230    cx.simulate_keystroke("l");
10231    cx.executor().run_until_parked();
10232    cx.update_editor(|editor, _| {
10233        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10234            assert_eq!(
10235                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10236                &["bg-blue", "bg-yellow"]
10237            );
10238        } else {
10239            panic!("expected completion menu to be open");
10240        }
10241    });
10242
10243    // When filtering completions, consider the character after the '-' to
10244    // be the start of a subword.
10245    cx.set_state(r#"<p class="yelˇ" />"#);
10246    cx.simulate_keystroke("l");
10247    cx.executor().run_until_parked();
10248    cx.update_editor(|editor, _| {
10249        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10250            assert_eq!(
10251                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10252                &["bg-yellow"]
10253            );
10254        } else {
10255            panic!("expected completion menu to be open");
10256        }
10257    });
10258}
10259
10260#[gpui::test]
10261async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10262    init_test(cx, |settings| {
10263        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10264            FormatterList(vec![Formatter::Prettier].into()),
10265        ))
10266    });
10267
10268    let fs = FakeFs::new(cx.executor());
10269    fs.insert_file("/file.ts", Default::default()).await;
10270
10271    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10272    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10273
10274    language_registry.add(Arc::new(Language::new(
10275        LanguageConfig {
10276            name: "TypeScript".into(),
10277            matcher: LanguageMatcher {
10278                path_suffixes: vec!["ts".to_string()],
10279                ..Default::default()
10280            },
10281            ..Default::default()
10282        },
10283        Some(tree_sitter_rust::LANGUAGE.into()),
10284    )));
10285    update_test_language_settings(cx, |settings| {
10286        settings.defaults.prettier = Some(PrettierSettings {
10287            allowed: true,
10288            ..PrettierSettings::default()
10289        });
10290    });
10291
10292    let test_plugin = "test_plugin";
10293    let _ = language_registry.register_fake_lsp(
10294        "TypeScript",
10295        FakeLspAdapter {
10296            prettier_plugins: vec![test_plugin],
10297            ..Default::default()
10298        },
10299    );
10300
10301    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10302    let buffer = project
10303        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10304        .await
10305        .unwrap();
10306
10307    let buffer_text = "one\ntwo\nthree\n";
10308    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10309    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10310    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10311
10312    editor
10313        .update(cx, |editor, cx| {
10314            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10315        })
10316        .unwrap()
10317        .await;
10318    assert_eq!(
10319        editor.update(cx, |editor, cx| editor.text(cx)),
10320        buffer_text.to_string() + prettier_format_suffix,
10321        "Test prettier formatting was not applied to the original buffer text",
10322    );
10323
10324    update_test_language_settings(cx, |settings| {
10325        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10326    });
10327    let format = editor.update(cx, |editor, cx| {
10328        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10329    });
10330    format.await.unwrap();
10331    assert_eq!(
10332        editor.update(cx, |editor, cx| editor.text(cx)),
10333        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10334        "Autoformatting (via test prettier) was not applied to the original buffer text",
10335    );
10336}
10337
10338#[gpui::test]
10339async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10340    init_test(cx, |_| {});
10341    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10342    let base_text = indoc! {r#"struct Row;
10343struct Row1;
10344struct Row2;
10345
10346struct Row4;
10347struct Row5;
10348struct Row6;
10349
10350struct Row8;
10351struct Row9;
10352struct Row10;"#};
10353
10354    // When addition hunks are not adjacent to carets, no hunk revert is performed
10355    assert_hunk_revert(
10356        indoc! {r#"struct Row;
10357                   struct Row1;
10358                   struct Row1.1;
10359                   struct Row1.2;
10360                   struct Row2;ˇ
10361
10362                   struct Row4;
10363                   struct Row5;
10364                   struct Row6;
10365
10366                   struct Row8;
10367                   ˇstruct Row9;
10368                   struct Row9.1;
10369                   struct Row9.2;
10370                   struct Row9.3;
10371                   struct Row10;"#},
10372        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10373        indoc! {r#"struct Row;
10374                   struct Row1;
10375                   struct Row1.1;
10376                   struct Row1.2;
10377                   struct Row2;ˇ
10378
10379                   struct Row4;
10380                   struct Row5;
10381                   struct Row6;
10382
10383                   struct Row8;
10384                   ˇstruct Row9;
10385                   struct Row9.1;
10386                   struct Row9.2;
10387                   struct Row9.3;
10388                   struct Row10;"#},
10389        base_text,
10390        &mut cx,
10391    );
10392    // Same for selections
10393    assert_hunk_revert(
10394        indoc! {r#"struct Row;
10395                   struct Row1;
10396                   struct Row2;
10397                   struct Row2.1;
10398                   struct Row2.2;
10399                   «ˇ
10400                   struct Row4;
10401                   struct» Row5;
10402                   «struct Row6;
10403                   ˇ»
10404                   struct Row9.1;
10405                   struct Row9.2;
10406                   struct Row9.3;
10407                   struct Row8;
10408                   struct Row9;
10409                   struct Row10;"#},
10410        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10411        indoc! {r#"struct Row;
10412                   struct Row1;
10413                   struct Row2;
10414                   struct Row2.1;
10415                   struct Row2.2;
10416                   «ˇ
10417                   struct Row4;
10418                   struct» Row5;
10419                   «struct Row6;
10420                   ˇ»
10421                   struct Row9.1;
10422                   struct Row9.2;
10423                   struct Row9.3;
10424                   struct Row8;
10425                   struct Row9;
10426                   struct Row10;"#},
10427        base_text,
10428        &mut cx,
10429    );
10430
10431    // When carets and selections intersect the addition hunks, those are reverted.
10432    // Adjacent carets got merged.
10433    assert_hunk_revert(
10434        indoc! {r#"struct Row;
10435                   ˇ// something on the top
10436                   struct Row1;
10437                   struct Row2;
10438                   struct Roˇw3.1;
10439                   struct Row2.2;
10440                   struct Row2.3;ˇ
10441
10442                   struct Row4;
10443                   struct ˇRow5.1;
10444                   struct Row5.2;
10445                   struct «Rowˇ»5.3;
10446                   struct Row5;
10447                   struct Row6;
10448                   ˇ
10449                   struct Row9.1;
10450                   struct «Rowˇ»9.2;
10451                   struct «ˇRow»9.3;
10452                   struct Row8;
10453                   struct Row9;
10454                   «ˇ// something on bottom»
10455                   struct Row10;"#},
10456        vec![
10457            DiffHunkStatus::Added,
10458            DiffHunkStatus::Added,
10459            DiffHunkStatus::Added,
10460            DiffHunkStatus::Added,
10461            DiffHunkStatus::Added,
10462        ],
10463        indoc! {r#"struct Row;
10464                   ˇstruct Row1;
10465                   struct Row2;
10466                   ˇ
10467                   struct Row4;
10468                   ˇstruct Row5;
10469                   struct Row6;
10470                   ˇ
10471                   ˇstruct Row8;
10472                   struct Row9;
10473                   ˇstruct Row10;"#},
10474        base_text,
10475        &mut cx,
10476    );
10477}
10478
10479#[gpui::test]
10480async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10481    init_test(cx, |_| {});
10482    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10483    let base_text = indoc! {r#"struct Row;
10484struct Row1;
10485struct Row2;
10486
10487struct Row4;
10488struct Row5;
10489struct Row6;
10490
10491struct Row8;
10492struct Row9;
10493struct Row10;"#};
10494
10495    // Modification hunks behave the same as the addition ones.
10496    assert_hunk_revert(
10497        indoc! {r#"struct Row;
10498                   struct Row1;
10499                   struct Row33;
10500                   ˇ
10501                   struct Row4;
10502                   struct Row5;
10503                   struct Row6;
10504                   ˇ
10505                   struct Row99;
10506                   struct Row9;
10507                   struct Row10;"#},
10508        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10509        indoc! {r#"struct Row;
10510                   struct Row1;
10511                   struct Row33;
10512                   ˇ
10513                   struct Row4;
10514                   struct Row5;
10515                   struct Row6;
10516                   ˇ
10517                   struct Row99;
10518                   struct Row9;
10519                   struct Row10;"#},
10520        base_text,
10521        &mut cx,
10522    );
10523    assert_hunk_revert(
10524        indoc! {r#"struct Row;
10525                   struct Row1;
10526                   struct Row33;
10527                   «ˇ
10528                   struct Row4;
10529                   struct» Row5;
10530                   «struct Row6;
10531                   ˇ»
10532                   struct Row99;
10533                   struct Row9;
10534                   struct Row10;"#},
10535        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10536        indoc! {r#"struct Row;
10537                   struct Row1;
10538                   struct Row33;
10539                   «ˇ
10540                   struct Row4;
10541                   struct» Row5;
10542                   «struct Row6;
10543                   ˇ»
10544                   struct Row99;
10545                   struct Row9;
10546                   struct Row10;"#},
10547        base_text,
10548        &mut cx,
10549    );
10550
10551    assert_hunk_revert(
10552        indoc! {r#"ˇstruct Row1.1;
10553                   struct Row1;
10554                   «ˇstr»uct Row22;
10555
10556                   struct ˇRow44;
10557                   struct Row5;
10558                   struct «Rˇ»ow66;ˇ
10559
10560                   «struˇ»ct Row88;
10561                   struct Row9;
10562                   struct Row1011;ˇ"#},
10563        vec![
10564            DiffHunkStatus::Modified,
10565            DiffHunkStatus::Modified,
10566            DiffHunkStatus::Modified,
10567            DiffHunkStatus::Modified,
10568            DiffHunkStatus::Modified,
10569            DiffHunkStatus::Modified,
10570        ],
10571        indoc! {r#"struct Row;
10572                   ˇstruct Row1;
10573                   struct Row2;
10574                   ˇ
10575                   struct Row4;
10576                   ˇstruct Row5;
10577                   struct Row6;
10578                   ˇ
10579                   struct Row8;
10580                   ˇstruct Row9;
10581                   struct Row10;ˇ"#},
10582        base_text,
10583        &mut cx,
10584    );
10585}
10586
10587#[gpui::test]
10588async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10589    init_test(cx, |_| {});
10590    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10591    let base_text = indoc! {r#"struct Row;
10592struct Row1;
10593struct Row2;
10594
10595struct Row4;
10596struct Row5;
10597struct Row6;
10598
10599struct Row8;
10600struct Row9;
10601struct Row10;"#};
10602
10603    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10604    assert_hunk_revert(
10605        indoc! {r#"struct Row;
10606                   struct Row2;
10607
10608                   ˇstruct Row4;
10609                   struct Row5;
10610                   struct Row6;
10611                   ˇ
10612                   struct Row8;
10613                   struct Row10;"#},
10614        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10615        indoc! {r#"struct Row;
10616                   struct Row2;
10617
10618                   ˇstruct Row4;
10619                   struct Row5;
10620                   struct Row6;
10621                   ˇ
10622                   struct Row8;
10623                   struct Row10;"#},
10624        base_text,
10625        &mut cx,
10626    );
10627    assert_hunk_revert(
10628        indoc! {r#"struct Row;
10629                   struct Row2;
10630
10631                   «ˇstruct Row4;
10632                   struct» Row5;
10633                   «struct Row6;
10634                   ˇ»
10635                   struct Row8;
10636                   struct Row10;"#},
10637        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10638        indoc! {r#"struct Row;
10639                   struct Row2;
10640
10641                   «ˇstruct Row4;
10642                   struct» Row5;
10643                   «struct Row6;
10644                   ˇ»
10645                   struct Row8;
10646                   struct Row10;"#},
10647        base_text,
10648        &mut cx,
10649    );
10650
10651    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10652    assert_hunk_revert(
10653        indoc! {r#"struct Row;
10654                   ˇstruct Row2;
10655
10656                   struct Row4;
10657                   struct Row5;
10658                   struct Row6;
10659
10660                   struct Row8;ˇ
10661                   struct Row10;"#},
10662        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10663        indoc! {r#"struct Row;
10664                   struct Row1;
10665                   ˇstruct Row2;
10666
10667                   struct Row4;
10668                   struct Row5;
10669                   struct Row6;
10670
10671                   struct Row8;ˇ
10672                   struct Row9;
10673                   struct Row10;"#},
10674        base_text,
10675        &mut cx,
10676    );
10677    assert_hunk_revert(
10678        indoc! {r#"struct Row;
10679                   struct Row2«ˇ;
10680                   struct Row4;
10681                   struct» Row5;
10682                   «struct Row6;
10683
10684                   struct Row8;ˇ»
10685                   struct Row10;"#},
10686        vec![
10687            DiffHunkStatus::Removed,
10688            DiffHunkStatus::Removed,
10689            DiffHunkStatus::Removed,
10690        ],
10691        indoc! {r#"struct Row;
10692                   struct Row1;
10693                   struct Row2«ˇ;
10694
10695                   struct Row4;
10696                   struct» Row5;
10697                   «struct Row6;
10698
10699                   struct Row8;ˇ»
10700                   struct Row9;
10701                   struct Row10;"#},
10702        base_text,
10703        &mut cx,
10704    );
10705}
10706
10707#[gpui::test]
10708async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10709    init_test(cx, |_| {});
10710
10711    let cols = 4;
10712    let rows = 10;
10713    let sample_text_1 = sample_text(rows, cols, 'a');
10714    assert_eq!(
10715        sample_text_1,
10716        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10717    );
10718    let sample_text_2 = sample_text(rows, cols, 'l');
10719    assert_eq!(
10720        sample_text_2,
10721        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10722    );
10723    let sample_text_3 = sample_text(rows, cols, 'v');
10724    assert_eq!(
10725        sample_text_3,
10726        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10727    );
10728
10729    fn diff_every_buffer_row(
10730        buffer: &Model<Buffer>,
10731        sample_text: String,
10732        cols: usize,
10733        cx: &mut gpui::TestAppContext,
10734    ) {
10735        // revert first character in each row, creating one large diff hunk per buffer
10736        let is_first_char = |offset: usize| offset % cols == 0;
10737        buffer.update(cx, |buffer, cx| {
10738            buffer.set_text(
10739                sample_text
10740                    .chars()
10741                    .enumerate()
10742                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10743                    .collect::<String>(),
10744                cx,
10745            );
10746            buffer.set_diff_base(Some(sample_text), cx);
10747        });
10748        cx.executor().run_until_parked();
10749    }
10750
10751    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10752    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10753
10754    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10755    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10756
10757    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10758    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10759
10760    let multibuffer = cx.new_model(|cx| {
10761        let mut multibuffer = MultiBuffer::new(ReadWrite);
10762        multibuffer.push_excerpts(
10763            buffer_1.clone(),
10764            [
10765                ExcerptRange {
10766                    context: Point::new(0, 0)..Point::new(3, 0),
10767                    primary: None,
10768                },
10769                ExcerptRange {
10770                    context: Point::new(5, 0)..Point::new(7, 0),
10771                    primary: None,
10772                },
10773                ExcerptRange {
10774                    context: Point::new(9, 0)..Point::new(10, 4),
10775                    primary: None,
10776                },
10777            ],
10778            cx,
10779        );
10780        multibuffer.push_excerpts(
10781            buffer_2.clone(),
10782            [
10783                ExcerptRange {
10784                    context: Point::new(0, 0)..Point::new(3, 0),
10785                    primary: None,
10786                },
10787                ExcerptRange {
10788                    context: Point::new(5, 0)..Point::new(7, 0),
10789                    primary: None,
10790                },
10791                ExcerptRange {
10792                    context: Point::new(9, 0)..Point::new(10, 4),
10793                    primary: None,
10794                },
10795            ],
10796            cx,
10797        );
10798        multibuffer.push_excerpts(
10799            buffer_3.clone(),
10800            [
10801                ExcerptRange {
10802                    context: Point::new(0, 0)..Point::new(3, 0),
10803                    primary: None,
10804                },
10805                ExcerptRange {
10806                    context: Point::new(5, 0)..Point::new(7, 0),
10807                    primary: None,
10808                },
10809                ExcerptRange {
10810                    context: Point::new(9, 0)..Point::new(10, 4),
10811                    primary: None,
10812                },
10813            ],
10814            cx,
10815        );
10816        multibuffer
10817    });
10818
10819    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10820    editor.update(cx, |editor, cx| {
10821        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");
10822        editor.select_all(&SelectAll, cx);
10823        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10824    });
10825    cx.executor().run_until_parked();
10826    // When all ranges are selected, all buffer hunks are reverted.
10827    editor.update(cx, |editor, cx| {
10828        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");
10829    });
10830    buffer_1.update(cx, |buffer, _| {
10831        assert_eq!(buffer.text(), sample_text_1);
10832    });
10833    buffer_2.update(cx, |buffer, _| {
10834        assert_eq!(buffer.text(), sample_text_2);
10835    });
10836    buffer_3.update(cx, |buffer, _| {
10837        assert_eq!(buffer.text(), sample_text_3);
10838    });
10839
10840    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10841    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10842    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10843    editor.update(cx, |editor, cx| {
10844        editor.change_selections(None, cx, |s| {
10845            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10846        });
10847        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10848    });
10849    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10850    // but not affect buffer_2 and its related excerpts.
10851    editor.update(cx, |editor, cx| {
10852        assert_eq!(
10853            editor.text(cx),
10854            "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"
10855        );
10856    });
10857    buffer_1.update(cx, |buffer, _| {
10858        assert_eq!(buffer.text(), sample_text_1);
10859    });
10860    buffer_2.update(cx, |buffer, _| {
10861        assert_eq!(
10862            buffer.text(),
10863            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10864        );
10865    });
10866    buffer_3.update(cx, |buffer, _| {
10867        assert_eq!(
10868            buffer.text(),
10869            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10870        );
10871    });
10872}
10873
10874#[gpui::test]
10875async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10876    init_test(cx, |_| {});
10877
10878    let cols = 4;
10879    let rows = 10;
10880    let sample_text_1 = sample_text(rows, cols, 'a');
10881    assert_eq!(
10882        sample_text_1,
10883        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10884    );
10885    let sample_text_2 = sample_text(rows, cols, 'l');
10886    assert_eq!(
10887        sample_text_2,
10888        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10889    );
10890    let sample_text_3 = sample_text(rows, cols, 'v');
10891    assert_eq!(
10892        sample_text_3,
10893        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10894    );
10895
10896    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10897    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10898    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10899
10900    let multi_buffer = cx.new_model(|cx| {
10901        let mut multibuffer = MultiBuffer::new(ReadWrite);
10902        multibuffer.push_excerpts(
10903            buffer_1.clone(),
10904            [
10905                ExcerptRange {
10906                    context: Point::new(0, 0)..Point::new(3, 0),
10907                    primary: None,
10908                },
10909                ExcerptRange {
10910                    context: Point::new(5, 0)..Point::new(7, 0),
10911                    primary: None,
10912                },
10913                ExcerptRange {
10914                    context: Point::new(9, 0)..Point::new(10, 4),
10915                    primary: None,
10916                },
10917            ],
10918            cx,
10919        );
10920        multibuffer.push_excerpts(
10921            buffer_2.clone(),
10922            [
10923                ExcerptRange {
10924                    context: Point::new(0, 0)..Point::new(3, 0),
10925                    primary: None,
10926                },
10927                ExcerptRange {
10928                    context: Point::new(5, 0)..Point::new(7, 0),
10929                    primary: None,
10930                },
10931                ExcerptRange {
10932                    context: Point::new(9, 0)..Point::new(10, 4),
10933                    primary: None,
10934                },
10935            ],
10936            cx,
10937        );
10938        multibuffer.push_excerpts(
10939            buffer_3.clone(),
10940            [
10941                ExcerptRange {
10942                    context: Point::new(0, 0)..Point::new(3, 0),
10943                    primary: None,
10944                },
10945                ExcerptRange {
10946                    context: Point::new(5, 0)..Point::new(7, 0),
10947                    primary: None,
10948                },
10949                ExcerptRange {
10950                    context: Point::new(9, 0)..Point::new(10, 4),
10951                    primary: None,
10952                },
10953            ],
10954            cx,
10955        );
10956        multibuffer
10957    });
10958
10959    let fs = FakeFs::new(cx.executor());
10960    fs.insert_tree(
10961        "/a",
10962        json!({
10963            "main.rs": sample_text_1,
10964            "other.rs": sample_text_2,
10965            "lib.rs": sample_text_3,
10966        }),
10967    )
10968    .await;
10969    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10970    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10971    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10972    let multi_buffer_editor = cx.new_view(|cx| {
10973        Editor::new(
10974            EditorMode::Full,
10975            multi_buffer,
10976            Some(project.clone()),
10977            true,
10978            cx,
10979        )
10980    });
10981    let multibuffer_item_id = workspace
10982        .update(cx, |workspace, cx| {
10983            assert!(
10984                workspace.active_item(cx).is_none(),
10985                "active item should be None before the first item is added"
10986            );
10987            workspace.add_item_to_active_pane(
10988                Box::new(multi_buffer_editor.clone()),
10989                None,
10990                true,
10991                cx,
10992            );
10993            let active_item = workspace
10994                .active_item(cx)
10995                .expect("should have an active item after adding the multi buffer");
10996            assert!(
10997                !active_item.is_singleton(cx),
10998                "A multi buffer was expected to active after adding"
10999            );
11000            active_item.item_id()
11001        })
11002        .unwrap();
11003    cx.executor().run_until_parked();
11004
11005    multi_buffer_editor.update(cx, |editor, cx| {
11006        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11007        editor.open_excerpts(&OpenExcerpts, cx);
11008    });
11009    cx.executor().run_until_parked();
11010    let first_item_id = workspace
11011        .update(cx, |workspace, cx| {
11012            let active_item = workspace
11013                .active_item(cx)
11014                .expect("should have an active item after navigating into the 1st buffer");
11015            let first_item_id = active_item.item_id();
11016            assert_ne!(
11017                first_item_id, multibuffer_item_id,
11018                "Should navigate into the 1st buffer and activate it"
11019            );
11020            assert!(
11021                active_item.is_singleton(cx),
11022                "New active item should be a singleton buffer"
11023            );
11024            assert_eq!(
11025                active_item
11026                    .act_as::<Editor>(cx)
11027                    .expect("should have navigated into an editor for the 1st buffer")
11028                    .read(cx)
11029                    .text(cx),
11030                sample_text_1
11031            );
11032
11033            workspace
11034                .go_back(workspace.active_pane().downgrade(), cx)
11035                .detach_and_log_err(cx);
11036
11037            first_item_id
11038        })
11039        .unwrap();
11040    cx.executor().run_until_parked();
11041    workspace
11042        .update(cx, |workspace, cx| {
11043            let active_item = workspace
11044                .active_item(cx)
11045                .expect("should have an active item after navigating back");
11046            assert_eq!(
11047                active_item.item_id(),
11048                multibuffer_item_id,
11049                "Should navigate back to the multi buffer"
11050            );
11051            assert!(!active_item.is_singleton(cx));
11052        })
11053        .unwrap();
11054
11055    multi_buffer_editor.update(cx, |editor, cx| {
11056        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11057            s.select_ranges(Some(39..40))
11058        });
11059        editor.open_excerpts(&OpenExcerpts, cx);
11060    });
11061    cx.executor().run_until_parked();
11062    let second_item_id = workspace
11063        .update(cx, |workspace, cx| {
11064            let active_item = workspace
11065                .active_item(cx)
11066                .expect("should have an active item after navigating into the 2nd buffer");
11067            let second_item_id = active_item.item_id();
11068            assert_ne!(
11069                second_item_id, multibuffer_item_id,
11070                "Should navigate away from the multibuffer"
11071            );
11072            assert_ne!(
11073                second_item_id, first_item_id,
11074                "Should navigate into the 2nd buffer and activate it"
11075            );
11076            assert!(
11077                active_item.is_singleton(cx),
11078                "New active item should be a singleton buffer"
11079            );
11080            assert_eq!(
11081                active_item
11082                    .act_as::<Editor>(cx)
11083                    .expect("should have navigated into an editor")
11084                    .read(cx)
11085                    .text(cx),
11086                sample_text_2
11087            );
11088
11089            workspace
11090                .go_back(workspace.active_pane().downgrade(), cx)
11091                .detach_and_log_err(cx);
11092
11093            second_item_id
11094        })
11095        .unwrap();
11096    cx.executor().run_until_parked();
11097    workspace
11098        .update(cx, |workspace, cx| {
11099            let active_item = workspace
11100                .active_item(cx)
11101                .expect("should have an active item after navigating back from the 2nd buffer");
11102            assert_eq!(
11103                active_item.item_id(),
11104                multibuffer_item_id,
11105                "Should navigate back from the 2nd buffer to the multi buffer"
11106            );
11107            assert!(!active_item.is_singleton(cx));
11108        })
11109        .unwrap();
11110
11111    multi_buffer_editor.update(cx, |editor, cx| {
11112        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11113            s.select_ranges(Some(60..70))
11114        });
11115        editor.open_excerpts(&OpenExcerpts, cx);
11116    });
11117    cx.executor().run_until_parked();
11118    workspace
11119        .update(cx, |workspace, cx| {
11120            let active_item = workspace
11121                .active_item(cx)
11122                .expect("should have an active item after navigating into the 3rd buffer");
11123            let third_item_id = active_item.item_id();
11124            assert_ne!(
11125                third_item_id, multibuffer_item_id,
11126                "Should navigate into the 3rd buffer and activate it"
11127            );
11128            assert_ne!(third_item_id, first_item_id);
11129            assert_ne!(third_item_id, second_item_id);
11130            assert!(
11131                active_item.is_singleton(cx),
11132                "New active item should be a singleton buffer"
11133            );
11134            assert_eq!(
11135                active_item
11136                    .act_as::<Editor>(cx)
11137                    .expect("should have navigated into an editor")
11138                    .read(cx)
11139                    .text(cx),
11140                sample_text_3
11141            );
11142
11143            workspace
11144                .go_back(workspace.active_pane().downgrade(), cx)
11145                .detach_and_log_err(cx);
11146        })
11147        .unwrap();
11148    cx.executor().run_until_parked();
11149    workspace
11150        .update(cx, |workspace, cx| {
11151            let active_item = workspace
11152                .active_item(cx)
11153                .expect("should have an active item after navigating back from the 3rd buffer");
11154            assert_eq!(
11155                active_item.item_id(),
11156                multibuffer_item_id,
11157                "Should navigate back from the 3rd buffer to the multi buffer"
11158            );
11159            assert!(!active_item.is_singleton(cx));
11160        })
11161        .unwrap();
11162}
11163
11164#[gpui::test]
11165async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11166    init_test(cx, |_| {});
11167
11168    let mut cx = EditorTestContext::new(cx).await;
11169
11170    let diff_base = r#"
11171        use some::mod;
11172
11173        const A: u32 = 42;
11174
11175        fn main() {
11176            println!("hello");
11177
11178            println!("world");
11179        }
11180        "#
11181    .unindent();
11182
11183    cx.set_state(
11184        &r#"
11185        use some::modified;
11186
11187        ˇ
11188        fn main() {
11189            println!("hello there");
11190
11191            println!("around the");
11192            println!("world");
11193        }
11194        "#
11195        .unindent(),
11196    );
11197
11198    cx.set_diff_base(Some(&diff_base));
11199    executor.run_until_parked();
11200
11201    cx.update_editor(|editor, cx| {
11202        editor.go_to_next_hunk(&GoToHunk, cx);
11203        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11204    });
11205    executor.run_until_parked();
11206    cx.assert_diff_hunks(
11207        r#"
11208          use some::modified;
11209
11210
11211          fn main() {
11212        -     println!("hello");
11213        +     println!("hello there");
11214
11215              println!("around the");
11216              println!("world");
11217          }
11218        "#
11219        .unindent(),
11220    );
11221
11222    cx.update_editor(|editor, cx| {
11223        for _ in 0..3 {
11224            editor.go_to_next_hunk(&GoToHunk, cx);
11225            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11226        }
11227    });
11228    executor.run_until_parked();
11229    cx.assert_editor_state(
11230        &r#"
11231        use some::modified;
11232
11233        ˇ
11234        fn main() {
11235            println!("hello there");
11236
11237            println!("around the");
11238            println!("world");
11239        }
11240        "#
11241        .unindent(),
11242    );
11243
11244    cx.assert_diff_hunks(
11245        r#"
11246        - use some::mod;
11247        + use some::modified;
11248
11249        - const A: u32 = 42;
11250
11251          fn main() {
11252        -     println!("hello");
11253        +     println!("hello there");
11254
11255        +     println!("around the");
11256              println!("world");
11257          }
11258        "#
11259        .unindent(),
11260    );
11261
11262    cx.update_editor(|editor, cx| {
11263        editor.cancel(&Cancel, cx);
11264    });
11265
11266    cx.assert_diff_hunks(
11267        r#"
11268          use some::modified;
11269
11270
11271          fn main() {
11272              println!("hello there");
11273
11274              println!("around the");
11275              println!("world");
11276          }
11277        "#
11278        .unindent(),
11279    );
11280}
11281
11282#[gpui::test]
11283async fn test_diff_base_change_with_expanded_diff_hunks(
11284    executor: BackgroundExecutor,
11285    cx: &mut gpui::TestAppContext,
11286) {
11287    init_test(cx, |_| {});
11288
11289    let mut cx = EditorTestContext::new(cx).await;
11290
11291    let diff_base = r#"
11292        use some::mod1;
11293        use some::mod2;
11294
11295        const A: u32 = 42;
11296        const B: u32 = 42;
11297        const C: u32 = 42;
11298
11299        fn main() {
11300            println!("hello");
11301
11302            println!("world");
11303        }
11304        "#
11305    .unindent();
11306
11307    cx.set_state(
11308        &r#"
11309        use some::mod2;
11310
11311        const A: u32 = 42;
11312        const C: u32 = 42;
11313
11314        fn main(ˇ) {
11315            //println!("hello");
11316
11317            println!("world");
11318            //
11319            //
11320        }
11321        "#
11322        .unindent(),
11323    );
11324
11325    cx.set_diff_base(Some(&diff_base));
11326    executor.run_until_parked();
11327
11328    cx.update_editor(|editor, cx| {
11329        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11330    });
11331    executor.run_until_parked();
11332    cx.assert_diff_hunks(
11333        r#"
11334        - use some::mod1;
11335          use some::mod2;
11336
11337          const A: u32 = 42;
11338        - const B: u32 = 42;
11339          const C: u32 = 42;
11340
11341          fn main() {
11342        -     println!("hello");
11343        +     //println!("hello");
11344
11345              println!("world");
11346        +     //
11347        +     //
11348          }
11349        "#
11350        .unindent(),
11351    );
11352
11353    cx.set_diff_base(Some("new diff base!"));
11354    executor.run_until_parked();
11355    cx.assert_diff_hunks(
11356        r#"
11357          use some::mod2;
11358
11359          const A: u32 = 42;
11360          const C: u32 = 42;
11361
11362          fn main() {
11363              //println!("hello");
11364
11365              println!("world");
11366              //
11367              //
11368          }
11369        "#
11370        .unindent(),
11371    );
11372
11373    cx.update_editor(|editor, cx| {
11374        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11375    });
11376    executor.run_until_parked();
11377    cx.assert_diff_hunks(
11378        r#"
11379        - new diff base!
11380        + use some::mod2;
11381        +
11382        + const A: u32 = 42;
11383        + const C: u32 = 42;
11384        +
11385        + fn main() {
11386        +     //println!("hello");
11387        +
11388        +     println!("world");
11389        +     //
11390        +     //
11391        + }
11392        "#
11393        .unindent(),
11394    );
11395}
11396
11397#[gpui::test]
11398async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11399    init_test(cx, |_| {});
11400
11401    let mut cx = EditorTestContext::new(cx).await;
11402
11403    let diff_base = r#"
11404        use some::mod1;
11405        use some::mod2;
11406
11407        const A: u32 = 42;
11408        const B: u32 = 42;
11409        const C: u32 = 42;
11410
11411        fn main() {
11412            println!("hello");
11413
11414            println!("world");
11415        }
11416
11417        fn another() {
11418            println!("another");
11419        }
11420
11421        fn another2() {
11422            println!("another2");
11423        }
11424        "#
11425    .unindent();
11426
11427    cx.set_state(
11428        &r#"
11429        «use some::mod2;
11430
11431        const A: u32 = 42;
11432        const C: u32 = 42;
11433
11434        fn main() {
11435            //println!("hello");
11436
11437            println!("world");
11438            //
11439            //ˇ»
11440        }
11441
11442        fn another() {
11443            println!("another");
11444            println!("another");
11445        }
11446
11447            println!("another2");
11448        }
11449        "#
11450        .unindent(),
11451    );
11452
11453    cx.set_diff_base(Some(&diff_base));
11454    executor.run_until_parked();
11455
11456    cx.update_editor(|editor, cx| {
11457        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11458    });
11459    executor.run_until_parked();
11460
11461    cx.assert_diff_hunks(
11462        r#"
11463        - use some::mod1;
11464          use some::mod2;
11465
11466          const A: u32 = 42;
11467        - const B: u32 = 42;
11468          const C: u32 = 42;
11469
11470          fn main() {
11471        -     println!("hello");
11472        +     //println!("hello");
11473
11474              println!("world");
11475        +     //
11476        +     //
11477          }
11478
11479          fn another() {
11480              println!("another");
11481        +     println!("another");
11482          }
11483
11484        - fn another2() {
11485              println!("another2");
11486          }
11487        "#
11488        .unindent(),
11489    );
11490
11491    // Fold across some of the diff hunks. They should no longer appear expanded.
11492    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11493    cx.executor().run_until_parked();
11494
11495    // Hunks are not shown if their position is within a fold
11496    cx.assert_diff_hunks(
11497        r#"
11498          use some::mod2;
11499
11500          const A: u32 = 42;
11501          const C: u32 = 42;
11502
11503          fn main() {
11504              //println!("hello");
11505
11506              println!("world");
11507              //
11508              //
11509          }
11510
11511          fn another() {
11512              println!("another");
11513        +     println!("another");
11514          }
11515
11516        - fn another2() {
11517              println!("another2");
11518          }
11519        "#
11520        .unindent(),
11521    );
11522
11523    cx.update_editor(|editor, cx| {
11524        editor.select_all(&SelectAll, cx);
11525        editor.unfold_lines(&UnfoldLines, cx);
11526    });
11527    cx.executor().run_until_parked();
11528
11529    // The deletions reappear when unfolding.
11530    cx.assert_diff_hunks(
11531        r#"
11532        - use some::mod1;
11533          use some::mod2;
11534
11535          const A: u32 = 42;
11536        - const B: u32 = 42;
11537          const C: u32 = 42;
11538
11539          fn main() {
11540        -     println!("hello");
11541        +     //println!("hello");
11542
11543              println!("world");
11544        +     //
11545        +     //
11546          }
11547
11548          fn another() {
11549              println!("another");
11550        +     println!("another");
11551          }
11552
11553        - fn another2() {
11554              println!("another2");
11555          }
11556        "#
11557        .unindent(),
11558    );
11559}
11560
11561#[gpui::test]
11562async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11563    init_test(cx, |_| {});
11564
11565    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11566    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11567    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11568    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11569    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11570    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11571
11572    let buffer_1 = cx.new_model(|cx| {
11573        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11574        buffer.set_diff_base(Some(file_1_old.into()), cx);
11575        buffer
11576    });
11577    let buffer_2 = cx.new_model(|cx| {
11578        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11579        buffer.set_diff_base(Some(file_2_old.into()), cx);
11580        buffer
11581    });
11582    let buffer_3 = cx.new_model(|cx| {
11583        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11584        buffer.set_diff_base(Some(file_3_old.into()), cx);
11585        buffer
11586    });
11587
11588    let multi_buffer = cx.new_model(|cx| {
11589        let mut multibuffer = MultiBuffer::new(ReadWrite);
11590        multibuffer.push_excerpts(
11591            buffer_1.clone(),
11592            [
11593                ExcerptRange {
11594                    context: Point::new(0, 0)..Point::new(3, 0),
11595                    primary: None,
11596                },
11597                ExcerptRange {
11598                    context: Point::new(5, 0)..Point::new(7, 0),
11599                    primary: None,
11600                },
11601                ExcerptRange {
11602                    context: Point::new(9, 0)..Point::new(10, 3),
11603                    primary: None,
11604                },
11605            ],
11606            cx,
11607        );
11608        multibuffer.push_excerpts(
11609            buffer_2.clone(),
11610            [
11611                ExcerptRange {
11612                    context: Point::new(0, 0)..Point::new(3, 0),
11613                    primary: None,
11614                },
11615                ExcerptRange {
11616                    context: Point::new(5, 0)..Point::new(7, 0),
11617                    primary: None,
11618                },
11619                ExcerptRange {
11620                    context: Point::new(9, 0)..Point::new(10, 3),
11621                    primary: None,
11622                },
11623            ],
11624            cx,
11625        );
11626        multibuffer.push_excerpts(
11627            buffer_3.clone(),
11628            [
11629                ExcerptRange {
11630                    context: Point::new(0, 0)..Point::new(3, 0),
11631                    primary: None,
11632                },
11633                ExcerptRange {
11634                    context: Point::new(5, 0)..Point::new(7, 0),
11635                    primary: None,
11636                },
11637                ExcerptRange {
11638                    context: Point::new(9, 0)..Point::new(10, 3),
11639                    primary: None,
11640                },
11641            ],
11642            cx,
11643        );
11644        multibuffer
11645    });
11646
11647    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11648    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11649    cx.run_until_parked();
11650
11651    cx.assert_editor_state(
11652        &"
11653            ˇaaa
11654            ccc
11655            ddd
11656
11657            ggg
11658            hhh
11659
11660
11661            lll
11662            mmm
11663            NNN
11664
11665            qqq
11666            rrr
11667
11668            uuu
11669            111
11670            222
11671            333
11672
11673            666
11674            777
11675
11676            000
11677            !!!"
11678        .unindent(),
11679    );
11680
11681    cx.update_editor(|editor, cx| {
11682        editor.select_all(&SelectAll, cx);
11683        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11684    });
11685    cx.executor().run_until_parked();
11686
11687    cx.assert_diff_hunks(
11688        "
11689            aaa
11690          - bbb
11691            ccc
11692            ddd
11693
11694            ggg
11695            hhh
11696
11697
11698            lll
11699            mmm
11700          - nnn
11701          + NNN
11702
11703            qqq
11704            rrr
11705
11706            uuu
11707            111
11708            222
11709            333
11710
11711          + 666
11712            777
11713
11714            000
11715            !!!"
11716        .unindent(),
11717    );
11718}
11719
11720#[gpui::test]
11721async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
11722    init_test(cx, |_| {});
11723
11724    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
11725    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
11726
11727    let buffer = cx.new_model(|cx| {
11728        let mut buffer = Buffer::local(text.to_string(), cx);
11729        buffer.set_diff_base(Some(base.into()), cx);
11730        buffer
11731    });
11732
11733    let multi_buffer = cx.new_model(|cx| {
11734        let mut multibuffer = MultiBuffer::new(ReadWrite);
11735        multibuffer.push_excerpts(
11736            buffer.clone(),
11737            [
11738                ExcerptRange {
11739                    context: Point::new(0, 0)..Point::new(2, 0),
11740                    primary: None,
11741                },
11742                ExcerptRange {
11743                    context: Point::new(5, 0)..Point::new(7, 0),
11744                    primary: None,
11745                },
11746            ],
11747            cx,
11748        );
11749        multibuffer
11750    });
11751
11752    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11753    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11754    cx.run_until_parked();
11755
11756    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
11757    cx.executor().run_until_parked();
11758
11759    cx.assert_diff_hunks(
11760        "
11761            aaa
11762          - bbb
11763          + BBB
11764
11765          - ddd
11766          - eee
11767          + EEE
11768            fff
11769        "
11770        .unindent(),
11771    );
11772}
11773
11774#[gpui::test]
11775async fn test_edits_around_expanded_insertion_hunks(
11776    executor: BackgroundExecutor,
11777    cx: &mut gpui::TestAppContext,
11778) {
11779    init_test(cx, |_| {});
11780
11781    let mut cx = EditorTestContext::new(cx).await;
11782
11783    let diff_base = r#"
11784        use some::mod1;
11785        use some::mod2;
11786
11787        const A: u32 = 42;
11788
11789        fn main() {
11790            println!("hello");
11791
11792            println!("world");
11793        }
11794        "#
11795    .unindent();
11796    executor.run_until_parked();
11797    cx.set_state(
11798        &r#"
11799        use some::mod1;
11800        use some::mod2;
11801
11802        const A: u32 = 42;
11803        const B: u32 = 42;
11804        const C: u32 = 42;
11805        ˇ
11806
11807        fn main() {
11808            println!("hello");
11809
11810            println!("world");
11811        }
11812        "#
11813        .unindent(),
11814    );
11815
11816    cx.set_diff_base(Some(&diff_base));
11817    executor.run_until_parked();
11818
11819    cx.update_editor(|editor, cx| {
11820        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11821    });
11822    executor.run_until_parked();
11823
11824    cx.assert_diff_hunks(
11825        r#"
11826        use some::mod1;
11827        use some::mod2;
11828
11829        const A: u32 = 42;
11830      + const B: u32 = 42;
11831      + const C: u32 = 42;
11832      +
11833
11834        fn main() {
11835            println!("hello");
11836
11837            println!("world");
11838        }
11839        "#
11840        .unindent(),
11841    );
11842
11843    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11844    executor.run_until_parked();
11845
11846    cx.assert_diff_hunks(
11847        r#"
11848        use some::mod1;
11849        use some::mod2;
11850
11851        const A: u32 = 42;
11852      + const B: u32 = 42;
11853      + const C: u32 = 42;
11854      + const D: u32 = 42;
11855      +
11856
11857        fn main() {
11858            println!("hello");
11859
11860            println!("world");
11861        }
11862        "#
11863        .unindent(),
11864    );
11865
11866    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11867    executor.run_until_parked();
11868
11869    cx.assert_diff_hunks(
11870        r#"
11871        use some::mod1;
11872        use some::mod2;
11873
11874        const A: u32 = 42;
11875      + const B: u32 = 42;
11876      + const C: u32 = 42;
11877      + const D: u32 = 42;
11878      + const E: u32 = 42;
11879      +
11880
11881        fn main() {
11882            println!("hello");
11883
11884            println!("world");
11885        }
11886        "#
11887        .unindent(),
11888    );
11889
11890    cx.update_editor(|editor, cx| {
11891        editor.delete_line(&DeleteLine, cx);
11892    });
11893    executor.run_until_parked();
11894
11895    cx.assert_diff_hunks(
11896        r#"
11897        use some::mod1;
11898        use some::mod2;
11899
11900        const A: u32 = 42;
11901      + const B: u32 = 42;
11902      + const C: u32 = 42;
11903      + const D: u32 = 42;
11904      + const E: u32 = 42;
11905
11906        fn main() {
11907            println!("hello");
11908
11909            println!("world");
11910        }
11911        "#
11912        .unindent(),
11913    );
11914
11915    cx.update_editor(|editor, cx| {
11916        editor.move_up(&MoveUp, cx);
11917        editor.delete_line(&DeleteLine, cx);
11918        editor.move_up(&MoveUp, cx);
11919        editor.delete_line(&DeleteLine, cx);
11920        editor.move_up(&MoveUp, cx);
11921        editor.delete_line(&DeleteLine, cx);
11922    });
11923    executor.run_until_parked();
11924    cx.assert_editor_state(
11925        &r#"
11926        use some::mod1;
11927        use some::mod2;
11928
11929        const A: u32 = 42;
11930        const B: u32 = 42;
11931        ˇ
11932        fn main() {
11933            println!("hello");
11934
11935            println!("world");
11936        }
11937        "#
11938        .unindent(),
11939    );
11940
11941    cx.assert_diff_hunks(
11942        r#"
11943        use some::mod1;
11944        use some::mod2;
11945
11946        const A: u32 = 42;
11947      + const B: u32 = 42;
11948
11949        fn main() {
11950            println!("hello");
11951
11952            println!("world");
11953        }
11954        "#
11955        .unindent(),
11956    );
11957
11958    cx.update_editor(|editor, cx| {
11959        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11960        editor.delete_line(&DeleteLine, cx);
11961    });
11962    executor.run_until_parked();
11963    cx.assert_diff_hunks(
11964        r#"
11965        use some::mod1;
11966      - use some::mod2;
11967      -
11968      - const A: u32 = 42;
11969
11970        fn main() {
11971            println!("hello");
11972
11973            println!("world");
11974        }
11975        "#
11976        .unindent(),
11977    );
11978}
11979
11980#[gpui::test]
11981async fn test_edits_around_expanded_deletion_hunks(
11982    executor: BackgroundExecutor,
11983    cx: &mut gpui::TestAppContext,
11984) {
11985    init_test(cx, |_| {});
11986
11987    let mut cx = EditorTestContext::new(cx).await;
11988
11989    let diff_base = r#"
11990        use some::mod1;
11991        use some::mod2;
11992
11993        const A: u32 = 42;
11994        const B: u32 = 42;
11995        const C: u32 = 42;
11996
11997
11998        fn main() {
11999            println!("hello");
12000
12001            println!("world");
12002        }
12003    "#
12004    .unindent();
12005    executor.run_until_parked();
12006    cx.set_state(
12007        &r#"
12008        use some::mod1;
12009        use some::mod2;
12010
12011        ˇconst B: u32 = 42;
12012        const C: u32 = 42;
12013
12014
12015        fn main() {
12016            println!("hello");
12017
12018            println!("world");
12019        }
12020        "#
12021        .unindent(),
12022    );
12023
12024    cx.set_diff_base(Some(&diff_base));
12025    executor.run_until_parked();
12026
12027    cx.update_editor(|editor, cx| {
12028        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12029    });
12030    executor.run_until_parked();
12031
12032    cx.assert_diff_hunks(
12033        r#"
12034        use some::mod1;
12035        use some::mod2;
12036
12037      - const A: u32 = 42;
12038        const B: u32 = 42;
12039        const C: u32 = 42;
12040
12041
12042        fn main() {
12043            println!("hello");
12044
12045            println!("world");
12046        }
12047        "#
12048        .unindent(),
12049    );
12050
12051    cx.update_editor(|editor, cx| {
12052        editor.delete_line(&DeleteLine, cx);
12053    });
12054    executor.run_until_parked();
12055    cx.assert_editor_state(
12056        &r#"
12057        use some::mod1;
12058        use some::mod2;
12059
12060        ˇconst C: u32 = 42;
12061
12062
12063        fn main() {
12064            println!("hello");
12065
12066            println!("world");
12067        }
12068        "#
12069        .unindent(),
12070    );
12071    cx.assert_diff_hunks(
12072        r#"
12073        use some::mod1;
12074        use some::mod2;
12075
12076      - const A: u32 = 42;
12077      - const B: u32 = 42;
12078        const C: u32 = 42;
12079
12080
12081        fn main() {
12082            println!("hello");
12083
12084            println!("world");
12085        }
12086        "#
12087        .unindent(),
12088    );
12089
12090    cx.update_editor(|editor, cx| {
12091        editor.delete_line(&DeleteLine, cx);
12092    });
12093    executor.run_until_parked();
12094    cx.assert_editor_state(
12095        &r#"
12096        use some::mod1;
12097        use some::mod2;
12098
12099        ˇ
12100
12101        fn main() {
12102            println!("hello");
12103
12104            println!("world");
12105        }
12106        "#
12107        .unindent(),
12108    );
12109    cx.assert_diff_hunks(
12110        r#"
12111        use some::mod1;
12112        use some::mod2;
12113
12114      - const A: u32 = 42;
12115      - const B: u32 = 42;
12116      - const C: u32 = 42;
12117
12118
12119        fn main() {
12120            println!("hello");
12121
12122            println!("world");
12123        }
12124        "#
12125        .unindent(),
12126    );
12127
12128    cx.update_editor(|editor, cx| {
12129        editor.handle_input("replacement", cx);
12130    });
12131    executor.run_until_parked();
12132    cx.assert_editor_state(
12133        &r#"
12134        use some::mod1;
12135        use some::mod2;
12136
12137        replacementˇ
12138
12139        fn main() {
12140            println!("hello");
12141
12142            println!("world");
12143        }
12144        "#
12145        .unindent(),
12146    );
12147    cx.assert_diff_hunks(
12148        r#"
12149        use some::mod1;
12150        use some::mod2;
12151
12152      - const A: u32 = 42;
12153      - const B: u32 = 42;
12154      - const C: u32 = 42;
12155      -
12156      + replacement
12157
12158        fn main() {
12159            println!("hello");
12160
12161            println!("world");
12162        }
12163        "#
12164        .unindent(),
12165    );
12166}
12167
12168#[gpui::test]
12169async fn test_edit_after_expanded_modification_hunk(
12170    executor: BackgroundExecutor,
12171    cx: &mut gpui::TestAppContext,
12172) {
12173    init_test(cx, |_| {});
12174
12175    let mut cx = EditorTestContext::new(cx).await;
12176
12177    let diff_base = r#"
12178        use some::mod1;
12179        use some::mod2;
12180
12181        const A: u32 = 42;
12182        const B: u32 = 42;
12183        const C: u32 = 42;
12184        const D: u32 = 42;
12185
12186
12187        fn main() {
12188            println!("hello");
12189
12190            println!("world");
12191        }"#
12192    .unindent();
12193
12194    cx.set_state(
12195        &r#"
12196        use some::mod1;
12197        use some::mod2;
12198
12199        const A: u32 = 42;
12200        const B: u32 = 42;
12201        const C: u32 = 43ˇ
12202        const D: u32 = 42;
12203
12204
12205        fn main() {
12206            println!("hello");
12207
12208            println!("world");
12209        }"#
12210        .unindent(),
12211    );
12212
12213    cx.set_diff_base(Some(&diff_base));
12214    executor.run_until_parked();
12215    cx.update_editor(|editor, cx| {
12216        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12217    });
12218    executor.run_until_parked();
12219
12220    cx.assert_diff_hunks(
12221        r#"
12222        use some::mod1;
12223        use some::mod2;
12224
12225        const A: u32 = 42;
12226        const B: u32 = 42;
12227      - const C: u32 = 42;
12228      + const C: u32 = 43
12229        const D: u32 = 42;
12230
12231
12232        fn main() {
12233            println!("hello");
12234
12235            println!("world");
12236        }"#
12237        .unindent(),
12238    );
12239
12240    cx.update_editor(|editor, cx| {
12241        editor.handle_input("\nnew_line\n", cx);
12242    });
12243    executor.run_until_parked();
12244
12245    cx.assert_diff_hunks(
12246        r#"
12247        use some::mod1;
12248        use some::mod2;
12249
12250        const A: u32 = 42;
12251        const B: u32 = 42;
12252      - const C: u32 = 42;
12253      + const C: u32 = 43
12254      + new_line
12255      +
12256        const D: u32 = 42;
12257
12258
12259        fn main() {
12260            println!("hello");
12261
12262            println!("world");
12263        }"#
12264        .unindent(),
12265    );
12266}
12267
12268async fn setup_indent_guides_editor(
12269    text: &str,
12270    cx: &mut gpui::TestAppContext,
12271) -> (BufferId, EditorTestContext) {
12272    init_test(cx, |_| {});
12273
12274    let mut cx = EditorTestContext::new(cx).await;
12275
12276    let buffer_id = cx.update_editor(|editor, cx| {
12277        editor.set_text(text, cx);
12278        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12279
12280        buffer_ids[0]
12281    });
12282
12283    (buffer_id, cx)
12284}
12285
12286fn assert_indent_guides(
12287    range: Range<u32>,
12288    expected: Vec<IndentGuide>,
12289    active_indices: Option<Vec<usize>>,
12290    cx: &mut EditorTestContext,
12291) {
12292    let indent_guides = cx.update_editor(|editor, cx| {
12293        let snapshot = editor.snapshot(cx).display_snapshot;
12294        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12295            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12296            true,
12297            &snapshot,
12298            cx,
12299        );
12300
12301        indent_guides.sort_by(|a, b| {
12302            a.depth.cmp(&b.depth).then(
12303                a.start_row
12304                    .cmp(&b.start_row)
12305                    .then(a.end_row.cmp(&b.end_row)),
12306            )
12307        });
12308        indent_guides
12309    });
12310
12311    if let Some(expected) = active_indices {
12312        let active_indices = cx.update_editor(|editor, cx| {
12313            let snapshot = editor.snapshot(cx).display_snapshot;
12314            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12315        });
12316
12317        assert_eq!(
12318            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12319            expected,
12320            "Active indent guide indices do not match"
12321        );
12322    }
12323
12324    let expected: Vec<_> = expected
12325        .into_iter()
12326        .map(|guide| MultiBufferIndentGuide {
12327            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12328            buffer: guide,
12329        })
12330        .collect();
12331
12332    assert_eq!(indent_guides, expected, "Indent guides do not match");
12333}
12334
12335fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12336    IndentGuide {
12337        buffer_id,
12338        start_row,
12339        end_row,
12340        depth,
12341        tab_size: 4,
12342        settings: IndentGuideSettings {
12343            enabled: true,
12344            line_width: 1,
12345            active_line_width: 1,
12346            ..Default::default()
12347        },
12348    }
12349}
12350
12351#[gpui::test]
12352async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12353    let (buffer_id, mut cx) = setup_indent_guides_editor(
12354        &"
12355    fn main() {
12356        let a = 1;
12357    }"
12358        .unindent(),
12359        cx,
12360    )
12361    .await;
12362
12363    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12364}
12365
12366#[gpui::test]
12367async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12368    let (buffer_id, mut cx) = setup_indent_guides_editor(
12369        &"
12370    fn main() {
12371        let a = 1;
12372        let b = 2;
12373    }"
12374        .unindent(),
12375        cx,
12376    )
12377    .await;
12378
12379    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12380}
12381
12382#[gpui::test]
12383async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12384    let (buffer_id, mut cx) = setup_indent_guides_editor(
12385        &"
12386    fn main() {
12387        let a = 1;
12388        if a == 3 {
12389            let b = 2;
12390        } else {
12391            let c = 3;
12392        }
12393    }"
12394        .unindent(),
12395        cx,
12396    )
12397    .await;
12398
12399    assert_indent_guides(
12400        0..8,
12401        vec![
12402            indent_guide(buffer_id, 1, 6, 0),
12403            indent_guide(buffer_id, 3, 3, 1),
12404            indent_guide(buffer_id, 5, 5, 1),
12405        ],
12406        None,
12407        &mut cx,
12408    );
12409}
12410
12411#[gpui::test]
12412async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12413    let (buffer_id, mut cx) = setup_indent_guides_editor(
12414        &"
12415    fn main() {
12416        let a = 1;
12417            let b = 2;
12418        let c = 3;
12419    }"
12420        .unindent(),
12421        cx,
12422    )
12423    .await;
12424
12425    assert_indent_guides(
12426        0..5,
12427        vec![
12428            indent_guide(buffer_id, 1, 3, 0),
12429            indent_guide(buffer_id, 2, 2, 1),
12430        ],
12431        None,
12432        &mut cx,
12433    );
12434}
12435
12436#[gpui::test]
12437async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12438    let (buffer_id, mut cx) = setup_indent_guides_editor(
12439        &"
12440        fn main() {
12441            let a = 1;
12442
12443            let c = 3;
12444        }"
12445        .unindent(),
12446        cx,
12447    )
12448    .await;
12449
12450    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12451}
12452
12453#[gpui::test]
12454async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12455    let (buffer_id, mut cx) = setup_indent_guides_editor(
12456        &"
12457        fn main() {
12458            let a = 1;
12459
12460            let c = 3;
12461
12462            if a == 3 {
12463                let b = 2;
12464            } else {
12465                let c = 3;
12466            }
12467        }"
12468        .unindent(),
12469        cx,
12470    )
12471    .await;
12472
12473    assert_indent_guides(
12474        0..11,
12475        vec![
12476            indent_guide(buffer_id, 1, 9, 0),
12477            indent_guide(buffer_id, 6, 6, 1),
12478            indent_guide(buffer_id, 8, 8, 1),
12479        ],
12480        None,
12481        &mut cx,
12482    );
12483}
12484
12485#[gpui::test]
12486async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12487    let (buffer_id, mut cx) = setup_indent_guides_editor(
12488        &"
12489        fn main() {
12490            let a = 1;
12491
12492            let c = 3;
12493
12494            if a == 3 {
12495                let b = 2;
12496            } else {
12497                let c = 3;
12498            }
12499        }"
12500        .unindent(),
12501        cx,
12502    )
12503    .await;
12504
12505    assert_indent_guides(
12506        1..11,
12507        vec![
12508            indent_guide(buffer_id, 1, 9, 0),
12509            indent_guide(buffer_id, 6, 6, 1),
12510            indent_guide(buffer_id, 8, 8, 1),
12511        ],
12512        None,
12513        &mut cx,
12514    );
12515}
12516
12517#[gpui::test]
12518async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12519    let (buffer_id, mut cx) = setup_indent_guides_editor(
12520        &"
12521        fn main() {
12522            let a = 1;
12523
12524            let c = 3;
12525
12526            if a == 3 {
12527                let b = 2;
12528            } else {
12529                let c = 3;
12530            }
12531        }"
12532        .unindent(),
12533        cx,
12534    )
12535    .await;
12536
12537    assert_indent_guides(
12538        1..10,
12539        vec![
12540            indent_guide(buffer_id, 1, 9, 0),
12541            indent_guide(buffer_id, 6, 6, 1),
12542            indent_guide(buffer_id, 8, 8, 1),
12543        ],
12544        None,
12545        &mut cx,
12546    );
12547}
12548
12549#[gpui::test]
12550async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12551    let (buffer_id, mut cx) = setup_indent_guides_editor(
12552        &"
12553        block1
12554            block2
12555                block3
12556                    block4
12557            block2
12558        block1
12559        block1"
12560            .unindent(),
12561        cx,
12562    )
12563    .await;
12564
12565    assert_indent_guides(
12566        1..10,
12567        vec![
12568            indent_guide(buffer_id, 1, 4, 0),
12569            indent_guide(buffer_id, 2, 3, 1),
12570            indent_guide(buffer_id, 3, 3, 2),
12571        ],
12572        None,
12573        &mut cx,
12574    );
12575}
12576
12577#[gpui::test]
12578async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12579    let (buffer_id, mut cx) = setup_indent_guides_editor(
12580        &"
12581        block1
12582            block2
12583                block3
12584
12585        block1
12586        block1"
12587            .unindent(),
12588        cx,
12589    )
12590    .await;
12591
12592    assert_indent_guides(
12593        0..6,
12594        vec![
12595            indent_guide(buffer_id, 1, 2, 0),
12596            indent_guide(buffer_id, 2, 2, 1),
12597        ],
12598        None,
12599        &mut cx,
12600    );
12601}
12602
12603#[gpui::test]
12604async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12605    let (buffer_id, mut cx) = setup_indent_guides_editor(
12606        &"
12607        block1
12608
12609
12610
12611            block2
12612        "
12613        .unindent(),
12614        cx,
12615    )
12616    .await;
12617
12618    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12619}
12620
12621#[gpui::test]
12622async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12623    let (buffer_id, mut cx) = setup_indent_guides_editor(
12624        &"
12625        def a:
12626        \tb = 3
12627        \tif True:
12628        \t\tc = 4
12629        \t\td = 5
12630        \tprint(b)
12631        "
12632        .unindent(),
12633        cx,
12634    )
12635    .await;
12636
12637    assert_indent_guides(
12638        0..6,
12639        vec![
12640            indent_guide(buffer_id, 1, 6, 0),
12641            indent_guide(buffer_id, 3, 4, 1),
12642        ],
12643        None,
12644        &mut cx,
12645    );
12646}
12647
12648#[gpui::test]
12649async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12650    let (buffer_id, mut cx) = setup_indent_guides_editor(
12651        &"
12652    fn main() {
12653        let a = 1;
12654    }"
12655        .unindent(),
12656        cx,
12657    )
12658    .await;
12659
12660    cx.update_editor(|editor, cx| {
12661        editor.change_selections(None, cx, |s| {
12662            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12663        });
12664    });
12665
12666    assert_indent_guides(
12667        0..3,
12668        vec![indent_guide(buffer_id, 1, 1, 0)],
12669        Some(vec![0]),
12670        &mut cx,
12671    );
12672}
12673
12674#[gpui::test]
12675async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12676    let (buffer_id, mut cx) = setup_indent_guides_editor(
12677        &"
12678    fn main() {
12679        if 1 == 2 {
12680            let a = 1;
12681        }
12682    }"
12683        .unindent(),
12684        cx,
12685    )
12686    .await;
12687
12688    cx.update_editor(|editor, cx| {
12689        editor.change_selections(None, cx, |s| {
12690            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12691        });
12692    });
12693
12694    assert_indent_guides(
12695        0..4,
12696        vec![
12697            indent_guide(buffer_id, 1, 3, 0),
12698            indent_guide(buffer_id, 2, 2, 1),
12699        ],
12700        Some(vec![1]),
12701        &mut cx,
12702    );
12703
12704    cx.update_editor(|editor, cx| {
12705        editor.change_selections(None, cx, |s| {
12706            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12707        });
12708    });
12709
12710    assert_indent_guides(
12711        0..4,
12712        vec![
12713            indent_guide(buffer_id, 1, 3, 0),
12714            indent_guide(buffer_id, 2, 2, 1),
12715        ],
12716        Some(vec![1]),
12717        &mut cx,
12718    );
12719
12720    cx.update_editor(|editor, cx| {
12721        editor.change_selections(None, cx, |s| {
12722            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12723        });
12724    });
12725
12726    assert_indent_guides(
12727        0..4,
12728        vec![
12729            indent_guide(buffer_id, 1, 3, 0),
12730            indent_guide(buffer_id, 2, 2, 1),
12731        ],
12732        Some(vec![0]),
12733        &mut cx,
12734    );
12735}
12736
12737#[gpui::test]
12738async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12739    let (buffer_id, mut cx) = setup_indent_guides_editor(
12740        &"
12741    fn main() {
12742        let a = 1;
12743
12744        let b = 2;
12745    }"
12746        .unindent(),
12747        cx,
12748    )
12749    .await;
12750
12751    cx.update_editor(|editor, cx| {
12752        editor.change_selections(None, cx, |s| {
12753            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12754        });
12755    });
12756
12757    assert_indent_guides(
12758        0..5,
12759        vec![indent_guide(buffer_id, 1, 3, 0)],
12760        Some(vec![0]),
12761        &mut cx,
12762    );
12763}
12764
12765#[gpui::test]
12766async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12767    let (buffer_id, mut cx) = setup_indent_guides_editor(
12768        &"
12769    def m:
12770        a = 1
12771        pass"
12772            .unindent(),
12773        cx,
12774    )
12775    .await;
12776
12777    cx.update_editor(|editor, cx| {
12778        editor.change_selections(None, cx, |s| {
12779            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12780        });
12781    });
12782
12783    assert_indent_guides(
12784        0..3,
12785        vec![indent_guide(buffer_id, 1, 2, 0)],
12786        Some(vec![0]),
12787        &mut cx,
12788    );
12789}
12790
12791#[gpui::test]
12792fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12793    init_test(cx, |_| {});
12794
12795    let editor = cx.add_window(|cx| {
12796        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12797        build_editor(buffer, cx)
12798    });
12799
12800    let render_args = Arc::new(Mutex::new(None));
12801    let snapshot = editor
12802        .update(cx, |editor, cx| {
12803            let snapshot = editor.buffer().read(cx).snapshot(cx);
12804            let range =
12805                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12806
12807            struct RenderArgs {
12808                row: MultiBufferRow,
12809                folded: bool,
12810                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12811            }
12812
12813            let crease = Crease::new(
12814                range,
12815                FoldPlaceholder::test(),
12816                {
12817                    let toggle_callback = render_args.clone();
12818                    move |row, folded, callback, _cx| {
12819                        *toggle_callback.lock() = Some(RenderArgs {
12820                            row,
12821                            folded,
12822                            callback,
12823                        });
12824                        div()
12825                    }
12826                },
12827                |_row, _folded, _cx| div(),
12828            );
12829
12830            editor.insert_creases(Some(crease), cx);
12831            let snapshot = editor.snapshot(cx);
12832            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12833            snapshot
12834        })
12835        .unwrap();
12836
12837    let render_args = render_args.lock().take().unwrap();
12838    assert_eq!(render_args.row, MultiBufferRow(1));
12839    assert!(!render_args.folded);
12840    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12841
12842    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12843        .unwrap();
12844    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12845    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12846
12847    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12848        .unwrap();
12849    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12850    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12851}
12852
12853#[gpui::test]
12854async fn test_input_text(cx: &mut gpui::TestAppContext) {
12855    init_test(cx, |_| {});
12856    let mut cx = EditorTestContext::new(cx).await;
12857
12858    cx.set_state(
12859        &r#"ˇone
12860        two
12861
12862        three
12863        fourˇ
12864        five
12865
12866        siˇx"#
12867            .unindent(),
12868    );
12869
12870    cx.dispatch_action(HandleInput(String::new()));
12871    cx.assert_editor_state(
12872        &r#"ˇone
12873        two
12874
12875        three
12876        fourˇ
12877        five
12878
12879        siˇx"#
12880            .unindent(),
12881    );
12882
12883    cx.dispatch_action(HandleInput("AAAA".to_string()));
12884    cx.assert_editor_state(
12885        &r#"AAAAˇone
12886        two
12887
12888        three
12889        fourAAAAˇ
12890        five
12891
12892        siAAAAˇx"#
12893            .unindent(),
12894    );
12895}
12896
12897#[gpui::test]
12898async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
12899    init_test(cx, |_| {});
12900
12901    let mut cx = EditorTestContext::new(cx).await;
12902    cx.set_state(
12903        r#"let foo = 1;
12904let foo = 2;
12905let foo = 3;
12906let fooˇ = 4;
12907let foo = 5;
12908let foo = 6;
12909let foo = 7;
12910let foo = 8;
12911let foo = 9;
12912let foo = 10;
12913let foo = 11;
12914let foo = 12;
12915let foo = 13;
12916let foo = 14;
12917let foo = 15;"#,
12918    );
12919
12920    cx.update_editor(|e, cx| {
12921        assert_eq!(
12922            e.next_scroll_position,
12923            NextScrollCursorCenterTopBottom::Center,
12924            "Default next scroll direction is center",
12925        );
12926
12927        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12928        assert_eq!(
12929            e.next_scroll_position,
12930            NextScrollCursorCenterTopBottom::Top,
12931            "After center, next scroll direction should be top",
12932        );
12933
12934        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12935        assert_eq!(
12936            e.next_scroll_position,
12937            NextScrollCursorCenterTopBottom::Bottom,
12938            "After top, next scroll direction should be bottom",
12939        );
12940
12941        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12942        assert_eq!(
12943            e.next_scroll_position,
12944            NextScrollCursorCenterTopBottom::Center,
12945            "After bottom, scrolling should start over",
12946        );
12947
12948        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12949        assert_eq!(
12950            e.next_scroll_position,
12951            NextScrollCursorCenterTopBottom::Top,
12952            "Scrolling continues if retriggered fast enough"
12953        );
12954    });
12955
12956    cx.executor()
12957        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
12958    cx.executor().run_until_parked();
12959    cx.update_editor(|e, _| {
12960        assert_eq!(
12961            e.next_scroll_position,
12962            NextScrollCursorCenterTopBottom::Center,
12963            "If scrolling is not triggered fast enough, it should reset"
12964        );
12965    });
12966}
12967
12968#[gpui::test]
12969async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
12970    init_test(cx, |_| {});
12971    let mut cx = EditorLspTestContext::new_rust(
12972        lsp::ServerCapabilities {
12973            definition_provider: Some(lsp::OneOf::Left(true)),
12974            references_provider: Some(lsp::OneOf::Left(true)),
12975            ..lsp::ServerCapabilities::default()
12976        },
12977        cx,
12978    )
12979    .await;
12980
12981    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
12982        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
12983            move |params, _| async move {
12984                if empty_go_to_definition {
12985                    Ok(None)
12986                } else {
12987                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
12988                        uri: params.text_document_position_params.text_document.uri,
12989                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
12990                    })))
12991                }
12992            },
12993        );
12994        let references =
12995            cx.lsp
12996                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
12997                    Ok(Some(vec![lsp::Location {
12998                        uri: params.text_document_position.text_document.uri,
12999                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13000                    }]))
13001                });
13002        (go_to_definition, references)
13003    };
13004
13005    cx.set_state(
13006        &r#"fn one() {
13007            let mut a = ˇtwo();
13008        }
13009
13010        fn two() {}"#
13011            .unindent(),
13012    );
13013    set_up_lsp_handlers(false, &mut cx);
13014    let navigated = cx
13015        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13016        .await
13017        .expect("Failed to navigate to definition");
13018    assert_eq!(
13019        navigated,
13020        Navigated::Yes,
13021        "Should have navigated to definition from the GetDefinition response"
13022    );
13023    cx.assert_editor_state(
13024        &r#"fn one() {
13025            let mut a = two();
13026        }
13027
13028        fn «twoˇ»() {}"#
13029            .unindent(),
13030    );
13031
13032    let editors = cx.update_workspace(|workspace, cx| {
13033        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13034    });
13035    cx.update_editor(|_, test_editor_cx| {
13036        assert_eq!(
13037            editors.len(),
13038            1,
13039            "Initially, only one, test, editor should be open in the workspace"
13040        );
13041        assert_eq!(
13042            test_editor_cx.view(),
13043            editors.last().expect("Asserted len is 1")
13044        );
13045    });
13046
13047    set_up_lsp_handlers(true, &mut cx);
13048    let navigated = cx
13049        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13050        .await
13051        .expect("Failed to navigate to lookup references");
13052    assert_eq!(
13053        navigated,
13054        Navigated::Yes,
13055        "Should have navigated to references as a fallback after empty GoToDefinition response"
13056    );
13057    // We should not change the selections in the existing file,
13058    // if opening another milti buffer with the references
13059    cx.assert_editor_state(
13060        &r#"fn one() {
13061            let mut a = two();
13062        }
13063
13064        fn «twoˇ»() {}"#
13065            .unindent(),
13066    );
13067    let editors = cx.update_workspace(|workspace, cx| {
13068        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13069    });
13070    cx.update_editor(|_, test_editor_cx| {
13071        assert_eq!(
13072            editors.len(),
13073            2,
13074            "After falling back to references search, we open a new editor with the results"
13075        );
13076        let references_fallback_text = editors
13077            .into_iter()
13078            .find(|new_editor| new_editor != test_editor_cx.view())
13079            .expect("Should have one non-test editor now")
13080            .read(test_editor_cx)
13081            .text(test_editor_cx);
13082        assert_eq!(
13083            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13084            "Should use the range from the references response and not the GoToDefinition one"
13085        );
13086    });
13087}
13088
13089fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13090    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13091    point..point
13092}
13093
13094fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13095    let (text, ranges) = marked_text_ranges(marked_text, true);
13096    assert_eq!(view.text(cx), text);
13097    assert_eq!(
13098        view.selections.ranges(cx),
13099        ranges,
13100        "Assert selections are {}",
13101        marked_text
13102    );
13103}
13104
13105pub fn handle_signature_help_request(
13106    cx: &mut EditorLspTestContext,
13107    mocked_response: lsp::SignatureHelp,
13108) -> impl Future<Output = ()> {
13109    let mut request =
13110        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13111            let mocked_response = mocked_response.clone();
13112            async move { Ok(Some(mocked_response)) }
13113        });
13114
13115    async move {
13116        request.next().await;
13117    }
13118}
13119
13120/// Handle completion request passing a marked string specifying where the completion
13121/// should be triggered from using '|' character, what range should be replaced, and what completions
13122/// should be returned using '<' and '>' to delimit the range
13123pub fn handle_completion_request(
13124    cx: &mut EditorLspTestContext,
13125    marked_string: &str,
13126    completions: Vec<&'static str>,
13127    counter: Arc<AtomicUsize>,
13128) -> impl Future<Output = ()> {
13129    let complete_from_marker: TextRangeMarker = '|'.into();
13130    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13131    let (_, mut marked_ranges) = marked_text_ranges_by(
13132        marked_string,
13133        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13134    );
13135
13136    let complete_from_position =
13137        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13138    let replace_range =
13139        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13140
13141    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13142        let completions = completions.clone();
13143        counter.fetch_add(1, atomic::Ordering::Release);
13144        async move {
13145            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13146            assert_eq!(
13147                params.text_document_position.position,
13148                complete_from_position
13149            );
13150            Ok(Some(lsp::CompletionResponse::Array(
13151                completions
13152                    .iter()
13153                    .map(|completion_text| lsp::CompletionItem {
13154                        label: completion_text.to_string(),
13155                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13156                            range: replace_range,
13157                            new_text: completion_text.to_string(),
13158                        })),
13159                        ..Default::default()
13160                    })
13161                    .collect(),
13162            )))
13163        }
13164    });
13165
13166    async move {
13167        request.next().await;
13168    }
13169}
13170
13171fn handle_resolve_completion_request(
13172    cx: &mut EditorLspTestContext,
13173    edits: Option<Vec<(&'static str, &'static str)>>,
13174) -> impl Future<Output = ()> {
13175    let edits = edits.map(|edits| {
13176        edits
13177            .iter()
13178            .map(|(marked_string, new_text)| {
13179                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13180                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13181                lsp::TextEdit::new(replace_range, new_text.to_string())
13182            })
13183            .collect::<Vec<_>>()
13184    });
13185
13186    let mut request =
13187        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13188            let edits = edits.clone();
13189            async move {
13190                Ok(lsp::CompletionItem {
13191                    additional_text_edits: edits,
13192                    ..Default::default()
13193                })
13194            }
13195        });
13196
13197    async move {
13198        request.next().await;
13199    }
13200}
13201
13202pub(crate) fn update_test_language_settings(
13203    cx: &mut TestAppContext,
13204    f: impl Fn(&mut AllLanguageSettingsContent),
13205) {
13206    cx.update(|cx| {
13207        SettingsStore::update_global(cx, |store, cx| {
13208            store.update_user_settings::<AllLanguageSettings>(cx, f);
13209        });
13210    });
13211}
13212
13213pub(crate) fn update_test_project_settings(
13214    cx: &mut TestAppContext,
13215    f: impl Fn(&mut ProjectSettings),
13216) {
13217    cx.update(|cx| {
13218        SettingsStore::update_global(cx, |store, cx| {
13219            store.update_user_settings::<ProjectSettings>(cx, f);
13220        });
13221    });
13222}
13223
13224pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13225    cx.update(|cx| {
13226        assets::Assets.load_test_fonts(cx);
13227        let store = SettingsStore::test(cx);
13228        cx.set_global(store);
13229        theme::init(theme::LoadThemes::JustBase, cx);
13230        release_channel::init(SemanticVersion::default(), cx);
13231        client::init_settings(cx);
13232        language::init(cx);
13233        Project::init_settings(cx);
13234        workspace::init_settings(cx);
13235        crate::init(cx);
13236    });
13237
13238    update_test_language_settings(cx, f);
13239}
13240
13241pub(crate) fn rust_lang() -> Arc<Language> {
13242    Arc::new(Language::new(
13243        LanguageConfig {
13244            name: "Rust".into(),
13245            matcher: LanguageMatcher {
13246                path_suffixes: vec!["rs".to_string()],
13247                ..Default::default()
13248            },
13249            ..Default::default()
13250        },
13251        Some(tree_sitter_rust::LANGUAGE.into()),
13252    ))
13253}
13254
13255#[track_caller]
13256fn assert_hunk_revert(
13257    not_reverted_text_with_selections: &str,
13258    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13259    expected_reverted_text_with_selections: &str,
13260    base_text: &str,
13261    cx: &mut EditorLspTestContext,
13262) {
13263    cx.set_state(not_reverted_text_with_selections);
13264    cx.update_editor(|editor, cx| {
13265        editor
13266            .buffer()
13267            .read(cx)
13268            .as_singleton()
13269            .unwrap()
13270            .update(cx, |buffer, cx| {
13271                buffer.set_diff_base(Some(base_text.into()), cx);
13272            });
13273    });
13274    cx.executor().run_until_parked();
13275
13276    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13277        let snapshot = editor.buffer().read(cx).snapshot(cx);
13278        let reverted_hunk_statuses = snapshot
13279            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13280            .map(|hunk| hunk_status(&hunk))
13281            .collect::<Vec<_>>();
13282
13283        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13284        reverted_hunk_statuses
13285    });
13286    cx.executor().run_until_parked();
13287    cx.assert_editor_state(expected_reverted_text_with_selections);
13288    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13289}