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