editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowBounds,
   13    WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   27use parking_lot::Mutex;
   28use project::FakeFs;
   29use project::{
   30    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   31    project_settings::{LspSettings, ProjectSettings},
   32};
   33use serde_json::{self, json};
   34use std::sync::atomic;
   35use std::sync::atomic::AtomicUsize;
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use unindent::Unindent;
   38use util::{
   39    assert_set_eq,
   40    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   41};
   42use workspace::{
   43    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   44    NavigationEntry, ViewId,
   45};
   46
   47#[gpui::test]
   48fn test_edit_events(cx: &mut TestAppContext) {
   49    init_test(cx, |_| {});
   50
   51    let buffer = cx.new_model(|cx| {
   52        let mut buffer = language::Buffer::local("123456", cx);
   53        buffer.set_group_interval(Duration::from_secs(1));
   54        buffer
   55    });
   56
   57    let events = Rc::new(RefCell::new(Vec::new()));
   58    let editor1 = cx.add_window({
   59        let events = events.clone();
   60        |cx| {
   61            let view = cx.view().clone();
   62            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   63                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   64                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   65                _ => {}
   66            })
   67            .detach();
   68            Editor::for_buffer(buffer.clone(), None, cx)
   69        }
   70    });
   71
   72    let editor2 = cx.add_window({
   73        let events = events.clone();
   74        |cx| {
   75            cx.subscribe(
   76                &cx.view().clone(),
   77                move |_, _, event: &EditorEvent, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor2", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, cx)
   87        }
   88    });
   89
   90    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   91
   92    // Mutating editor 1 will emit an `Edited` event only for that editor.
   93    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   94    assert_eq!(
   95        mem::take(&mut *events.borrow_mut()),
   96        [
   97            ("editor1", "edited"),
   98            ("editor1", "buffer edited"),
   99            ("editor2", "buffer edited"),
  100        ]
  101    );
  102
  103    // Mutating editor 2 will emit an `Edited` event only for that editor.
  104    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  105    assert_eq!(
  106        mem::take(&mut *events.borrow_mut()),
  107        [
  108            ("editor2", "edited"),
  109            ("editor1", "buffer edited"),
  110            ("editor2", "buffer edited"),
  111        ]
  112    );
  113
  114    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  115    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  116    assert_eq!(
  117        mem::take(&mut *events.borrow_mut()),
  118        [
  119            ("editor1", "edited"),
  120            ("editor1", "buffer edited"),
  121            ("editor2", "buffer edited"),
  122        ]
  123    );
  124
  125    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  126    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  127    assert_eq!(
  128        mem::take(&mut *events.borrow_mut()),
  129        [
  130            ("editor1", "edited"),
  131            ("editor1", "buffer edited"),
  132            ("editor2", "buffer edited"),
  133        ]
  134    );
  135
  136    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  137    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  138    assert_eq!(
  139        mem::take(&mut *events.borrow_mut()),
  140        [
  141            ("editor2", "edited"),
  142            ("editor1", "buffer edited"),
  143            ("editor2", "buffer edited"),
  144        ]
  145    );
  146
  147    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  148    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  149    assert_eq!(
  150        mem::take(&mut *events.borrow_mut()),
  151        [
  152            ("editor2", "edited"),
  153            ("editor1", "buffer edited"),
  154            ("editor2", "buffer edited"),
  155        ]
  156    );
  157
  158    // No event is emitted when the mutation is a no-op.
  159    _ = editor2.update(cx, |editor, cx| {
  160        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  161
  162        editor.backspace(&Backspace, cx);
  163    });
  164    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  165}
  166
  167#[gpui::test]
  168fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  169    init_test(cx, |_| {});
  170
  171    let mut now = Instant::now();
  172    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  173    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  174    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  175    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  176
  177    _ = editor.update(cx, |editor, cx| {
  178        editor.start_transaction_at(now, cx);
  179        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  180
  181        editor.insert("cd", cx);
  182        editor.end_transaction_at(now, cx);
  183        assert_eq!(editor.text(cx), "12cd56");
  184        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  185
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  188        editor.insert("e", cx);
  189        editor.end_transaction_at(now, cx);
  190        assert_eq!(editor.text(cx), "12cde6");
  191        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  192
  193        now += group_interval + Duration::from_millis(1);
  194        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  195
  196        // Simulate an edit in another editor
  197        buffer.update(cx, |buffer, cx| {
  198            buffer.start_transaction_at(now, cx);
  199            buffer.edit([(0..1, "a")], None, cx);
  200            buffer.edit([(1..1, "b")], None, cx);
  201            buffer.end_transaction_at(now, cx);
  202        });
  203
  204        assert_eq!(editor.text(cx), "ab2cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  206
  207        // Last transaction happened past the group interval in a different editor.
  208        // Undo it individually and don't restore selections.
  209        editor.undo(&Undo, cx);
  210        assert_eq!(editor.text(cx), "12cde6");
  211        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  212
  213        // First two transactions happened within the group interval in this editor.
  214        // Undo them together and restore selections.
  215        editor.undo(&Undo, cx);
  216        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  217        assert_eq!(editor.text(cx), "123456");
  218        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  219
  220        // Redo the first two transactions together.
  221        editor.redo(&Redo, cx);
  222        assert_eq!(editor.text(cx), "12cde6");
  223        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  224
  225        // Redo the last transaction on its own.
  226        editor.redo(&Redo, cx);
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  229
  230        // Test empty transactions.
  231        editor.start_transaction_at(now, cx);
  232        editor.end_transaction_at(now, cx);
  233        editor.undo(&Undo, cx);
  234        assert_eq!(editor.text(cx), "12cde6");
  235    });
  236}
  237
  238#[gpui::test]
  239fn test_ime_composition(cx: &mut TestAppContext) {
  240    init_test(cx, |_| {});
  241
  242    let buffer = cx.new_model(|cx| {
  243        let mut buffer = language::Buffer::local("abcde", cx);
  244        // Ensure automatic grouping doesn't occur.
  245        buffer.set_group_interval(Duration::ZERO);
  246        buffer
  247    });
  248
  249    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  250    cx.add_window(|cx| {
  251        let mut editor = build_editor(buffer.clone(), cx);
  252
  253        // Start a new IME composition.
  254        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  255        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  256        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  257        assert_eq!(editor.text(cx), "äbcde");
  258        assert_eq!(
  259            editor.marked_text_ranges(cx),
  260            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  261        );
  262
  263        // Finalize IME composition.
  264        editor.replace_text_in_range(None, "ā", cx);
  265        assert_eq!(editor.text(cx), "ābcde");
  266        assert_eq!(editor.marked_text_ranges(cx), None);
  267
  268        // IME composition edits are grouped and are undone/redone at once.
  269        editor.undo(&Default::default(), cx);
  270        assert_eq!(editor.text(cx), "abcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272        editor.redo(&Default::default(), cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  278        assert_eq!(
  279            editor.marked_text_ranges(cx),
  280            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  281        );
  282
  283        // Undoing during an IME composition cancels it.
  284        editor.undo(&Default::default(), cx);
  285        assert_eq!(editor.text(cx), "ābcde");
  286        assert_eq!(editor.marked_text_ranges(cx), None);
  287
  288        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  289        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  290        assert_eq!(editor.text(cx), "ābcdè");
  291        assert_eq!(
  292            editor.marked_text_ranges(cx),
  293            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  294        );
  295
  296        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  297        editor.replace_text_in_range(Some(4..999), "ę", cx);
  298        assert_eq!(editor.text(cx), "ābcdę");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition with multiple cursors.
  302        editor.change_selections(None, cx, |s| {
  303            s.select_ranges([
  304                OffsetUtf16(1)..OffsetUtf16(1),
  305                OffsetUtf16(3)..OffsetUtf16(3),
  306                OffsetUtf16(5)..OffsetUtf16(5),
  307            ])
  308        });
  309        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  310        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  311        assert_eq!(
  312            editor.marked_text_ranges(cx),
  313            Some(vec![
  314                OffsetUtf16(0)..OffsetUtf16(3),
  315                OffsetUtf16(4)..OffsetUtf16(7),
  316                OffsetUtf16(8)..OffsetUtf16(11)
  317            ])
  318        );
  319
  320        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  321        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  322        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  323        assert_eq!(
  324            editor.marked_text_ranges(cx),
  325            Some(vec![
  326                OffsetUtf16(1)..OffsetUtf16(2),
  327                OffsetUtf16(5)..OffsetUtf16(6),
  328                OffsetUtf16(9)..OffsetUtf16(10)
  329            ])
  330        );
  331
  332        // Finalize IME composition with multiple cursors.
  333        editor.replace_text_in_range(Some(9..10), "2", cx);
  334        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  335        assert_eq!(editor.marked_text_ranges(cx), None);
  336
  337        editor
  338    });
  339}
  340
  341#[gpui::test]
  342fn test_selection_with_mouse(cx: &mut TestAppContext) {
  343    init_test(cx, |_| {});
  344
  345    let editor = cx.add_window(|cx| {
  346        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  347        build_editor(buffer, cx)
  348    });
  349
  350    _ = editor.update(cx, |view, cx| {
  351        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  352    });
  353    assert_eq!(
  354        editor
  355            .update(cx, |view, cx| view.selections.display_ranges(cx))
  356            .unwrap(),
  357        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  358    );
  359
  360    _ = editor.update(cx, |view, cx| {
  361        view.update_selection(
  362            DisplayPoint::new(DisplayRow(3), 3),
  363            0,
  364            gpui::Point::<f32>::default(),
  365            cx,
  366        );
  367    });
  368
  369    assert_eq!(
  370        editor
  371            .update(cx, |view, cx| view.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  374    );
  375
  376    _ = editor.update(cx, |view, cx| {
  377        view.update_selection(
  378            DisplayPoint::new(DisplayRow(1), 1),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            cx,
  382        );
  383    });
  384
  385    assert_eq!(
  386        editor
  387            .update(cx, |view, cx| view.selections.display_ranges(cx))
  388            .unwrap(),
  389        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  390    );
  391
  392    _ = editor.update(cx, |view, cx| {
  393        view.end_selection(cx);
  394        view.update_selection(
  395            DisplayPoint::new(DisplayRow(3), 3),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            cx,
  399        );
  400    });
  401
  402    assert_eq!(
  403        editor
  404            .update(cx, |view, cx| view.selections.display_ranges(cx))
  405            .unwrap(),
  406        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  407    );
  408
  409    _ = editor.update(cx, |view, cx| {
  410        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  411        view.update_selection(
  412            DisplayPoint::new(DisplayRow(0), 0),
  413            0,
  414            gpui::Point::<f32>::default(),
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |view, cx| view.selections.display_ranges(cx))
  422            .unwrap(),
  423        [
  424            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  425            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  426        ]
  427    );
  428
  429    _ = editor.update(cx, |view, cx| {
  430        view.end_selection(cx);
  431    });
  432
  433    assert_eq!(
  434        editor
  435            .update(cx, |view, cx| view.selections.display_ranges(cx))
  436            .unwrap(),
  437        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  438    );
  439}
  440
  441#[gpui::test]
  442fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  443    init_test(cx, |_| {});
  444
  445    let editor = cx.add_window(|cx| {
  446        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  447        build_editor(buffer, cx)
  448    });
  449
  450    _ = editor.update(cx, |view, cx| {
  451        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  452    });
  453
  454    _ = editor.update(cx, |view, cx| {
  455        view.end_selection(cx);
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    assert_eq!(
  467        editor
  468            .update(cx, |view, cx| view.selections.display_ranges(cx))
  469            .unwrap(),
  470        [
  471            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  472            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  473        ]
  474    );
  475
  476    _ = editor.update(cx, |view, cx| {
  477        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  478    });
  479
  480    _ = editor.update(cx, |view, cx| {
  481        view.end_selection(cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |view, cx| view.selections.display_ranges(cx))
  487            .unwrap(),
  488        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  489    );
  490}
  491
  492#[gpui::test]
  493fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  494    init_test(cx, |_| {});
  495
  496    let view = cx.add_window(|cx| {
  497        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  498        build_editor(buffer, cx)
  499    });
  500
  501    _ = view.update(cx, |view, cx| {
  502        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  503        assert_eq!(
  504            view.selections.display_ranges(cx),
  505            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  506        );
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.update_selection(
  511            DisplayPoint::new(DisplayRow(3), 3),
  512            0,
  513            gpui::Point::<f32>::default(),
  514            cx,
  515        );
  516        assert_eq!(
  517            view.selections.display_ranges(cx),
  518            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  519        );
  520    });
  521
  522    _ = view.update(cx, |view, cx| {
  523        view.cancel(&Cancel, cx);
  524        view.update_selection(
  525            DisplayPoint::new(DisplayRow(1), 1),
  526            0,
  527            gpui::Point::<f32>::default(),
  528            cx,
  529        );
  530        assert_eq!(
  531            view.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  533        );
  534    });
  535}
  536
  537#[gpui::test]
  538fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  539    init_test(cx, |_| {});
  540
  541    let view = cx.add_window(|cx| {
  542        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  543        build_editor(buffer, cx)
  544    });
  545
  546    _ = view.update(cx, |view, cx| {
  547        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  548        assert_eq!(
  549            view.selections.display_ranges(cx),
  550            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  551        );
  552
  553        view.move_down(&Default::default(), cx);
  554        assert_eq!(
  555            view.selections.display_ranges(cx),
  556            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  557        );
  558
  559        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  560        assert_eq!(
  561            view.selections.display_ranges(cx),
  562            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  563        );
  564
  565        view.move_up(&Default::default(), cx);
  566        assert_eq!(
  567            view.selections.display_ranges(cx),
  568            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  569        );
  570    });
  571}
  572
  573#[gpui::test]
  574fn test_clone(cx: &mut TestAppContext) {
  575    init_test(cx, |_| {});
  576
  577    let (text, selection_ranges) = marked_text_ranges(
  578        indoc! {"
  579            one
  580            two
  581            threeˇ
  582            four
  583            fiveˇ
  584        "},
  585        true,
  586    );
  587
  588    let editor = cx.add_window(|cx| {
  589        let buffer = MultiBuffer::build_simple(&text, cx);
  590        build_editor(buffer, cx)
  591    });
  592
  593    _ = editor.update(cx, |editor, cx| {
  594        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  595        editor.fold_ranges(
  596            [
  597                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  598                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  599            ],
  600            true,
  601            cx,
  602        );
  603    });
  604
  605    let cloned_editor = editor
  606        .update(cx, |editor, cx| {
  607            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  608        })
  609        .unwrap()
  610        .unwrap();
  611
  612    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  613    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  614
  615    assert_eq!(
  616        cloned_editor
  617            .update(cx, |e, cx| e.display_text(cx))
  618            .unwrap(),
  619        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  620    );
  621    assert_eq!(
  622        cloned_snapshot
  623            .folds_in_range(0..text.len())
  624            .collect::<Vec<_>>(),
  625        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  626    );
  627    assert_set_eq!(
  628        cloned_editor
  629            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  630            .unwrap(),
  631        editor
  632            .update(cx, |editor, cx| editor.selections.ranges(cx))
  633            .unwrap()
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |e, cx| e.selections.display_ranges(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |e, cx| e.selections.display_ranges(cx))
  641            .unwrap()
  642    );
  643}
  644
  645#[gpui::test]
  646async fn test_navigation_history(cx: &mut TestAppContext) {
  647    init_test(cx, |_| {});
  648
  649    use workspace::item::Item;
  650
  651    let fs = FakeFs::new(cx.executor());
  652    let project = Project::test(fs, [], cx).await;
  653    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  654    let pane = workspace
  655        .update(cx, |workspace, _| workspace.active_pane().clone())
  656        .unwrap();
  657
  658    _ = workspace.update(cx, |_v, cx| {
  659        cx.new_view(|cx| {
  660            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  661            let mut editor = build_editor(buffer.clone(), cx);
  662            let handle = cx.view();
  663            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  664
  665            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  666                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  667            }
  668
  669            // Move the cursor a small distance.
  670            // Nothing is added to the navigation history.
  671            editor.change_selections(None, cx, |s| {
  672                s.select_display_ranges([
  673                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  674                ])
  675            });
  676            editor.change_selections(None, cx, |s| {
  677                s.select_display_ranges([
  678                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  679                ])
  680            });
  681            assert!(pop_history(&mut editor, cx).is_none());
  682
  683            // Move the cursor a large distance.
  684            // The history can jump back to the previous position.
  685            editor.change_selections(None, cx, |s| {
  686                s.select_display_ranges([
  687                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  688                ])
  689            });
  690            let nav_entry = pop_history(&mut editor, cx).unwrap();
  691            editor.navigate(nav_entry.data.unwrap(), cx);
  692            assert_eq!(nav_entry.item.id(), cx.entity_id());
  693            assert_eq!(
  694                editor.selections.display_ranges(cx),
  695                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  696            );
  697            assert!(pop_history(&mut editor, cx).is_none());
  698
  699            // Move the cursor a small distance via the mouse.
  700            // Nothing is added to the navigation history.
  701            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  702            editor.end_selection(cx);
  703            assert_eq!(
  704                editor.selections.display_ranges(cx),
  705                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  706            );
  707            assert!(pop_history(&mut editor, cx).is_none());
  708
  709            // Move the cursor a large distance via the mouse.
  710            // The history can jump back to the previous position.
  711            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  712            editor.end_selection(cx);
  713            assert_eq!(
  714                editor.selections.display_ranges(cx),
  715                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  716            );
  717            let nav_entry = pop_history(&mut editor, cx).unwrap();
  718            editor.navigate(nav_entry.data.unwrap(), cx);
  719            assert_eq!(nav_entry.item.id(), cx.entity_id());
  720            assert_eq!(
  721                editor.selections.display_ranges(cx),
  722                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  723            );
  724            assert!(pop_history(&mut editor, cx).is_none());
  725
  726            // Set scroll position to check later
  727            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  728            let original_scroll_position = editor.scroll_manager.anchor();
  729
  730            // Jump to the end of the document and adjust scroll
  731            editor.move_to_end(&MoveToEnd, cx);
  732            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  733            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  734
  735            let nav_entry = pop_history(&mut editor, cx).unwrap();
  736            editor.navigate(nav_entry.data.unwrap(), cx);
  737            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  738
  739            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  740            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  741            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  742            let invalid_point = Point::new(9999, 0);
  743            editor.navigate(
  744                Box::new(NavigationData {
  745                    cursor_anchor: invalid_anchor,
  746                    cursor_position: invalid_point,
  747                    scroll_anchor: ScrollAnchor {
  748                        anchor: invalid_anchor,
  749                        offset: Default::default(),
  750                    },
  751                    scroll_top_row: invalid_point.row,
  752                }),
  753                cx,
  754            );
  755            assert_eq!(
  756                editor.selections.display_ranges(cx),
  757                &[editor.max_point(cx)..editor.max_point(cx)]
  758            );
  759            assert_eq!(
  760                editor.scroll_position(cx),
  761                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  762            );
  763
  764            editor
  765        })
  766    });
  767}
  768
  769#[gpui::test]
  770fn test_cancel(cx: &mut TestAppContext) {
  771    init_test(cx, |_| {});
  772
  773    let view = cx.add_window(|cx| {
  774        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  775        build_editor(buffer, cx)
  776    });
  777
  778    _ = view.update(cx, |view, cx| {
  779        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  780        view.update_selection(
  781            DisplayPoint::new(DisplayRow(1), 1),
  782            0,
  783            gpui::Point::<f32>::default(),
  784            cx,
  785        );
  786        view.end_selection(cx);
  787
  788        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  789        view.update_selection(
  790            DisplayPoint::new(DisplayRow(0), 3),
  791            0,
  792            gpui::Point::<f32>::default(),
  793            cx,
  794        );
  795        view.end_selection(cx);
  796        assert_eq!(
  797            view.selections.display_ranges(cx),
  798            [
  799                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  800                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  801            ]
  802        );
  803    });
  804
  805    _ = view.update(cx, |view, cx| {
  806        view.cancel(&Cancel, cx);
  807        assert_eq!(
  808            view.selections.display_ranges(cx),
  809            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820}
  821
  822#[gpui::test]
  823fn test_fold_action(cx: &mut TestAppContext) {
  824    init_test(cx, |_| {});
  825
  826    let view = cx.add_window(|cx| {
  827        let buffer = MultiBuffer::build_simple(
  828            &"
  829                impl Foo {
  830                    // Hello!
  831
  832                    fn a() {
  833                        1
  834                    }
  835
  836                    fn b() {
  837                        2
  838                    }
  839
  840                    fn c() {
  841                        3
  842                    }
  843                }
  844            "
  845            .unindent(),
  846            cx,
  847        );
  848        build_editor(buffer.clone(), cx)
  849    });
  850
  851    _ = view.update(cx, |view, cx| {
  852        view.change_selections(None, cx, |s| {
  853            s.select_display_ranges([
  854                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  855            ]);
  856        });
  857        view.fold(&Fold, cx);
  858        assert_eq!(
  859            view.display_text(cx),
  860            "
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {⋯
  869                    }
  870
  871                    fn c() {⋯
  872                    }
  873                }
  874            "
  875            .unindent(),
  876        );
  877
  878        view.fold(&Fold, cx);
  879        assert_eq!(
  880            view.display_text(cx),
  881            "
  882                impl Foo {⋯
  883                }
  884            "
  885            .unindent(),
  886        );
  887
  888        view.unfold_lines(&UnfoldLines, cx);
  889        assert_eq!(
  890            view.display_text(cx),
  891            "
  892                impl Foo {
  893                    // Hello!
  894
  895                    fn a() {
  896                        1
  897                    }
  898
  899                    fn b() {⋯
  900                    }
  901
  902                    fn c() {⋯
  903                    }
  904                }
  905            "
  906            .unindent(),
  907        );
  908
  909        view.unfold_lines(&UnfoldLines, cx);
  910        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  911    });
  912}
  913
  914#[gpui::test]
  915fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  916    init_test(cx, |_| {});
  917
  918    let view = cx.add_window(|cx| {
  919        let buffer = MultiBuffer::build_simple(
  920            &"
  921                class Foo:
  922                    # Hello!
  923
  924                    def a():
  925                        print(1)
  926
  927                    def b():
  928                        print(2)
  929
  930                    def c():
  931                        print(3)
  932            "
  933            .unindent(),
  934            cx,
  935        );
  936        build_editor(buffer.clone(), cx)
  937    });
  938
  939    _ = view.update(cx, |view, cx| {
  940        view.change_selections(None, cx, |s| {
  941            s.select_display_ranges([
  942                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  943            ]);
  944        });
  945        view.fold(&Fold, cx);
  946        assert_eq!(
  947            view.display_text(cx),
  948            "
  949                class Foo:
  950                    # Hello!
  951
  952                    def a():
  953                        print(1)
  954
  955                    def b():⋯
  956
  957                    def c():⋯
  958            "
  959            .unindent(),
  960        );
  961
  962        view.fold(&Fold, cx);
  963        assert_eq!(
  964            view.display_text(cx),
  965            "
  966                class Foo:⋯
  967            "
  968            .unindent(),
  969        );
  970
  971        view.unfold_lines(&UnfoldLines, cx);
  972        assert_eq!(
  973            view.display_text(cx),
  974            "
  975                class Foo:
  976                    # Hello!
  977
  978                    def a():
  979                        print(1)
  980
  981                    def b():⋯
  982
  983                    def c():⋯
  984            "
  985            .unindent(),
  986        );
  987
  988        view.unfold_lines(&UnfoldLines, cx);
  989        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  990    });
  991}
  992
  993#[gpui::test]
  994fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
  995    init_test(cx, |_| {});
  996
  997    let view = cx.add_window(|cx| {
  998        let buffer = MultiBuffer::build_simple(
  999            &"
 1000                class Foo:
 1001                    # Hello!
 1002
 1003                    def a():
 1004                        print(1)
 1005
 1006                    def b():
 1007                        print(2)
 1008
 1009
 1010                    def c():
 1011                        print(3)
 1012
 1013
 1014            "
 1015            .unindent(),
 1016            cx,
 1017        );
 1018        build_editor(buffer.clone(), cx)
 1019    });
 1020
 1021    _ = view.update(cx, |view, cx| {
 1022        view.change_selections(None, cx, |s| {
 1023            s.select_display_ranges([
 1024                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1025            ]);
 1026        });
 1027        view.fold(&Fold, cx);
 1028        assert_eq!(
 1029            view.display_text(cx),
 1030            "
 1031                class Foo:
 1032                    # Hello!
 1033
 1034                    def a():
 1035                        print(1)
 1036
 1037                    def b():⋯
 1038
 1039
 1040                    def c():⋯
 1041
 1042
 1043            "
 1044            .unindent(),
 1045        );
 1046
 1047        view.fold(&Fold, cx);
 1048        assert_eq!(
 1049            view.display_text(cx),
 1050            "
 1051                class Foo:⋯
 1052
 1053
 1054            "
 1055            .unindent(),
 1056        );
 1057
 1058        view.unfold_lines(&UnfoldLines, cx);
 1059        assert_eq!(
 1060            view.display_text(cx),
 1061            "
 1062                class Foo:
 1063                    # Hello!
 1064
 1065                    def a():
 1066                        print(1)
 1067
 1068                    def b():⋯
 1069
 1070
 1071                    def c():⋯
 1072
 1073
 1074            "
 1075            .unindent(),
 1076        );
 1077
 1078        view.unfold_lines(&UnfoldLines, cx);
 1079        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1080    });
 1081}
 1082
 1083#[gpui::test]
 1084fn test_move_cursor(cx: &mut TestAppContext) {
 1085    init_test(cx, |_| {});
 1086
 1087    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1088    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1089
 1090    buffer.update(cx, |buffer, cx| {
 1091        buffer.edit(
 1092            vec![
 1093                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1094                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1095            ],
 1096            None,
 1097            cx,
 1098        );
 1099    });
 1100    _ = view.update(cx, |view, cx| {
 1101        assert_eq!(
 1102            view.selections.display_ranges(cx),
 1103            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1104        );
 1105
 1106        view.move_down(&MoveDown, cx);
 1107        assert_eq!(
 1108            view.selections.display_ranges(cx),
 1109            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1110        );
 1111
 1112        view.move_right(&MoveRight, cx);
 1113        assert_eq!(
 1114            view.selections.display_ranges(cx),
 1115            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1116        );
 1117
 1118        view.move_left(&MoveLeft, cx);
 1119        assert_eq!(
 1120            view.selections.display_ranges(cx),
 1121            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1122        );
 1123
 1124        view.move_up(&MoveUp, cx);
 1125        assert_eq!(
 1126            view.selections.display_ranges(cx),
 1127            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1128        );
 1129
 1130        view.move_to_end(&MoveToEnd, cx);
 1131        assert_eq!(
 1132            view.selections.display_ranges(cx),
 1133            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1134        );
 1135
 1136        view.move_to_beginning(&MoveToBeginning, cx);
 1137        assert_eq!(
 1138            view.selections.display_ranges(cx),
 1139            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1140        );
 1141
 1142        view.change_selections(None, cx, |s| {
 1143            s.select_display_ranges([
 1144                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1145            ]);
 1146        });
 1147        view.select_to_beginning(&SelectToBeginning, cx);
 1148        assert_eq!(
 1149            view.selections.display_ranges(cx),
 1150            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1151        );
 1152
 1153        view.select_to_end(&SelectToEnd, cx);
 1154        assert_eq!(
 1155            view.selections.display_ranges(cx),
 1156            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1157        );
 1158    });
 1159}
 1160
 1161// TODO: Re-enable this test
 1162#[cfg(target_os = "macos")]
 1163#[gpui::test]
 1164fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1165    init_test(cx, |_| {});
 1166
 1167    let view = cx.add_window(|cx| {
 1168        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1169        build_editor(buffer.clone(), cx)
 1170    });
 1171
 1172    assert_eq!('ⓐ'.len_utf8(), 3);
 1173    assert_eq!('α'.len_utf8(), 2);
 1174
 1175    _ = view.update(cx, |view, cx| {
 1176        view.fold_ranges(
 1177            vec![
 1178                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1179                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1180                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1181            ],
 1182            true,
 1183            cx,
 1184        );
 1185        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1186
 1187        view.move_right(&MoveRight, cx);
 1188        assert_eq!(
 1189            view.selections.display_ranges(cx),
 1190            &[empty_range(0, "".len())]
 1191        );
 1192        view.move_right(&MoveRight, cx);
 1193        assert_eq!(
 1194            view.selections.display_ranges(cx),
 1195            &[empty_range(0, "ⓐⓑ".len())]
 1196        );
 1197        view.move_right(&MoveRight, cx);
 1198        assert_eq!(
 1199            view.selections.display_ranges(cx),
 1200            &[empty_range(0, "ⓐⓑ⋯".len())]
 1201        );
 1202
 1203        view.move_down(&MoveDown, cx);
 1204        assert_eq!(
 1205            view.selections.display_ranges(cx),
 1206            &[empty_range(1, "ab⋯e".len())]
 1207        );
 1208        view.move_left(&MoveLeft, cx);
 1209        assert_eq!(
 1210            view.selections.display_ranges(cx),
 1211            &[empty_range(1, "ab⋯".len())]
 1212        );
 1213        view.move_left(&MoveLeft, cx);
 1214        assert_eq!(
 1215            view.selections.display_ranges(cx),
 1216            &[empty_range(1, "ab".len())]
 1217        );
 1218        view.move_left(&MoveLeft, cx);
 1219        assert_eq!(
 1220            view.selections.display_ranges(cx),
 1221            &[empty_range(1, "a".len())]
 1222        );
 1223
 1224        view.move_down(&MoveDown, cx);
 1225        assert_eq!(
 1226            view.selections.display_ranges(cx),
 1227            &[empty_range(2, "α".len())]
 1228        );
 1229        view.move_right(&MoveRight, cx);
 1230        assert_eq!(
 1231            view.selections.display_ranges(cx),
 1232            &[empty_range(2, "αβ".len())]
 1233        );
 1234        view.move_right(&MoveRight, cx);
 1235        assert_eq!(
 1236            view.selections.display_ranges(cx),
 1237            &[empty_range(2, "αβ⋯".len())]
 1238        );
 1239        view.move_right(&MoveRight, cx);
 1240        assert_eq!(
 1241            view.selections.display_ranges(cx),
 1242            &[empty_range(2, "αβ⋯ε".len())]
 1243        );
 1244
 1245        view.move_up(&MoveUp, cx);
 1246        assert_eq!(
 1247            view.selections.display_ranges(cx),
 1248            &[empty_range(1, "ab⋯e".len())]
 1249        );
 1250        view.move_down(&MoveDown, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[empty_range(2, "αβ⋯ε".len())]
 1254        );
 1255        view.move_up(&MoveUp, cx);
 1256        assert_eq!(
 1257            view.selections.display_ranges(cx),
 1258            &[empty_range(1, "ab⋯e".len())]
 1259        );
 1260
 1261        view.move_up(&MoveUp, cx);
 1262        assert_eq!(
 1263            view.selections.display_ranges(cx),
 1264            &[empty_range(0, "ⓐⓑ".len())]
 1265        );
 1266        view.move_left(&MoveLeft, cx);
 1267        assert_eq!(
 1268            view.selections.display_ranges(cx),
 1269            &[empty_range(0, "".len())]
 1270        );
 1271        view.move_left(&MoveLeft, cx);
 1272        assert_eq!(
 1273            view.selections.display_ranges(cx),
 1274            &[empty_range(0, "".len())]
 1275        );
 1276    });
 1277}
 1278
 1279#[gpui::test]
 1280fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1281    init_test(cx, |_| {});
 1282
 1283    let view = cx.add_window(|cx| {
 1284        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1285        build_editor(buffer.clone(), cx)
 1286    });
 1287    _ = view.update(cx, |view, cx| {
 1288        view.change_selections(None, cx, |s| {
 1289            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1290        });
 1291        view.move_down(&MoveDown, cx);
 1292        assert_eq!(
 1293            view.selections.display_ranges(cx),
 1294            &[empty_range(1, "abcd".len())]
 1295        );
 1296
 1297        view.move_down(&MoveDown, cx);
 1298        assert_eq!(
 1299            view.selections.display_ranges(cx),
 1300            &[empty_range(2, "αβγ".len())]
 1301        );
 1302
 1303        view.move_down(&MoveDown, cx);
 1304        assert_eq!(
 1305            view.selections.display_ranges(cx),
 1306            &[empty_range(3, "abcd".len())]
 1307        );
 1308
 1309        view.move_down(&MoveDown, cx);
 1310        assert_eq!(
 1311            view.selections.display_ranges(cx),
 1312            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1313        );
 1314
 1315        view.move_up(&MoveUp, cx);
 1316        assert_eq!(
 1317            view.selections.display_ranges(cx),
 1318            &[empty_range(3, "abcd".len())]
 1319        );
 1320
 1321        view.move_up(&MoveUp, cx);
 1322        assert_eq!(
 1323            view.selections.display_ranges(cx),
 1324            &[empty_range(2, "αβγ".len())]
 1325        );
 1326    });
 1327}
 1328
 1329#[gpui::test]
 1330fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1331    init_test(cx, |_| {});
 1332    let move_to_beg = MoveToBeginningOfLine {
 1333        stop_at_soft_wraps: true,
 1334    };
 1335
 1336    let move_to_end = MoveToEndOfLine {
 1337        stop_at_soft_wraps: true,
 1338    };
 1339
 1340    let view = cx.add_window(|cx| {
 1341        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1342        build_editor(buffer, cx)
 1343    });
 1344    _ = view.update(cx, |view, cx| {
 1345        view.change_selections(None, cx, |s| {
 1346            s.select_display_ranges([
 1347                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1348                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1349            ]);
 1350        });
 1351    });
 1352
 1353    _ = view.update(cx, |view, cx| {
 1354        view.move_to_beginning_of_line(&move_to_beg, cx);
 1355        assert_eq!(
 1356            view.selections.display_ranges(cx),
 1357            &[
 1358                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1359                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1360            ]
 1361        );
 1362    });
 1363
 1364    _ = view.update(cx, |view, cx| {
 1365        view.move_to_beginning_of_line(&move_to_beg, cx);
 1366        assert_eq!(
 1367            view.selections.display_ranges(cx),
 1368            &[
 1369                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1370                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1371            ]
 1372        );
 1373    });
 1374
 1375    _ = view.update(cx, |view, cx| {
 1376        view.move_to_beginning_of_line(&move_to_beg, cx);
 1377        assert_eq!(
 1378            view.selections.display_ranges(cx),
 1379            &[
 1380                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1381                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1382            ]
 1383        );
 1384    });
 1385
 1386    _ = view.update(cx, |view, cx| {
 1387        view.move_to_end_of_line(&move_to_end, cx);
 1388        assert_eq!(
 1389            view.selections.display_ranges(cx),
 1390            &[
 1391                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1392                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1393            ]
 1394        );
 1395    });
 1396
 1397    // Moving to the end of line again is a no-op.
 1398    _ = view.update(cx, |view, cx| {
 1399        view.move_to_end_of_line(&move_to_end, cx);
 1400        assert_eq!(
 1401            view.selections.display_ranges(cx),
 1402            &[
 1403                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1404                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1405            ]
 1406        );
 1407    });
 1408
 1409    _ = view.update(cx, |view, cx| {
 1410        view.move_left(&MoveLeft, cx);
 1411        view.select_to_beginning_of_line(
 1412            &SelectToBeginningOfLine {
 1413                stop_at_soft_wraps: true,
 1414            },
 1415            cx,
 1416        );
 1417        assert_eq!(
 1418            view.selections.display_ranges(cx),
 1419            &[
 1420                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1421                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1422            ]
 1423        );
 1424    });
 1425
 1426    _ = view.update(cx, |view, cx| {
 1427        view.select_to_beginning_of_line(
 1428            &SelectToBeginningOfLine {
 1429                stop_at_soft_wraps: true,
 1430            },
 1431            cx,
 1432        );
 1433        assert_eq!(
 1434            view.selections.display_ranges(cx),
 1435            &[
 1436                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1437                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1438            ]
 1439        );
 1440    });
 1441
 1442    _ = view.update(cx, |view, cx| {
 1443        view.select_to_beginning_of_line(
 1444            &SelectToBeginningOfLine {
 1445                stop_at_soft_wraps: true,
 1446            },
 1447            cx,
 1448        );
 1449        assert_eq!(
 1450            view.selections.display_ranges(cx),
 1451            &[
 1452                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1453                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1454            ]
 1455        );
 1456    });
 1457
 1458    _ = view.update(cx, |view, cx| {
 1459        view.select_to_end_of_line(
 1460            &SelectToEndOfLine {
 1461                stop_at_soft_wraps: true,
 1462            },
 1463            cx,
 1464        );
 1465        assert_eq!(
 1466            view.selections.display_ranges(cx),
 1467            &[
 1468                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1469                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1470            ]
 1471        );
 1472    });
 1473
 1474    _ = view.update(cx, |view, cx| {
 1475        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1476        assert_eq!(view.display_text(cx), "ab\n  de");
 1477        assert_eq!(
 1478            view.selections.display_ranges(cx),
 1479            &[
 1480                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1481                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1482            ]
 1483        );
 1484    });
 1485
 1486    _ = view.update(cx, |view, cx| {
 1487        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1488        assert_eq!(view.display_text(cx), "\n");
 1489        assert_eq!(
 1490            view.selections.display_ranges(cx),
 1491            &[
 1492                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1493                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1494            ]
 1495        );
 1496    });
 1497}
 1498
 1499#[gpui::test]
 1500fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1501    init_test(cx, |_| {});
 1502    let move_to_beg = MoveToBeginningOfLine {
 1503        stop_at_soft_wraps: false,
 1504    };
 1505
 1506    let move_to_end = MoveToEndOfLine {
 1507        stop_at_soft_wraps: false,
 1508    };
 1509
 1510    let view = cx.add_window(|cx| {
 1511        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1512        build_editor(buffer, cx)
 1513    });
 1514
 1515    _ = view.update(cx, |view, cx| {
 1516        view.set_wrap_width(Some(140.0.into()), cx);
 1517
 1518        // We expect the following lines after wrapping
 1519        // ```
 1520        // thequickbrownfox
 1521        // jumpedoverthelazydo
 1522        // gs
 1523        // ```
 1524        // The final `gs` was soft-wrapped onto a new line.
 1525        assert_eq!(
 1526            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1527            view.display_text(cx),
 1528        );
 1529
 1530        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1531        // Start the cursor at the `k` on the first line
 1532        view.change_selections(None, cx, |s| {
 1533            s.select_display_ranges([
 1534                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1535            ]);
 1536        });
 1537
 1538        // Moving to the beginning of the line should put us at the beginning of the line.
 1539        view.move_to_beginning_of_line(&move_to_beg, cx);
 1540        assert_eq!(
 1541            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1542            view.selections.display_ranges(cx)
 1543        );
 1544
 1545        // Moving to the end of the line should put us at the end of the line.
 1546        view.move_to_end_of_line(&move_to_end, cx);
 1547        assert_eq!(
 1548            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1549            view.selections.display_ranges(cx)
 1550        );
 1551
 1552        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1553        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1554        view.change_selections(None, cx, |s| {
 1555            s.select_display_ranges([
 1556                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1557            ]);
 1558        });
 1559
 1560        // Moving to the beginning of the line should put us at the start of the second line of
 1561        // display text, i.e., the `j`.
 1562        view.move_to_beginning_of_line(&move_to_beg, cx);
 1563        assert_eq!(
 1564            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1565            view.selections.display_ranges(cx)
 1566        );
 1567
 1568        // Moving to the beginning of the line again should be a no-op.
 1569        view.move_to_beginning_of_line(&move_to_beg, cx);
 1570        assert_eq!(
 1571            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1572            view.selections.display_ranges(cx)
 1573        );
 1574
 1575        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1576        // next display line.
 1577        view.move_to_end_of_line(&move_to_end, cx);
 1578        assert_eq!(
 1579            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1580            view.selections.display_ranges(cx)
 1581        );
 1582
 1583        // Moving to the end of the line again should be a no-op.
 1584        view.move_to_end_of_line(&move_to_end, cx);
 1585        assert_eq!(
 1586            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1587            view.selections.display_ranges(cx)
 1588        );
 1589    });
 1590}
 1591
 1592#[gpui::test]
 1593fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1594    init_test(cx, |_| {});
 1595
 1596    let view = cx.add_window(|cx| {
 1597        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1598        build_editor(buffer, cx)
 1599    });
 1600    _ = view.update(cx, |view, cx| {
 1601        view.change_selections(None, cx, |s| {
 1602            s.select_display_ranges([
 1603                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1604                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1605            ])
 1606        });
 1607
 1608        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1609        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1610
 1611        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1612        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1613
 1614        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1615        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1616
 1617        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1618        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1619
 1620        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1621        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1622
 1623        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1624        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1625
 1626        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1627        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1628
 1629        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1630        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1631
 1632        view.move_right(&MoveRight, cx);
 1633        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1634        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1635
 1636        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1637        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1638
 1639        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1640        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1641    });
 1642}
 1643
 1644#[gpui::test]
 1645fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1646    init_test(cx, |_| {});
 1647
 1648    let view = cx.add_window(|cx| {
 1649        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1650        build_editor(buffer, cx)
 1651    });
 1652
 1653    _ = view.update(cx, |view, cx| {
 1654        view.set_wrap_width(Some(140.0.into()), cx);
 1655        assert_eq!(
 1656            view.display_text(cx),
 1657            "use one::{\n    two::three::\n    four::five\n};"
 1658        );
 1659
 1660        view.change_selections(None, cx, |s| {
 1661            s.select_display_ranges([
 1662                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1663            ]);
 1664        });
 1665
 1666        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1667        assert_eq!(
 1668            view.selections.display_ranges(cx),
 1669            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1670        );
 1671
 1672        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1673        assert_eq!(
 1674            view.selections.display_ranges(cx),
 1675            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1676        );
 1677
 1678        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1679        assert_eq!(
 1680            view.selections.display_ranges(cx),
 1681            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1682        );
 1683
 1684        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1685        assert_eq!(
 1686            view.selections.display_ranges(cx),
 1687            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1688        );
 1689
 1690        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1691        assert_eq!(
 1692            view.selections.display_ranges(cx),
 1693            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1694        );
 1695
 1696        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1697        assert_eq!(
 1698            view.selections.display_ranges(cx),
 1699            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1700        );
 1701    });
 1702}
 1703
 1704#[gpui::test]
 1705async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1706    init_test(cx, |_| {});
 1707    let mut cx = EditorTestContext::new(cx).await;
 1708
 1709    let line_height = cx.editor(|editor, cx| {
 1710        editor
 1711            .style()
 1712            .unwrap()
 1713            .text
 1714            .line_height_in_pixels(cx.rem_size())
 1715    });
 1716    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1717
 1718    cx.set_state(
 1719        &r#"ˇone
 1720        two
 1721
 1722        three
 1723        fourˇ
 1724        five
 1725
 1726        six"#
 1727            .unindent(),
 1728    );
 1729
 1730    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1731    cx.assert_editor_state(
 1732        &r#"one
 1733        two
 1734        ˇ
 1735        three
 1736        four
 1737        five
 1738        ˇ
 1739        six"#
 1740            .unindent(),
 1741    );
 1742
 1743    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1744    cx.assert_editor_state(
 1745        &r#"one
 1746        two
 1747
 1748        three
 1749        four
 1750        five
 1751        ˇ
 1752        sixˇ"#
 1753            .unindent(),
 1754    );
 1755
 1756    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1757    cx.assert_editor_state(
 1758        &r#"one
 1759        two
 1760
 1761        three
 1762        four
 1763        five
 1764
 1765        sixˇ"#
 1766            .unindent(),
 1767    );
 1768
 1769    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1770    cx.assert_editor_state(
 1771        &r#"one
 1772        two
 1773
 1774        three
 1775        four
 1776        five
 1777        ˇ
 1778        six"#
 1779            .unindent(),
 1780    );
 1781
 1782    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1783    cx.assert_editor_state(
 1784        &r#"one
 1785        two
 1786        ˇ
 1787        three
 1788        four
 1789        five
 1790
 1791        six"#
 1792            .unindent(),
 1793    );
 1794
 1795    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1796    cx.assert_editor_state(
 1797        &r#"ˇone
 1798        two
 1799
 1800        three
 1801        four
 1802        five
 1803
 1804        six"#
 1805            .unindent(),
 1806    );
 1807}
 1808
 1809#[gpui::test]
 1810async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1811    init_test(cx, |_| {});
 1812    let mut cx = EditorTestContext::new(cx).await;
 1813    let line_height = cx.editor(|editor, cx| {
 1814        editor
 1815            .style()
 1816            .unwrap()
 1817            .text
 1818            .line_height_in_pixels(cx.rem_size())
 1819    });
 1820    let window = cx.window;
 1821    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1822
 1823    cx.set_state(
 1824        r#"ˇone
 1825        two
 1826        three
 1827        four
 1828        five
 1829        six
 1830        seven
 1831        eight
 1832        nine
 1833        ten
 1834        "#,
 1835    );
 1836
 1837    cx.update_editor(|editor, cx| {
 1838        assert_eq!(
 1839            editor.snapshot(cx).scroll_position(),
 1840            gpui::Point::new(0., 0.)
 1841        );
 1842        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1843        assert_eq!(
 1844            editor.snapshot(cx).scroll_position(),
 1845            gpui::Point::new(0., 3.)
 1846        );
 1847        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1848        assert_eq!(
 1849            editor.snapshot(cx).scroll_position(),
 1850            gpui::Point::new(0., 6.)
 1851        );
 1852        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1853        assert_eq!(
 1854            editor.snapshot(cx).scroll_position(),
 1855            gpui::Point::new(0., 3.)
 1856        );
 1857
 1858        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1859        assert_eq!(
 1860            editor.snapshot(cx).scroll_position(),
 1861            gpui::Point::new(0., 1.)
 1862        );
 1863        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1864        assert_eq!(
 1865            editor.snapshot(cx).scroll_position(),
 1866            gpui::Point::new(0., 3.)
 1867        );
 1868    });
 1869}
 1870
 1871#[gpui::test]
 1872async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1873    init_test(cx, |_| {});
 1874    let mut cx = EditorTestContext::new(cx).await;
 1875
 1876    let line_height = cx.update_editor(|editor, cx| {
 1877        editor.set_vertical_scroll_margin(2, cx);
 1878        editor
 1879            .style()
 1880            .unwrap()
 1881            .text
 1882            .line_height_in_pixels(cx.rem_size())
 1883    });
 1884    let window = cx.window;
 1885    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1886
 1887    cx.set_state(
 1888        r#"ˇone
 1889            two
 1890            three
 1891            four
 1892            five
 1893            six
 1894            seven
 1895            eight
 1896            nine
 1897            ten
 1898        "#,
 1899    );
 1900    cx.update_editor(|editor, cx| {
 1901        assert_eq!(
 1902            editor.snapshot(cx).scroll_position(),
 1903            gpui::Point::new(0., 0.0)
 1904        );
 1905    });
 1906
 1907    // Add a cursor below the visible area. Since both cursors cannot fit
 1908    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1909    // allows the vertical scroll margin below that cursor.
 1910    cx.update_editor(|editor, cx| {
 1911        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1912            selections.select_ranges([
 1913                Point::new(0, 0)..Point::new(0, 0),
 1914                Point::new(6, 0)..Point::new(6, 0),
 1915            ]);
 1916        })
 1917    });
 1918    cx.update_editor(|editor, cx| {
 1919        assert_eq!(
 1920            editor.snapshot(cx).scroll_position(),
 1921            gpui::Point::new(0., 3.0)
 1922        );
 1923    });
 1924
 1925    // Move down. The editor cursor scrolls down to track the newest cursor.
 1926    cx.update_editor(|editor, cx| {
 1927        editor.move_down(&Default::default(), cx);
 1928    });
 1929    cx.update_editor(|editor, cx| {
 1930        assert_eq!(
 1931            editor.snapshot(cx).scroll_position(),
 1932            gpui::Point::new(0., 4.0)
 1933        );
 1934    });
 1935
 1936    // Add a cursor above the visible area. Since both cursors fit on screen,
 1937    // the editor scrolls to show both.
 1938    cx.update_editor(|editor, cx| {
 1939        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1940            selections.select_ranges([
 1941                Point::new(1, 0)..Point::new(1, 0),
 1942                Point::new(6, 0)..Point::new(6, 0),
 1943            ]);
 1944        })
 1945    });
 1946    cx.update_editor(|editor, cx| {
 1947        assert_eq!(
 1948            editor.snapshot(cx).scroll_position(),
 1949            gpui::Point::new(0., 1.0)
 1950        );
 1951    });
 1952}
 1953
 1954#[gpui::test]
 1955async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1956    init_test(cx, |_| {});
 1957    let mut cx = EditorTestContext::new(cx).await;
 1958
 1959    let line_height = cx.editor(|editor, cx| {
 1960        editor
 1961            .style()
 1962            .unwrap()
 1963            .text
 1964            .line_height_in_pixels(cx.rem_size())
 1965    });
 1966    let window = cx.window;
 1967    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1968    cx.set_state(
 1969        &r#"
 1970        ˇone
 1971        two
 1972        threeˇ
 1973        four
 1974        five
 1975        six
 1976        seven
 1977        eight
 1978        nine
 1979        ten
 1980        "#
 1981        .unindent(),
 1982    );
 1983
 1984    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1985    cx.assert_editor_state(
 1986        &r#"
 1987        one
 1988        two
 1989        three
 1990        ˇfour
 1991        five
 1992        sixˇ
 1993        seven
 1994        eight
 1995        nine
 1996        ten
 1997        "#
 1998        .unindent(),
 1999    );
 2000
 2001    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2002    cx.assert_editor_state(
 2003        &r#"
 2004        one
 2005        two
 2006        three
 2007        four
 2008        five
 2009        six
 2010        ˇseven
 2011        eight
 2012        nineˇ
 2013        ten
 2014        "#
 2015        .unindent(),
 2016    );
 2017
 2018    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2019    cx.assert_editor_state(
 2020        &r#"
 2021        one
 2022        two
 2023        three
 2024        ˇfour
 2025        five
 2026        sixˇ
 2027        seven
 2028        eight
 2029        nine
 2030        ten
 2031        "#
 2032        .unindent(),
 2033    );
 2034
 2035    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2036    cx.assert_editor_state(
 2037        &r#"
 2038        ˇone
 2039        two
 2040        threeˇ
 2041        four
 2042        five
 2043        six
 2044        seven
 2045        eight
 2046        nine
 2047        ten
 2048        "#
 2049        .unindent(),
 2050    );
 2051
 2052    // Test select collapsing
 2053    cx.update_editor(|editor, cx| {
 2054        editor.move_page_down(&MovePageDown::default(), cx);
 2055        editor.move_page_down(&MovePageDown::default(), cx);
 2056        editor.move_page_down(&MovePageDown::default(), cx);
 2057    });
 2058    cx.assert_editor_state(
 2059        &r#"
 2060        one
 2061        two
 2062        three
 2063        four
 2064        five
 2065        six
 2066        seven
 2067        eight
 2068        nine
 2069        ˇten
 2070        ˇ"#
 2071        .unindent(),
 2072    );
 2073}
 2074
 2075#[gpui::test]
 2076async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2077    init_test(cx, |_| {});
 2078    let mut cx = EditorTestContext::new(cx).await;
 2079    cx.set_state("one «two threeˇ» four");
 2080    cx.update_editor(|editor, cx| {
 2081        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2082        assert_eq!(editor.text(cx), " four");
 2083    });
 2084}
 2085
 2086#[gpui::test]
 2087fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2088    init_test(cx, |_| {});
 2089
 2090    let view = cx.add_window(|cx| {
 2091        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2092        build_editor(buffer.clone(), cx)
 2093    });
 2094
 2095    _ = view.update(cx, |view, cx| {
 2096        view.change_selections(None, cx, |s| {
 2097            s.select_display_ranges([
 2098                // an empty selection - the preceding word fragment is deleted
 2099                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2100                // characters selected - they are deleted
 2101                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2102            ])
 2103        });
 2104        view.delete_to_previous_word_start(
 2105            &DeleteToPreviousWordStart {
 2106                ignore_newlines: false,
 2107            },
 2108            cx,
 2109        );
 2110        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2111    });
 2112
 2113    _ = view.update(cx, |view, cx| {
 2114        view.change_selections(None, cx, |s| {
 2115            s.select_display_ranges([
 2116                // an empty selection - the following word fragment is deleted
 2117                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2118                // characters selected - they are deleted
 2119                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2120            ])
 2121        });
 2122        view.delete_to_next_word_end(
 2123            &DeleteToNextWordEnd {
 2124                ignore_newlines: false,
 2125            },
 2126            cx,
 2127        );
 2128        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2129    });
 2130}
 2131
 2132#[gpui::test]
 2133fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135
 2136    let view = cx.add_window(|cx| {
 2137        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2138        build_editor(buffer.clone(), cx)
 2139    });
 2140    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2141        ignore_newlines: false,
 2142    };
 2143    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2144        ignore_newlines: true,
 2145    };
 2146
 2147    _ = view.update(cx, |view, cx| {
 2148        view.change_selections(None, cx, |s| {
 2149            s.select_display_ranges([
 2150                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2151            ])
 2152        });
 2153        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2154        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2155        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2156        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2157        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2158        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2159        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2160        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2161        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2162        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2163        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2164        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2165    });
 2166}
 2167
 2168#[gpui::test]
 2169fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2170    init_test(cx, |_| {});
 2171
 2172    let view = cx.add_window(|cx| {
 2173        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2174        build_editor(buffer.clone(), cx)
 2175    });
 2176    let del_to_next_word_end = DeleteToNextWordEnd {
 2177        ignore_newlines: false,
 2178    };
 2179    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2180        ignore_newlines: true,
 2181    };
 2182
 2183    _ = view.update(cx, |view, cx| {
 2184        view.change_selections(None, cx, |s| {
 2185            s.select_display_ranges([
 2186                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2187            ])
 2188        });
 2189        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2190        assert_eq!(
 2191            view.buffer.read(cx).read(cx).text(),
 2192            "one\n   two\nthree\n   four"
 2193        );
 2194        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2195        assert_eq!(
 2196            view.buffer.read(cx).read(cx).text(),
 2197            "\n   two\nthree\n   four"
 2198        );
 2199        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2200        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2201        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2202        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2203        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2204        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2205        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2206        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2207    });
 2208}
 2209
 2210#[gpui::test]
 2211fn test_newline(cx: &mut TestAppContext) {
 2212    init_test(cx, |_| {});
 2213
 2214    let view = cx.add_window(|cx| {
 2215        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2216        build_editor(buffer.clone(), cx)
 2217    });
 2218
 2219    _ = view.update(cx, |view, cx| {
 2220        view.change_selections(None, cx, |s| {
 2221            s.select_display_ranges([
 2222                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2223                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2224                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2225            ])
 2226        });
 2227
 2228        view.newline(&Newline, cx);
 2229        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2230    });
 2231}
 2232
 2233#[gpui::test]
 2234fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2235    init_test(cx, |_| {});
 2236
 2237    let editor = cx.add_window(|cx| {
 2238        let buffer = MultiBuffer::build_simple(
 2239            "
 2240                a
 2241                b(
 2242                    X
 2243                )
 2244                c(
 2245                    X
 2246                )
 2247            "
 2248            .unindent()
 2249            .as_str(),
 2250            cx,
 2251        );
 2252        let mut editor = build_editor(buffer.clone(), cx);
 2253        editor.change_selections(None, cx, |s| {
 2254            s.select_ranges([
 2255                Point::new(2, 4)..Point::new(2, 5),
 2256                Point::new(5, 4)..Point::new(5, 5),
 2257            ])
 2258        });
 2259        editor
 2260    });
 2261
 2262    _ = editor.update(cx, |editor, cx| {
 2263        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2264        editor.buffer.update(cx, |buffer, cx| {
 2265            buffer.edit(
 2266                [
 2267                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2268                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2269                ],
 2270                None,
 2271                cx,
 2272            );
 2273            assert_eq!(
 2274                buffer.read(cx).text(),
 2275                "
 2276                    a
 2277                    b()
 2278                    c()
 2279                "
 2280                .unindent()
 2281            );
 2282        });
 2283        assert_eq!(
 2284            editor.selections.ranges(cx),
 2285            &[
 2286                Point::new(1, 2)..Point::new(1, 2),
 2287                Point::new(2, 2)..Point::new(2, 2),
 2288            ],
 2289        );
 2290
 2291        editor.newline(&Newline, cx);
 2292        assert_eq!(
 2293            editor.text(cx),
 2294            "
 2295                a
 2296                b(
 2297                )
 2298                c(
 2299                )
 2300            "
 2301            .unindent()
 2302        );
 2303
 2304        // The selections are moved after the inserted newlines
 2305        assert_eq!(
 2306            editor.selections.ranges(cx),
 2307            &[
 2308                Point::new(2, 0)..Point::new(2, 0),
 2309                Point::new(4, 0)..Point::new(4, 0),
 2310            ],
 2311        );
 2312    });
 2313}
 2314
 2315#[gpui::test]
 2316async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2317    init_test(cx, |settings| {
 2318        settings.defaults.tab_size = NonZeroU32::new(4)
 2319    });
 2320
 2321    let language = Arc::new(
 2322        Language::new(
 2323            LanguageConfig::default(),
 2324            Some(tree_sitter_rust::LANGUAGE.into()),
 2325        )
 2326        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2327        .unwrap(),
 2328    );
 2329
 2330    let mut cx = EditorTestContext::new(cx).await;
 2331    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2332    cx.set_state(indoc! {"
 2333        const a: ˇA = (
 2334 2335                «const_functionˇ»(ˇ),
 2336                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2337 2338        ˇ);ˇ
 2339    "});
 2340
 2341    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2342    cx.assert_editor_state(indoc! {"
 2343        ˇ
 2344        const a: A = (
 2345            ˇ
 2346            (
 2347                ˇ
 2348                ˇ
 2349                const_function(),
 2350                ˇ
 2351                ˇ
 2352                ˇ
 2353                ˇ
 2354                something_else,
 2355                ˇ
 2356            )
 2357            ˇ
 2358            ˇ
 2359        );
 2360    "});
 2361}
 2362
 2363#[gpui::test]
 2364async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2365    init_test(cx, |settings| {
 2366        settings.defaults.tab_size = NonZeroU32::new(4)
 2367    });
 2368
 2369    let language = Arc::new(
 2370        Language::new(
 2371            LanguageConfig::default(),
 2372            Some(tree_sitter_rust::LANGUAGE.into()),
 2373        )
 2374        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2375        .unwrap(),
 2376    );
 2377
 2378    let mut cx = EditorTestContext::new(cx).await;
 2379    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2380    cx.set_state(indoc! {"
 2381        const a: ˇA = (
 2382 2383                «const_functionˇ»(ˇ),
 2384                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2385 2386        ˇ);ˇ
 2387    "});
 2388
 2389    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2390    cx.assert_editor_state(indoc! {"
 2391        const a: A = (
 2392            ˇ
 2393            (
 2394                ˇ
 2395                const_function(),
 2396                ˇ
 2397                ˇ
 2398                something_else,
 2399                ˇ
 2400                ˇ
 2401                ˇ
 2402                ˇ
 2403            )
 2404            ˇ
 2405        );
 2406        ˇ
 2407        ˇ
 2408    "});
 2409}
 2410
 2411#[gpui::test]
 2412async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2413    init_test(cx, |settings| {
 2414        settings.defaults.tab_size = NonZeroU32::new(4)
 2415    });
 2416
 2417    let language = Arc::new(Language::new(
 2418        LanguageConfig {
 2419            line_comments: vec!["//".into()],
 2420            ..LanguageConfig::default()
 2421        },
 2422        None,
 2423    ));
 2424    {
 2425        let mut cx = EditorTestContext::new(cx).await;
 2426        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2427        cx.set_state(indoc! {"
 2428        // Fooˇ
 2429    "});
 2430
 2431        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2432        cx.assert_editor_state(indoc! {"
 2433        // Foo
 2434        //ˇ
 2435    "});
 2436        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2437        cx.set_state(indoc! {"
 2438        ˇ// Foo
 2439    "});
 2440        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2441        cx.assert_editor_state(indoc! {"
 2442
 2443        ˇ// Foo
 2444    "});
 2445    }
 2446    // Ensure that comment continuations can be disabled.
 2447    update_test_language_settings(cx, |settings| {
 2448        settings.defaults.extend_comment_on_newline = Some(false);
 2449    });
 2450    let mut cx = EditorTestContext::new(cx).await;
 2451    cx.set_state(indoc! {"
 2452        // Fooˇ
 2453    "});
 2454    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2455    cx.assert_editor_state(indoc! {"
 2456        // Foo
 2457        ˇ
 2458    "});
 2459}
 2460
 2461#[gpui::test]
 2462fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2463    init_test(cx, |_| {});
 2464
 2465    let editor = cx.add_window(|cx| {
 2466        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2467        let mut editor = build_editor(buffer.clone(), cx);
 2468        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2469        editor
 2470    });
 2471
 2472    _ = editor.update(cx, |editor, cx| {
 2473        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2474        editor.buffer.update(cx, |buffer, cx| {
 2475            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2476            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2477        });
 2478        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2479
 2480        editor.insert("Z", cx);
 2481        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2482
 2483        // The selections are moved after the inserted characters
 2484        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2485    });
 2486}
 2487
 2488#[gpui::test]
 2489async fn test_tab(cx: &mut gpui::TestAppContext) {
 2490    init_test(cx, |settings| {
 2491        settings.defaults.tab_size = NonZeroU32::new(3)
 2492    });
 2493
 2494    let mut cx = EditorTestContext::new(cx).await;
 2495    cx.set_state(indoc! {"
 2496        ˇabˇc
 2497        ˇ🏀ˇ🏀ˇefg
 2498 2499    "});
 2500    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2501    cx.assert_editor_state(indoc! {"
 2502           ˇab ˇc
 2503           ˇ🏀  ˇ🏀  ˇefg
 2504        d  ˇ
 2505    "});
 2506
 2507    cx.set_state(indoc! {"
 2508        a
 2509        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2510    "});
 2511    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2512    cx.assert_editor_state(indoc! {"
 2513        a
 2514           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2515    "});
 2516}
 2517
 2518#[gpui::test]
 2519async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2520    init_test(cx, |_| {});
 2521
 2522    let mut cx = EditorTestContext::new(cx).await;
 2523    let language = Arc::new(
 2524        Language::new(
 2525            LanguageConfig::default(),
 2526            Some(tree_sitter_rust::LANGUAGE.into()),
 2527        )
 2528        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2529        .unwrap(),
 2530    );
 2531    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2532
 2533    // cursors that are already at the suggested indent level insert
 2534    // a soft tab. cursors that are to the left of the suggested indent
 2535    // auto-indent their line.
 2536    cx.set_state(indoc! {"
 2537        ˇ
 2538        const a: B = (
 2539            c(
 2540                d(
 2541        ˇ
 2542                )
 2543        ˇ
 2544        ˇ    )
 2545        );
 2546    "});
 2547    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2548    cx.assert_editor_state(indoc! {"
 2549            ˇ
 2550        const a: B = (
 2551            c(
 2552                d(
 2553                    ˇ
 2554                )
 2555                ˇ
 2556            ˇ)
 2557        );
 2558    "});
 2559
 2560    // handle auto-indent when there are multiple cursors on the same line
 2561    cx.set_state(indoc! {"
 2562        const a: B = (
 2563            c(
 2564        ˇ    ˇ
 2565        ˇ    )
 2566        );
 2567    "});
 2568    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2569    cx.assert_editor_state(indoc! {"
 2570        const a: B = (
 2571            c(
 2572                ˇ
 2573            ˇ)
 2574        );
 2575    "});
 2576}
 2577
 2578#[gpui::test]
 2579async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2580    init_test(cx, |settings| {
 2581        settings.defaults.tab_size = NonZeroU32::new(4)
 2582    });
 2583
 2584    let language = Arc::new(
 2585        Language::new(
 2586            LanguageConfig::default(),
 2587            Some(tree_sitter_rust::LANGUAGE.into()),
 2588        )
 2589        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2590        .unwrap(),
 2591    );
 2592
 2593    let mut cx = EditorTestContext::new(cx).await;
 2594    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2595    cx.set_state(indoc! {"
 2596        fn a() {
 2597            if b {
 2598        \t ˇc
 2599            }
 2600        }
 2601    "});
 2602
 2603    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2604    cx.assert_editor_state(indoc! {"
 2605        fn a() {
 2606            if b {
 2607                ˇc
 2608            }
 2609        }
 2610    "});
 2611}
 2612
 2613#[gpui::test]
 2614async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2615    init_test(cx, |settings| {
 2616        settings.defaults.tab_size = NonZeroU32::new(4);
 2617    });
 2618
 2619    let mut cx = EditorTestContext::new(cx).await;
 2620
 2621    cx.set_state(indoc! {"
 2622          «oneˇ» «twoˇ»
 2623        three
 2624         four
 2625    "});
 2626    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2627    cx.assert_editor_state(indoc! {"
 2628            «oneˇ» «twoˇ»
 2629        three
 2630         four
 2631    "});
 2632
 2633    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2634    cx.assert_editor_state(indoc! {"
 2635        «oneˇ» «twoˇ»
 2636        three
 2637         four
 2638    "});
 2639
 2640    // select across line ending
 2641    cx.set_state(indoc! {"
 2642        one two
 2643        t«hree
 2644        ˇ» four
 2645    "});
 2646    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2647    cx.assert_editor_state(indoc! {"
 2648        one two
 2649            t«hree
 2650        ˇ» four
 2651    "});
 2652
 2653    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655        one two
 2656        t«hree
 2657        ˇ» four
 2658    "});
 2659
 2660    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2661    cx.set_state(indoc! {"
 2662        one two
 2663        ˇthree
 2664            four
 2665    "});
 2666    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2667    cx.assert_editor_state(indoc! {"
 2668        one two
 2669            ˇthree
 2670            four
 2671    "});
 2672
 2673    cx.set_state(indoc! {"
 2674        one two
 2675        ˇ    three
 2676            four
 2677    "});
 2678    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2679    cx.assert_editor_state(indoc! {"
 2680        one two
 2681        ˇthree
 2682            four
 2683    "});
 2684}
 2685
 2686#[gpui::test]
 2687async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2688    init_test(cx, |settings| {
 2689        settings.defaults.hard_tabs = Some(true);
 2690    });
 2691
 2692    let mut cx = EditorTestContext::new(cx).await;
 2693
 2694    // select two ranges on one line
 2695    cx.set_state(indoc! {"
 2696        «oneˇ» «twoˇ»
 2697        three
 2698        four
 2699    "});
 2700    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2701    cx.assert_editor_state(indoc! {"
 2702        \t«oneˇ» «twoˇ»
 2703        three
 2704        four
 2705    "});
 2706    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2707    cx.assert_editor_state(indoc! {"
 2708        \t\t«oneˇ» «twoˇ»
 2709        three
 2710        four
 2711    "});
 2712    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2713    cx.assert_editor_state(indoc! {"
 2714        \t«oneˇ» «twoˇ»
 2715        three
 2716        four
 2717    "});
 2718    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2719    cx.assert_editor_state(indoc! {"
 2720        «oneˇ» «twoˇ»
 2721        three
 2722        four
 2723    "});
 2724
 2725    // select across a line ending
 2726    cx.set_state(indoc! {"
 2727        one two
 2728        t«hree
 2729        ˇ»four
 2730    "});
 2731    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2732    cx.assert_editor_state(indoc! {"
 2733        one two
 2734        \tt«hree
 2735        ˇ»four
 2736    "});
 2737    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2738    cx.assert_editor_state(indoc! {"
 2739        one two
 2740        \t\tt«hree
 2741        ˇ»four
 2742    "});
 2743    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2744    cx.assert_editor_state(indoc! {"
 2745        one two
 2746        \tt«hree
 2747        ˇ»four
 2748    "});
 2749    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2750    cx.assert_editor_state(indoc! {"
 2751        one two
 2752        t«hree
 2753        ˇ»four
 2754    "});
 2755
 2756    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2757    cx.set_state(indoc! {"
 2758        one two
 2759        ˇthree
 2760        four
 2761    "});
 2762    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2763    cx.assert_editor_state(indoc! {"
 2764        one two
 2765        ˇthree
 2766        four
 2767    "});
 2768    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770        one two
 2771        \tˇthree
 2772        four
 2773    "});
 2774    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2775    cx.assert_editor_state(indoc! {"
 2776        one two
 2777        ˇthree
 2778        four
 2779    "});
 2780}
 2781
 2782#[gpui::test]
 2783fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2784    init_test(cx, |settings| {
 2785        settings.languages.extend([
 2786            (
 2787                "TOML".into(),
 2788                LanguageSettingsContent {
 2789                    tab_size: NonZeroU32::new(2),
 2790                    ..Default::default()
 2791                },
 2792            ),
 2793            (
 2794                "Rust".into(),
 2795                LanguageSettingsContent {
 2796                    tab_size: NonZeroU32::new(4),
 2797                    ..Default::default()
 2798                },
 2799            ),
 2800        ]);
 2801    });
 2802
 2803    let toml_language = Arc::new(Language::new(
 2804        LanguageConfig {
 2805            name: "TOML".into(),
 2806            ..Default::default()
 2807        },
 2808        None,
 2809    ));
 2810    let rust_language = Arc::new(Language::new(
 2811        LanguageConfig {
 2812            name: "Rust".into(),
 2813            ..Default::default()
 2814        },
 2815        None,
 2816    ));
 2817
 2818    let toml_buffer =
 2819        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2820    let rust_buffer = cx.new_model(|cx| {
 2821        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2822    });
 2823    let multibuffer = cx.new_model(|cx| {
 2824        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2825        multibuffer.push_excerpts(
 2826            toml_buffer.clone(),
 2827            [ExcerptRange {
 2828                context: Point::new(0, 0)..Point::new(2, 0),
 2829                primary: None,
 2830            }],
 2831            cx,
 2832        );
 2833        multibuffer.push_excerpts(
 2834            rust_buffer.clone(),
 2835            [ExcerptRange {
 2836                context: Point::new(0, 0)..Point::new(1, 0),
 2837                primary: None,
 2838            }],
 2839            cx,
 2840        );
 2841        multibuffer
 2842    });
 2843
 2844    cx.add_window(|cx| {
 2845        let mut editor = build_editor(multibuffer, cx);
 2846
 2847        assert_eq!(
 2848            editor.text(cx),
 2849            indoc! {"
 2850                a = 1
 2851                b = 2
 2852
 2853                const c: usize = 3;
 2854            "}
 2855        );
 2856
 2857        select_ranges(
 2858            &mut editor,
 2859            indoc! {"
 2860                «aˇ» = 1
 2861                b = 2
 2862
 2863                «const c:ˇ» usize = 3;
 2864            "},
 2865            cx,
 2866        );
 2867
 2868        editor.tab(&Tab, cx);
 2869        assert_text_with_selections(
 2870            &mut editor,
 2871            indoc! {"
 2872                  «aˇ» = 1
 2873                b = 2
 2874
 2875                    «const c:ˇ» usize = 3;
 2876            "},
 2877            cx,
 2878        );
 2879        editor.tab_prev(&TabPrev, cx);
 2880        assert_text_with_selections(
 2881            &mut editor,
 2882            indoc! {"
 2883                «aˇ» = 1
 2884                b = 2
 2885
 2886                «const c:ˇ» usize = 3;
 2887            "},
 2888            cx,
 2889        );
 2890
 2891        editor
 2892    });
 2893}
 2894
 2895#[gpui::test]
 2896async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2897    init_test(cx, |_| {});
 2898
 2899    let mut cx = EditorTestContext::new(cx).await;
 2900
 2901    // Basic backspace
 2902    cx.set_state(indoc! {"
 2903        onˇe two three
 2904        fou«rˇ» five six
 2905        seven «ˇeight nine
 2906        »ten
 2907    "});
 2908    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        oˇe two three
 2911        fouˇ five six
 2912        seven ˇten
 2913    "});
 2914
 2915    // Test backspace inside and around indents
 2916    cx.set_state(indoc! {"
 2917        zero
 2918            ˇone
 2919                ˇtwo
 2920            ˇ ˇ ˇ  three
 2921        ˇ  ˇ  four
 2922    "});
 2923    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2924    cx.assert_editor_state(indoc! {"
 2925        zero
 2926        ˇone
 2927            ˇtwo
 2928        ˇ  threeˇ  four
 2929    "});
 2930
 2931    // Test backspace with line_mode set to true
 2932    cx.update_editor(|e, _| e.selections.line_mode = true);
 2933    cx.set_state(indoc! {"
 2934        The ˇquick ˇbrown
 2935        fox jumps over
 2936        the lazy dog
 2937        ˇThe qu«ick bˇ»rown"});
 2938    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2939    cx.assert_editor_state(indoc! {"
 2940        ˇfox jumps over
 2941        the lazy dogˇ"});
 2942}
 2943
 2944#[gpui::test]
 2945async fn test_delete(cx: &mut gpui::TestAppContext) {
 2946    init_test(cx, |_| {});
 2947
 2948    let mut cx = EditorTestContext::new(cx).await;
 2949    cx.set_state(indoc! {"
 2950        onˇe two three
 2951        fou«rˇ» five six
 2952        seven «ˇeight nine
 2953        »ten
 2954    "});
 2955    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        onˇ two three
 2958        fouˇ five six
 2959        seven ˇten
 2960    "});
 2961
 2962    // Test backspace with line_mode set to true
 2963    cx.update_editor(|e, _| e.selections.line_mode = true);
 2964    cx.set_state(indoc! {"
 2965        The ˇquick ˇbrown
 2966        fox «ˇjum»ps over
 2967        the lazy dog
 2968        ˇThe qu«ick bˇ»rown"});
 2969    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2970    cx.assert_editor_state("ˇthe lazy dogˇ");
 2971}
 2972
 2973#[gpui::test]
 2974fn test_delete_line(cx: &mut TestAppContext) {
 2975    init_test(cx, |_| {});
 2976
 2977    let view = cx.add_window(|cx| {
 2978        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2979        build_editor(buffer, cx)
 2980    });
 2981    _ = view.update(cx, |view, cx| {
 2982        view.change_selections(None, cx, |s| {
 2983            s.select_display_ranges([
 2984                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2985                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2986                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2987            ])
 2988        });
 2989        view.delete_line(&DeleteLine, cx);
 2990        assert_eq!(view.display_text(cx), "ghi");
 2991        assert_eq!(
 2992            view.selections.display_ranges(cx),
 2993            vec![
 2994                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2995                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2996            ]
 2997        );
 2998    });
 2999
 3000    let view = cx.add_window(|cx| {
 3001        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3002        build_editor(buffer, cx)
 3003    });
 3004    _ = view.update(cx, |view, cx| {
 3005        view.change_selections(None, cx, |s| {
 3006            s.select_display_ranges([
 3007                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3008            ])
 3009        });
 3010        view.delete_line(&DeleteLine, cx);
 3011        assert_eq!(view.display_text(cx), "ghi\n");
 3012        assert_eq!(
 3013            view.selections.display_ranges(cx),
 3014            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3015        );
 3016    });
 3017}
 3018
 3019#[gpui::test]
 3020fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3021    init_test(cx, |_| {});
 3022
 3023    cx.add_window(|cx| {
 3024        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3025        let mut editor = build_editor(buffer.clone(), cx);
 3026        let buffer = buffer.read(cx).as_singleton().unwrap();
 3027
 3028        assert_eq!(
 3029            editor.selections.ranges::<Point>(cx),
 3030            &[Point::new(0, 0)..Point::new(0, 0)]
 3031        );
 3032
 3033        // When on single line, replace newline at end by space
 3034        editor.join_lines(&JoinLines, cx);
 3035        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3036        assert_eq!(
 3037            editor.selections.ranges::<Point>(cx),
 3038            &[Point::new(0, 3)..Point::new(0, 3)]
 3039        );
 3040
 3041        // When multiple lines are selected, remove newlines that are spanned by the selection
 3042        editor.change_selections(None, cx, |s| {
 3043            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3044        });
 3045        editor.join_lines(&JoinLines, cx);
 3046        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3047        assert_eq!(
 3048            editor.selections.ranges::<Point>(cx),
 3049            &[Point::new(0, 11)..Point::new(0, 11)]
 3050        );
 3051
 3052        // Undo should be transactional
 3053        editor.undo(&Undo, cx);
 3054        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3055        assert_eq!(
 3056            editor.selections.ranges::<Point>(cx),
 3057            &[Point::new(0, 5)..Point::new(2, 2)]
 3058        );
 3059
 3060        // When joining an empty line don't insert a space
 3061        editor.change_selections(None, cx, |s| {
 3062            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3063        });
 3064        editor.join_lines(&JoinLines, cx);
 3065        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3066        assert_eq!(
 3067            editor.selections.ranges::<Point>(cx),
 3068            [Point::new(2, 3)..Point::new(2, 3)]
 3069        );
 3070
 3071        // We can remove trailing newlines
 3072        editor.join_lines(&JoinLines, cx);
 3073        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3074        assert_eq!(
 3075            editor.selections.ranges::<Point>(cx),
 3076            [Point::new(2, 3)..Point::new(2, 3)]
 3077        );
 3078
 3079        // We don't blow up on the last line
 3080        editor.join_lines(&JoinLines, cx);
 3081        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3082        assert_eq!(
 3083            editor.selections.ranges::<Point>(cx),
 3084            [Point::new(2, 3)..Point::new(2, 3)]
 3085        );
 3086
 3087        // reset to test indentation
 3088        editor.buffer.update(cx, |buffer, cx| {
 3089            buffer.edit(
 3090                [
 3091                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3092                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3093                ],
 3094                None,
 3095                cx,
 3096            )
 3097        });
 3098
 3099        // We remove any leading spaces
 3100        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3101        editor.change_selections(None, cx, |s| {
 3102            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3103        });
 3104        editor.join_lines(&JoinLines, cx);
 3105        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3106
 3107        // We don't insert a space for a line containing only spaces
 3108        editor.join_lines(&JoinLines, cx);
 3109        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3110
 3111        // We ignore any leading tabs
 3112        editor.join_lines(&JoinLines, cx);
 3113        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3114
 3115        editor
 3116    });
 3117}
 3118
 3119#[gpui::test]
 3120fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3121    init_test(cx, |_| {});
 3122
 3123    cx.add_window(|cx| {
 3124        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3125        let mut editor = build_editor(buffer.clone(), cx);
 3126        let buffer = buffer.read(cx).as_singleton().unwrap();
 3127
 3128        editor.change_selections(None, cx, |s| {
 3129            s.select_ranges([
 3130                Point::new(0, 2)..Point::new(1, 1),
 3131                Point::new(1, 2)..Point::new(1, 2),
 3132                Point::new(3, 1)..Point::new(3, 2),
 3133            ])
 3134        });
 3135
 3136        editor.join_lines(&JoinLines, cx);
 3137        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3138
 3139        assert_eq!(
 3140            editor.selections.ranges::<Point>(cx),
 3141            [
 3142                Point::new(0, 7)..Point::new(0, 7),
 3143                Point::new(1, 3)..Point::new(1, 3)
 3144            ]
 3145        );
 3146        editor
 3147    });
 3148}
 3149
 3150#[gpui::test]
 3151async fn test_join_lines_with_git_diff_base(
 3152    executor: BackgroundExecutor,
 3153    cx: &mut gpui::TestAppContext,
 3154) {
 3155    init_test(cx, |_| {});
 3156
 3157    let mut cx = EditorTestContext::new(cx).await;
 3158
 3159    let diff_base = r#"
 3160        Line 0
 3161        Line 1
 3162        Line 2
 3163        Line 3
 3164        "#
 3165    .unindent();
 3166
 3167    cx.set_state(
 3168        &r#"
 3169        ˇLine 0
 3170        Line 1
 3171        Line 2
 3172        Line 3
 3173        "#
 3174        .unindent(),
 3175    );
 3176
 3177    cx.set_diff_base(Some(&diff_base));
 3178    executor.run_until_parked();
 3179
 3180    // Join lines
 3181    cx.update_editor(|editor, cx| {
 3182        editor.join_lines(&JoinLines, cx);
 3183    });
 3184    executor.run_until_parked();
 3185
 3186    cx.assert_editor_state(
 3187        &r#"
 3188        Line 0ˇ Line 1
 3189        Line 2
 3190        Line 3
 3191        "#
 3192        .unindent(),
 3193    );
 3194    // Join again
 3195    cx.update_editor(|editor, cx| {
 3196        editor.join_lines(&JoinLines, cx);
 3197    });
 3198    executor.run_until_parked();
 3199
 3200    cx.assert_editor_state(
 3201        &r#"
 3202        Line 0 Line 1ˇ Line 2
 3203        Line 3
 3204        "#
 3205        .unindent(),
 3206    );
 3207}
 3208
 3209#[gpui::test]
 3210async fn test_custom_newlines_cause_no_false_positive_diffs(
 3211    executor: BackgroundExecutor,
 3212    cx: &mut gpui::TestAppContext,
 3213) {
 3214    init_test(cx, |_| {});
 3215    let mut cx = EditorTestContext::new(cx).await;
 3216    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3217    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 3218    executor.run_until_parked();
 3219
 3220    cx.update_editor(|editor, cx| {
 3221        assert_eq!(
 3222            editor
 3223                .buffer()
 3224                .read(cx)
 3225                .snapshot(cx)
 3226                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 3227                .collect::<Vec<_>>(),
 3228            Vec::new(),
 3229            "Should not have any diffs for files with custom newlines"
 3230        );
 3231    });
 3232}
 3233
 3234#[gpui::test]
 3235async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3236    init_test(cx, |_| {});
 3237
 3238    let mut cx = EditorTestContext::new(cx).await;
 3239
 3240    // Test sort_lines_case_insensitive()
 3241    cx.set_state(indoc! {"
 3242        «z
 3243        y
 3244        x
 3245        Z
 3246        Y
 3247        Xˇ»
 3248    "});
 3249    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3250    cx.assert_editor_state(indoc! {"
 3251        «x
 3252        X
 3253        y
 3254        Y
 3255        z
 3256        Zˇ»
 3257    "});
 3258
 3259    // Test reverse_lines()
 3260    cx.set_state(indoc! {"
 3261        «5
 3262        4
 3263        3
 3264        2
 3265        1ˇ»
 3266    "});
 3267    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        «1
 3270        2
 3271        3
 3272        4
 3273        5ˇ»
 3274    "});
 3275
 3276    // Skip testing shuffle_line()
 3277
 3278    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3279    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3280
 3281    // Don't manipulate when cursor is on single line, but expand the selection
 3282    cx.set_state(indoc! {"
 3283        ddˇdd
 3284        ccc
 3285        bb
 3286        a
 3287    "});
 3288    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3289    cx.assert_editor_state(indoc! {"
 3290        «ddddˇ»
 3291        ccc
 3292        bb
 3293        a
 3294    "});
 3295
 3296    // Basic manipulate case
 3297    // Start selection moves to column 0
 3298    // End of selection shrinks to fit shorter line
 3299    cx.set_state(indoc! {"
 3300        dd«d
 3301        ccc
 3302        bb
 3303        aaaaaˇ»
 3304    "});
 3305    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3306    cx.assert_editor_state(indoc! {"
 3307        «aaaaa
 3308        bb
 3309        ccc
 3310        dddˇ»
 3311    "});
 3312
 3313    // Manipulate case with newlines
 3314    cx.set_state(indoc! {"
 3315        dd«d
 3316        ccc
 3317
 3318        bb
 3319        aaaaa
 3320
 3321        ˇ»
 3322    "});
 3323    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3324    cx.assert_editor_state(indoc! {"
 3325        «
 3326
 3327        aaaaa
 3328        bb
 3329        ccc
 3330        dddˇ»
 3331
 3332    "});
 3333
 3334    // Adding new line
 3335    cx.set_state(indoc! {"
 3336        aa«a
 3337        bbˇ»b
 3338    "});
 3339    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3340    cx.assert_editor_state(indoc! {"
 3341        «aaa
 3342        bbb
 3343        added_lineˇ»
 3344    "});
 3345
 3346    // Removing line
 3347    cx.set_state(indoc! {"
 3348        aa«a
 3349        bbbˇ»
 3350    "});
 3351    cx.update_editor(|e, cx| {
 3352        e.manipulate_lines(cx, |lines| {
 3353            lines.pop();
 3354        })
 3355    });
 3356    cx.assert_editor_state(indoc! {"
 3357        «aaaˇ»
 3358    "});
 3359
 3360    // Removing all lines
 3361    cx.set_state(indoc! {"
 3362        aa«a
 3363        bbbˇ»
 3364    "});
 3365    cx.update_editor(|e, cx| {
 3366        e.manipulate_lines(cx, |lines| {
 3367            lines.drain(..);
 3368        })
 3369    });
 3370    cx.assert_editor_state(indoc! {"
 3371        ˇ
 3372    "});
 3373}
 3374
 3375#[gpui::test]
 3376async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3377    init_test(cx, |_| {});
 3378
 3379    let mut cx = EditorTestContext::new(cx).await;
 3380
 3381    // Consider continuous selection as single selection
 3382    cx.set_state(indoc! {"
 3383        Aaa«aa
 3384        cˇ»c«c
 3385        bb
 3386        aaaˇ»aa
 3387    "});
 3388    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3389    cx.assert_editor_state(indoc! {"
 3390        «Aaaaa
 3391        ccc
 3392        bb
 3393        aaaaaˇ»
 3394    "});
 3395
 3396    cx.set_state(indoc! {"
 3397        Aaa«aa
 3398        cˇ»c«c
 3399        bb
 3400        aaaˇ»aa
 3401    "});
 3402    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        «Aaaaa
 3405        ccc
 3406        bbˇ»
 3407    "});
 3408
 3409    // Consider non continuous selection as distinct dedup operations
 3410    cx.set_state(indoc! {"
 3411        «aaaaa
 3412        bb
 3413        aaaaa
 3414        aaaaaˇ»
 3415
 3416        aaa«aaˇ»
 3417    "});
 3418    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3419    cx.assert_editor_state(indoc! {"
 3420        «aaaaa
 3421        bbˇ»
 3422
 3423        «aaaaaˇ»
 3424    "});
 3425}
 3426
 3427#[gpui::test]
 3428async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3429    init_test(cx, |_| {});
 3430
 3431    let mut cx = EditorTestContext::new(cx).await;
 3432
 3433    cx.set_state(indoc! {"
 3434        «Aaa
 3435        aAa
 3436        Aaaˇ»
 3437    "});
 3438    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3439    cx.assert_editor_state(indoc! {"
 3440        «Aaa
 3441        aAaˇ»
 3442    "});
 3443
 3444    cx.set_state(indoc! {"
 3445        «Aaa
 3446        aAa
 3447        aaAˇ»
 3448    "});
 3449    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3450    cx.assert_editor_state(indoc! {"
 3451        «Aaaˇ»
 3452    "});
 3453}
 3454
 3455#[gpui::test]
 3456async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    let mut cx = EditorTestContext::new(cx).await;
 3460
 3461    // Manipulate with multiple selections on a single line
 3462    cx.set_state(indoc! {"
 3463        dd«dd
 3464        cˇ»c«c
 3465        bb
 3466        aaaˇ»aa
 3467    "});
 3468    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3469    cx.assert_editor_state(indoc! {"
 3470        «aaaaa
 3471        bb
 3472        ccc
 3473        ddddˇ»
 3474    "});
 3475
 3476    // Manipulate with multiple disjoin selections
 3477    cx.set_state(indoc! {"
 3478 3479        4
 3480        3
 3481        2
 3482        1ˇ»
 3483
 3484        dd«dd
 3485        ccc
 3486        bb
 3487        aaaˇ»aa
 3488    "});
 3489    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        «1
 3492        2
 3493        3
 3494        4
 3495        5ˇ»
 3496
 3497        «aaaaa
 3498        bb
 3499        ccc
 3500        ddddˇ»
 3501    "});
 3502
 3503    // Adding lines on each selection
 3504    cx.set_state(indoc! {"
 3505 3506        1ˇ»
 3507
 3508        bb«bb
 3509        aaaˇ»aa
 3510    "});
 3511    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3512    cx.assert_editor_state(indoc! {"
 3513        «2
 3514        1
 3515        added lineˇ»
 3516
 3517        «bbbb
 3518        aaaaa
 3519        added lineˇ»
 3520    "});
 3521
 3522    // Removing lines on each selection
 3523    cx.set_state(indoc! {"
 3524 3525        1ˇ»
 3526
 3527        bb«bb
 3528        aaaˇ»aa
 3529    "});
 3530    cx.update_editor(|e, cx| {
 3531        e.manipulate_lines(cx, |lines| {
 3532            lines.pop();
 3533        })
 3534    });
 3535    cx.assert_editor_state(indoc! {"
 3536        «2ˇ»
 3537
 3538        «bbbbˇ»
 3539    "});
 3540}
 3541
 3542#[gpui::test]
 3543async fn test_manipulate_text(cx: &mut TestAppContext) {
 3544    init_test(cx, |_| {});
 3545
 3546    let mut cx = EditorTestContext::new(cx).await;
 3547
 3548    // Test convert_to_upper_case()
 3549    cx.set_state(indoc! {"
 3550        «hello worldˇ»
 3551    "});
 3552    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3553    cx.assert_editor_state(indoc! {"
 3554        «HELLO WORLDˇ»
 3555    "});
 3556
 3557    // Test convert_to_lower_case()
 3558    cx.set_state(indoc! {"
 3559        «HELLO WORLDˇ»
 3560    "});
 3561    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3562    cx.assert_editor_state(indoc! {"
 3563        «hello worldˇ»
 3564    "});
 3565
 3566    // Test multiple line, single selection case
 3567    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3568    cx.set_state(indoc! {"
 3569        «The quick brown
 3570        fox jumps over
 3571        the lazy dogˇ»
 3572    "});
 3573    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3574    cx.assert_editor_state(indoc! {"
 3575        «The Quick Brown
 3576        Fox Jumps Over
 3577        The Lazy Dogˇ»
 3578    "});
 3579
 3580    // Test multiple line, single selection case
 3581    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3582    cx.set_state(indoc! {"
 3583        «The quick brown
 3584        fox jumps over
 3585        the lazy dogˇ»
 3586    "});
 3587    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3588    cx.assert_editor_state(indoc! {"
 3589        «TheQuickBrown
 3590        FoxJumpsOver
 3591        TheLazyDogˇ»
 3592    "});
 3593
 3594    // From here on out, test more complex cases of manipulate_text()
 3595
 3596    // Test no selection case - should affect words cursors are in
 3597    // Cursor at beginning, middle, and end of word
 3598    cx.set_state(indoc! {"
 3599        ˇhello big beauˇtiful worldˇ
 3600    "});
 3601    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3602    cx.assert_editor_state(indoc! {"
 3603        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3604    "});
 3605
 3606    // Test multiple selections on a single line and across multiple lines
 3607    cx.set_state(indoc! {"
 3608        «Theˇ» quick «brown
 3609        foxˇ» jumps «overˇ»
 3610        the «lazyˇ» dog
 3611    "});
 3612    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3613    cx.assert_editor_state(indoc! {"
 3614        «THEˇ» quick «BROWN
 3615        FOXˇ» jumps «OVERˇ»
 3616        the «LAZYˇ» dog
 3617    "});
 3618
 3619    // Test case where text length grows
 3620    cx.set_state(indoc! {"
 3621        «tschüߡ»
 3622    "});
 3623    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3624    cx.assert_editor_state(indoc! {"
 3625        «TSCHÜSSˇ»
 3626    "});
 3627
 3628    // Test to make sure we don't crash when text shrinks
 3629    cx.set_state(indoc! {"
 3630        aaa_bbbˇ
 3631    "});
 3632    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3633    cx.assert_editor_state(indoc! {"
 3634        «aaaBbbˇ»
 3635    "});
 3636
 3637    // Test to make sure we all aware of the fact that each word can grow and shrink
 3638    // Final selections should be aware of this fact
 3639    cx.set_state(indoc! {"
 3640        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3641    "});
 3642    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3643    cx.assert_editor_state(indoc! {"
 3644        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3645    "});
 3646
 3647    cx.set_state(indoc! {"
 3648        «hElLo, WoRld!ˇ»
 3649    "});
 3650    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3651    cx.assert_editor_state(indoc! {"
 3652        «HeLlO, wOrLD!ˇ»
 3653    "});
 3654}
 3655
 3656#[gpui::test]
 3657fn test_duplicate_line(cx: &mut TestAppContext) {
 3658    init_test(cx, |_| {});
 3659
 3660    let view = cx.add_window(|cx| {
 3661        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3662        build_editor(buffer, cx)
 3663    });
 3664    _ = view.update(cx, |view, cx| {
 3665        view.change_selections(None, cx, |s| {
 3666            s.select_display_ranges([
 3667                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3669                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3670                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3671            ])
 3672        });
 3673        view.duplicate_line_down(&DuplicateLineDown, cx);
 3674        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3675        assert_eq!(
 3676            view.selections.display_ranges(cx),
 3677            vec![
 3678                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3679                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3680                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3681                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3682            ]
 3683        );
 3684    });
 3685
 3686    let view = cx.add_window(|cx| {
 3687        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3688        build_editor(buffer, cx)
 3689    });
 3690    _ = view.update(cx, |view, cx| {
 3691        view.change_selections(None, cx, |s| {
 3692            s.select_display_ranges([
 3693                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3694                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3695            ])
 3696        });
 3697        view.duplicate_line_down(&DuplicateLineDown, cx);
 3698        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3699        assert_eq!(
 3700            view.selections.display_ranges(cx),
 3701            vec![
 3702                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3703                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3704            ]
 3705        );
 3706    });
 3707
 3708    // With `move_upwards` the selections stay in place, except for
 3709    // the lines inserted above them
 3710    let view = cx.add_window(|cx| {
 3711        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3712        build_editor(buffer, cx)
 3713    });
 3714    _ = view.update(cx, |view, cx| {
 3715        view.change_selections(None, cx, |s| {
 3716            s.select_display_ranges([
 3717                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3718                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3719                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3720                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3721            ])
 3722        });
 3723        view.duplicate_line_up(&DuplicateLineUp, cx);
 3724        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3725        assert_eq!(
 3726            view.selections.display_ranges(cx),
 3727            vec![
 3728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3729                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3730                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3731                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3732            ]
 3733        );
 3734    });
 3735
 3736    let view = cx.add_window(|cx| {
 3737        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3738        build_editor(buffer, cx)
 3739    });
 3740    _ = view.update(cx, |view, cx| {
 3741        view.change_selections(None, cx, |s| {
 3742            s.select_display_ranges([
 3743                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3744                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3745            ])
 3746        });
 3747        view.duplicate_line_up(&DuplicateLineUp, cx);
 3748        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3749        assert_eq!(
 3750            view.selections.display_ranges(cx),
 3751            vec![
 3752                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3753                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3754            ]
 3755        );
 3756    });
 3757}
 3758
 3759#[gpui::test]
 3760fn test_move_line_up_down(cx: &mut TestAppContext) {
 3761    init_test(cx, |_| {});
 3762
 3763    let view = cx.add_window(|cx| {
 3764        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3765        build_editor(buffer, cx)
 3766    });
 3767    _ = view.update(cx, |view, cx| {
 3768        view.fold_ranges(
 3769            vec![
 3770                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3771                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3772                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3773            ],
 3774            true,
 3775            cx,
 3776        );
 3777        view.change_selections(None, cx, |s| {
 3778            s.select_display_ranges([
 3779                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3780                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3781                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3782                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3783            ])
 3784        });
 3785        assert_eq!(
 3786            view.display_text(cx),
 3787            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3788        );
 3789
 3790        view.move_line_up(&MoveLineUp, cx);
 3791        assert_eq!(
 3792            view.display_text(cx),
 3793            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3794        );
 3795        assert_eq!(
 3796            view.selections.display_ranges(cx),
 3797            vec![
 3798                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3799                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3800                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3801                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3802            ]
 3803        );
 3804    });
 3805
 3806    _ = view.update(cx, |view, cx| {
 3807        view.move_line_down(&MoveLineDown, cx);
 3808        assert_eq!(
 3809            view.display_text(cx),
 3810            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3811        );
 3812        assert_eq!(
 3813            view.selections.display_ranges(cx),
 3814            vec![
 3815                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3816                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3817                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3818                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3819            ]
 3820        );
 3821    });
 3822
 3823    _ = view.update(cx, |view, cx| {
 3824        view.move_line_down(&MoveLineDown, cx);
 3825        assert_eq!(
 3826            view.display_text(cx),
 3827            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3828        );
 3829        assert_eq!(
 3830            view.selections.display_ranges(cx),
 3831            vec![
 3832                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3833                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3834                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3835                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3836            ]
 3837        );
 3838    });
 3839
 3840    _ = view.update(cx, |view, cx| {
 3841        view.move_line_up(&MoveLineUp, cx);
 3842        assert_eq!(
 3843            view.display_text(cx),
 3844            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3845        );
 3846        assert_eq!(
 3847            view.selections.display_ranges(cx),
 3848            vec![
 3849                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3850                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3851                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3852                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3853            ]
 3854        );
 3855    });
 3856}
 3857
 3858#[gpui::test]
 3859fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3860    init_test(cx, |_| {});
 3861
 3862    let editor = cx.add_window(|cx| {
 3863        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3864        build_editor(buffer, cx)
 3865    });
 3866    _ = editor.update(cx, |editor, cx| {
 3867        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3868        editor.insert_blocks(
 3869            [BlockProperties {
 3870                style: BlockStyle::Fixed,
 3871                position: snapshot.anchor_after(Point::new(2, 0)),
 3872                disposition: BlockDisposition::Below,
 3873                height: 1,
 3874                render: Box::new(|_| div().into_any()),
 3875                priority: 0,
 3876            }],
 3877            Some(Autoscroll::fit()),
 3878            cx,
 3879        );
 3880        editor.change_selections(None, cx, |s| {
 3881            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3882        });
 3883        editor.move_line_down(&MoveLineDown, cx);
 3884    });
 3885}
 3886
 3887#[gpui::test]
 3888fn test_transpose(cx: &mut TestAppContext) {
 3889    init_test(cx, |_| {});
 3890
 3891    _ = cx.add_window(|cx| {
 3892        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3893        editor.set_style(EditorStyle::default(), cx);
 3894        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3895        editor.transpose(&Default::default(), cx);
 3896        assert_eq!(editor.text(cx), "bac");
 3897        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3898
 3899        editor.transpose(&Default::default(), cx);
 3900        assert_eq!(editor.text(cx), "bca");
 3901        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3902
 3903        editor.transpose(&Default::default(), cx);
 3904        assert_eq!(editor.text(cx), "bac");
 3905        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3906
 3907        editor
 3908    });
 3909
 3910    _ = cx.add_window(|cx| {
 3911        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3912        editor.set_style(EditorStyle::default(), cx);
 3913        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3914        editor.transpose(&Default::default(), cx);
 3915        assert_eq!(editor.text(cx), "acb\nde");
 3916        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3917
 3918        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3919        editor.transpose(&Default::default(), cx);
 3920        assert_eq!(editor.text(cx), "acbd\ne");
 3921        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3922
 3923        editor.transpose(&Default::default(), cx);
 3924        assert_eq!(editor.text(cx), "acbde\n");
 3925        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3926
 3927        editor.transpose(&Default::default(), cx);
 3928        assert_eq!(editor.text(cx), "acbd\ne");
 3929        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3930
 3931        editor
 3932    });
 3933
 3934    _ = cx.add_window(|cx| {
 3935        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3936        editor.set_style(EditorStyle::default(), cx);
 3937        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3938        editor.transpose(&Default::default(), cx);
 3939        assert_eq!(editor.text(cx), "bacd\ne");
 3940        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3941
 3942        editor.transpose(&Default::default(), cx);
 3943        assert_eq!(editor.text(cx), "bcade\n");
 3944        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3945
 3946        editor.transpose(&Default::default(), cx);
 3947        assert_eq!(editor.text(cx), "bcda\ne");
 3948        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3949
 3950        editor.transpose(&Default::default(), cx);
 3951        assert_eq!(editor.text(cx), "bcade\n");
 3952        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3953
 3954        editor.transpose(&Default::default(), cx);
 3955        assert_eq!(editor.text(cx), "bcaed\n");
 3956        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3957
 3958        editor
 3959    });
 3960
 3961    _ = cx.add_window(|cx| {
 3962        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3963        editor.set_style(EditorStyle::default(), cx);
 3964        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3965        editor.transpose(&Default::default(), cx);
 3966        assert_eq!(editor.text(cx), "🏀🍐✋");
 3967        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3968
 3969        editor.transpose(&Default::default(), cx);
 3970        assert_eq!(editor.text(cx), "🏀✋🍐");
 3971        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3972
 3973        editor.transpose(&Default::default(), cx);
 3974        assert_eq!(editor.text(cx), "🏀🍐✋");
 3975        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3976
 3977        editor
 3978    });
 3979}
 3980
 3981#[gpui::test]
 3982async fn test_rewrap(cx: &mut TestAppContext) {
 3983    init_test(cx, |_| {});
 3984
 3985    let mut cx = EditorTestContext::new(cx).await;
 3986
 3987    {
 3988        let language = Arc::new(Language::new(
 3989            LanguageConfig {
 3990                line_comments: vec!["// ".into()],
 3991                ..LanguageConfig::default()
 3992            },
 3993            None,
 3994        ));
 3995        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3996
 3997        let unwrapped_text = indoc! {"
 3998            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 3999        "};
 4000
 4001        let wrapped_text = indoc! {"
 4002            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4003            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4004            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4005            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4006            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4007            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4008            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4009            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4010            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4011            // porttitor id. Aliquam id accumsan eros.ˇ
 4012        "};
 4013
 4014        cx.set_state(unwrapped_text);
 4015        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4016        cx.assert_editor_state(wrapped_text);
 4017    }
 4018
 4019    // Test that rewrapping works inside of a selection
 4020    {
 4021        let language = Arc::new(Language::new(
 4022            LanguageConfig {
 4023                line_comments: vec!["// ".into()],
 4024                ..LanguageConfig::default()
 4025            },
 4026            None,
 4027        ));
 4028        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4029
 4030        let unwrapped_text = indoc! {"
 4031            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4032        "};
 4033
 4034        let wrapped_text = indoc! {"
 4035            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4036            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4037            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4038            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4039            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4040            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4041            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4042            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4043            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4044            // porttitor id. Aliquam id accumsan eros.ˇ
 4045        "};
 4046
 4047        cx.set_state(unwrapped_text);
 4048        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4049        cx.assert_editor_state(wrapped_text);
 4050    }
 4051
 4052    // Test that cursors that expand to the same region are collapsed.
 4053    {
 4054        let language = Arc::new(Language::new(
 4055            LanguageConfig {
 4056                line_comments: vec!["// ".into()],
 4057                ..LanguageConfig::default()
 4058            },
 4059            None,
 4060        ));
 4061        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4062
 4063        let unwrapped_text = indoc! {"
 4064            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4065            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4066            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4067            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4068        "};
 4069
 4070        let wrapped_text = indoc! {"
 4071            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4072            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4073            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4074            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4075            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4076            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4077            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4078            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4079            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4080            // porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
 4081        "};
 4082
 4083        cx.set_state(unwrapped_text);
 4084        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4085        cx.assert_editor_state(wrapped_text);
 4086    }
 4087
 4088    // Test that non-contiguous selections are treated separately.
 4089    {
 4090        let language = Arc::new(Language::new(
 4091            LanguageConfig {
 4092                line_comments: vec!["// ".into()],
 4093                ..LanguageConfig::default()
 4094            },
 4095            None,
 4096        ));
 4097        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4098
 4099        let unwrapped_text = indoc! {"
 4100            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4101            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4102            //
 4103            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4104            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4105        "};
 4106
 4107        let wrapped_text = indoc! {"
 4108            // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4109            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4110            // auctor, eu lacinia sapien scelerisque.ˇˇ
 4111            //
 4112            // Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4113            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4114            // blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4115            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4116            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4117            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4118            // vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
 4119        "};
 4120
 4121        cx.set_state(unwrapped_text);
 4122        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4123        cx.assert_editor_state(wrapped_text);
 4124    }
 4125
 4126    // Test that different comment prefixes are supported.
 4127    {
 4128        let language = Arc::new(Language::new(
 4129            LanguageConfig {
 4130                line_comments: vec!["# ".into()],
 4131                ..LanguageConfig::default()
 4132            },
 4133            None,
 4134        ));
 4135        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4136
 4137        let unwrapped_text = indoc! {"
 4138            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4139        "};
 4140
 4141        let wrapped_text = indoc! {"
 4142            # Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4143            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4144            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4145            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4146            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4147            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4148            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4149            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4150            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4151            # accumsan eros.ˇ
 4152        "};
 4153
 4154        cx.set_state(unwrapped_text);
 4155        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4156        cx.assert_editor_state(wrapped_text);
 4157    }
 4158
 4159    // Test that rewrapping is ignored outside of comments in most languages.
 4160    {
 4161        let language = Arc::new(Language::new(
 4162            LanguageConfig {
 4163                line_comments: vec!["// ".into(), "/// ".into()],
 4164                ..LanguageConfig::default()
 4165            },
 4166            Some(tree_sitter_rust::LANGUAGE.into()),
 4167        ));
 4168        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4169
 4170        let unwrapped_text = indoc! {"
 4171            /// Adds two numbers.
 4172            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4173            fn add(a: u32, b: u32) -> u32 {
 4174                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4175            }
 4176        "};
 4177
 4178        let wrapped_text = indoc! {"
 4179            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4180            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4181            fn add(a: u32, b: u32) -> u32 {
 4182                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4183            }
 4184        "};
 4185
 4186        cx.set_state(unwrapped_text);
 4187        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4188        cx.assert_editor_state(wrapped_text);
 4189    }
 4190
 4191    // Test that rewrapping works in Markdown and Plain Text languages.
 4192    {
 4193        let markdown_language = Arc::new(Language::new(
 4194            LanguageConfig {
 4195                name: "Markdown".into(),
 4196                ..LanguageConfig::default()
 4197            },
 4198            None,
 4199        ));
 4200        cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
 4201
 4202        let unwrapped_text = indoc! {"
 4203            # Hello
 4204
 4205            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4206        "};
 4207
 4208        let wrapped_text = indoc! {"
 4209            # Hello
 4210
 4211            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4212            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4213            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4214            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4215            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4216            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4217            Integer sit amet scelerisque nisi.ˇ
 4218        "};
 4219
 4220        cx.set_state(unwrapped_text);
 4221        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4222        cx.assert_editor_state(wrapped_text);
 4223
 4224        let plaintext_language = Arc::new(Language::new(
 4225            LanguageConfig {
 4226                name: "Plain Text".into(),
 4227                ..LanguageConfig::default()
 4228            },
 4229            None,
 4230        ));
 4231        cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
 4232
 4233        let unwrapped_text = indoc! {"
 4234            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4235        "};
 4236
 4237        let wrapped_text = indoc! {"
 4238            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4239            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4240            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4241            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4242            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4243            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4244            Integer sit amet scelerisque nisi.ˇ
 4245        "};
 4246
 4247        cx.set_state(unwrapped_text);
 4248        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4249        cx.assert_editor_state(wrapped_text);
 4250    }
 4251
 4252    // Test rewrapping unaligned comments in a selection.
 4253    {
 4254        let language = Arc::new(Language::new(
 4255            LanguageConfig {
 4256                line_comments: vec!["// ".into(), "/// ".into()],
 4257                ..LanguageConfig::default()
 4258            },
 4259            Some(tree_sitter_rust::LANGUAGE.into()),
 4260        ));
 4261        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4262
 4263        let unwrapped_text = indoc! {"
 4264            fn foo() {
 4265                if true {
 4266            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4267            // Praesent semper egestas tellus id dignissim.ˇ»
 4268                    do_something();
 4269                } else {
 4270                    //
 4271                }
 4272            }
 4273        "};
 4274
 4275        let wrapped_text = indoc! {"
 4276            fn foo() {
 4277                if true {
 4278                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4279                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4280                    // egestas tellus id dignissim.ˇ
 4281                    do_something();
 4282                } else {
 4283                    //
 4284                }
 4285            }
 4286        "};
 4287
 4288        cx.set_state(unwrapped_text);
 4289        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4290        cx.assert_editor_state(wrapped_text);
 4291
 4292        let unwrapped_text = indoc! {"
 4293            fn foo() {
 4294                if true {
 4295            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4296            // Praesent semper egestas tellus id dignissim.»
 4297                    do_something();
 4298                } else {
 4299                    //
 4300                }
 4301
 4302            }
 4303        "};
 4304
 4305        let wrapped_text = indoc! {"
 4306            fn foo() {
 4307                if true {
 4308                    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4309                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4310                    // egestas tellus id dignissim.ˇ
 4311                    do_something();
 4312                } else {
 4313                    //
 4314                }
 4315
 4316            }
 4317        "};
 4318
 4319        cx.set_state(unwrapped_text);
 4320        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4321        cx.assert_editor_state(wrapped_text);
 4322    }
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4327    init_test(cx, |_| {});
 4328
 4329    let mut cx = EditorTestContext::new(cx).await;
 4330
 4331    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4332    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4333    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4334
 4335    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4336    cx.set_state("two ˇfour ˇsix ˇ");
 4337    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4338    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4339
 4340    // Paste again but with only two cursors. Since the number of cursors doesn't
 4341    // match the number of slices in the clipboard, the entire clipboard text
 4342    // is pasted at each cursor.
 4343    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4344    cx.update_editor(|e, cx| {
 4345        e.handle_input("( ", cx);
 4346        e.paste(&Paste, cx);
 4347        e.handle_input(") ", cx);
 4348    });
 4349    cx.assert_editor_state(
 4350        &([
 4351            "( one✅ ",
 4352            "three ",
 4353            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4354            "three ",
 4355            "five ) ˇ",
 4356        ]
 4357        .join("\n")),
 4358    );
 4359
 4360    // Cut with three selections, one of which is full-line.
 4361    cx.set_state(indoc! {"
 4362        1«2ˇ»3
 4363        4ˇ567
 4364        «8ˇ»9"});
 4365    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4366    cx.assert_editor_state(indoc! {"
 4367        1ˇ3
 4368        ˇ9"});
 4369
 4370    // Paste with three selections, noticing how the copied selection that was full-line
 4371    // gets inserted before the second cursor.
 4372    cx.set_state(indoc! {"
 4373        1ˇ3
 4374 4375        «oˇ»ne"});
 4376    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4377    cx.assert_editor_state(indoc! {"
 4378        12ˇ3
 4379        4567
 4380 4381        8ˇne"});
 4382
 4383    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4384    cx.set_state(indoc! {"
 4385        The quick brown
 4386        fox juˇmps over
 4387        the lazy dog"});
 4388    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4389    assert_eq!(
 4390        cx.read_from_clipboard()
 4391            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4392        Some("fox jumps over\n".to_string())
 4393    );
 4394
 4395    // Paste with three selections, noticing how the copied full-line selection is inserted
 4396    // before the empty selections but replaces the selection that is non-empty.
 4397    cx.set_state(indoc! {"
 4398        Tˇhe quick brown
 4399        «foˇ»x jumps over
 4400        tˇhe lazy dog"});
 4401    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4402    cx.assert_editor_state(indoc! {"
 4403        fox jumps over
 4404        Tˇhe quick brown
 4405        fox jumps over
 4406        ˇx jumps over
 4407        fox jumps over
 4408        tˇhe lazy dog"});
 4409}
 4410
 4411#[gpui::test]
 4412async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4413    init_test(cx, |_| {});
 4414
 4415    let mut cx = EditorTestContext::new(cx).await;
 4416    let language = Arc::new(Language::new(
 4417        LanguageConfig::default(),
 4418        Some(tree_sitter_rust::LANGUAGE.into()),
 4419    ));
 4420    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4421
 4422    // Cut an indented block, without the leading whitespace.
 4423    cx.set_state(indoc! {"
 4424        const a: B = (
 4425            c(),
 4426            «d(
 4427                e,
 4428                f
 4429            )ˇ»
 4430        );
 4431    "});
 4432    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4433    cx.assert_editor_state(indoc! {"
 4434        const a: B = (
 4435            c(),
 4436            ˇ
 4437        );
 4438    "});
 4439
 4440    // Paste it at the same position.
 4441    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4442    cx.assert_editor_state(indoc! {"
 4443        const a: B = (
 4444            c(),
 4445            d(
 4446                e,
 4447                f
 4448 4449        );
 4450    "});
 4451
 4452    // Paste it at a line with a lower indent level.
 4453    cx.set_state(indoc! {"
 4454        ˇ
 4455        const a: B = (
 4456            c(),
 4457        );
 4458    "});
 4459    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4460    cx.assert_editor_state(indoc! {"
 4461        d(
 4462            e,
 4463            f
 4464 4465        const a: B = (
 4466            c(),
 4467        );
 4468    "});
 4469
 4470    // Cut an indented block, with the leading whitespace.
 4471    cx.set_state(indoc! {"
 4472        const a: B = (
 4473            c(),
 4474        «    d(
 4475                e,
 4476                f
 4477            )
 4478        ˇ»);
 4479    "});
 4480    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4481    cx.assert_editor_state(indoc! {"
 4482        const a: B = (
 4483            c(),
 4484        ˇ);
 4485    "});
 4486
 4487    // Paste it at the same position.
 4488    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4489    cx.assert_editor_state(indoc! {"
 4490        const a: B = (
 4491            c(),
 4492            d(
 4493                e,
 4494                f
 4495            )
 4496        ˇ);
 4497    "});
 4498
 4499    // Paste it at a line with a higher indent level.
 4500    cx.set_state(indoc! {"
 4501        const a: B = (
 4502            c(),
 4503            d(
 4504                e,
 4505 4506            )
 4507        );
 4508    "});
 4509    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4510    cx.assert_editor_state(indoc! {"
 4511        const a: B = (
 4512            c(),
 4513            d(
 4514                e,
 4515                f    d(
 4516                    e,
 4517                    f
 4518                )
 4519        ˇ
 4520            )
 4521        );
 4522    "});
 4523}
 4524
 4525#[gpui::test]
 4526fn test_select_all(cx: &mut TestAppContext) {
 4527    init_test(cx, |_| {});
 4528
 4529    let view = cx.add_window(|cx| {
 4530        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4531        build_editor(buffer, cx)
 4532    });
 4533    _ = view.update(cx, |view, cx| {
 4534        view.select_all(&SelectAll, cx);
 4535        assert_eq!(
 4536            view.selections.display_ranges(cx),
 4537            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4538        );
 4539    });
 4540}
 4541
 4542#[gpui::test]
 4543fn test_select_line(cx: &mut TestAppContext) {
 4544    init_test(cx, |_| {});
 4545
 4546    let view = cx.add_window(|cx| {
 4547        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4548        build_editor(buffer, cx)
 4549    });
 4550    _ = view.update(cx, |view, cx| {
 4551        view.change_selections(None, cx, |s| {
 4552            s.select_display_ranges([
 4553                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4554                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4555                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4556                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4557            ])
 4558        });
 4559        view.select_line(&SelectLine, cx);
 4560        assert_eq!(
 4561            view.selections.display_ranges(cx),
 4562            vec![
 4563                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4564                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4565            ]
 4566        );
 4567    });
 4568
 4569    _ = view.update(cx, |view, cx| {
 4570        view.select_line(&SelectLine, cx);
 4571        assert_eq!(
 4572            view.selections.display_ranges(cx),
 4573            vec![
 4574                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4575                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4576            ]
 4577        );
 4578    });
 4579
 4580    _ = view.update(cx, |view, cx| {
 4581        view.select_line(&SelectLine, cx);
 4582        assert_eq!(
 4583            view.selections.display_ranges(cx),
 4584            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4585        );
 4586    });
 4587}
 4588
 4589#[gpui::test]
 4590fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4591    init_test(cx, |_| {});
 4592
 4593    let view = cx.add_window(|cx| {
 4594        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4595        build_editor(buffer, cx)
 4596    });
 4597    _ = view.update(cx, |view, cx| {
 4598        view.fold_ranges(
 4599            vec![
 4600                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4601                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4602                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4603            ],
 4604            true,
 4605            cx,
 4606        );
 4607        view.change_selections(None, cx, |s| {
 4608            s.select_display_ranges([
 4609                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4610                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4611                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4612                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4613            ])
 4614        });
 4615        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4616    });
 4617
 4618    _ = view.update(cx, |view, cx| {
 4619        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4620        assert_eq!(
 4621            view.display_text(cx),
 4622            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4623        );
 4624        assert_eq!(
 4625            view.selections.display_ranges(cx),
 4626            [
 4627                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4628                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4629                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4630                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4631            ]
 4632        );
 4633    });
 4634
 4635    _ = view.update(cx, |view, cx| {
 4636        view.change_selections(None, cx, |s| {
 4637            s.select_display_ranges([
 4638                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4639            ])
 4640        });
 4641        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4642        assert_eq!(
 4643            view.display_text(cx),
 4644            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4645        );
 4646        assert_eq!(
 4647            view.selections.display_ranges(cx),
 4648            [
 4649                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4650                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4651                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4652                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4653                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4654                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4655                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4656                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4657            ]
 4658        );
 4659    });
 4660}
 4661
 4662#[gpui::test]
 4663async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4664    init_test(cx, |_| {});
 4665
 4666    let mut cx = EditorTestContext::new(cx).await;
 4667
 4668    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4669    cx.set_state(indoc!(
 4670        r#"abc
 4671           defˇghi
 4672
 4673           jk
 4674           nlmo
 4675           "#
 4676    ));
 4677
 4678    cx.update_editor(|editor, cx| {
 4679        editor.add_selection_above(&Default::default(), cx);
 4680    });
 4681
 4682    cx.assert_editor_state(indoc!(
 4683        r#"abcˇ
 4684           defˇghi
 4685
 4686           jk
 4687           nlmo
 4688           "#
 4689    ));
 4690
 4691    cx.update_editor(|editor, cx| {
 4692        editor.add_selection_above(&Default::default(), cx);
 4693    });
 4694
 4695    cx.assert_editor_state(indoc!(
 4696        r#"abcˇ
 4697            defˇghi
 4698
 4699            jk
 4700            nlmo
 4701            "#
 4702    ));
 4703
 4704    cx.update_editor(|view, cx| {
 4705        view.add_selection_below(&Default::default(), cx);
 4706    });
 4707
 4708    cx.assert_editor_state(indoc!(
 4709        r#"abc
 4710           defˇghi
 4711
 4712           jk
 4713           nlmo
 4714           "#
 4715    ));
 4716
 4717    cx.update_editor(|view, cx| {
 4718        view.undo_selection(&Default::default(), cx);
 4719    });
 4720
 4721    cx.assert_editor_state(indoc!(
 4722        r#"abcˇ
 4723           defˇghi
 4724
 4725           jk
 4726           nlmo
 4727           "#
 4728    ));
 4729
 4730    cx.update_editor(|view, cx| {
 4731        view.redo_selection(&Default::default(), cx);
 4732    });
 4733
 4734    cx.assert_editor_state(indoc!(
 4735        r#"abc
 4736           defˇghi
 4737
 4738           jk
 4739           nlmo
 4740           "#
 4741    ));
 4742
 4743    cx.update_editor(|view, cx| {
 4744        view.add_selection_below(&Default::default(), cx);
 4745    });
 4746
 4747    cx.assert_editor_state(indoc!(
 4748        r#"abc
 4749           defˇghi
 4750
 4751           jk
 4752           nlmˇo
 4753           "#
 4754    ));
 4755
 4756    cx.update_editor(|view, cx| {
 4757        view.add_selection_below(&Default::default(), cx);
 4758    });
 4759
 4760    cx.assert_editor_state(indoc!(
 4761        r#"abc
 4762           defˇghi
 4763
 4764           jk
 4765           nlmˇo
 4766           "#
 4767    ));
 4768
 4769    // change selections
 4770    cx.set_state(indoc!(
 4771        r#"abc
 4772           def«ˇg»hi
 4773
 4774           jk
 4775           nlmo
 4776           "#
 4777    ));
 4778
 4779    cx.update_editor(|view, cx| {
 4780        view.add_selection_below(&Default::default(), cx);
 4781    });
 4782
 4783    cx.assert_editor_state(indoc!(
 4784        r#"abc
 4785           def«ˇg»hi
 4786
 4787           jk
 4788           nlm«ˇo»
 4789           "#
 4790    ));
 4791
 4792    cx.update_editor(|view, cx| {
 4793        view.add_selection_below(&Default::default(), cx);
 4794    });
 4795
 4796    cx.assert_editor_state(indoc!(
 4797        r#"abc
 4798           def«ˇg»hi
 4799
 4800           jk
 4801           nlm«ˇo»
 4802           "#
 4803    ));
 4804
 4805    cx.update_editor(|view, cx| {
 4806        view.add_selection_above(&Default::default(), cx);
 4807    });
 4808
 4809    cx.assert_editor_state(indoc!(
 4810        r#"abc
 4811           def«ˇg»hi
 4812
 4813           jk
 4814           nlmo
 4815           "#
 4816    ));
 4817
 4818    cx.update_editor(|view, cx| {
 4819        view.add_selection_above(&Default::default(), cx);
 4820    });
 4821
 4822    cx.assert_editor_state(indoc!(
 4823        r#"abc
 4824           def«ˇg»hi
 4825
 4826           jk
 4827           nlmo
 4828           "#
 4829    ));
 4830
 4831    // Change selections again
 4832    cx.set_state(indoc!(
 4833        r#"a«bc
 4834           defgˇ»hi
 4835
 4836           jk
 4837           nlmo
 4838           "#
 4839    ));
 4840
 4841    cx.update_editor(|view, cx| {
 4842        view.add_selection_below(&Default::default(), cx);
 4843    });
 4844
 4845    cx.assert_editor_state(indoc!(
 4846        r#"a«bcˇ»
 4847           d«efgˇ»hi
 4848
 4849           j«kˇ»
 4850           nlmo
 4851           "#
 4852    ));
 4853
 4854    cx.update_editor(|view, cx| {
 4855        view.add_selection_below(&Default::default(), cx);
 4856    });
 4857    cx.assert_editor_state(indoc!(
 4858        r#"a«bcˇ»
 4859           d«efgˇ»hi
 4860
 4861           j«kˇ»
 4862           n«lmoˇ»
 4863           "#
 4864    ));
 4865    cx.update_editor(|view, cx| {
 4866        view.add_selection_above(&Default::default(), cx);
 4867    });
 4868
 4869    cx.assert_editor_state(indoc!(
 4870        r#"a«bcˇ»
 4871           d«efgˇ»hi
 4872
 4873           j«kˇ»
 4874           nlmo
 4875           "#
 4876    ));
 4877
 4878    // Change selections again
 4879    cx.set_state(indoc!(
 4880        r#"abc
 4881           d«ˇefghi
 4882
 4883           jk
 4884           nlm»o
 4885           "#
 4886    ));
 4887
 4888    cx.update_editor(|view, cx| {
 4889        view.add_selection_above(&Default::default(), cx);
 4890    });
 4891
 4892    cx.assert_editor_state(indoc!(
 4893        r#"a«ˇbc»
 4894           d«ˇef»ghi
 4895
 4896           j«ˇk»
 4897           n«ˇlm»o
 4898           "#
 4899    ));
 4900
 4901    cx.update_editor(|view, cx| {
 4902        view.add_selection_below(&Default::default(), cx);
 4903    });
 4904
 4905    cx.assert_editor_state(indoc!(
 4906        r#"abc
 4907           d«ˇef»ghi
 4908
 4909           j«ˇk»
 4910           n«ˇlm»o
 4911           "#
 4912    ));
 4913}
 4914
 4915#[gpui::test]
 4916async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4917    init_test(cx, |_| {});
 4918
 4919    let mut cx = EditorTestContext::new(cx).await;
 4920    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4921
 4922    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4923        .unwrap();
 4924    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4925
 4926    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4927        .unwrap();
 4928    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4929
 4930    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4931    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4932
 4933    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4934    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4935
 4936    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4937        .unwrap();
 4938    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4939
 4940    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4941        .unwrap();
 4942    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4943}
 4944
 4945#[gpui::test]
 4946async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4947    init_test(cx, |_| {});
 4948
 4949    let mut cx = EditorTestContext::new(cx).await;
 4950    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4951
 4952    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4953        .unwrap();
 4954    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4955}
 4956
 4957#[gpui::test]
 4958async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4959    init_test(cx, |_| {});
 4960
 4961    let mut cx = EditorTestContext::new(cx).await;
 4962    cx.set_state(
 4963        r#"let foo = 2;
 4964lˇet foo = 2;
 4965let fooˇ = 2;
 4966let foo = 2;
 4967let foo = ˇ2;"#,
 4968    );
 4969
 4970    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4971        .unwrap();
 4972    cx.assert_editor_state(
 4973        r#"let foo = 2;
 4974«letˇ» foo = 2;
 4975let «fooˇ» = 2;
 4976let foo = 2;
 4977let foo = «2ˇ»;"#,
 4978    );
 4979
 4980    // noop for multiple selections with different contents
 4981    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4982        .unwrap();
 4983    cx.assert_editor_state(
 4984        r#"let foo = 2;
 4985«letˇ» foo = 2;
 4986let «fooˇ» = 2;
 4987let foo = 2;
 4988let foo = «2ˇ»;"#,
 4989    );
 4990}
 4991
 4992#[gpui::test]
 4993async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4994    init_test(cx, |_| {});
 4995
 4996    let mut cx =
 4997        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 4998
 4999    cx.assert_editor_state(indoc! {"
 5000        ˇbbb
 5001        ccc
 5002
 5003        bbb
 5004        ccc
 5005        "});
 5006    cx.dispatch_action(SelectPrevious::default());
 5007    cx.assert_editor_state(indoc! {"
 5008                «bbbˇ»
 5009                ccc
 5010
 5011                bbb
 5012                ccc
 5013                "});
 5014    cx.dispatch_action(SelectPrevious::default());
 5015    cx.assert_editor_state(indoc! {"
 5016                «bbbˇ»
 5017                ccc
 5018
 5019                «bbbˇ»
 5020                ccc
 5021                "});
 5022}
 5023
 5024#[gpui::test]
 5025async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5026    init_test(cx, |_| {});
 5027
 5028    let mut cx = EditorTestContext::new(cx).await;
 5029    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5030
 5031    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5032        .unwrap();
 5033    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5034
 5035    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5036        .unwrap();
 5037    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5038
 5039    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5040    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5041
 5042    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5043    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5044
 5045    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5046        .unwrap();
 5047    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5048
 5049    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5050        .unwrap();
 5051    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5052
 5053    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5054        .unwrap();
 5055    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5056}
 5057
 5058#[gpui::test]
 5059async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5060    init_test(cx, |_| {});
 5061
 5062    let mut cx = EditorTestContext::new(cx).await;
 5063    cx.set_state(
 5064        r#"let foo = 2;
 5065lˇet foo = 2;
 5066let fooˇ = 2;
 5067let foo = 2;
 5068let foo = ˇ2;"#,
 5069    );
 5070
 5071    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5072        .unwrap();
 5073    cx.assert_editor_state(
 5074        r#"let foo = 2;
 5075«letˇ» foo = 2;
 5076let «fooˇ» = 2;
 5077let foo = 2;
 5078let foo = «2ˇ»;"#,
 5079    );
 5080
 5081    // noop for multiple selections with different contents
 5082    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5083        .unwrap();
 5084    cx.assert_editor_state(
 5085        r#"let foo = 2;
 5086«letˇ» foo = 2;
 5087let «fooˇ» = 2;
 5088let foo = 2;
 5089let foo = «2ˇ»;"#,
 5090    );
 5091}
 5092
 5093#[gpui::test]
 5094async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5095    init_test(cx, |_| {});
 5096
 5097    let mut cx = EditorTestContext::new(cx).await;
 5098    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5099
 5100    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5101        .unwrap();
 5102    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5103
 5104    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5105        .unwrap();
 5106    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5107
 5108    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5109    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5110
 5111    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5112    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5113
 5114    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5115        .unwrap();
 5116    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5117
 5118    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5119        .unwrap();
 5120    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5121}
 5122
 5123#[gpui::test]
 5124async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5125    init_test(cx, |_| {});
 5126
 5127    let language = Arc::new(Language::new(
 5128        LanguageConfig::default(),
 5129        Some(tree_sitter_rust::LANGUAGE.into()),
 5130    ));
 5131
 5132    let text = r#"
 5133        use mod1::mod2::{mod3, mod4};
 5134
 5135        fn fn_1(param1: bool, param2: &str) {
 5136            let var1 = "text";
 5137        }
 5138    "#
 5139    .unindent();
 5140
 5141    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5142    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5143    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5144
 5145    editor
 5146        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5147        .await;
 5148
 5149    editor.update(cx, |view, cx| {
 5150        view.change_selections(None, cx, |s| {
 5151            s.select_display_ranges([
 5152                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5153                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5154                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5155            ]);
 5156        });
 5157        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5158    });
 5159    editor.update(cx, |editor, cx| {
 5160        assert_text_with_selections(
 5161            editor,
 5162            indoc! {r#"
 5163                use mod1::mod2::{mod3, «mod4ˇ»};
 5164
 5165                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5166                    let var1 = "«textˇ»";
 5167                }
 5168            "#},
 5169            cx,
 5170        );
 5171    });
 5172
 5173    editor.update(cx, |view, cx| {
 5174        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5175    });
 5176    editor.update(cx, |editor, cx| {
 5177        assert_text_with_selections(
 5178            editor,
 5179            indoc! {r#"
 5180                use mod1::mod2::«{mod3, mod4}ˇ»;
 5181
 5182                «ˇfn fn_1(param1: bool, param2: &str) {
 5183                    let var1 = "text";
 5184 5185            "#},
 5186            cx,
 5187        );
 5188    });
 5189
 5190    editor.update(cx, |view, cx| {
 5191        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5192    });
 5193    assert_eq!(
 5194        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5195        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5196    );
 5197
 5198    // Trying to expand the selected syntax node one more time has no effect.
 5199    editor.update(cx, |view, cx| {
 5200        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5201    });
 5202    assert_eq!(
 5203        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5204        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5205    );
 5206
 5207    editor.update(cx, |view, cx| {
 5208        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5209    });
 5210    editor.update(cx, |editor, cx| {
 5211        assert_text_with_selections(
 5212            editor,
 5213            indoc! {r#"
 5214                use mod1::mod2::«{mod3, mod4}ˇ»;
 5215
 5216                «ˇfn fn_1(param1: bool, param2: &str) {
 5217                    let var1 = "text";
 5218 5219            "#},
 5220            cx,
 5221        );
 5222    });
 5223
 5224    editor.update(cx, |view, cx| {
 5225        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5226    });
 5227    editor.update(cx, |editor, cx| {
 5228        assert_text_with_selections(
 5229            editor,
 5230            indoc! {r#"
 5231                use mod1::mod2::{mod3, «mod4ˇ»};
 5232
 5233                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5234                    let var1 = "«textˇ»";
 5235                }
 5236            "#},
 5237            cx,
 5238        );
 5239    });
 5240
 5241    editor.update(cx, |view, cx| {
 5242        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5243    });
 5244    editor.update(cx, |editor, cx| {
 5245        assert_text_with_selections(
 5246            editor,
 5247            indoc! {r#"
 5248                use mod1::mod2::{mod3, mo«ˇ»d4};
 5249
 5250                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5251                    let var1 = "te«ˇ»xt";
 5252                }
 5253            "#},
 5254            cx,
 5255        );
 5256    });
 5257
 5258    // Trying to shrink the selected syntax node one more time has no effect.
 5259    editor.update(cx, |view, cx| {
 5260        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5261    });
 5262    editor.update(cx, |editor, cx| {
 5263        assert_text_with_selections(
 5264            editor,
 5265            indoc! {r#"
 5266                use mod1::mod2::{mod3, mo«ˇ»d4};
 5267
 5268                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5269                    let var1 = "te«ˇ»xt";
 5270                }
 5271            "#},
 5272            cx,
 5273        );
 5274    });
 5275
 5276    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5277    // a fold.
 5278    editor.update(cx, |view, cx| {
 5279        view.fold_ranges(
 5280            vec![
 5281                (
 5282                    Point::new(0, 21)..Point::new(0, 24),
 5283                    FoldPlaceholder::test(),
 5284                ),
 5285                (
 5286                    Point::new(3, 20)..Point::new(3, 22),
 5287                    FoldPlaceholder::test(),
 5288                ),
 5289            ],
 5290            true,
 5291            cx,
 5292        );
 5293        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5294    });
 5295    editor.update(cx, |editor, cx| {
 5296        assert_text_with_selections(
 5297            editor,
 5298            indoc! {r#"
 5299                use mod1::mod2::«{mod3, mod4}ˇ»;
 5300
 5301                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5302                    «let var1 = "text";ˇ»
 5303                }
 5304            "#},
 5305            cx,
 5306        );
 5307    });
 5308}
 5309
 5310#[gpui::test]
 5311async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5312    init_test(cx, |_| {});
 5313
 5314    let language = Arc::new(
 5315        Language::new(
 5316            LanguageConfig {
 5317                brackets: BracketPairConfig {
 5318                    pairs: vec![
 5319                        BracketPair {
 5320                            start: "{".to_string(),
 5321                            end: "}".to_string(),
 5322                            close: false,
 5323                            surround: false,
 5324                            newline: true,
 5325                        },
 5326                        BracketPair {
 5327                            start: "(".to_string(),
 5328                            end: ")".to_string(),
 5329                            close: false,
 5330                            surround: false,
 5331                            newline: true,
 5332                        },
 5333                    ],
 5334                    ..Default::default()
 5335                },
 5336                ..Default::default()
 5337            },
 5338            Some(tree_sitter_rust::LANGUAGE.into()),
 5339        )
 5340        .with_indents_query(
 5341            r#"
 5342                (_ "(" ")" @end) @indent
 5343                (_ "{" "}" @end) @indent
 5344            "#,
 5345        )
 5346        .unwrap(),
 5347    );
 5348
 5349    let text = "fn a() {}";
 5350
 5351    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5352    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5353    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5354    editor
 5355        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5356        .await;
 5357
 5358    editor.update(cx, |editor, cx| {
 5359        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5360        editor.newline(&Newline, cx);
 5361        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5362        assert_eq!(
 5363            editor.selections.ranges(cx),
 5364            &[
 5365                Point::new(1, 4)..Point::new(1, 4),
 5366                Point::new(3, 4)..Point::new(3, 4),
 5367                Point::new(5, 0)..Point::new(5, 0)
 5368            ]
 5369        );
 5370    });
 5371}
 5372
 5373#[gpui::test]
 5374async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5375    init_test(cx, |_| {});
 5376
 5377    let mut cx = EditorTestContext::new(cx).await;
 5378
 5379    let language = Arc::new(Language::new(
 5380        LanguageConfig {
 5381            brackets: BracketPairConfig {
 5382                pairs: vec![
 5383                    BracketPair {
 5384                        start: "{".to_string(),
 5385                        end: "}".to_string(),
 5386                        close: true,
 5387                        surround: true,
 5388                        newline: true,
 5389                    },
 5390                    BracketPair {
 5391                        start: "(".to_string(),
 5392                        end: ")".to_string(),
 5393                        close: true,
 5394                        surround: true,
 5395                        newline: true,
 5396                    },
 5397                    BracketPair {
 5398                        start: "/*".to_string(),
 5399                        end: " */".to_string(),
 5400                        close: true,
 5401                        surround: true,
 5402                        newline: true,
 5403                    },
 5404                    BracketPair {
 5405                        start: "[".to_string(),
 5406                        end: "]".to_string(),
 5407                        close: false,
 5408                        surround: false,
 5409                        newline: true,
 5410                    },
 5411                    BracketPair {
 5412                        start: "\"".to_string(),
 5413                        end: "\"".to_string(),
 5414                        close: true,
 5415                        surround: true,
 5416                        newline: false,
 5417                    },
 5418                    BracketPair {
 5419                        start: "<".to_string(),
 5420                        end: ">".to_string(),
 5421                        close: false,
 5422                        surround: true,
 5423                        newline: true,
 5424                    },
 5425                ],
 5426                ..Default::default()
 5427            },
 5428            autoclose_before: "})]".to_string(),
 5429            ..Default::default()
 5430        },
 5431        Some(tree_sitter_rust::LANGUAGE.into()),
 5432    ));
 5433
 5434    cx.language_registry().add(language.clone());
 5435    cx.update_buffer(|buffer, cx| {
 5436        buffer.set_language(Some(language), cx);
 5437    });
 5438
 5439    cx.set_state(
 5440        &r#"
 5441            🏀ˇ
 5442            εˇ
 5443            ❤️ˇ
 5444        "#
 5445        .unindent(),
 5446    );
 5447
 5448    // autoclose multiple nested brackets at multiple cursors
 5449    cx.update_editor(|view, cx| {
 5450        view.handle_input("{", cx);
 5451        view.handle_input("{", cx);
 5452        view.handle_input("{", cx);
 5453    });
 5454    cx.assert_editor_state(
 5455        &"
 5456            🏀{{{ˇ}}}
 5457            ε{{{ˇ}}}
 5458            ❤️{{{ˇ}}}
 5459        "
 5460        .unindent(),
 5461    );
 5462
 5463    // insert a different closing bracket
 5464    cx.update_editor(|view, cx| {
 5465        view.handle_input(")", cx);
 5466    });
 5467    cx.assert_editor_state(
 5468        &"
 5469            🏀{{{)ˇ}}}
 5470            ε{{{)ˇ}}}
 5471            ❤️{{{)ˇ}}}
 5472        "
 5473        .unindent(),
 5474    );
 5475
 5476    // skip over the auto-closed brackets when typing a closing bracket
 5477    cx.update_editor(|view, cx| {
 5478        view.move_right(&MoveRight, cx);
 5479        view.handle_input("}", cx);
 5480        view.handle_input("}", cx);
 5481        view.handle_input("}", cx);
 5482    });
 5483    cx.assert_editor_state(
 5484        &"
 5485            🏀{{{)}}}}ˇ
 5486            ε{{{)}}}}ˇ
 5487            ❤️{{{)}}}}ˇ
 5488        "
 5489        .unindent(),
 5490    );
 5491
 5492    // autoclose multi-character pairs
 5493    cx.set_state(
 5494        &"
 5495            ˇ
 5496            ˇ
 5497        "
 5498        .unindent(),
 5499    );
 5500    cx.update_editor(|view, cx| {
 5501        view.handle_input("/", cx);
 5502        view.handle_input("*", cx);
 5503    });
 5504    cx.assert_editor_state(
 5505        &"
 5506            /*ˇ */
 5507            /*ˇ */
 5508        "
 5509        .unindent(),
 5510    );
 5511
 5512    // one cursor autocloses a multi-character pair, one cursor
 5513    // does not autoclose.
 5514    cx.set_state(
 5515        &"
 5516 5517            ˇ
 5518        "
 5519        .unindent(),
 5520    );
 5521    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5522    cx.assert_editor_state(
 5523        &"
 5524            /*ˇ */
 5525 5526        "
 5527        .unindent(),
 5528    );
 5529
 5530    // Don't autoclose if the next character isn't whitespace and isn't
 5531    // listed in the language's "autoclose_before" section.
 5532    cx.set_state("ˇa b");
 5533    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5534    cx.assert_editor_state("{ˇa b");
 5535
 5536    // Don't autoclose if `close` is false for the bracket pair
 5537    cx.set_state("ˇ");
 5538    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5539    cx.assert_editor_state("");
 5540
 5541    // Surround with brackets if text is selected
 5542    cx.set_state("«aˇ» b");
 5543    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5544    cx.assert_editor_state("{«aˇ»} b");
 5545
 5546    // Autclose pair where the start and end characters are the same
 5547    cx.set_state("");
 5548    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5549    cx.assert_editor_state("a\"ˇ\"");
 5550    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5551    cx.assert_editor_state("a\"\"ˇ");
 5552
 5553    // Don't autoclose pair if autoclose is disabled
 5554    cx.set_state("ˇ");
 5555    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5556    cx.assert_editor_state("");
 5557
 5558    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5559    cx.set_state("«aˇ» b");
 5560    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5561    cx.assert_editor_state("<«aˇ»> b");
 5562}
 5563
 5564#[gpui::test]
 5565async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5566    init_test(cx, |settings| {
 5567        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5568    });
 5569
 5570    let mut cx = EditorTestContext::new(cx).await;
 5571
 5572    let language = Arc::new(Language::new(
 5573        LanguageConfig {
 5574            brackets: BracketPairConfig {
 5575                pairs: vec![
 5576                    BracketPair {
 5577                        start: "{".to_string(),
 5578                        end: "}".to_string(),
 5579                        close: true,
 5580                        surround: true,
 5581                        newline: true,
 5582                    },
 5583                    BracketPair {
 5584                        start: "(".to_string(),
 5585                        end: ")".to_string(),
 5586                        close: true,
 5587                        surround: true,
 5588                        newline: true,
 5589                    },
 5590                    BracketPair {
 5591                        start: "[".to_string(),
 5592                        end: "]".to_string(),
 5593                        close: false,
 5594                        surround: false,
 5595                        newline: true,
 5596                    },
 5597                ],
 5598                ..Default::default()
 5599            },
 5600            autoclose_before: "})]".to_string(),
 5601            ..Default::default()
 5602        },
 5603        Some(tree_sitter_rust::LANGUAGE.into()),
 5604    ));
 5605
 5606    cx.language_registry().add(language.clone());
 5607    cx.update_buffer(|buffer, cx| {
 5608        buffer.set_language(Some(language), cx);
 5609    });
 5610
 5611    cx.set_state(
 5612        &"
 5613            ˇ
 5614            ˇ
 5615            ˇ
 5616        "
 5617        .unindent(),
 5618    );
 5619
 5620    // ensure only matching closing brackets are skipped over
 5621    cx.update_editor(|view, cx| {
 5622        view.handle_input("}", cx);
 5623        view.move_left(&MoveLeft, cx);
 5624        view.handle_input(")", cx);
 5625        view.move_left(&MoveLeft, cx);
 5626    });
 5627    cx.assert_editor_state(
 5628        &"
 5629            ˇ)}
 5630            ˇ)}
 5631            ˇ)}
 5632        "
 5633        .unindent(),
 5634    );
 5635
 5636    // skip-over closing brackets at multiple cursors
 5637    cx.update_editor(|view, cx| {
 5638        view.handle_input(")", cx);
 5639        view.handle_input("}", cx);
 5640    });
 5641    cx.assert_editor_state(
 5642        &"
 5643            )}ˇ
 5644            )}ˇ
 5645            )}ˇ
 5646        "
 5647        .unindent(),
 5648    );
 5649
 5650    // ignore non-close brackets
 5651    cx.update_editor(|view, cx| {
 5652        view.handle_input("]", cx);
 5653        view.move_left(&MoveLeft, cx);
 5654        view.handle_input("]", cx);
 5655    });
 5656    cx.assert_editor_state(
 5657        &"
 5658            )}]ˇ]
 5659            )}]ˇ]
 5660            )}]ˇ]
 5661        "
 5662        .unindent(),
 5663    );
 5664}
 5665
 5666#[gpui::test]
 5667async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5668    init_test(cx, |_| {});
 5669
 5670    let mut cx = EditorTestContext::new(cx).await;
 5671
 5672    let html_language = Arc::new(
 5673        Language::new(
 5674            LanguageConfig {
 5675                name: "HTML".into(),
 5676                brackets: BracketPairConfig {
 5677                    pairs: vec![
 5678                        BracketPair {
 5679                            start: "<".into(),
 5680                            end: ">".into(),
 5681                            close: true,
 5682                            ..Default::default()
 5683                        },
 5684                        BracketPair {
 5685                            start: "{".into(),
 5686                            end: "}".into(),
 5687                            close: true,
 5688                            ..Default::default()
 5689                        },
 5690                        BracketPair {
 5691                            start: "(".into(),
 5692                            end: ")".into(),
 5693                            close: true,
 5694                            ..Default::default()
 5695                        },
 5696                    ],
 5697                    ..Default::default()
 5698                },
 5699                autoclose_before: "})]>".into(),
 5700                ..Default::default()
 5701            },
 5702            Some(tree_sitter_html::language()),
 5703        )
 5704        .with_injection_query(
 5705            r#"
 5706            (script_element
 5707                (raw_text) @content
 5708                (#set! "language" "javascript"))
 5709            "#,
 5710        )
 5711        .unwrap(),
 5712    );
 5713
 5714    let javascript_language = Arc::new(Language::new(
 5715        LanguageConfig {
 5716            name: "JavaScript".into(),
 5717            brackets: BracketPairConfig {
 5718                pairs: vec![
 5719                    BracketPair {
 5720                        start: "/*".into(),
 5721                        end: " */".into(),
 5722                        close: true,
 5723                        ..Default::default()
 5724                    },
 5725                    BracketPair {
 5726                        start: "{".into(),
 5727                        end: "}".into(),
 5728                        close: true,
 5729                        ..Default::default()
 5730                    },
 5731                    BracketPair {
 5732                        start: "(".into(),
 5733                        end: ")".into(),
 5734                        close: true,
 5735                        ..Default::default()
 5736                    },
 5737                ],
 5738                ..Default::default()
 5739            },
 5740            autoclose_before: "})]>".into(),
 5741            ..Default::default()
 5742        },
 5743        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 5744    ));
 5745
 5746    cx.language_registry().add(html_language.clone());
 5747    cx.language_registry().add(javascript_language.clone());
 5748
 5749    cx.update_buffer(|buffer, cx| {
 5750        buffer.set_language(Some(html_language), cx);
 5751    });
 5752
 5753    cx.set_state(
 5754        &r#"
 5755            <body>ˇ
 5756                <script>
 5757                    var x = 1;ˇ
 5758                </script>
 5759            </body>ˇ
 5760        "#
 5761        .unindent(),
 5762    );
 5763
 5764    // Precondition: different languages are active at different locations.
 5765    cx.update_editor(|editor, cx| {
 5766        let snapshot = editor.snapshot(cx);
 5767        let cursors = editor.selections.ranges::<usize>(cx);
 5768        let languages = cursors
 5769            .iter()
 5770            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5771            .collect::<Vec<_>>();
 5772        assert_eq!(
 5773            languages,
 5774            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5775        );
 5776    });
 5777
 5778    // Angle brackets autoclose in HTML, but not JavaScript.
 5779    cx.update_editor(|editor, cx| {
 5780        editor.handle_input("<", cx);
 5781        editor.handle_input("a", cx);
 5782    });
 5783    cx.assert_editor_state(
 5784        &r#"
 5785            <body><aˇ>
 5786                <script>
 5787                    var x = 1;<aˇ
 5788                </script>
 5789            </body><aˇ>
 5790        "#
 5791        .unindent(),
 5792    );
 5793
 5794    // Curly braces and parens autoclose in both HTML and JavaScript.
 5795    cx.update_editor(|editor, cx| {
 5796        editor.handle_input(" b=", cx);
 5797        editor.handle_input("{", cx);
 5798        editor.handle_input("c", cx);
 5799        editor.handle_input("(", cx);
 5800    });
 5801    cx.assert_editor_state(
 5802        &r#"
 5803            <body><a b={c(ˇ)}>
 5804                <script>
 5805                    var x = 1;<a b={c(ˇ)}
 5806                </script>
 5807            </body><a b={c(ˇ)}>
 5808        "#
 5809        .unindent(),
 5810    );
 5811
 5812    // Brackets that were already autoclosed are skipped.
 5813    cx.update_editor(|editor, cx| {
 5814        editor.handle_input(")", cx);
 5815        editor.handle_input("d", cx);
 5816        editor.handle_input("}", cx);
 5817    });
 5818    cx.assert_editor_state(
 5819        &r#"
 5820            <body><a b={c()d}ˇ>
 5821                <script>
 5822                    var x = 1;<a b={c()d}ˇ
 5823                </script>
 5824            </body><a b={c()d}ˇ>
 5825        "#
 5826        .unindent(),
 5827    );
 5828    cx.update_editor(|editor, cx| {
 5829        editor.handle_input(">", cx);
 5830    });
 5831    cx.assert_editor_state(
 5832        &r#"
 5833            <body><a b={c()d}>ˇ
 5834                <script>
 5835                    var x = 1;<a b={c()d}>ˇ
 5836                </script>
 5837            </body><a b={c()d}>ˇ
 5838        "#
 5839        .unindent(),
 5840    );
 5841
 5842    // Reset
 5843    cx.set_state(
 5844        &r#"
 5845            <body>ˇ
 5846                <script>
 5847                    var x = 1;ˇ
 5848                </script>
 5849            </body>ˇ
 5850        "#
 5851        .unindent(),
 5852    );
 5853
 5854    cx.update_editor(|editor, cx| {
 5855        editor.handle_input("<", cx);
 5856    });
 5857    cx.assert_editor_state(
 5858        &r#"
 5859            <body><ˇ>
 5860                <script>
 5861                    var x = 1;<ˇ
 5862                </script>
 5863            </body><ˇ>
 5864        "#
 5865        .unindent(),
 5866    );
 5867
 5868    // When backspacing, the closing angle brackets are removed.
 5869    cx.update_editor(|editor, cx| {
 5870        editor.backspace(&Backspace, cx);
 5871    });
 5872    cx.assert_editor_state(
 5873        &r#"
 5874            <body>ˇ
 5875                <script>
 5876                    var x = 1;ˇ
 5877                </script>
 5878            </body>ˇ
 5879        "#
 5880        .unindent(),
 5881    );
 5882
 5883    // Block comments autoclose in JavaScript, but not HTML.
 5884    cx.update_editor(|editor, cx| {
 5885        editor.handle_input("/", cx);
 5886        editor.handle_input("*", cx);
 5887    });
 5888    cx.assert_editor_state(
 5889        &r#"
 5890            <body>/*ˇ
 5891                <script>
 5892                    var x = 1;/*ˇ */
 5893                </script>
 5894            </body>/*ˇ
 5895        "#
 5896        .unindent(),
 5897    );
 5898}
 5899
 5900#[gpui::test]
 5901async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5902    init_test(cx, |_| {});
 5903
 5904    let mut cx = EditorTestContext::new(cx).await;
 5905
 5906    let rust_language = Arc::new(
 5907        Language::new(
 5908            LanguageConfig {
 5909                name: "Rust".into(),
 5910                brackets: serde_json::from_value(json!([
 5911                    { "start": "{", "end": "}", "close": true, "newline": true },
 5912                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5913                ]))
 5914                .unwrap(),
 5915                autoclose_before: "})]>".into(),
 5916                ..Default::default()
 5917            },
 5918            Some(tree_sitter_rust::LANGUAGE.into()),
 5919        )
 5920        .with_override_query("(string_literal) @string")
 5921        .unwrap(),
 5922    );
 5923
 5924    cx.language_registry().add(rust_language.clone());
 5925    cx.update_buffer(|buffer, cx| {
 5926        buffer.set_language(Some(rust_language), cx);
 5927    });
 5928
 5929    cx.set_state(
 5930        &r#"
 5931            let x = ˇ
 5932        "#
 5933        .unindent(),
 5934    );
 5935
 5936    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5937    cx.update_editor(|editor, cx| {
 5938        editor.handle_input("\"", cx);
 5939    });
 5940    cx.assert_editor_state(
 5941        &r#"
 5942            let x = "ˇ"
 5943        "#
 5944        .unindent(),
 5945    );
 5946
 5947    // Inserting another quotation mark. The cursor moves across the existing
 5948    // automatically-inserted quotation mark.
 5949    cx.update_editor(|editor, cx| {
 5950        editor.handle_input("\"", cx);
 5951    });
 5952    cx.assert_editor_state(
 5953        &r#"
 5954            let x = ""ˇ
 5955        "#
 5956        .unindent(),
 5957    );
 5958
 5959    // Reset
 5960    cx.set_state(
 5961        &r#"
 5962            let x = ˇ
 5963        "#
 5964        .unindent(),
 5965    );
 5966
 5967    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5968    cx.update_editor(|editor, cx| {
 5969        editor.handle_input("\"", cx);
 5970        editor.handle_input(" ", cx);
 5971        editor.move_left(&Default::default(), cx);
 5972        editor.handle_input("\\", cx);
 5973        editor.handle_input("\"", cx);
 5974    });
 5975    cx.assert_editor_state(
 5976        &r#"
 5977            let x = "\"ˇ "
 5978        "#
 5979        .unindent(),
 5980    );
 5981
 5982    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5983    // mark. Nothing is inserted.
 5984    cx.update_editor(|editor, cx| {
 5985        editor.move_right(&Default::default(), cx);
 5986        editor.handle_input("\"", cx);
 5987    });
 5988    cx.assert_editor_state(
 5989        &r#"
 5990            let x = "\" "ˇ
 5991        "#
 5992        .unindent(),
 5993    );
 5994}
 5995
 5996#[gpui::test]
 5997async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5998    init_test(cx, |_| {});
 5999
 6000    let language = Arc::new(Language::new(
 6001        LanguageConfig {
 6002            brackets: BracketPairConfig {
 6003                pairs: vec![
 6004                    BracketPair {
 6005                        start: "{".to_string(),
 6006                        end: "}".to_string(),
 6007                        close: true,
 6008                        surround: true,
 6009                        newline: true,
 6010                    },
 6011                    BracketPair {
 6012                        start: "/* ".to_string(),
 6013                        end: "*/".to_string(),
 6014                        close: true,
 6015                        surround: true,
 6016                        ..Default::default()
 6017                    },
 6018                ],
 6019                ..Default::default()
 6020            },
 6021            ..Default::default()
 6022        },
 6023        Some(tree_sitter_rust::LANGUAGE.into()),
 6024    ));
 6025
 6026    let text = r#"
 6027        a
 6028        b
 6029        c
 6030    "#
 6031    .unindent();
 6032
 6033    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6034    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6035    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6036    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6037        .await;
 6038
 6039    view.update(cx, |view, cx| {
 6040        view.change_selections(None, cx, |s| {
 6041            s.select_display_ranges([
 6042                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6043                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6044                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6045            ])
 6046        });
 6047
 6048        view.handle_input("{", cx);
 6049        view.handle_input("{", cx);
 6050        view.handle_input("{", cx);
 6051        assert_eq!(
 6052            view.text(cx),
 6053            "
 6054                {{{a}}}
 6055                {{{b}}}
 6056                {{{c}}}
 6057            "
 6058            .unindent()
 6059        );
 6060        assert_eq!(
 6061            view.selections.display_ranges(cx),
 6062            [
 6063                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6064                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6065                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6066            ]
 6067        );
 6068
 6069        view.undo(&Undo, cx);
 6070        view.undo(&Undo, cx);
 6071        view.undo(&Undo, cx);
 6072        assert_eq!(
 6073            view.text(cx),
 6074            "
 6075                a
 6076                b
 6077                c
 6078            "
 6079            .unindent()
 6080        );
 6081        assert_eq!(
 6082            view.selections.display_ranges(cx),
 6083            [
 6084                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6085                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6086                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6087            ]
 6088        );
 6089
 6090        // Ensure inserting the first character of a multi-byte bracket pair
 6091        // doesn't surround the selections with the bracket.
 6092        view.handle_input("/", cx);
 6093        assert_eq!(
 6094            view.text(cx),
 6095            "
 6096                /
 6097                /
 6098                /
 6099            "
 6100            .unindent()
 6101        );
 6102        assert_eq!(
 6103            view.selections.display_ranges(cx),
 6104            [
 6105                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6106                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6107                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6108            ]
 6109        );
 6110
 6111        view.undo(&Undo, cx);
 6112        assert_eq!(
 6113            view.text(cx),
 6114            "
 6115                a
 6116                b
 6117                c
 6118            "
 6119            .unindent()
 6120        );
 6121        assert_eq!(
 6122            view.selections.display_ranges(cx),
 6123            [
 6124                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6125                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6126                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6127            ]
 6128        );
 6129
 6130        // Ensure inserting the last character of a multi-byte bracket pair
 6131        // doesn't surround the selections with the bracket.
 6132        view.handle_input("*", cx);
 6133        assert_eq!(
 6134            view.text(cx),
 6135            "
 6136                *
 6137                *
 6138                *
 6139            "
 6140            .unindent()
 6141        );
 6142        assert_eq!(
 6143            view.selections.display_ranges(cx),
 6144            [
 6145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6146                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6147                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6148            ]
 6149        );
 6150    });
 6151}
 6152
 6153#[gpui::test]
 6154async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6155    init_test(cx, |_| {});
 6156
 6157    let language = Arc::new(Language::new(
 6158        LanguageConfig {
 6159            brackets: BracketPairConfig {
 6160                pairs: vec![BracketPair {
 6161                    start: "{".to_string(),
 6162                    end: "}".to_string(),
 6163                    close: true,
 6164                    surround: true,
 6165                    newline: true,
 6166                }],
 6167                ..Default::default()
 6168            },
 6169            autoclose_before: "}".to_string(),
 6170            ..Default::default()
 6171        },
 6172        Some(tree_sitter_rust::LANGUAGE.into()),
 6173    ));
 6174
 6175    let text = r#"
 6176        a
 6177        b
 6178        c
 6179    "#
 6180    .unindent();
 6181
 6182    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6183    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6184    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6185    editor
 6186        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6187        .await;
 6188
 6189    editor.update(cx, |editor, cx| {
 6190        editor.change_selections(None, cx, |s| {
 6191            s.select_ranges([
 6192                Point::new(0, 1)..Point::new(0, 1),
 6193                Point::new(1, 1)..Point::new(1, 1),
 6194                Point::new(2, 1)..Point::new(2, 1),
 6195            ])
 6196        });
 6197
 6198        editor.handle_input("{", cx);
 6199        editor.handle_input("{", cx);
 6200        editor.handle_input("_", cx);
 6201        assert_eq!(
 6202            editor.text(cx),
 6203            "
 6204                a{{_}}
 6205                b{{_}}
 6206                c{{_}}
 6207            "
 6208            .unindent()
 6209        );
 6210        assert_eq!(
 6211            editor.selections.ranges::<Point>(cx),
 6212            [
 6213                Point::new(0, 4)..Point::new(0, 4),
 6214                Point::new(1, 4)..Point::new(1, 4),
 6215                Point::new(2, 4)..Point::new(2, 4)
 6216            ]
 6217        );
 6218
 6219        editor.backspace(&Default::default(), cx);
 6220        editor.backspace(&Default::default(), cx);
 6221        assert_eq!(
 6222            editor.text(cx),
 6223            "
 6224                a{}
 6225                b{}
 6226                c{}
 6227            "
 6228            .unindent()
 6229        );
 6230        assert_eq!(
 6231            editor.selections.ranges::<Point>(cx),
 6232            [
 6233                Point::new(0, 2)..Point::new(0, 2),
 6234                Point::new(1, 2)..Point::new(1, 2),
 6235                Point::new(2, 2)..Point::new(2, 2)
 6236            ]
 6237        );
 6238
 6239        editor.delete_to_previous_word_start(&Default::default(), cx);
 6240        assert_eq!(
 6241            editor.text(cx),
 6242            "
 6243                a
 6244                b
 6245                c
 6246            "
 6247            .unindent()
 6248        );
 6249        assert_eq!(
 6250            editor.selections.ranges::<Point>(cx),
 6251            [
 6252                Point::new(0, 1)..Point::new(0, 1),
 6253                Point::new(1, 1)..Point::new(1, 1),
 6254                Point::new(2, 1)..Point::new(2, 1)
 6255            ]
 6256        );
 6257    });
 6258}
 6259
 6260#[gpui::test]
 6261async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6262    init_test(cx, |settings| {
 6263        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6264    });
 6265
 6266    let mut cx = EditorTestContext::new(cx).await;
 6267
 6268    let language = Arc::new(Language::new(
 6269        LanguageConfig {
 6270            brackets: BracketPairConfig {
 6271                pairs: vec![
 6272                    BracketPair {
 6273                        start: "{".to_string(),
 6274                        end: "}".to_string(),
 6275                        close: true,
 6276                        surround: true,
 6277                        newline: true,
 6278                    },
 6279                    BracketPair {
 6280                        start: "(".to_string(),
 6281                        end: ")".to_string(),
 6282                        close: true,
 6283                        surround: true,
 6284                        newline: true,
 6285                    },
 6286                    BracketPair {
 6287                        start: "[".to_string(),
 6288                        end: "]".to_string(),
 6289                        close: false,
 6290                        surround: true,
 6291                        newline: true,
 6292                    },
 6293                ],
 6294                ..Default::default()
 6295            },
 6296            autoclose_before: "})]".to_string(),
 6297            ..Default::default()
 6298        },
 6299        Some(tree_sitter_rust::LANGUAGE.into()),
 6300    ));
 6301
 6302    cx.language_registry().add(language.clone());
 6303    cx.update_buffer(|buffer, cx| {
 6304        buffer.set_language(Some(language), cx);
 6305    });
 6306
 6307    cx.set_state(
 6308        &"
 6309            {(ˇ)}
 6310            [[ˇ]]
 6311            {(ˇ)}
 6312        "
 6313        .unindent(),
 6314    );
 6315
 6316    cx.update_editor(|view, cx| {
 6317        view.backspace(&Default::default(), cx);
 6318        view.backspace(&Default::default(), cx);
 6319    });
 6320
 6321    cx.assert_editor_state(
 6322        &"
 6323            ˇ
 6324            ˇ]]
 6325            ˇ
 6326        "
 6327        .unindent(),
 6328    );
 6329
 6330    cx.update_editor(|view, cx| {
 6331        view.handle_input("{", cx);
 6332        view.handle_input("{", cx);
 6333        view.move_right(&MoveRight, cx);
 6334        view.move_right(&MoveRight, cx);
 6335        view.move_left(&MoveLeft, cx);
 6336        view.move_left(&MoveLeft, cx);
 6337        view.backspace(&Default::default(), cx);
 6338    });
 6339
 6340    cx.assert_editor_state(
 6341        &"
 6342            {ˇ}
 6343            {ˇ}]]
 6344            {ˇ}
 6345        "
 6346        .unindent(),
 6347    );
 6348
 6349    cx.update_editor(|view, cx| {
 6350        view.backspace(&Default::default(), cx);
 6351    });
 6352
 6353    cx.assert_editor_state(
 6354        &"
 6355            ˇ
 6356            ˇ]]
 6357            ˇ
 6358        "
 6359        .unindent(),
 6360    );
 6361}
 6362
 6363#[gpui::test]
 6364async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6365    init_test(cx, |_| {});
 6366
 6367    let language = Arc::new(Language::new(
 6368        LanguageConfig::default(),
 6369        Some(tree_sitter_rust::LANGUAGE.into()),
 6370    ));
 6371
 6372    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6373    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6374    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6375    editor
 6376        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6377        .await;
 6378
 6379    editor.update(cx, |editor, cx| {
 6380        editor.set_auto_replace_emoji_shortcode(true);
 6381
 6382        editor.handle_input("Hello ", cx);
 6383        editor.handle_input(":wave", cx);
 6384        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6385
 6386        editor.handle_input(":", cx);
 6387        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6388
 6389        editor.handle_input(" :smile", cx);
 6390        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6391
 6392        editor.handle_input(":", cx);
 6393        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6394
 6395        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6396        editor.handle_input(":wave", cx);
 6397        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6398
 6399        editor.handle_input(":", cx);
 6400        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6401
 6402        editor.handle_input(":1", cx);
 6403        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6404
 6405        editor.handle_input(":", cx);
 6406        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6407
 6408        // Ensure shortcode does not get replaced when it is part of a word
 6409        editor.handle_input(" Test:wave", cx);
 6410        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6411
 6412        editor.handle_input(":", cx);
 6413        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6414
 6415        editor.set_auto_replace_emoji_shortcode(false);
 6416
 6417        // Ensure shortcode does not get replaced when auto replace is off
 6418        editor.handle_input(" :wave", cx);
 6419        assert_eq!(
 6420            editor.text(cx),
 6421            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6422        );
 6423
 6424        editor.handle_input(":", cx);
 6425        assert_eq!(
 6426            editor.text(cx),
 6427            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6428        );
 6429    });
 6430}
 6431
 6432#[gpui::test]
 6433async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6434    init_test(cx, |_| {});
 6435
 6436    let (text, insertion_ranges) = marked_text_ranges(
 6437        indoc! {"
 6438            a.ˇ b
 6439            a.ˇ b
 6440            a.ˇ b
 6441        "},
 6442        false,
 6443    );
 6444
 6445    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6446    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6447
 6448    editor.update(cx, |editor, cx| {
 6449        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6450
 6451        editor
 6452            .insert_snippet(&insertion_ranges, snippet, cx)
 6453            .unwrap();
 6454
 6455        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6456            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6457            assert_eq!(editor.text(cx), expected_text);
 6458            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6459        }
 6460
 6461        assert(
 6462            editor,
 6463            cx,
 6464            indoc! {"
 6465                a.f(«one», two, «three») b
 6466                a.f(«one», two, «three») b
 6467                a.f(«one», two, «three») b
 6468            "},
 6469        );
 6470
 6471        // Can't move earlier than the first tab stop
 6472        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6473        assert(
 6474            editor,
 6475            cx,
 6476            indoc! {"
 6477                a.f(«one», two, «three») b
 6478                a.f(«one», two, «three») b
 6479                a.f(«one», two, «three») b
 6480            "},
 6481        );
 6482
 6483        assert!(editor.move_to_next_snippet_tabstop(cx));
 6484        assert(
 6485            editor,
 6486            cx,
 6487            indoc! {"
 6488                a.f(one, «two», three) b
 6489                a.f(one, «two», three) b
 6490                a.f(one, «two», three) b
 6491            "},
 6492        );
 6493
 6494        editor.move_to_prev_snippet_tabstop(cx);
 6495        assert(
 6496            editor,
 6497            cx,
 6498            indoc! {"
 6499                a.f(«one», two, «three») b
 6500                a.f(«one», two, «three») b
 6501                a.f(«one», two, «three») b
 6502            "},
 6503        );
 6504
 6505        assert!(editor.move_to_next_snippet_tabstop(cx));
 6506        assert(
 6507            editor,
 6508            cx,
 6509            indoc! {"
 6510                a.f(one, «two», three) b
 6511                a.f(one, «two», three) b
 6512                a.f(one, «two», three) b
 6513            "},
 6514        );
 6515        assert!(editor.move_to_next_snippet_tabstop(cx));
 6516        assert(
 6517            editor,
 6518            cx,
 6519            indoc! {"
 6520                a.f(one, two, three)ˇ b
 6521                a.f(one, two, three)ˇ b
 6522                a.f(one, two, three)ˇ b
 6523            "},
 6524        );
 6525
 6526        // As soon as the last tab stop is reached, snippet state is gone
 6527        editor.move_to_prev_snippet_tabstop(cx);
 6528        assert(
 6529            editor,
 6530            cx,
 6531            indoc! {"
 6532                a.f(one, two, three)ˇ b
 6533                a.f(one, two, three)ˇ b
 6534                a.f(one, two, three)ˇ b
 6535            "},
 6536        );
 6537    });
 6538}
 6539
 6540#[gpui::test]
 6541async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6542    init_test(cx, |_| {});
 6543
 6544    let fs = FakeFs::new(cx.executor());
 6545    fs.insert_file("/file.rs", Default::default()).await;
 6546
 6547    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6548
 6549    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6550    language_registry.add(rust_lang());
 6551    let mut fake_servers = language_registry.register_fake_lsp(
 6552        "Rust",
 6553        FakeLspAdapter {
 6554            capabilities: lsp::ServerCapabilities {
 6555                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6556                ..Default::default()
 6557            },
 6558            ..Default::default()
 6559        },
 6560    );
 6561
 6562    let buffer = project
 6563        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6564        .await
 6565        .unwrap();
 6566
 6567    cx.executor().start_waiting();
 6568    let fake_server = fake_servers.next().await.unwrap();
 6569
 6570    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6571    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6572    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6573    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6574
 6575    let save = editor
 6576        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6577        .unwrap();
 6578    fake_server
 6579        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6580            assert_eq!(
 6581                params.text_document.uri,
 6582                lsp::Url::from_file_path("/file.rs").unwrap()
 6583            );
 6584            assert_eq!(params.options.tab_size, 4);
 6585            Ok(Some(vec![lsp::TextEdit::new(
 6586                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6587                ", ".to_string(),
 6588            )]))
 6589        })
 6590        .next()
 6591        .await;
 6592    cx.executor().start_waiting();
 6593    save.await;
 6594
 6595    assert_eq!(
 6596        editor.update(cx, |editor, cx| editor.text(cx)),
 6597        "one, two\nthree\n"
 6598    );
 6599    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6600
 6601    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6602    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6603
 6604    // Ensure we can still save even if formatting hangs.
 6605    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6606        assert_eq!(
 6607            params.text_document.uri,
 6608            lsp::Url::from_file_path("/file.rs").unwrap()
 6609        );
 6610        futures::future::pending::<()>().await;
 6611        unreachable!()
 6612    });
 6613    let save = editor
 6614        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6615        .unwrap();
 6616    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6617    cx.executor().start_waiting();
 6618    save.await;
 6619    assert_eq!(
 6620        editor.update(cx, |editor, cx| editor.text(cx)),
 6621        "one\ntwo\nthree\n"
 6622    );
 6623    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6624
 6625    // For non-dirty buffer, no formatting request should be sent
 6626    let save = editor
 6627        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6628        .unwrap();
 6629    let _pending_format_request = fake_server
 6630        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6631            panic!("Should not be invoked on non-dirty buffer");
 6632        })
 6633        .next();
 6634    cx.executor().start_waiting();
 6635    save.await;
 6636
 6637    // Set rust language override and assert overridden tabsize is sent to language server
 6638    update_test_language_settings(cx, |settings| {
 6639        settings.languages.insert(
 6640            "Rust".into(),
 6641            LanguageSettingsContent {
 6642                tab_size: NonZeroU32::new(8),
 6643                ..Default::default()
 6644            },
 6645        );
 6646    });
 6647
 6648    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6649    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6650    let save = editor
 6651        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6652        .unwrap();
 6653    fake_server
 6654        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6655            assert_eq!(
 6656                params.text_document.uri,
 6657                lsp::Url::from_file_path("/file.rs").unwrap()
 6658            );
 6659            assert_eq!(params.options.tab_size, 8);
 6660            Ok(Some(vec![]))
 6661        })
 6662        .next()
 6663        .await;
 6664    cx.executor().start_waiting();
 6665    save.await;
 6666}
 6667
 6668#[gpui::test]
 6669async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6670    init_test(cx, |_| {});
 6671
 6672    let cols = 4;
 6673    let rows = 10;
 6674    let sample_text_1 = sample_text(rows, cols, 'a');
 6675    assert_eq!(
 6676        sample_text_1,
 6677        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6678    );
 6679    let sample_text_2 = sample_text(rows, cols, 'l');
 6680    assert_eq!(
 6681        sample_text_2,
 6682        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6683    );
 6684    let sample_text_3 = sample_text(rows, cols, 'v');
 6685    assert_eq!(
 6686        sample_text_3,
 6687        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6688    );
 6689
 6690    let fs = FakeFs::new(cx.executor());
 6691    fs.insert_tree(
 6692        "/a",
 6693        json!({
 6694            "main.rs": sample_text_1,
 6695            "other.rs": sample_text_2,
 6696            "lib.rs": sample_text_3,
 6697        }),
 6698    )
 6699    .await;
 6700
 6701    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6702    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6703    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 6704
 6705    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6706    language_registry.add(rust_lang());
 6707    let mut fake_servers = language_registry.register_fake_lsp(
 6708        "Rust",
 6709        FakeLspAdapter {
 6710            capabilities: lsp::ServerCapabilities {
 6711                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6712                ..Default::default()
 6713            },
 6714            ..Default::default()
 6715        },
 6716    );
 6717
 6718    let worktree = project.update(cx, |project, cx| {
 6719        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 6720        assert_eq!(worktrees.len(), 1);
 6721        worktrees.pop().unwrap()
 6722    });
 6723    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 6724
 6725    let buffer_1 = project
 6726        .update(cx, |project, cx| {
 6727            project.open_buffer((worktree_id, "main.rs"), cx)
 6728        })
 6729        .await
 6730        .unwrap();
 6731    let buffer_2 = project
 6732        .update(cx, |project, cx| {
 6733            project.open_buffer((worktree_id, "other.rs"), cx)
 6734        })
 6735        .await
 6736        .unwrap();
 6737    let buffer_3 = project
 6738        .update(cx, |project, cx| {
 6739            project.open_buffer((worktree_id, "lib.rs"), cx)
 6740        })
 6741        .await
 6742        .unwrap();
 6743
 6744    let multi_buffer = cx.new_model(|cx| {
 6745        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 6746        multi_buffer.push_excerpts(
 6747            buffer_1.clone(),
 6748            [
 6749                ExcerptRange {
 6750                    context: Point::new(0, 0)..Point::new(3, 0),
 6751                    primary: None,
 6752                },
 6753                ExcerptRange {
 6754                    context: Point::new(5, 0)..Point::new(7, 0),
 6755                    primary: None,
 6756                },
 6757                ExcerptRange {
 6758                    context: Point::new(9, 0)..Point::new(10, 4),
 6759                    primary: None,
 6760                },
 6761            ],
 6762            cx,
 6763        );
 6764        multi_buffer.push_excerpts(
 6765            buffer_2.clone(),
 6766            [
 6767                ExcerptRange {
 6768                    context: Point::new(0, 0)..Point::new(3, 0),
 6769                    primary: None,
 6770                },
 6771                ExcerptRange {
 6772                    context: Point::new(5, 0)..Point::new(7, 0),
 6773                    primary: None,
 6774                },
 6775                ExcerptRange {
 6776                    context: Point::new(9, 0)..Point::new(10, 4),
 6777                    primary: None,
 6778                },
 6779            ],
 6780            cx,
 6781        );
 6782        multi_buffer.push_excerpts(
 6783            buffer_3.clone(),
 6784            [
 6785                ExcerptRange {
 6786                    context: Point::new(0, 0)..Point::new(3, 0),
 6787                    primary: None,
 6788                },
 6789                ExcerptRange {
 6790                    context: Point::new(5, 0)..Point::new(7, 0),
 6791                    primary: None,
 6792                },
 6793                ExcerptRange {
 6794                    context: Point::new(9, 0)..Point::new(10, 4),
 6795                    primary: None,
 6796                },
 6797            ],
 6798            cx,
 6799        );
 6800        multi_buffer
 6801    });
 6802    let multi_buffer_editor = cx.new_view(|cx| {
 6803        Editor::new(
 6804            EditorMode::Full,
 6805            multi_buffer,
 6806            Some(project.clone()),
 6807            true,
 6808            cx,
 6809        )
 6810    });
 6811
 6812    multi_buffer_editor.update(cx, |editor, cx| {
 6813        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6814        editor.insert("|one|two|three|", cx);
 6815    });
 6816    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6817    multi_buffer_editor.update(cx, |editor, cx| {
 6818        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6819            s.select_ranges(Some(60..70))
 6820        });
 6821        editor.insert("|four|five|six|", cx);
 6822    });
 6823    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6824
 6825    // First two buffers should be edited, but not the third one.
 6826    assert_eq!(
 6827        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6828        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6829    );
 6830    buffer_1.update(cx, |buffer, _| {
 6831        assert!(buffer.is_dirty());
 6832        assert_eq!(
 6833            buffer.text(),
 6834            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6835        )
 6836    });
 6837    buffer_2.update(cx, |buffer, _| {
 6838        assert!(buffer.is_dirty());
 6839        assert_eq!(
 6840            buffer.text(),
 6841            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6842        )
 6843    });
 6844    buffer_3.update(cx, |buffer, _| {
 6845        assert!(!buffer.is_dirty());
 6846        assert_eq!(buffer.text(), sample_text_3,)
 6847    });
 6848
 6849    cx.executor().start_waiting();
 6850    let save = multi_buffer_editor
 6851        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6852        .unwrap();
 6853
 6854    let fake_server = fake_servers.next().await.unwrap();
 6855    fake_server
 6856        .server
 6857        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6858            Ok(Some(vec![lsp::TextEdit::new(
 6859                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6860                format!("[{} formatted]", params.text_document.uri),
 6861            )]))
 6862        })
 6863        .detach();
 6864    save.await;
 6865
 6866    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6867    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6868    assert_eq!(
 6869        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6870        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 6871    );
 6872    buffer_1.update(cx, |buffer, _| {
 6873        assert!(!buffer.is_dirty());
 6874        assert_eq!(
 6875            buffer.text(),
 6876            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6877        )
 6878    });
 6879    buffer_2.update(cx, |buffer, _| {
 6880        assert!(!buffer.is_dirty());
 6881        assert_eq!(
 6882            buffer.text(),
 6883            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6884        )
 6885    });
 6886    buffer_3.update(cx, |buffer, _| {
 6887        assert!(!buffer.is_dirty());
 6888        assert_eq!(buffer.text(), sample_text_3,)
 6889    });
 6890}
 6891
 6892#[gpui::test]
 6893async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6894    init_test(cx, |_| {});
 6895
 6896    let fs = FakeFs::new(cx.executor());
 6897    fs.insert_file("/file.rs", Default::default()).await;
 6898
 6899    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6900
 6901    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6902    language_registry.add(rust_lang());
 6903    let mut fake_servers = language_registry.register_fake_lsp(
 6904        "Rust",
 6905        FakeLspAdapter {
 6906            capabilities: lsp::ServerCapabilities {
 6907                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6908                ..Default::default()
 6909            },
 6910            ..Default::default()
 6911        },
 6912    );
 6913
 6914    let buffer = project
 6915        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6916        .await
 6917        .unwrap();
 6918
 6919    cx.executor().start_waiting();
 6920    let fake_server = fake_servers.next().await.unwrap();
 6921
 6922    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6923    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6924    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6925    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6926
 6927    let save = editor
 6928        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6929        .unwrap();
 6930    fake_server
 6931        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6932            assert_eq!(
 6933                params.text_document.uri,
 6934                lsp::Url::from_file_path("/file.rs").unwrap()
 6935            );
 6936            assert_eq!(params.options.tab_size, 4);
 6937            Ok(Some(vec![lsp::TextEdit::new(
 6938                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6939                ", ".to_string(),
 6940            )]))
 6941        })
 6942        .next()
 6943        .await;
 6944    cx.executor().start_waiting();
 6945    save.await;
 6946    assert_eq!(
 6947        editor.update(cx, |editor, cx| editor.text(cx)),
 6948        "one, two\nthree\n"
 6949    );
 6950    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6951
 6952    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6953    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6954
 6955    // Ensure we can still save even if formatting hangs.
 6956    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6957        move |params, _| async move {
 6958            assert_eq!(
 6959                params.text_document.uri,
 6960                lsp::Url::from_file_path("/file.rs").unwrap()
 6961            );
 6962            futures::future::pending::<()>().await;
 6963            unreachable!()
 6964        },
 6965    );
 6966    let save = editor
 6967        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6968        .unwrap();
 6969    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6970    cx.executor().start_waiting();
 6971    save.await;
 6972    assert_eq!(
 6973        editor.update(cx, |editor, cx| editor.text(cx)),
 6974        "one\ntwo\nthree\n"
 6975    );
 6976    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6977
 6978    // For non-dirty buffer, no formatting request should be sent
 6979    let save = editor
 6980        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6981        .unwrap();
 6982    let _pending_format_request = fake_server
 6983        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6984            panic!("Should not be invoked on non-dirty buffer");
 6985        })
 6986        .next();
 6987    cx.executor().start_waiting();
 6988    save.await;
 6989
 6990    // Set Rust language override and assert overridden tabsize is sent to language server
 6991    update_test_language_settings(cx, |settings| {
 6992        settings.languages.insert(
 6993            "Rust".into(),
 6994            LanguageSettingsContent {
 6995                tab_size: NonZeroU32::new(8),
 6996                ..Default::default()
 6997            },
 6998        );
 6999    });
 7000
 7001    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7002    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7003    let save = editor
 7004        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7005        .unwrap();
 7006    fake_server
 7007        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7008            assert_eq!(
 7009                params.text_document.uri,
 7010                lsp::Url::from_file_path("/file.rs").unwrap()
 7011            );
 7012            assert_eq!(params.options.tab_size, 8);
 7013            Ok(Some(vec![]))
 7014        })
 7015        .next()
 7016        .await;
 7017    cx.executor().start_waiting();
 7018    save.await;
 7019}
 7020
 7021#[gpui::test]
 7022async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7023    init_test(cx, |settings| {
 7024        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7025            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7026        ))
 7027    });
 7028
 7029    let fs = FakeFs::new(cx.executor());
 7030    fs.insert_file("/file.rs", Default::default()).await;
 7031
 7032    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7033
 7034    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7035    language_registry.add(Arc::new(Language::new(
 7036        LanguageConfig {
 7037            name: "Rust".into(),
 7038            matcher: LanguageMatcher {
 7039                path_suffixes: vec!["rs".to_string()],
 7040                ..Default::default()
 7041            },
 7042            ..LanguageConfig::default()
 7043        },
 7044        Some(tree_sitter_rust::LANGUAGE.into()),
 7045    )));
 7046    update_test_language_settings(cx, |settings| {
 7047        // Enable Prettier formatting for the same buffer, and ensure
 7048        // LSP is called instead of Prettier.
 7049        settings.defaults.prettier = Some(PrettierSettings {
 7050            allowed: true,
 7051            ..PrettierSettings::default()
 7052        });
 7053    });
 7054    let mut fake_servers = language_registry.register_fake_lsp(
 7055        "Rust",
 7056        FakeLspAdapter {
 7057            capabilities: lsp::ServerCapabilities {
 7058                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7059                ..Default::default()
 7060            },
 7061            ..Default::default()
 7062        },
 7063    );
 7064
 7065    let buffer = project
 7066        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7067        .await
 7068        .unwrap();
 7069
 7070    cx.executor().start_waiting();
 7071    let fake_server = fake_servers.next().await.unwrap();
 7072
 7073    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7074    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7075    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7076
 7077    let format = editor
 7078        .update(cx, |editor, cx| {
 7079            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 7080        })
 7081        .unwrap();
 7082    fake_server
 7083        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7084            assert_eq!(
 7085                params.text_document.uri,
 7086                lsp::Url::from_file_path("/file.rs").unwrap()
 7087            );
 7088            assert_eq!(params.options.tab_size, 4);
 7089            Ok(Some(vec![lsp::TextEdit::new(
 7090                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7091                ", ".to_string(),
 7092            )]))
 7093        })
 7094        .next()
 7095        .await;
 7096    cx.executor().start_waiting();
 7097    format.await;
 7098    assert_eq!(
 7099        editor.update(cx, |editor, cx| editor.text(cx)),
 7100        "one, two\nthree\n"
 7101    );
 7102
 7103    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7104    // Ensure we don't lock if formatting hangs.
 7105    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7106        assert_eq!(
 7107            params.text_document.uri,
 7108            lsp::Url::from_file_path("/file.rs").unwrap()
 7109        );
 7110        futures::future::pending::<()>().await;
 7111        unreachable!()
 7112    });
 7113    let format = editor
 7114        .update(cx, |editor, cx| {
 7115            editor.perform_format(project, FormatTrigger::Manual, cx)
 7116        })
 7117        .unwrap();
 7118    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7119    cx.executor().start_waiting();
 7120    format.await;
 7121    assert_eq!(
 7122        editor.update(cx, |editor, cx| editor.text(cx)),
 7123        "one\ntwo\nthree\n"
 7124    );
 7125}
 7126
 7127#[gpui::test]
 7128async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7129    init_test(cx, |_| {});
 7130
 7131    let mut cx = EditorLspTestContext::new_rust(
 7132        lsp::ServerCapabilities {
 7133            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7134            ..Default::default()
 7135        },
 7136        cx,
 7137    )
 7138    .await;
 7139
 7140    cx.set_state(indoc! {"
 7141        one.twoˇ
 7142    "});
 7143
 7144    // The format request takes a long time. When it completes, it inserts
 7145    // a newline and an indent before the `.`
 7146    cx.lsp
 7147        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7148            let executor = cx.background_executor().clone();
 7149            async move {
 7150                executor.timer(Duration::from_millis(100)).await;
 7151                Ok(Some(vec![lsp::TextEdit {
 7152                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7153                    new_text: "\n    ".into(),
 7154                }]))
 7155            }
 7156        });
 7157
 7158    // Submit a format request.
 7159    let format_1 = cx
 7160        .update_editor(|editor, cx| editor.format(&Format, cx))
 7161        .unwrap();
 7162    cx.executor().run_until_parked();
 7163
 7164    // Submit a second format request.
 7165    let format_2 = cx
 7166        .update_editor(|editor, cx| editor.format(&Format, cx))
 7167        .unwrap();
 7168    cx.executor().run_until_parked();
 7169
 7170    // Wait for both format requests to complete
 7171    cx.executor().advance_clock(Duration::from_millis(200));
 7172    cx.executor().start_waiting();
 7173    format_1.await.unwrap();
 7174    cx.executor().start_waiting();
 7175    format_2.await.unwrap();
 7176
 7177    // The formatting edits only happens once.
 7178    cx.assert_editor_state(indoc! {"
 7179        one
 7180            .twoˇ
 7181    "});
 7182}
 7183
 7184#[gpui::test]
 7185async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7186    init_test(cx, |settings| {
 7187        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7188    });
 7189
 7190    let mut cx = EditorLspTestContext::new_rust(
 7191        lsp::ServerCapabilities {
 7192            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7193            ..Default::default()
 7194        },
 7195        cx,
 7196    )
 7197    .await;
 7198
 7199    // Set up a buffer white some trailing whitespace and no trailing newline.
 7200    cx.set_state(
 7201        &[
 7202            "one ",   //
 7203            "twoˇ",   //
 7204            "three ", //
 7205            "four",   //
 7206        ]
 7207        .join("\n"),
 7208    );
 7209
 7210    // Submit a format request.
 7211    let format = cx
 7212        .update_editor(|editor, cx| editor.format(&Format, cx))
 7213        .unwrap();
 7214
 7215    // Record which buffer changes have been sent to the language server
 7216    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7217    cx.lsp
 7218        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7219            let buffer_changes = buffer_changes.clone();
 7220            move |params, _| {
 7221                buffer_changes.lock().extend(
 7222                    params
 7223                        .content_changes
 7224                        .into_iter()
 7225                        .map(|e| (e.range.unwrap(), e.text)),
 7226                );
 7227            }
 7228        });
 7229
 7230    // Handle formatting requests to the language server.
 7231    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7232        let buffer_changes = buffer_changes.clone();
 7233        move |_, _| {
 7234            // When formatting is requested, trailing whitespace has already been stripped,
 7235            // and the trailing newline has already been added.
 7236            assert_eq!(
 7237                &buffer_changes.lock()[1..],
 7238                &[
 7239                    (
 7240                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7241                        "".into()
 7242                    ),
 7243                    (
 7244                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7245                        "".into()
 7246                    ),
 7247                    (
 7248                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7249                        "\n".into()
 7250                    ),
 7251                ]
 7252            );
 7253
 7254            // Insert blank lines between each line of the buffer.
 7255            async move {
 7256                Ok(Some(vec![
 7257                    lsp::TextEdit {
 7258                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7259                        new_text: "\n".into(),
 7260                    },
 7261                    lsp::TextEdit {
 7262                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7263                        new_text: "\n".into(),
 7264                    },
 7265                ]))
 7266            }
 7267        }
 7268    });
 7269
 7270    // After formatting the buffer, the trailing whitespace is stripped,
 7271    // a newline is appended, and the edits provided by the language server
 7272    // have been applied.
 7273    format.await.unwrap();
 7274    cx.assert_editor_state(
 7275        &[
 7276            "one",   //
 7277            "",      //
 7278            "twoˇ",  //
 7279            "",      //
 7280            "three", //
 7281            "four",  //
 7282            "",      //
 7283        ]
 7284        .join("\n"),
 7285    );
 7286
 7287    // Undoing the formatting undoes the trailing whitespace removal, the
 7288    // trailing newline, and the LSP edits.
 7289    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7290    cx.assert_editor_state(
 7291        &[
 7292            "one ",   //
 7293            "twoˇ",   //
 7294            "three ", //
 7295            "four",   //
 7296        ]
 7297        .join("\n"),
 7298    );
 7299}
 7300
 7301#[gpui::test]
 7302async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7303    cx: &mut gpui::TestAppContext,
 7304) {
 7305    init_test(cx, |_| {});
 7306
 7307    cx.update(|cx| {
 7308        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7309            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7310                settings.auto_signature_help = Some(true);
 7311            });
 7312        });
 7313    });
 7314
 7315    let mut cx = EditorLspTestContext::new_rust(
 7316        lsp::ServerCapabilities {
 7317            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7318                ..Default::default()
 7319            }),
 7320            ..Default::default()
 7321        },
 7322        cx,
 7323    )
 7324    .await;
 7325
 7326    let language = Language::new(
 7327        LanguageConfig {
 7328            name: "Rust".into(),
 7329            brackets: BracketPairConfig {
 7330                pairs: vec![
 7331                    BracketPair {
 7332                        start: "{".to_string(),
 7333                        end: "}".to_string(),
 7334                        close: true,
 7335                        surround: true,
 7336                        newline: true,
 7337                    },
 7338                    BracketPair {
 7339                        start: "(".to_string(),
 7340                        end: ")".to_string(),
 7341                        close: true,
 7342                        surround: true,
 7343                        newline: true,
 7344                    },
 7345                    BracketPair {
 7346                        start: "/*".to_string(),
 7347                        end: " */".to_string(),
 7348                        close: true,
 7349                        surround: true,
 7350                        newline: true,
 7351                    },
 7352                    BracketPair {
 7353                        start: "[".to_string(),
 7354                        end: "]".to_string(),
 7355                        close: false,
 7356                        surround: false,
 7357                        newline: true,
 7358                    },
 7359                    BracketPair {
 7360                        start: "\"".to_string(),
 7361                        end: "\"".to_string(),
 7362                        close: true,
 7363                        surround: true,
 7364                        newline: false,
 7365                    },
 7366                    BracketPair {
 7367                        start: "<".to_string(),
 7368                        end: ">".to_string(),
 7369                        close: false,
 7370                        surround: true,
 7371                        newline: true,
 7372                    },
 7373                ],
 7374                ..Default::default()
 7375            },
 7376            autoclose_before: "})]".to_string(),
 7377            ..Default::default()
 7378        },
 7379        Some(tree_sitter_rust::LANGUAGE.into()),
 7380    );
 7381    let language = Arc::new(language);
 7382
 7383    cx.language_registry().add(language.clone());
 7384    cx.update_buffer(|buffer, cx| {
 7385        buffer.set_language(Some(language), cx);
 7386    });
 7387
 7388    cx.set_state(
 7389        &r#"
 7390            fn main() {
 7391                sampleˇ
 7392            }
 7393        "#
 7394        .unindent(),
 7395    );
 7396
 7397    cx.update_editor(|view, cx| {
 7398        view.handle_input("(", cx);
 7399    });
 7400    cx.assert_editor_state(
 7401        &"
 7402            fn main() {
 7403                sample(ˇ)
 7404            }
 7405        "
 7406        .unindent(),
 7407    );
 7408
 7409    let mocked_response = lsp::SignatureHelp {
 7410        signatures: vec![lsp::SignatureInformation {
 7411            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7412            documentation: None,
 7413            parameters: Some(vec![
 7414                lsp::ParameterInformation {
 7415                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7416                    documentation: None,
 7417                },
 7418                lsp::ParameterInformation {
 7419                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7420                    documentation: None,
 7421                },
 7422            ]),
 7423            active_parameter: None,
 7424        }],
 7425        active_signature: Some(0),
 7426        active_parameter: Some(0),
 7427    };
 7428    handle_signature_help_request(&mut cx, mocked_response).await;
 7429
 7430    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7431        .await;
 7432
 7433    cx.editor(|editor, _| {
 7434        let signature_help_state = editor.signature_help_state.popover().cloned();
 7435        assert!(signature_help_state.is_some());
 7436        let ParsedMarkdown {
 7437            text, highlights, ..
 7438        } = signature_help_state.unwrap().parsed_content;
 7439        assert_eq!(text, "param1: u8, param2: u8");
 7440        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7441    });
 7442}
 7443
 7444#[gpui::test]
 7445async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7446    init_test(cx, |_| {});
 7447
 7448    cx.update(|cx| {
 7449        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7450            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7451                settings.auto_signature_help = Some(false);
 7452                settings.show_signature_help_after_edits = Some(false);
 7453            });
 7454        });
 7455    });
 7456
 7457    let mut cx = EditorLspTestContext::new_rust(
 7458        lsp::ServerCapabilities {
 7459            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7460                ..Default::default()
 7461            }),
 7462            ..Default::default()
 7463        },
 7464        cx,
 7465    )
 7466    .await;
 7467
 7468    let language = Language::new(
 7469        LanguageConfig {
 7470            name: "Rust".into(),
 7471            brackets: BracketPairConfig {
 7472                pairs: vec![
 7473                    BracketPair {
 7474                        start: "{".to_string(),
 7475                        end: "}".to_string(),
 7476                        close: true,
 7477                        surround: true,
 7478                        newline: true,
 7479                    },
 7480                    BracketPair {
 7481                        start: "(".to_string(),
 7482                        end: ")".to_string(),
 7483                        close: true,
 7484                        surround: true,
 7485                        newline: true,
 7486                    },
 7487                    BracketPair {
 7488                        start: "/*".to_string(),
 7489                        end: " */".to_string(),
 7490                        close: true,
 7491                        surround: true,
 7492                        newline: true,
 7493                    },
 7494                    BracketPair {
 7495                        start: "[".to_string(),
 7496                        end: "]".to_string(),
 7497                        close: false,
 7498                        surround: false,
 7499                        newline: true,
 7500                    },
 7501                    BracketPair {
 7502                        start: "\"".to_string(),
 7503                        end: "\"".to_string(),
 7504                        close: true,
 7505                        surround: true,
 7506                        newline: false,
 7507                    },
 7508                    BracketPair {
 7509                        start: "<".to_string(),
 7510                        end: ">".to_string(),
 7511                        close: false,
 7512                        surround: true,
 7513                        newline: true,
 7514                    },
 7515                ],
 7516                ..Default::default()
 7517            },
 7518            autoclose_before: "})]".to_string(),
 7519            ..Default::default()
 7520        },
 7521        Some(tree_sitter_rust::LANGUAGE.into()),
 7522    );
 7523    let language = Arc::new(language);
 7524
 7525    cx.language_registry().add(language.clone());
 7526    cx.update_buffer(|buffer, cx| {
 7527        buffer.set_language(Some(language), cx);
 7528    });
 7529
 7530    // Ensure that signature_help is not called when no signature help is enabled.
 7531    cx.set_state(
 7532        &r#"
 7533            fn main() {
 7534                sampleˇ
 7535            }
 7536        "#
 7537        .unindent(),
 7538    );
 7539    cx.update_editor(|view, cx| {
 7540        view.handle_input("(", cx);
 7541    });
 7542    cx.assert_editor_state(
 7543        &"
 7544            fn main() {
 7545                sample(ˇ)
 7546            }
 7547        "
 7548        .unindent(),
 7549    );
 7550    cx.editor(|editor, _| {
 7551        assert!(editor.signature_help_state.task().is_none());
 7552    });
 7553
 7554    let mocked_response = lsp::SignatureHelp {
 7555        signatures: vec![lsp::SignatureInformation {
 7556            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7557            documentation: None,
 7558            parameters: Some(vec![
 7559                lsp::ParameterInformation {
 7560                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7561                    documentation: None,
 7562                },
 7563                lsp::ParameterInformation {
 7564                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7565                    documentation: None,
 7566                },
 7567            ]),
 7568            active_parameter: None,
 7569        }],
 7570        active_signature: Some(0),
 7571        active_parameter: Some(0),
 7572    };
 7573
 7574    // Ensure that signature_help is called when enabled afte edits
 7575    cx.update(|cx| {
 7576        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7577            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7578                settings.auto_signature_help = Some(false);
 7579                settings.show_signature_help_after_edits = Some(true);
 7580            });
 7581        });
 7582    });
 7583    cx.set_state(
 7584        &r#"
 7585            fn main() {
 7586                sampleˇ
 7587            }
 7588        "#
 7589        .unindent(),
 7590    );
 7591    cx.update_editor(|view, cx| {
 7592        view.handle_input("(", cx);
 7593    });
 7594    cx.assert_editor_state(
 7595        &"
 7596            fn main() {
 7597                sample(ˇ)
 7598            }
 7599        "
 7600        .unindent(),
 7601    );
 7602    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7603    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7604        .await;
 7605    cx.update_editor(|editor, _| {
 7606        let signature_help_state = editor.signature_help_state.popover().cloned();
 7607        assert!(signature_help_state.is_some());
 7608        let ParsedMarkdown {
 7609            text, highlights, ..
 7610        } = signature_help_state.unwrap().parsed_content;
 7611        assert_eq!(text, "param1: u8, param2: u8");
 7612        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7613        editor.signature_help_state = SignatureHelpState::default();
 7614    });
 7615
 7616    // Ensure that signature_help is called when auto signature help override is enabled
 7617    cx.update(|cx| {
 7618        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7619            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7620                settings.auto_signature_help = Some(true);
 7621                settings.show_signature_help_after_edits = Some(false);
 7622            });
 7623        });
 7624    });
 7625    cx.set_state(
 7626        &r#"
 7627            fn main() {
 7628                sampleˇ
 7629            }
 7630        "#
 7631        .unindent(),
 7632    );
 7633    cx.update_editor(|view, cx| {
 7634        view.handle_input("(", cx);
 7635    });
 7636    cx.assert_editor_state(
 7637        &"
 7638            fn main() {
 7639                sample(ˇ)
 7640            }
 7641        "
 7642        .unindent(),
 7643    );
 7644    handle_signature_help_request(&mut cx, mocked_response).await;
 7645    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7646        .await;
 7647    cx.editor(|editor, _| {
 7648        let signature_help_state = editor.signature_help_state.popover().cloned();
 7649        assert!(signature_help_state.is_some());
 7650        let ParsedMarkdown {
 7651            text, highlights, ..
 7652        } = signature_help_state.unwrap().parsed_content;
 7653        assert_eq!(text, "param1: u8, param2: u8");
 7654        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7655    });
 7656}
 7657
 7658#[gpui::test]
 7659async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7660    init_test(cx, |_| {});
 7661    cx.update(|cx| {
 7662        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7663            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7664                settings.auto_signature_help = Some(true);
 7665            });
 7666        });
 7667    });
 7668
 7669    let mut cx = EditorLspTestContext::new_rust(
 7670        lsp::ServerCapabilities {
 7671            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7672                ..Default::default()
 7673            }),
 7674            ..Default::default()
 7675        },
 7676        cx,
 7677    )
 7678    .await;
 7679
 7680    // A test that directly calls `show_signature_help`
 7681    cx.update_editor(|editor, cx| {
 7682        editor.show_signature_help(&ShowSignatureHelp, cx);
 7683    });
 7684
 7685    let mocked_response = lsp::SignatureHelp {
 7686        signatures: vec![lsp::SignatureInformation {
 7687            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7688            documentation: None,
 7689            parameters: Some(vec![
 7690                lsp::ParameterInformation {
 7691                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7692                    documentation: None,
 7693                },
 7694                lsp::ParameterInformation {
 7695                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7696                    documentation: None,
 7697                },
 7698            ]),
 7699            active_parameter: None,
 7700        }],
 7701        active_signature: Some(0),
 7702        active_parameter: Some(0),
 7703    };
 7704    handle_signature_help_request(&mut cx, mocked_response).await;
 7705
 7706    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7707        .await;
 7708
 7709    cx.editor(|editor, _| {
 7710        let signature_help_state = editor.signature_help_state.popover().cloned();
 7711        assert!(signature_help_state.is_some());
 7712        let ParsedMarkdown {
 7713            text, highlights, ..
 7714        } = signature_help_state.unwrap().parsed_content;
 7715        assert_eq!(text, "param1: u8, param2: u8");
 7716        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7717    });
 7718
 7719    // When exiting outside from inside the brackets, `signature_help` is closed.
 7720    cx.set_state(indoc! {"
 7721        fn main() {
 7722            sample(ˇ);
 7723        }
 7724
 7725        fn sample(param1: u8, param2: u8) {}
 7726    "});
 7727
 7728    cx.update_editor(|editor, cx| {
 7729        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7730    });
 7731
 7732    let mocked_response = lsp::SignatureHelp {
 7733        signatures: Vec::new(),
 7734        active_signature: None,
 7735        active_parameter: None,
 7736    };
 7737    handle_signature_help_request(&mut cx, mocked_response).await;
 7738
 7739    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7740        .await;
 7741
 7742    cx.editor(|editor, _| {
 7743        assert!(!editor.signature_help_state.is_shown());
 7744    });
 7745
 7746    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7747    cx.set_state(indoc! {"
 7748        fn main() {
 7749            sample(ˇ);
 7750        }
 7751
 7752        fn sample(param1: u8, param2: u8) {}
 7753    "});
 7754
 7755    let mocked_response = lsp::SignatureHelp {
 7756        signatures: vec![lsp::SignatureInformation {
 7757            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7758            documentation: None,
 7759            parameters: Some(vec![
 7760                lsp::ParameterInformation {
 7761                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7762                    documentation: None,
 7763                },
 7764                lsp::ParameterInformation {
 7765                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7766                    documentation: None,
 7767                },
 7768            ]),
 7769            active_parameter: None,
 7770        }],
 7771        active_signature: Some(0),
 7772        active_parameter: Some(0),
 7773    };
 7774    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7775    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7776        .await;
 7777    cx.editor(|editor, _| {
 7778        assert!(editor.signature_help_state.is_shown());
 7779    });
 7780
 7781    // Restore the popover with more parameter input
 7782    cx.set_state(indoc! {"
 7783        fn main() {
 7784            sample(param1, param2ˇ);
 7785        }
 7786
 7787        fn sample(param1: u8, param2: u8) {}
 7788    "});
 7789
 7790    let mocked_response = lsp::SignatureHelp {
 7791        signatures: vec![lsp::SignatureInformation {
 7792            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7793            documentation: None,
 7794            parameters: Some(vec![
 7795                lsp::ParameterInformation {
 7796                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7797                    documentation: None,
 7798                },
 7799                lsp::ParameterInformation {
 7800                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7801                    documentation: None,
 7802                },
 7803            ]),
 7804            active_parameter: None,
 7805        }],
 7806        active_signature: Some(0),
 7807        active_parameter: Some(1),
 7808    };
 7809    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7810    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7811        .await;
 7812
 7813    // When selecting a range, the popover is gone.
 7814    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7815    cx.update_editor(|editor, cx| {
 7816        editor.change_selections(None, cx, |s| {
 7817            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7818        })
 7819    });
 7820    cx.assert_editor_state(indoc! {"
 7821        fn main() {
 7822            sample(param1, «ˇparam2»);
 7823        }
 7824
 7825        fn sample(param1: u8, param2: u8) {}
 7826    "});
 7827    cx.editor(|editor, _| {
 7828        assert!(!editor.signature_help_state.is_shown());
 7829    });
 7830
 7831    // When unselecting again, the popover is back if within the brackets.
 7832    cx.update_editor(|editor, cx| {
 7833        editor.change_selections(None, cx, |s| {
 7834            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7835        })
 7836    });
 7837    cx.assert_editor_state(indoc! {"
 7838        fn main() {
 7839            sample(param1, ˇparam2);
 7840        }
 7841
 7842        fn sample(param1: u8, param2: u8) {}
 7843    "});
 7844    handle_signature_help_request(&mut cx, mocked_response).await;
 7845    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7846        .await;
 7847    cx.editor(|editor, _| {
 7848        assert!(editor.signature_help_state.is_shown());
 7849    });
 7850
 7851    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7852    cx.update_editor(|editor, cx| {
 7853        editor.change_selections(None, cx, |s| {
 7854            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7855            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7856        })
 7857    });
 7858    cx.assert_editor_state(indoc! {"
 7859        fn main() {
 7860            sample(param1, ˇparam2);
 7861        }
 7862
 7863        fn sample(param1: u8, param2: u8) {}
 7864    "});
 7865
 7866    let mocked_response = lsp::SignatureHelp {
 7867        signatures: vec![lsp::SignatureInformation {
 7868            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7869            documentation: None,
 7870            parameters: Some(vec![
 7871                lsp::ParameterInformation {
 7872                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7873                    documentation: None,
 7874                },
 7875                lsp::ParameterInformation {
 7876                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7877                    documentation: None,
 7878                },
 7879            ]),
 7880            active_parameter: None,
 7881        }],
 7882        active_signature: Some(0),
 7883        active_parameter: Some(1),
 7884    };
 7885    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7887        .await;
 7888    cx.update_editor(|editor, cx| {
 7889        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7890    });
 7891    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7892        .await;
 7893    cx.update_editor(|editor, cx| {
 7894        editor.change_selections(None, cx, |s| {
 7895            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7896        })
 7897    });
 7898    cx.assert_editor_state(indoc! {"
 7899        fn main() {
 7900            sample(param1, «ˇparam2»);
 7901        }
 7902
 7903        fn sample(param1: u8, param2: u8) {}
 7904    "});
 7905    cx.update_editor(|editor, cx| {
 7906        editor.change_selections(None, cx, |s| {
 7907            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7908        })
 7909    });
 7910    cx.assert_editor_state(indoc! {"
 7911        fn main() {
 7912            sample(param1, ˇparam2);
 7913        }
 7914
 7915        fn sample(param1: u8, param2: u8) {}
 7916    "});
 7917    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7918        .await;
 7919}
 7920
 7921#[gpui::test]
 7922async fn test_completion(cx: &mut gpui::TestAppContext) {
 7923    init_test(cx, |_| {});
 7924
 7925    let mut cx = EditorLspTestContext::new_rust(
 7926        lsp::ServerCapabilities {
 7927            completion_provider: Some(lsp::CompletionOptions {
 7928                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7929                resolve_provider: Some(true),
 7930                ..Default::default()
 7931            }),
 7932            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7933            ..Default::default()
 7934        },
 7935        cx,
 7936    )
 7937    .await;
 7938    let counter = Arc::new(AtomicUsize::new(0));
 7939
 7940    cx.set_state(indoc! {"
 7941        oneˇ
 7942        two
 7943        three
 7944    "});
 7945    cx.simulate_keystroke(".");
 7946    handle_completion_request(
 7947        &mut cx,
 7948        indoc! {"
 7949            one.|<>
 7950            two
 7951            three
 7952        "},
 7953        vec!["first_completion", "second_completion"],
 7954        counter.clone(),
 7955    )
 7956    .await;
 7957    cx.condition(|editor, _| editor.context_menu_visible())
 7958        .await;
 7959    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7960
 7961    let _handler = handle_signature_help_request(
 7962        &mut cx,
 7963        lsp::SignatureHelp {
 7964            signatures: vec![lsp::SignatureInformation {
 7965                label: "test signature".to_string(),
 7966                documentation: None,
 7967                parameters: Some(vec![lsp::ParameterInformation {
 7968                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7969                    documentation: None,
 7970                }]),
 7971                active_parameter: None,
 7972            }],
 7973            active_signature: None,
 7974            active_parameter: None,
 7975        },
 7976    );
 7977    cx.update_editor(|editor, cx| {
 7978        assert!(
 7979            !editor.signature_help_state.is_shown(),
 7980            "No signature help was called for"
 7981        );
 7982        editor.show_signature_help(&ShowSignatureHelp, cx);
 7983    });
 7984    cx.run_until_parked();
 7985    cx.update_editor(|editor, _| {
 7986        assert!(
 7987            !editor.signature_help_state.is_shown(),
 7988            "No signature help should be shown when completions menu is open"
 7989        );
 7990    });
 7991
 7992    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7993        editor.context_menu_next(&Default::default(), cx);
 7994        editor
 7995            .confirm_completion(&ConfirmCompletion::default(), cx)
 7996            .unwrap()
 7997    });
 7998    cx.assert_editor_state(indoc! {"
 7999        one.second_completionˇ
 8000        two
 8001        three
 8002    "});
 8003
 8004    handle_resolve_completion_request(
 8005        &mut cx,
 8006        Some(vec![
 8007            (
 8008                //This overlaps with the primary completion edit which is
 8009                //misbehavior from the LSP spec, test that we filter it out
 8010                indoc! {"
 8011                    one.second_ˇcompletion
 8012                    two
 8013                    threeˇ
 8014                "},
 8015                "overlapping additional edit",
 8016            ),
 8017            (
 8018                indoc! {"
 8019                    one.second_completion
 8020                    two
 8021                    threeˇ
 8022                "},
 8023                "\nadditional edit",
 8024            ),
 8025        ]),
 8026    )
 8027    .await;
 8028    apply_additional_edits.await.unwrap();
 8029    cx.assert_editor_state(indoc! {"
 8030        one.second_completionˇ
 8031        two
 8032        three
 8033        additional edit
 8034    "});
 8035
 8036    cx.set_state(indoc! {"
 8037        one.second_completion
 8038        twoˇ
 8039        threeˇ
 8040        additional edit
 8041    "});
 8042    cx.simulate_keystroke(" ");
 8043    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8044    cx.simulate_keystroke("s");
 8045    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8046
 8047    cx.assert_editor_state(indoc! {"
 8048        one.second_completion
 8049        two sˇ
 8050        three sˇ
 8051        additional edit
 8052    "});
 8053    handle_completion_request(
 8054        &mut cx,
 8055        indoc! {"
 8056            one.second_completion
 8057            two s
 8058            three <s|>
 8059            additional edit
 8060        "},
 8061        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8062        counter.clone(),
 8063    )
 8064    .await;
 8065    cx.condition(|editor, _| editor.context_menu_visible())
 8066        .await;
 8067    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8068
 8069    cx.simulate_keystroke("i");
 8070
 8071    handle_completion_request(
 8072        &mut cx,
 8073        indoc! {"
 8074            one.second_completion
 8075            two si
 8076            three <si|>
 8077            additional edit
 8078        "},
 8079        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8080        counter.clone(),
 8081    )
 8082    .await;
 8083    cx.condition(|editor, _| editor.context_menu_visible())
 8084        .await;
 8085    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8086
 8087    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8088        editor
 8089            .confirm_completion(&ConfirmCompletion::default(), cx)
 8090            .unwrap()
 8091    });
 8092    cx.assert_editor_state(indoc! {"
 8093        one.second_completion
 8094        two sixth_completionˇ
 8095        three sixth_completionˇ
 8096        additional edit
 8097    "});
 8098
 8099    handle_resolve_completion_request(&mut cx, None).await;
 8100    apply_additional_edits.await.unwrap();
 8101
 8102    cx.update(|cx| {
 8103        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8104            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8105                settings.show_completions_on_input = Some(false);
 8106            });
 8107        })
 8108    });
 8109    cx.set_state("editorˇ");
 8110    cx.simulate_keystroke(".");
 8111    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8112    cx.simulate_keystroke("c");
 8113    cx.simulate_keystroke("l");
 8114    cx.simulate_keystroke("o");
 8115    cx.assert_editor_state("editor.cloˇ");
 8116    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8117    cx.update_editor(|editor, cx| {
 8118        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8119    });
 8120    handle_completion_request(
 8121        &mut cx,
 8122        "editor.<clo|>",
 8123        vec!["close", "clobber"],
 8124        counter.clone(),
 8125    )
 8126    .await;
 8127    cx.condition(|editor, _| editor.context_menu_visible())
 8128        .await;
 8129    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8130
 8131    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8132        editor
 8133            .confirm_completion(&ConfirmCompletion::default(), cx)
 8134            .unwrap()
 8135    });
 8136    cx.assert_editor_state("editor.closeˇ");
 8137    handle_resolve_completion_request(&mut cx, None).await;
 8138    apply_additional_edits.await.unwrap();
 8139}
 8140
 8141#[gpui::test]
 8142async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8143    init_test(cx, |_| {});
 8144    let mut cx = EditorLspTestContext::new_rust(
 8145        lsp::ServerCapabilities {
 8146            completion_provider: Some(lsp::CompletionOptions {
 8147                trigger_characters: Some(vec![".".to_string()]),
 8148                ..Default::default()
 8149            }),
 8150            ..Default::default()
 8151        },
 8152        cx,
 8153    )
 8154    .await;
 8155    cx.lsp
 8156        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8157            Ok(Some(lsp::CompletionResponse::Array(vec![
 8158                lsp::CompletionItem {
 8159                    label: "first".into(),
 8160                    ..Default::default()
 8161                },
 8162                lsp::CompletionItem {
 8163                    label: "last".into(),
 8164                    ..Default::default()
 8165                },
 8166            ])))
 8167        });
 8168    cx.set_state("variableˇ");
 8169    cx.simulate_keystroke(".");
 8170    cx.executor().run_until_parked();
 8171
 8172    cx.update_editor(|editor, _| {
 8173        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8174            assert_eq!(
 8175                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8176                &["first", "last"]
 8177            );
 8178        } else {
 8179            panic!("expected completion menu to be open");
 8180        }
 8181    });
 8182
 8183    cx.update_editor(|editor, cx| {
 8184        editor.move_page_down(&MovePageDown::default(), cx);
 8185        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8186            assert!(
 8187                menu.selected_item == 1,
 8188                "expected PageDown to select the last item from the context menu"
 8189            );
 8190        } else {
 8191            panic!("expected completion menu to stay open after PageDown");
 8192        }
 8193    });
 8194
 8195    cx.update_editor(|editor, cx| {
 8196        editor.move_page_up(&MovePageUp::default(), cx);
 8197        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8198            assert!(
 8199                menu.selected_item == 0,
 8200                "expected PageUp to select the first item from the context menu"
 8201            );
 8202        } else {
 8203            panic!("expected completion menu to stay open after PageUp");
 8204        }
 8205    });
 8206}
 8207
 8208#[gpui::test]
 8209async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8210    init_test(cx, |_| {});
 8211
 8212    let mut cx = EditorLspTestContext::new_rust(
 8213        lsp::ServerCapabilities {
 8214            completion_provider: Some(lsp::CompletionOptions {
 8215                trigger_characters: Some(vec![".".to_string()]),
 8216                resolve_provider: Some(true),
 8217                ..Default::default()
 8218            }),
 8219            ..Default::default()
 8220        },
 8221        cx,
 8222    )
 8223    .await;
 8224
 8225    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8226    cx.simulate_keystroke(".");
 8227    let completion_item = lsp::CompletionItem {
 8228        label: "Some".into(),
 8229        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8230        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8231        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8232            kind: lsp::MarkupKind::Markdown,
 8233            value: "```rust\nSome(2)\n```".to_string(),
 8234        })),
 8235        deprecated: Some(false),
 8236        sort_text: Some("Some".to_string()),
 8237        filter_text: Some("Some".to_string()),
 8238        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8239        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8240            range: lsp::Range {
 8241                start: lsp::Position {
 8242                    line: 0,
 8243                    character: 22,
 8244                },
 8245                end: lsp::Position {
 8246                    line: 0,
 8247                    character: 22,
 8248                },
 8249            },
 8250            new_text: "Some(2)".to_string(),
 8251        })),
 8252        additional_text_edits: Some(vec![lsp::TextEdit {
 8253            range: lsp::Range {
 8254                start: lsp::Position {
 8255                    line: 0,
 8256                    character: 20,
 8257                },
 8258                end: lsp::Position {
 8259                    line: 0,
 8260                    character: 22,
 8261                },
 8262            },
 8263            new_text: "".to_string(),
 8264        }]),
 8265        ..Default::default()
 8266    };
 8267
 8268    let closure_completion_item = completion_item.clone();
 8269    let counter = Arc::new(AtomicUsize::new(0));
 8270    let counter_clone = counter.clone();
 8271    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8272        let task_completion_item = closure_completion_item.clone();
 8273        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8274        async move {
 8275            Ok(Some(lsp::CompletionResponse::Array(vec![
 8276                task_completion_item,
 8277            ])))
 8278        }
 8279    });
 8280
 8281    cx.condition(|editor, _| editor.context_menu_visible())
 8282        .await;
 8283    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8284    assert!(request.next().await.is_some());
 8285    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8286
 8287    cx.simulate_keystroke("S");
 8288    cx.simulate_keystroke("o");
 8289    cx.simulate_keystroke("m");
 8290    cx.condition(|editor, _| editor.context_menu_visible())
 8291        .await;
 8292    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8293    assert!(request.next().await.is_some());
 8294    assert!(request.next().await.is_some());
 8295    assert!(request.next().await.is_some());
 8296    request.close();
 8297    assert!(request.next().await.is_none());
 8298    assert_eq!(
 8299        counter.load(atomic::Ordering::Acquire),
 8300        4,
 8301        "With the completions menu open, only one LSP request should happen per input"
 8302    );
 8303}
 8304
 8305#[gpui::test]
 8306async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8307    init_test(cx, |_| {});
 8308    let mut cx = EditorTestContext::new(cx).await;
 8309    let language = Arc::new(Language::new(
 8310        LanguageConfig {
 8311            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8312            ..Default::default()
 8313        },
 8314        Some(tree_sitter_rust::LANGUAGE.into()),
 8315    ));
 8316    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8317
 8318    // If multiple selections intersect a line, the line is only toggled once.
 8319    cx.set_state(indoc! {"
 8320        fn a() {
 8321            «//b();
 8322            ˇ»// «c();
 8323            //ˇ»  d();
 8324        }
 8325    "});
 8326
 8327    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8328
 8329    cx.assert_editor_state(indoc! {"
 8330        fn a() {
 8331            «b();
 8332            c();
 8333            ˇ» d();
 8334        }
 8335    "});
 8336
 8337    // The comment prefix is inserted at the same column for every line in a
 8338    // selection.
 8339    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8340
 8341    cx.assert_editor_state(indoc! {"
 8342        fn a() {
 8343            // «b();
 8344            // c();
 8345            ˇ»//  d();
 8346        }
 8347    "});
 8348
 8349    // If a selection ends at the beginning of a line, that line is not toggled.
 8350    cx.set_selections_state(indoc! {"
 8351        fn a() {
 8352            // b();
 8353            «// c();
 8354        ˇ»    //  d();
 8355        }
 8356    "});
 8357
 8358    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8359
 8360    cx.assert_editor_state(indoc! {"
 8361        fn a() {
 8362            // b();
 8363            «c();
 8364        ˇ»    //  d();
 8365        }
 8366    "});
 8367
 8368    // If a selection span a single line and is empty, the line is toggled.
 8369    cx.set_state(indoc! {"
 8370        fn a() {
 8371            a();
 8372            b();
 8373        ˇ
 8374        }
 8375    "});
 8376
 8377    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8378
 8379    cx.assert_editor_state(indoc! {"
 8380        fn a() {
 8381            a();
 8382            b();
 8383        //•ˇ
 8384        }
 8385    "});
 8386
 8387    // If a selection span multiple lines, empty lines are not toggled.
 8388    cx.set_state(indoc! {"
 8389        fn a() {
 8390            «a();
 8391
 8392            c();ˇ»
 8393        }
 8394    "});
 8395
 8396    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8397
 8398    cx.assert_editor_state(indoc! {"
 8399        fn a() {
 8400            // «a();
 8401
 8402            // c();ˇ»
 8403        }
 8404    "});
 8405
 8406    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8407    cx.set_state(indoc! {"
 8408        fn a() {
 8409            «// a();
 8410            /// b();
 8411            //! c();ˇ»
 8412        }
 8413    "});
 8414
 8415    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8416
 8417    cx.assert_editor_state(indoc! {"
 8418        fn a() {
 8419            «a();
 8420            b();
 8421            c();ˇ»
 8422        }
 8423    "});
 8424}
 8425
 8426#[gpui::test]
 8427async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8428    init_test(cx, |_| {});
 8429
 8430    let language = Arc::new(Language::new(
 8431        LanguageConfig {
 8432            line_comments: vec!["// ".into()],
 8433            ..Default::default()
 8434        },
 8435        Some(tree_sitter_rust::LANGUAGE.into()),
 8436    ));
 8437
 8438    let mut cx = EditorTestContext::new(cx).await;
 8439
 8440    cx.language_registry().add(language.clone());
 8441    cx.update_buffer(|buffer, cx| {
 8442        buffer.set_language(Some(language), cx);
 8443    });
 8444
 8445    let toggle_comments = &ToggleComments {
 8446        advance_downwards: true,
 8447    };
 8448
 8449    // Single cursor on one line -> advance
 8450    // Cursor moves horizontally 3 characters as well on non-blank line
 8451    cx.set_state(indoc!(
 8452        "fn a() {
 8453             ˇdog();
 8454             cat();
 8455        }"
 8456    ));
 8457    cx.update_editor(|editor, cx| {
 8458        editor.toggle_comments(toggle_comments, cx);
 8459    });
 8460    cx.assert_editor_state(indoc!(
 8461        "fn a() {
 8462             // dog();
 8463             catˇ();
 8464        }"
 8465    ));
 8466
 8467    // Single selection on one line -> don't advance
 8468    cx.set_state(indoc!(
 8469        "fn a() {
 8470             «dog()ˇ»;
 8471             cat();
 8472        }"
 8473    ));
 8474    cx.update_editor(|editor, cx| {
 8475        editor.toggle_comments(toggle_comments, cx);
 8476    });
 8477    cx.assert_editor_state(indoc!(
 8478        "fn a() {
 8479             // «dog()ˇ»;
 8480             cat();
 8481        }"
 8482    ));
 8483
 8484    // Multiple cursors on one line -> advance
 8485    cx.set_state(indoc!(
 8486        "fn a() {
 8487             ˇdˇog();
 8488             cat();
 8489        }"
 8490    ));
 8491    cx.update_editor(|editor, cx| {
 8492        editor.toggle_comments(toggle_comments, cx);
 8493    });
 8494    cx.assert_editor_state(indoc!(
 8495        "fn a() {
 8496             // dog();
 8497             catˇ(ˇ);
 8498        }"
 8499    ));
 8500
 8501    // Multiple cursors on one line, with selection -> don't advance
 8502    cx.set_state(indoc!(
 8503        "fn a() {
 8504             ˇdˇog«()ˇ»;
 8505             cat();
 8506        }"
 8507    ));
 8508    cx.update_editor(|editor, cx| {
 8509        editor.toggle_comments(toggle_comments, cx);
 8510    });
 8511    cx.assert_editor_state(indoc!(
 8512        "fn a() {
 8513             // ˇdˇog«()ˇ»;
 8514             cat();
 8515        }"
 8516    ));
 8517
 8518    // Single cursor on one line -> advance
 8519    // Cursor moves to column 0 on blank line
 8520    cx.set_state(indoc!(
 8521        "fn a() {
 8522             ˇdog();
 8523
 8524             cat();
 8525        }"
 8526    ));
 8527    cx.update_editor(|editor, cx| {
 8528        editor.toggle_comments(toggle_comments, cx);
 8529    });
 8530    cx.assert_editor_state(indoc!(
 8531        "fn a() {
 8532             // dog();
 8533        ˇ
 8534             cat();
 8535        }"
 8536    ));
 8537
 8538    // Single cursor on one line -> advance
 8539    // Cursor starts and ends at column 0
 8540    cx.set_state(indoc!(
 8541        "fn a() {
 8542         ˇ    dog();
 8543             cat();
 8544        }"
 8545    ));
 8546    cx.update_editor(|editor, cx| {
 8547        editor.toggle_comments(toggle_comments, cx);
 8548    });
 8549    cx.assert_editor_state(indoc!(
 8550        "fn a() {
 8551             // dog();
 8552         ˇ    cat();
 8553        }"
 8554    ));
 8555}
 8556
 8557#[gpui::test]
 8558async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8559    init_test(cx, |_| {});
 8560
 8561    let mut cx = EditorTestContext::new(cx).await;
 8562
 8563    let html_language = Arc::new(
 8564        Language::new(
 8565            LanguageConfig {
 8566                name: "HTML".into(),
 8567                block_comment: Some(("<!-- ".into(), " -->".into())),
 8568                ..Default::default()
 8569            },
 8570            Some(tree_sitter_html::language()),
 8571        )
 8572        .with_injection_query(
 8573            r#"
 8574            (script_element
 8575                (raw_text) @content
 8576                (#set! "language" "javascript"))
 8577            "#,
 8578        )
 8579        .unwrap(),
 8580    );
 8581
 8582    let javascript_language = Arc::new(Language::new(
 8583        LanguageConfig {
 8584            name: "JavaScript".into(),
 8585            line_comments: vec!["// ".into()],
 8586            ..Default::default()
 8587        },
 8588        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8589    ));
 8590
 8591    cx.language_registry().add(html_language.clone());
 8592    cx.language_registry().add(javascript_language.clone());
 8593    cx.update_buffer(|buffer, cx| {
 8594        buffer.set_language(Some(html_language), cx);
 8595    });
 8596
 8597    // Toggle comments for empty selections
 8598    cx.set_state(
 8599        &r#"
 8600            <p>A</p>ˇ
 8601            <p>B</p>ˇ
 8602            <p>C</p>ˇ
 8603        "#
 8604        .unindent(),
 8605    );
 8606    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8607    cx.assert_editor_state(
 8608        &r#"
 8609            <!-- <p>A</p>ˇ -->
 8610            <!-- <p>B</p>ˇ -->
 8611            <!-- <p>C</p>ˇ -->
 8612        "#
 8613        .unindent(),
 8614    );
 8615    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8616    cx.assert_editor_state(
 8617        &r#"
 8618            <p>A</p>ˇ
 8619            <p>B</p>ˇ
 8620            <p>C</p>ˇ
 8621        "#
 8622        .unindent(),
 8623    );
 8624
 8625    // Toggle comments for mixture of empty and non-empty selections, where
 8626    // multiple selections occupy a given line.
 8627    cx.set_state(
 8628        &r#"
 8629            <p>A«</p>
 8630            <p>ˇ»B</p>ˇ
 8631            <p>C«</p>
 8632            <p>ˇ»D</p>ˇ
 8633        "#
 8634        .unindent(),
 8635    );
 8636
 8637    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8638    cx.assert_editor_state(
 8639        &r#"
 8640            <!-- <p>A«</p>
 8641            <p>ˇ»B</p>ˇ -->
 8642            <!-- <p>C«</p>
 8643            <p>ˇ»D</p>ˇ -->
 8644        "#
 8645        .unindent(),
 8646    );
 8647    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8648    cx.assert_editor_state(
 8649        &r#"
 8650            <p>A«</p>
 8651            <p>ˇ»B</p>ˇ
 8652            <p>C«</p>
 8653            <p>ˇ»D</p>ˇ
 8654        "#
 8655        .unindent(),
 8656    );
 8657
 8658    // Toggle comments when different languages are active for different
 8659    // selections.
 8660    cx.set_state(
 8661        &r#"
 8662            ˇ<script>
 8663                ˇvar x = new Y();
 8664            ˇ</script>
 8665        "#
 8666        .unindent(),
 8667    );
 8668    cx.executor().run_until_parked();
 8669    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8670    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8671    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8672    cx.assert_editor_state(
 8673        &r#"
 8674            <!-- ˇ<script> -->
 8675                // ˇvar x = new Y();
 8676            // ˇ</script>
 8677        "#
 8678        .unindent(),
 8679    );
 8680}
 8681
 8682#[gpui::test]
 8683fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8684    init_test(cx, |_| {});
 8685
 8686    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8687    let multibuffer = cx.new_model(|cx| {
 8688        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8689        multibuffer.push_excerpts(
 8690            buffer.clone(),
 8691            [
 8692                ExcerptRange {
 8693                    context: Point::new(0, 0)..Point::new(0, 4),
 8694                    primary: None,
 8695                },
 8696                ExcerptRange {
 8697                    context: Point::new(1, 0)..Point::new(1, 4),
 8698                    primary: None,
 8699                },
 8700            ],
 8701            cx,
 8702        );
 8703        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8704        multibuffer
 8705    });
 8706
 8707    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8708    view.update(cx, |view, cx| {
 8709        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8710        view.change_selections(None, cx, |s| {
 8711            s.select_ranges([
 8712                Point::new(0, 0)..Point::new(0, 0),
 8713                Point::new(1, 0)..Point::new(1, 0),
 8714            ])
 8715        });
 8716
 8717        view.handle_input("X", cx);
 8718        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8719        assert_eq!(
 8720            view.selections.ranges(cx),
 8721            [
 8722                Point::new(0, 1)..Point::new(0, 1),
 8723                Point::new(1, 1)..Point::new(1, 1),
 8724            ]
 8725        );
 8726
 8727        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8728        view.change_selections(None, cx, |s| {
 8729            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8730        });
 8731        view.backspace(&Default::default(), cx);
 8732        assert_eq!(view.text(cx), "Xa\nbbb");
 8733        assert_eq!(
 8734            view.selections.ranges(cx),
 8735            [Point::new(1, 0)..Point::new(1, 0)]
 8736        );
 8737
 8738        view.change_selections(None, cx, |s| {
 8739            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8740        });
 8741        view.backspace(&Default::default(), cx);
 8742        assert_eq!(view.text(cx), "X\nbb");
 8743        assert_eq!(
 8744            view.selections.ranges(cx),
 8745            [Point::new(0, 1)..Point::new(0, 1)]
 8746        );
 8747    });
 8748}
 8749
 8750#[gpui::test]
 8751fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8752    init_test(cx, |_| {});
 8753
 8754    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8755    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8756        indoc! {"
 8757            [aaaa
 8758            (bbbb]
 8759            cccc)",
 8760        },
 8761        markers.clone(),
 8762    );
 8763    let excerpt_ranges = markers.into_iter().map(|marker| {
 8764        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8765        ExcerptRange {
 8766            context,
 8767            primary: None,
 8768        }
 8769    });
 8770    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8771    let multibuffer = cx.new_model(|cx| {
 8772        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8773        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8774        multibuffer
 8775    });
 8776
 8777    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8778    view.update(cx, |view, cx| {
 8779        let (expected_text, selection_ranges) = marked_text_ranges(
 8780            indoc! {"
 8781                aaaa
 8782                bˇbbb
 8783                bˇbbˇb
 8784                cccc"
 8785            },
 8786            true,
 8787        );
 8788        assert_eq!(view.text(cx), expected_text);
 8789        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8790
 8791        view.handle_input("X", cx);
 8792
 8793        let (expected_text, expected_selections) = marked_text_ranges(
 8794            indoc! {"
 8795                aaaa
 8796                bXˇbbXb
 8797                bXˇbbXˇb
 8798                cccc"
 8799            },
 8800            false,
 8801        );
 8802        assert_eq!(view.text(cx), expected_text);
 8803        assert_eq!(view.selections.ranges(cx), expected_selections);
 8804
 8805        view.newline(&Newline, cx);
 8806        let (expected_text, expected_selections) = marked_text_ranges(
 8807            indoc! {"
 8808                aaaa
 8809                bX
 8810                ˇbbX
 8811                b
 8812                bX
 8813                ˇbbX
 8814                ˇb
 8815                cccc"
 8816            },
 8817            false,
 8818        );
 8819        assert_eq!(view.text(cx), expected_text);
 8820        assert_eq!(view.selections.ranges(cx), expected_selections);
 8821    });
 8822}
 8823
 8824#[gpui::test]
 8825fn test_refresh_selections(cx: &mut TestAppContext) {
 8826    init_test(cx, |_| {});
 8827
 8828    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8829    let mut excerpt1_id = None;
 8830    let multibuffer = cx.new_model(|cx| {
 8831        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8832        excerpt1_id = multibuffer
 8833            .push_excerpts(
 8834                buffer.clone(),
 8835                [
 8836                    ExcerptRange {
 8837                        context: Point::new(0, 0)..Point::new(1, 4),
 8838                        primary: None,
 8839                    },
 8840                    ExcerptRange {
 8841                        context: Point::new(1, 0)..Point::new(2, 4),
 8842                        primary: None,
 8843                    },
 8844                ],
 8845                cx,
 8846            )
 8847            .into_iter()
 8848            .next();
 8849        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8850        multibuffer
 8851    });
 8852
 8853    let editor = cx.add_window(|cx| {
 8854        let mut editor = build_editor(multibuffer.clone(), cx);
 8855        let snapshot = editor.snapshot(cx);
 8856        editor.change_selections(None, cx, |s| {
 8857            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8858        });
 8859        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8860        assert_eq!(
 8861            editor.selections.ranges(cx),
 8862            [
 8863                Point::new(1, 3)..Point::new(1, 3),
 8864                Point::new(2, 1)..Point::new(2, 1),
 8865            ]
 8866        );
 8867        editor
 8868    });
 8869
 8870    // Refreshing selections is a no-op when excerpts haven't changed.
 8871    _ = editor.update(cx, |editor, cx| {
 8872        editor.change_selections(None, cx, |s| s.refresh());
 8873        assert_eq!(
 8874            editor.selections.ranges(cx),
 8875            [
 8876                Point::new(1, 3)..Point::new(1, 3),
 8877                Point::new(2, 1)..Point::new(2, 1),
 8878            ]
 8879        );
 8880    });
 8881
 8882    multibuffer.update(cx, |multibuffer, cx| {
 8883        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8884    });
 8885    _ = editor.update(cx, |editor, cx| {
 8886        // Removing an excerpt causes the first selection to become degenerate.
 8887        assert_eq!(
 8888            editor.selections.ranges(cx),
 8889            [
 8890                Point::new(0, 0)..Point::new(0, 0),
 8891                Point::new(0, 1)..Point::new(0, 1)
 8892            ]
 8893        );
 8894
 8895        // Refreshing selections will relocate the first selection to the original buffer
 8896        // location.
 8897        editor.change_selections(None, cx, |s| s.refresh());
 8898        assert_eq!(
 8899            editor.selections.ranges(cx),
 8900            [
 8901                Point::new(0, 1)..Point::new(0, 1),
 8902                Point::new(0, 3)..Point::new(0, 3)
 8903            ]
 8904        );
 8905        assert!(editor.selections.pending_anchor().is_some());
 8906    });
 8907}
 8908
 8909#[gpui::test]
 8910fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8911    init_test(cx, |_| {});
 8912
 8913    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8914    let mut excerpt1_id = None;
 8915    let multibuffer = cx.new_model(|cx| {
 8916        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8917        excerpt1_id = multibuffer
 8918            .push_excerpts(
 8919                buffer.clone(),
 8920                [
 8921                    ExcerptRange {
 8922                        context: Point::new(0, 0)..Point::new(1, 4),
 8923                        primary: None,
 8924                    },
 8925                    ExcerptRange {
 8926                        context: Point::new(1, 0)..Point::new(2, 4),
 8927                        primary: None,
 8928                    },
 8929                ],
 8930                cx,
 8931            )
 8932            .into_iter()
 8933            .next();
 8934        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8935        multibuffer
 8936    });
 8937
 8938    let editor = cx.add_window(|cx| {
 8939        let mut editor = build_editor(multibuffer.clone(), cx);
 8940        let snapshot = editor.snapshot(cx);
 8941        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8942        assert_eq!(
 8943            editor.selections.ranges(cx),
 8944            [Point::new(1, 3)..Point::new(1, 3)]
 8945        );
 8946        editor
 8947    });
 8948
 8949    multibuffer.update(cx, |multibuffer, cx| {
 8950        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8951    });
 8952    _ = editor.update(cx, |editor, cx| {
 8953        assert_eq!(
 8954            editor.selections.ranges(cx),
 8955            [Point::new(0, 0)..Point::new(0, 0)]
 8956        );
 8957
 8958        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8959        editor.change_selections(None, cx, |s| s.refresh());
 8960        assert_eq!(
 8961            editor.selections.ranges(cx),
 8962            [Point::new(0, 3)..Point::new(0, 3)]
 8963        );
 8964        assert!(editor.selections.pending_anchor().is_some());
 8965    });
 8966}
 8967
 8968#[gpui::test]
 8969async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8970    init_test(cx, |_| {});
 8971
 8972    let language = Arc::new(
 8973        Language::new(
 8974            LanguageConfig {
 8975                brackets: BracketPairConfig {
 8976                    pairs: vec![
 8977                        BracketPair {
 8978                            start: "{".to_string(),
 8979                            end: "}".to_string(),
 8980                            close: true,
 8981                            surround: true,
 8982                            newline: true,
 8983                        },
 8984                        BracketPair {
 8985                            start: "/* ".to_string(),
 8986                            end: " */".to_string(),
 8987                            close: true,
 8988                            surround: true,
 8989                            newline: true,
 8990                        },
 8991                    ],
 8992                    ..Default::default()
 8993                },
 8994                ..Default::default()
 8995            },
 8996            Some(tree_sitter_rust::LANGUAGE.into()),
 8997        )
 8998        .with_indents_query("")
 8999        .unwrap(),
 9000    );
 9001
 9002    let text = concat!(
 9003        "{   }\n",     //
 9004        "  x\n",       //
 9005        "  /*   */\n", //
 9006        "x\n",         //
 9007        "{{} }\n",     //
 9008    );
 9009
 9010    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9011    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9012    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9013    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9014        .await;
 9015
 9016    view.update(cx, |view, cx| {
 9017        view.change_selections(None, cx, |s| {
 9018            s.select_display_ranges([
 9019                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9020                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9021                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9022            ])
 9023        });
 9024        view.newline(&Newline, cx);
 9025
 9026        assert_eq!(
 9027            view.buffer().read(cx).read(cx).text(),
 9028            concat!(
 9029                "{ \n",    // Suppress rustfmt
 9030                "\n",      //
 9031                "}\n",     //
 9032                "  x\n",   //
 9033                "  /* \n", //
 9034                "  \n",    //
 9035                "  */\n",  //
 9036                "x\n",     //
 9037                "{{} \n",  //
 9038                "}\n",     //
 9039            )
 9040        );
 9041    });
 9042}
 9043
 9044#[gpui::test]
 9045fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9046    init_test(cx, |_| {});
 9047
 9048    let editor = cx.add_window(|cx| {
 9049        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9050        build_editor(buffer.clone(), cx)
 9051    });
 9052
 9053    _ = editor.update(cx, |editor, cx| {
 9054        struct Type1;
 9055        struct Type2;
 9056
 9057        let buffer = editor.buffer.read(cx).snapshot(cx);
 9058
 9059        let anchor_range =
 9060            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9061
 9062        editor.highlight_background::<Type1>(
 9063            &[
 9064                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9065                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9066                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9067                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9068            ],
 9069            |_| Hsla::red(),
 9070            cx,
 9071        );
 9072        editor.highlight_background::<Type2>(
 9073            &[
 9074                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9075                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9076                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9077                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9078            ],
 9079            |_| Hsla::green(),
 9080            cx,
 9081        );
 9082
 9083        let snapshot = editor.snapshot(cx);
 9084        let mut highlighted_ranges = editor.background_highlights_in_range(
 9085            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9086            &snapshot,
 9087            cx.theme().colors(),
 9088        );
 9089        // Enforce a consistent ordering based on color without relying on the ordering of the
 9090        // highlight's `TypeId` which is non-executor.
 9091        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9092        assert_eq!(
 9093            highlighted_ranges,
 9094            &[
 9095                (
 9096                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9097                    Hsla::red(),
 9098                ),
 9099                (
 9100                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9101                    Hsla::red(),
 9102                ),
 9103                (
 9104                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9105                    Hsla::green(),
 9106                ),
 9107                (
 9108                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9109                    Hsla::green(),
 9110                ),
 9111            ]
 9112        );
 9113        assert_eq!(
 9114            editor.background_highlights_in_range(
 9115                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9116                &snapshot,
 9117                cx.theme().colors(),
 9118            ),
 9119            &[(
 9120                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9121                Hsla::red(),
 9122            )]
 9123        );
 9124    });
 9125}
 9126
 9127#[gpui::test]
 9128async fn test_following(cx: &mut gpui::TestAppContext) {
 9129    init_test(cx, |_| {});
 9130
 9131    let fs = FakeFs::new(cx.executor());
 9132    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9133
 9134    let buffer = project.update(cx, |project, cx| {
 9135        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9136        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9137    });
 9138    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9139    let follower = cx.update(|cx| {
 9140        cx.open_window(
 9141            WindowOptions {
 9142                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9143                    gpui::Point::new(px(0.), px(0.)),
 9144                    gpui::Point::new(px(10.), px(80.)),
 9145                ))),
 9146                ..Default::default()
 9147            },
 9148            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9149        )
 9150        .unwrap()
 9151    });
 9152
 9153    let is_still_following = Rc::new(RefCell::new(true));
 9154    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9155    let pending_update = Rc::new(RefCell::new(None));
 9156    _ = follower.update(cx, {
 9157        let update = pending_update.clone();
 9158        let is_still_following = is_still_following.clone();
 9159        let follower_edit_event_count = follower_edit_event_count.clone();
 9160        |_, cx| {
 9161            cx.subscribe(
 9162                &leader.root_view(cx).unwrap(),
 9163                move |_, leader, event, cx| {
 9164                    leader
 9165                        .read(cx)
 9166                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9167                },
 9168            )
 9169            .detach();
 9170
 9171            cx.subscribe(
 9172                &follower.root_view(cx).unwrap(),
 9173                move |_, _, event: &EditorEvent, _cx| {
 9174                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9175                        *is_still_following.borrow_mut() = false;
 9176                    }
 9177
 9178                    if let EditorEvent::BufferEdited = event {
 9179                        *follower_edit_event_count.borrow_mut() += 1;
 9180                    }
 9181                },
 9182            )
 9183            .detach();
 9184        }
 9185    });
 9186
 9187    // Update the selections only
 9188    _ = leader.update(cx, |leader, cx| {
 9189        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9190    });
 9191    follower
 9192        .update(cx, |follower, cx| {
 9193            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9194        })
 9195        .unwrap()
 9196        .await
 9197        .unwrap();
 9198    _ = follower.update(cx, |follower, cx| {
 9199        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9200    });
 9201    assert!(*is_still_following.borrow());
 9202    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9203
 9204    // Update the scroll position only
 9205    _ = leader.update(cx, |leader, cx| {
 9206        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9207    });
 9208    follower
 9209        .update(cx, |follower, cx| {
 9210            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9211        })
 9212        .unwrap()
 9213        .await
 9214        .unwrap();
 9215    assert_eq!(
 9216        follower
 9217            .update(cx, |follower, cx| follower.scroll_position(cx))
 9218            .unwrap(),
 9219        gpui::Point::new(1.5, 3.5)
 9220    );
 9221    assert!(*is_still_following.borrow());
 9222    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9223
 9224    // Update the selections and scroll position. The follower's scroll position is updated
 9225    // via autoscroll, not via the leader's exact scroll position.
 9226    _ = leader.update(cx, |leader, cx| {
 9227        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9228        leader.request_autoscroll(Autoscroll::newest(), cx);
 9229        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9230    });
 9231    follower
 9232        .update(cx, |follower, cx| {
 9233            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9234        })
 9235        .unwrap()
 9236        .await
 9237        .unwrap();
 9238    _ = follower.update(cx, |follower, cx| {
 9239        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9240        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9241    });
 9242    assert!(*is_still_following.borrow());
 9243
 9244    // Creating a pending selection that precedes another selection
 9245    _ = leader.update(cx, |leader, cx| {
 9246        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9247        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9248    });
 9249    follower
 9250        .update(cx, |follower, cx| {
 9251            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9252        })
 9253        .unwrap()
 9254        .await
 9255        .unwrap();
 9256    _ = follower.update(cx, |follower, cx| {
 9257        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9258    });
 9259    assert!(*is_still_following.borrow());
 9260
 9261    // Extend the pending selection so that it surrounds another selection
 9262    _ = leader.update(cx, |leader, cx| {
 9263        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9264    });
 9265    follower
 9266        .update(cx, |follower, cx| {
 9267            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9268        })
 9269        .unwrap()
 9270        .await
 9271        .unwrap();
 9272    _ = follower.update(cx, |follower, cx| {
 9273        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9274    });
 9275
 9276    // Scrolling locally breaks the follow
 9277    _ = follower.update(cx, |follower, cx| {
 9278        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9279        follower.set_scroll_anchor(
 9280            ScrollAnchor {
 9281                anchor: top_anchor,
 9282                offset: gpui::Point::new(0.0, 0.5),
 9283            },
 9284            cx,
 9285        );
 9286    });
 9287    assert!(!(*is_still_following.borrow()));
 9288}
 9289
 9290#[gpui::test]
 9291async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9292    init_test(cx, |_| {});
 9293
 9294    let fs = FakeFs::new(cx.executor());
 9295    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9296    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9297    let pane = workspace
 9298        .update(cx, |workspace, _| workspace.active_pane().clone())
 9299        .unwrap();
 9300
 9301    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9302
 9303    let leader = pane.update(cx, |_, cx| {
 9304        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9305        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9306    });
 9307
 9308    // Start following the editor when it has no excerpts.
 9309    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9310    let follower_1 = cx
 9311        .update_window(*workspace.deref(), |_, cx| {
 9312            Editor::from_state_proto(
 9313                workspace.root_view(cx).unwrap(),
 9314                ViewId {
 9315                    creator: Default::default(),
 9316                    id: 0,
 9317                },
 9318                &mut state_message,
 9319                cx,
 9320            )
 9321        })
 9322        .unwrap()
 9323        .unwrap()
 9324        .await
 9325        .unwrap();
 9326
 9327    let update_message = Rc::new(RefCell::new(None));
 9328    follower_1.update(cx, {
 9329        let update = update_message.clone();
 9330        |_, cx| {
 9331            cx.subscribe(&leader, move |_, leader, event, cx| {
 9332                leader
 9333                    .read(cx)
 9334                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9335            })
 9336            .detach();
 9337        }
 9338    });
 9339
 9340    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9341        (
 9342            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9343            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9344        )
 9345    });
 9346
 9347    // Insert some excerpts.
 9348    leader.update(cx, |leader, cx| {
 9349        leader.buffer.update(cx, |multibuffer, cx| {
 9350            let excerpt_ids = multibuffer.push_excerpts(
 9351                buffer_1.clone(),
 9352                [
 9353                    ExcerptRange {
 9354                        context: 1..6,
 9355                        primary: None,
 9356                    },
 9357                    ExcerptRange {
 9358                        context: 12..15,
 9359                        primary: None,
 9360                    },
 9361                    ExcerptRange {
 9362                        context: 0..3,
 9363                        primary: None,
 9364                    },
 9365                ],
 9366                cx,
 9367            );
 9368            multibuffer.insert_excerpts_after(
 9369                excerpt_ids[0],
 9370                buffer_2.clone(),
 9371                [
 9372                    ExcerptRange {
 9373                        context: 8..12,
 9374                        primary: None,
 9375                    },
 9376                    ExcerptRange {
 9377                        context: 0..6,
 9378                        primary: None,
 9379                    },
 9380                ],
 9381                cx,
 9382            );
 9383        });
 9384    });
 9385
 9386    // Apply the update of adding the excerpts.
 9387    follower_1
 9388        .update(cx, |follower, cx| {
 9389            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9390        })
 9391        .await
 9392        .unwrap();
 9393    assert_eq!(
 9394        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9395        leader.update(cx, |editor, cx| editor.text(cx))
 9396    );
 9397    update_message.borrow_mut().take();
 9398
 9399    // Start following separately after it already has excerpts.
 9400    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9401    let follower_2 = cx
 9402        .update_window(*workspace.deref(), |_, cx| {
 9403            Editor::from_state_proto(
 9404                workspace.root_view(cx).unwrap().clone(),
 9405                ViewId {
 9406                    creator: Default::default(),
 9407                    id: 0,
 9408                },
 9409                &mut state_message,
 9410                cx,
 9411            )
 9412        })
 9413        .unwrap()
 9414        .unwrap()
 9415        .await
 9416        .unwrap();
 9417    assert_eq!(
 9418        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9419        leader.update(cx, |editor, cx| editor.text(cx))
 9420    );
 9421
 9422    // Remove some excerpts.
 9423    leader.update(cx, |leader, cx| {
 9424        leader.buffer.update(cx, |multibuffer, cx| {
 9425            let excerpt_ids = multibuffer.excerpt_ids();
 9426            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9427            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9428        });
 9429    });
 9430
 9431    // Apply the update of removing the excerpts.
 9432    follower_1
 9433        .update(cx, |follower, cx| {
 9434            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9435        })
 9436        .await
 9437        .unwrap();
 9438    follower_2
 9439        .update(cx, |follower, cx| {
 9440            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9441        })
 9442        .await
 9443        .unwrap();
 9444    update_message.borrow_mut().take();
 9445    assert_eq!(
 9446        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9447        leader.update(cx, |editor, cx| editor.text(cx))
 9448    );
 9449}
 9450
 9451#[gpui::test]
 9452async fn go_to_prev_overlapping_diagnostic(
 9453    executor: BackgroundExecutor,
 9454    cx: &mut gpui::TestAppContext,
 9455) {
 9456    init_test(cx, |_| {});
 9457
 9458    let mut cx = EditorTestContext::new(cx).await;
 9459    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9460
 9461    cx.set_state(indoc! {"
 9462        ˇfn func(abc def: i32) -> u32 {
 9463        }
 9464    "});
 9465
 9466    cx.update(|cx| {
 9467        project.update(cx, |project, cx| {
 9468            project
 9469                .update_diagnostics(
 9470                    LanguageServerId(0),
 9471                    lsp::PublishDiagnosticsParams {
 9472                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9473                        version: None,
 9474                        diagnostics: vec![
 9475                            lsp::Diagnostic {
 9476                                range: lsp::Range::new(
 9477                                    lsp::Position::new(0, 11),
 9478                                    lsp::Position::new(0, 12),
 9479                                ),
 9480                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9481                                ..Default::default()
 9482                            },
 9483                            lsp::Diagnostic {
 9484                                range: lsp::Range::new(
 9485                                    lsp::Position::new(0, 12),
 9486                                    lsp::Position::new(0, 15),
 9487                                ),
 9488                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9489                                ..Default::default()
 9490                            },
 9491                            lsp::Diagnostic {
 9492                                range: lsp::Range::new(
 9493                                    lsp::Position::new(0, 25),
 9494                                    lsp::Position::new(0, 28),
 9495                                ),
 9496                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9497                                ..Default::default()
 9498                            },
 9499                        ],
 9500                    },
 9501                    &[],
 9502                    cx,
 9503                )
 9504                .unwrap()
 9505        });
 9506    });
 9507
 9508    executor.run_until_parked();
 9509
 9510    cx.update_editor(|editor, cx| {
 9511        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9512    });
 9513
 9514    cx.assert_editor_state(indoc! {"
 9515        fn func(abc def: i32) -> ˇu32 {
 9516        }
 9517    "});
 9518
 9519    cx.update_editor(|editor, cx| {
 9520        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9521    });
 9522
 9523    cx.assert_editor_state(indoc! {"
 9524        fn func(abc ˇdef: i32) -> u32 {
 9525        }
 9526    "});
 9527
 9528    cx.update_editor(|editor, cx| {
 9529        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9530    });
 9531
 9532    cx.assert_editor_state(indoc! {"
 9533        fn func(abcˇ def: i32) -> u32 {
 9534        }
 9535    "});
 9536
 9537    cx.update_editor(|editor, cx| {
 9538        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9539    });
 9540
 9541    cx.assert_editor_state(indoc! {"
 9542        fn func(abc def: i32) -> ˇu32 {
 9543        }
 9544    "});
 9545}
 9546
 9547#[gpui::test]
 9548async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9549    init_test(cx, |_| {});
 9550
 9551    let mut cx = EditorTestContext::new(cx).await;
 9552
 9553    cx.set_state(indoc! {"
 9554        fn func(abˇc def: i32) -> u32 {
 9555        }
 9556    "});
 9557    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9558
 9559    cx.update(|cx| {
 9560        project.update(cx, |project, cx| {
 9561            project.update_diagnostics(
 9562                LanguageServerId(0),
 9563                lsp::PublishDiagnosticsParams {
 9564                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9565                    version: None,
 9566                    diagnostics: vec![lsp::Diagnostic {
 9567                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9568                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9569                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9570                        ..Default::default()
 9571                    }],
 9572                },
 9573                &[],
 9574                cx,
 9575            )
 9576        })
 9577    }).unwrap();
 9578    cx.run_until_parked();
 9579    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9580    cx.run_until_parked();
 9581    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9582}
 9583
 9584#[gpui::test]
 9585async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9586    init_test(cx, |_| {});
 9587
 9588    let mut cx = EditorTestContext::new(cx).await;
 9589
 9590    let diff_base = r#"
 9591        use some::mod;
 9592
 9593        const A: u32 = 42;
 9594
 9595        fn main() {
 9596            println!("hello");
 9597
 9598            println!("world");
 9599        }
 9600        "#
 9601    .unindent();
 9602
 9603    // Edits are modified, removed, modified, added
 9604    cx.set_state(
 9605        &r#"
 9606        use some::modified;
 9607
 9608        ˇ
 9609        fn main() {
 9610            println!("hello there");
 9611
 9612            println!("around the");
 9613            println!("world");
 9614        }
 9615        "#
 9616        .unindent(),
 9617    );
 9618
 9619    cx.set_diff_base(Some(&diff_base));
 9620    executor.run_until_parked();
 9621
 9622    cx.update_editor(|editor, cx| {
 9623        //Wrap around the bottom of the buffer
 9624        for _ in 0..3 {
 9625            editor.go_to_next_hunk(&GoToHunk, cx);
 9626        }
 9627    });
 9628
 9629    cx.assert_editor_state(
 9630        &r#"
 9631        ˇuse some::modified;
 9632
 9633
 9634        fn main() {
 9635            println!("hello there");
 9636
 9637            println!("around the");
 9638            println!("world");
 9639        }
 9640        "#
 9641        .unindent(),
 9642    );
 9643
 9644    cx.update_editor(|editor, cx| {
 9645        //Wrap around the top of the buffer
 9646        for _ in 0..2 {
 9647            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9648        }
 9649    });
 9650
 9651    cx.assert_editor_state(
 9652        &r#"
 9653        use some::modified;
 9654
 9655
 9656        fn main() {
 9657        ˇ    println!("hello there");
 9658
 9659            println!("around the");
 9660            println!("world");
 9661        }
 9662        "#
 9663        .unindent(),
 9664    );
 9665
 9666    cx.update_editor(|editor, cx| {
 9667        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9668    });
 9669
 9670    cx.assert_editor_state(
 9671        &r#"
 9672        use some::modified;
 9673
 9674        ˇ
 9675        fn main() {
 9676            println!("hello there");
 9677
 9678            println!("around the");
 9679            println!("world");
 9680        }
 9681        "#
 9682        .unindent(),
 9683    );
 9684
 9685    cx.update_editor(|editor, cx| {
 9686        for _ in 0..3 {
 9687            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9688        }
 9689    });
 9690
 9691    cx.assert_editor_state(
 9692        &r#"
 9693        use some::modified;
 9694
 9695
 9696        fn main() {
 9697        ˇ    println!("hello there");
 9698
 9699            println!("around the");
 9700            println!("world");
 9701        }
 9702        "#
 9703        .unindent(),
 9704    );
 9705
 9706    cx.update_editor(|editor, cx| {
 9707        editor.fold(&Fold, cx);
 9708
 9709        //Make sure that the fold only gets one hunk
 9710        for _ in 0..4 {
 9711            editor.go_to_next_hunk(&GoToHunk, cx);
 9712        }
 9713    });
 9714
 9715    cx.assert_editor_state(
 9716        &r#"
 9717        ˇuse some::modified;
 9718
 9719
 9720        fn main() {
 9721            println!("hello there");
 9722
 9723            println!("around the");
 9724            println!("world");
 9725        }
 9726        "#
 9727        .unindent(),
 9728    );
 9729}
 9730
 9731#[test]
 9732fn test_split_words() {
 9733    fn split(text: &str) -> Vec<&str> {
 9734        split_words(text).collect()
 9735    }
 9736
 9737    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9738    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9739    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9740    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9741    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9742    assert_eq!(split("helloworld"), &["helloworld"]);
 9743
 9744    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9745}
 9746
 9747#[gpui::test]
 9748async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9749    init_test(cx, |_| {});
 9750
 9751    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9752    let mut assert = |before, after| {
 9753        let _state_context = cx.set_state(before);
 9754        cx.update_editor(|editor, cx| {
 9755            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9756        });
 9757        cx.assert_editor_state(after);
 9758    };
 9759
 9760    // Outside bracket jumps to outside of matching bracket
 9761    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9762    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9763
 9764    // Inside bracket jumps to inside of matching bracket
 9765    assert("console.log(ˇvar);", "console.log(varˇ);");
 9766    assert("console.log(varˇ);", "console.log(ˇvar);");
 9767
 9768    // When outside a bracket and inside, favor jumping to the inside bracket
 9769    assert(
 9770        "console.log('foo', [1, 2, 3]ˇ);",
 9771        "console.log(ˇ'foo', [1, 2, 3]);",
 9772    );
 9773    assert(
 9774        "console.log(ˇ'foo', [1, 2, 3]);",
 9775        "console.log('foo', [1, 2, 3]ˇ);",
 9776    );
 9777
 9778    // Bias forward if two options are equally likely
 9779    assert(
 9780        "let result = curried_fun()ˇ();",
 9781        "let result = curried_fun()()ˇ;",
 9782    );
 9783
 9784    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9785    assert(
 9786        indoc! {"
 9787            function test() {
 9788                console.log('test')ˇ
 9789            }"},
 9790        indoc! {"
 9791            function test() {
 9792                console.logˇ('test')
 9793            }"},
 9794    );
 9795}
 9796
 9797#[gpui::test]
 9798async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9799    init_test(cx, |_| {});
 9800
 9801    let fs = FakeFs::new(cx.executor());
 9802    fs.insert_tree(
 9803        "/a",
 9804        json!({
 9805            "main.rs": "fn main() { let a = 5; }",
 9806            "other.rs": "// Test file",
 9807        }),
 9808    )
 9809    .await;
 9810    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9811
 9812    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9813    language_registry.add(Arc::new(Language::new(
 9814        LanguageConfig {
 9815            name: "Rust".into(),
 9816            matcher: LanguageMatcher {
 9817                path_suffixes: vec!["rs".to_string()],
 9818                ..Default::default()
 9819            },
 9820            brackets: BracketPairConfig {
 9821                pairs: vec![BracketPair {
 9822                    start: "{".to_string(),
 9823                    end: "}".to_string(),
 9824                    close: true,
 9825                    surround: true,
 9826                    newline: true,
 9827                }],
 9828                disabled_scopes_by_bracket_ix: Vec::new(),
 9829            },
 9830            ..Default::default()
 9831        },
 9832        Some(tree_sitter_rust::LANGUAGE.into()),
 9833    )));
 9834    let mut fake_servers = language_registry.register_fake_lsp(
 9835        "Rust",
 9836        FakeLspAdapter {
 9837            capabilities: lsp::ServerCapabilities {
 9838                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9839                    first_trigger_character: "{".to_string(),
 9840                    more_trigger_character: None,
 9841                }),
 9842                ..Default::default()
 9843            },
 9844            ..Default::default()
 9845        },
 9846    );
 9847
 9848    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9849
 9850    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9851
 9852    let worktree_id = workspace
 9853        .update(cx, |workspace, cx| {
 9854            workspace.project().update(cx, |project, cx| {
 9855                project.worktrees(cx).next().unwrap().read(cx).id()
 9856            })
 9857        })
 9858        .unwrap();
 9859
 9860    let buffer = project
 9861        .update(cx, |project, cx| {
 9862            project.open_local_buffer("/a/main.rs", cx)
 9863        })
 9864        .await
 9865        .unwrap();
 9866    cx.executor().run_until_parked();
 9867    cx.executor().start_waiting();
 9868    let fake_server = fake_servers.next().await.unwrap();
 9869    let editor_handle = workspace
 9870        .update(cx, |workspace, cx| {
 9871            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9872        })
 9873        .unwrap()
 9874        .await
 9875        .unwrap()
 9876        .downcast::<Editor>()
 9877        .unwrap();
 9878
 9879    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9880        assert_eq!(
 9881            params.text_document_position.text_document.uri,
 9882            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9883        );
 9884        assert_eq!(
 9885            params.text_document_position.position,
 9886            lsp::Position::new(0, 21),
 9887        );
 9888
 9889        Ok(Some(vec![lsp::TextEdit {
 9890            new_text: "]".to_string(),
 9891            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9892        }]))
 9893    });
 9894
 9895    editor_handle.update(cx, |editor, cx| {
 9896        editor.focus(cx);
 9897        editor.change_selections(None, cx, |s| {
 9898            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9899        });
 9900        editor.handle_input("{", cx);
 9901    });
 9902
 9903    cx.executor().run_until_parked();
 9904
 9905    buffer.update(cx, |buffer, _| {
 9906        assert_eq!(
 9907            buffer.text(),
 9908            "fn main() { let a = {5}; }",
 9909            "No extra braces from on type formatting should appear in the buffer"
 9910        )
 9911    });
 9912}
 9913
 9914#[gpui::test]
 9915async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9916    init_test(cx, |_| {});
 9917
 9918    let fs = FakeFs::new(cx.executor());
 9919    fs.insert_tree(
 9920        "/a",
 9921        json!({
 9922            "main.rs": "fn main() { let a = 5; }",
 9923            "other.rs": "// Test file",
 9924        }),
 9925    )
 9926    .await;
 9927
 9928    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9929
 9930    let server_restarts = Arc::new(AtomicUsize::new(0));
 9931    let closure_restarts = Arc::clone(&server_restarts);
 9932    let language_server_name = "test language server";
 9933    let language_name: LanguageName = "Rust".into();
 9934
 9935    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9936    language_registry.add(Arc::new(Language::new(
 9937        LanguageConfig {
 9938            name: language_name.clone(),
 9939            matcher: LanguageMatcher {
 9940                path_suffixes: vec!["rs".to_string()],
 9941                ..Default::default()
 9942            },
 9943            ..Default::default()
 9944        },
 9945        Some(tree_sitter_rust::LANGUAGE.into()),
 9946    )));
 9947    let mut fake_servers = language_registry.register_fake_lsp(
 9948        "Rust",
 9949        FakeLspAdapter {
 9950            name: language_server_name,
 9951            initialization_options: Some(json!({
 9952                "testOptionValue": true
 9953            })),
 9954            initializer: Some(Box::new(move |fake_server| {
 9955                let task_restarts = Arc::clone(&closure_restarts);
 9956                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9957                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9958                    futures::future::ready(Ok(()))
 9959                });
 9960            })),
 9961            ..Default::default()
 9962        },
 9963    );
 9964
 9965    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9966    let _buffer = project
 9967        .update(cx, |project, cx| {
 9968            project.open_local_buffer("/a/main.rs", cx)
 9969        })
 9970        .await
 9971        .unwrap();
 9972    let _fake_server = fake_servers.next().await.unwrap();
 9973    update_test_language_settings(cx, |language_settings| {
 9974        language_settings.languages.insert(
 9975            language_name.clone(),
 9976            LanguageSettingsContent {
 9977                tab_size: NonZeroU32::new(8),
 9978                ..Default::default()
 9979            },
 9980        );
 9981    });
 9982    cx.executor().run_until_parked();
 9983    assert_eq!(
 9984        server_restarts.load(atomic::Ordering::Acquire),
 9985        0,
 9986        "Should not restart LSP server on an unrelated change"
 9987    );
 9988
 9989    update_test_project_settings(cx, |project_settings| {
 9990        project_settings.lsp.insert(
 9991            "Some other server name".into(),
 9992            LspSettings {
 9993                binary: None,
 9994                settings: None,
 9995                initialization_options: Some(json!({
 9996                    "some other init value": false
 9997                })),
 9998            },
 9999        );
10000    });
10001    cx.executor().run_until_parked();
10002    assert_eq!(
10003        server_restarts.load(atomic::Ordering::Acquire),
10004        0,
10005        "Should not restart LSP server on an unrelated LSP settings change"
10006    );
10007
10008    update_test_project_settings(cx, |project_settings| {
10009        project_settings.lsp.insert(
10010            language_server_name.into(),
10011            LspSettings {
10012                binary: None,
10013                settings: None,
10014                initialization_options: Some(json!({
10015                    "anotherInitValue": false
10016                })),
10017            },
10018        );
10019    });
10020    cx.executor().run_until_parked();
10021    assert_eq!(
10022        server_restarts.load(atomic::Ordering::Acquire),
10023        1,
10024        "Should restart LSP server on a related LSP settings change"
10025    );
10026
10027    update_test_project_settings(cx, |project_settings| {
10028        project_settings.lsp.insert(
10029            language_server_name.into(),
10030            LspSettings {
10031                binary: None,
10032                settings: None,
10033                initialization_options: Some(json!({
10034                    "anotherInitValue": false
10035                })),
10036            },
10037        );
10038    });
10039    cx.executor().run_until_parked();
10040    assert_eq!(
10041        server_restarts.load(atomic::Ordering::Acquire),
10042        1,
10043        "Should not restart LSP server on a related LSP settings change that is the same"
10044    );
10045
10046    update_test_project_settings(cx, |project_settings| {
10047        project_settings.lsp.insert(
10048            language_server_name.into(),
10049            LspSettings {
10050                binary: None,
10051                settings: None,
10052                initialization_options: None,
10053            },
10054        );
10055    });
10056    cx.executor().run_until_parked();
10057    assert_eq!(
10058        server_restarts.load(atomic::Ordering::Acquire),
10059        2,
10060        "Should restart LSP server on another related LSP settings change"
10061    );
10062}
10063
10064#[gpui::test]
10065async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10066    init_test(cx, |_| {});
10067
10068    let mut cx = EditorLspTestContext::new_rust(
10069        lsp::ServerCapabilities {
10070            completion_provider: Some(lsp::CompletionOptions {
10071                trigger_characters: Some(vec![".".to_string()]),
10072                resolve_provider: Some(true),
10073                ..Default::default()
10074            }),
10075            ..Default::default()
10076        },
10077        cx,
10078    )
10079    .await;
10080
10081    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10082    cx.simulate_keystroke(".");
10083    let completion_item = lsp::CompletionItem {
10084        label: "some".into(),
10085        kind: Some(lsp::CompletionItemKind::SNIPPET),
10086        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10087        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10088            kind: lsp::MarkupKind::Markdown,
10089            value: "```rust\nSome(2)\n```".to_string(),
10090        })),
10091        deprecated: Some(false),
10092        sort_text: Some("fffffff2".to_string()),
10093        filter_text: Some("some".to_string()),
10094        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10095        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10096            range: lsp::Range {
10097                start: lsp::Position {
10098                    line: 0,
10099                    character: 22,
10100                },
10101                end: lsp::Position {
10102                    line: 0,
10103                    character: 22,
10104                },
10105            },
10106            new_text: "Some(2)".to_string(),
10107        })),
10108        additional_text_edits: Some(vec![lsp::TextEdit {
10109            range: lsp::Range {
10110                start: lsp::Position {
10111                    line: 0,
10112                    character: 20,
10113                },
10114                end: lsp::Position {
10115                    line: 0,
10116                    character: 22,
10117                },
10118            },
10119            new_text: "".to_string(),
10120        }]),
10121        ..Default::default()
10122    };
10123
10124    let closure_completion_item = completion_item.clone();
10125    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10126        let task_completion_item = closure_completion_item.clone();
10127        async move {
10128            Ok(Some(lsp::CompletionResponse::Array(vec![
10129                task_completion_item,
10130            ])))
10131        }
10132    });
10133
10134    request.next().await;
10135
10136    cx.condition(|editor, _| editor.context_menu_visible())
10137        .await;
10138    let apply_additional_edits = cx.update_editor(|editor, cx| {
10139        editor
10140            .confirm_completion(&ConfirmCompletion::default(), cx)
10141            .unwrap()
10142    });
10143    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10144
10145    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10146        let task_completion_item = completion_item.clone();
10147        async move { Ok(task_completion_item) }
10148    })
10149    .next()
10150    .await
10151    .unwrap();
10152    apply_additional_edits.await.unwrap();
10153    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10154}
10155
10156#[gpui::test]
10157async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10158    init_test(cx, |_| {});
10159
10160    let mut cx = EditorLspTestContext::new(
10161        Language::new(
10162            LanguageConfig {
10163                matcher: LanguageMatcher {
10164                    path_suffixes: vec!["jsx".into()],
10165                    ..Default::default()
10166                },
10167                overrides: [(
10168                    "element".into(),
10169                    LanguageConfigOverride {
10170                        word_characters: Override::Set(['-'].into_iter().collect()),
10171                        ..Default::default()
10172                    },
10173                )]
10174                .into_iter()
10175                .collect(),
10176                ..Default::default()
10177            },
10178            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10179        )
10180        .with_override_query("(jsx_self_closing_element) @element")
10181        .unwrap(),
10182        lsp::ServerCapabilities {
10183            completion_provider: Some(lsp::CompletionOptions {
10184                trigger_characters: Some(vec![":".to_string()]),
10185                ..Default::default()
10186            }),
10187            ..Default::default()
10188        },
10189        cx,
10190    )
10191    .await;
10192
10193    cx.lsp
10194        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10195            Ok(Some(lsp::CompletionResponse::Array(vec![
10196                lsp::CompletionItem {
10197                    label: "bg-blue".into(),
10198                    ..Default::default()
10199                },
10200                lsp::CompletionItem {
10201                    label: "bg-red".into(),
10202                    ..Default::default()
10203                },
10204                lsp::CompletionItem {
10205                    label: "bg-yellow".into(),
10206                    ..Default::default()
10207                },
10208            ])))
10209        });
10210
10211    cx.set_state(r#"<p class="bgˇ" />"#);
10212
10213    // Trigger completion when typing a dash, because the dash is an extra
10214    // word character in the 'element' scope, which contains the cursor.
10215    cx.simulate_keystroke("-");
10216    cx.executor().run_until_parked();
10217    cx.update_editor(|editor, _| {
10218        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10219            assert_eq!(
10220                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10221                &["bg-red", "bg-blue", "bg-yellow"]
10222            );
10223        } else {
10224            panic!("expected completion menu to be open");
10225        }
10226    });
10227
10228    cx.simulate_keystroke("l");
10229    cx.executor().run_until_parked();
10230    cx.update_editor(|editor, _| {
10231        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10232            assert_eq!(
10233                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10234                &["bg-blue", "bg-yellow"]
10235            );
10236        } else {
10237            panic!("expected completion menu to be open");
10238        }
10239    });
10240
10241    // When filtering completions, consider the character after the '-' to
10242    // be the start of a subword.
10243    cx.set_state(r#"<p class="yelˇ" />"#);
10244    cx.simulate_keystroke("l");
10245    cx.executor().run_until_parked();
10246    cx.update_editor(|editor, _| {
10247        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10248            assert_eq!(
10249                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10250                &["bg-yellow"]
10251            );
10252        } else {
10253            panic!("expected completion menu to be open");
10254        }
10255    });
10256}
10257
10258#[gpui::test]
10259async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10260    init_test(cx, |settings| {
10261        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10262            FormatterList(vec![Formatter::Prettier].into()),
10263        ))
10264    });
10265
10266    let fs = FakeFs::new(cx.executor());
10267    fs.insert_file("/file.ts", Default::default()).await;
10268
10269    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10270    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10271
10272    language_registry.add(Arc::new(Language::new(
10273        LanguageConfig {
10274            name: "TypeScript".into(),
10275            matcher: LanguageMatcher {
10276                path_suffixes: vec!["ts".to_string()],
10277                ..Default::default()
10278            },
10279            ..Default::default()
10280        },
10281        Some(tree_sitter_rust::LANGUAGE.into()),
10282    )));
10283    update_test_language_settings(cx, |settings| {
10284        settings.defaults.prettier = Some(PrettierSettings {
10285            allowed: true,
10286            ..PrettierSettings::default()
10287        });
10288    });
10289
10290    let test_plugin = "test_plugin";
10291    let _ = language_registry.register_fake_lsp(
10292        "TypeScript",
10293        FakeLspAdapter {
10294            prettier_plugins: vec![test_plugin],
10295            ..Default::default()
10296        },
10297    );
10298
10299    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10300    let buffer = project
10301        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10302        .await
10303        .unwrap();
10304
10305    let buffer_text = "one\ntwo\nthree\n";
10306    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10307    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10308    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10309
10310    editor
10311        .update(cx, |editor, cx| {
10312            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10313        })
10314        .unwrap()
10315        .await;
10316    assert_eq!(
10317        editor.update(cx, |editor, cx| editor.text(cx)),
10318        buffer_text.to_string() + prettier_format_suffix,
10319        "Test prettier formatting was not applied to the original buffer text",
10320    );
10321
10322    update_test_language_settings(cx, |settings| {
10323        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10324    });
10325    let format = editor.update(cx, |editor, cx| {
10326        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
10327    });
10328    format.await.unwrap();
10329    assert_eq!(
10330        editor.update(cx, |editor, cx| editor.text(cx)),
10331        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10332        "Autoformatting (via test prettier) was not applied to the original buffer text",
10333    );
10334}
10335
10336#[gpui::test]
10337async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10338    init_test(cx, |_| {});
10339    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10340    let base_text = indoc! {r#"struct Row;
10341struct Row1;
10342struct Row2;
10343
10344struct Row4;
10345struct Row5;
10346struct Row6;
10347
10348struct Row8;
10349struct Row9;
10350struct Row10;"#};
10351
10352    // When addition hunks are not adjacent to carets, no hunk revert is performed
10353    assert_hunk_revert(
10354        indoc! {r#"struct Row;
10355                   struct Row1;
10356                   struct Row1.1;
10357                   struct Row1.2;
10358                   struct Row2;ˇ
10359
10360                   struct Row4;
10361                   struct Row5;
10362                   struct Row6;
10363
10364                   struct Row8;
10365                   ˇstruct Row9;
10366                   struct Row9.1;
10367                   struct Row9.2;
10368                   struct Row9.3;
10369                   struct Row10;"#},
10370        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10371        indoc! {r#"struct Row;
10372                   struct Row1;
10373                   struct Row1.1;
10374                   struct Row1.2;
10375                   struct Row2;ˇ
10376
10377                   struct Row4;
10378                   struct Row5;
10379                   struct Row6;
10380
10381                   struct Row8;
10382                   ˇstruct Row9;
10383                   struct Row9.1;
10384                   struct Row9.2;
10385                   struct Row9.3;
10386                   struct Row10;"#},
10387        base_text,
10388        &mut cx,
10389    );
10390    // Same for selections
10391    assert_hunk_revert(
10392        indoc! {r#"struct Row;
10393                   struct Row1;
10394                   struct Row2;
10395                   struct Row2.1;
10396                   struct Row2.2;
10397                   «ˇ
10398                   struct Row4;
10399                   struct» Row5;
10400                   «struct Row6;
10401                   ˇ»
10402                   struct Row9.1;
10403                   struct Row9.2;
10404                   struct Row9.3;
10405                   struct Row8;
10406                   struct Row9;
10407                   struct Row10;"#},
10408        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10409        indoc! {r#"struct Row;
10410                   struct Row1;
10411                   struct Row2;
10412                   struct Row2.1;
10413                   struct Row2.2;
10414                   «ˇ
10415                   struct Row4;
10416                   struct» Row5;
10417                   «struct Row6;
10418                   ˇ»
10419                   struct Row9.1;
10420                   struct Row9.2;
10421                   struct Row9.3;
10422                   struct Row8;
10423                   struct Row9;
10424                   struct Row10;"#},
10425        base_text,
10426        &mut cx,
10427    );
10428
10429    // When carets and selections intersect the addition hunks, those are reverted.
10430    // Adjacent carets got merged.
10431    assert_hunk_revert(
10432        indoc! {r#"struct Row;
10433                   ˇ// something on the top
10434                   struct Row1;
10435                   struct Row2;
10436                   struct Roˇw3.1;
10437                   struct Row2.2;
10438                   struct Row2.3;ˇ
10439
10440                   struct Row4;
10441                   struct ˇRow5.1;
10442                   struct Row5.2;
10443                   struct «Rowˇ»5.3;
10444                   struct Row5;
10445                   struct Row6;
10446                   ˇ
10447                   struct Row9.1;
10448                   struct «Rowˇ»9.2;
10449                   struct «ˇRow»9.3;
10450                   struct Row8;
10451                   struct Row9;
10452                   «ˇ// something on bottom»
10453                   struct Row10;"#},
10454        vec![
10455            DiffHunkStatus::Added,
10456            DiffHunkStatus::Added,
10457            DiffHunkStatus::Added,
10458            DiffHunkStatus::Added,
10459            DiffHunkStatus::Added,
10460        ],
10461        indoc! {r#"struct Row;
10462                   ˇstruct Row1;
10463                   struct Row2;
10464                   ˇ
10465                   struct Row4;
10466                   ˇstruct Row5;
10467                   struct Row6;
10468                   ˇ
10469                   ˇstruct Row8;
10470                   struct Row9;
10471                   ˇstruct Row10;"#},
10472        base_text,
10473        &mut cx,
10474    );
10475}
10476
10477#[gpui::test]
10478async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10479    init_test(cx, |_| {});
10480    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10481    let base_text = indoc! {r#"struct Row;
10482struct Row1;
10483struct Row2;
10484
10485struct Row4;
10486struct Row5;
10487struct Row6;
10488
10489struct Row8;
10490struct Row9;
10491struct Row10;"#};
10492
10493    // Modification hunks behave the same as the addition ones.
10494    assert_hunk_revert(
10495        indoc! {r#"struct Row;
10496                   struct Row1;
10497                   struct Row33;
10498                   ˇ
10499                   struct Row4;
10500                   struct Row5;
10501                   struct Row6;
10502                   ˇ
10503                   struct Row99;
10504                   struct Row9;
10505                   struct Row10;"#},
10506        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10507        indoc! {r#"struct Row;
10508                   struct Row1;
10509                   struct Row33;
10510                   ˇ
10511                   struct Row4;
10512                   struct Row5;
10513                   struct Row6;
10514                   ˇ
10515                   struct Row99;
10516                   struct Row9;
10517                   struct Row10;"#},
10518        base_text,
10519        &mut cx,
10520    );
10521    assert_hunk_revert(
10522        indoc! {r#"struct Row;
10523                   struct Row1;
10524                   struct Row33;
10525                   «ˇ
10526                   struct Row4;
10527                   struct» Row5;
10528                   «struct Row6;
10529                   ˇ»
10530                   struct Row99;
10531                   struct Row9;
10532                   struct Row10;"#},
10533        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10534        indoc! {r#"struct Row;
10535                   struct Row1;
10536                   struct Row33;
10537                   «ˇ
10538                   struct Row4;
10539                   struct» Row5;
10540                   «struct Row6;
10541                   ˇ»
10542                   struct Row99;
10543                   struct Row9;
10544                   struct Row10;"#},
10545        base_text,
10546        &mut cx,
10547    );
10548
10549    assert_hunk_revert(
10550        indoc! {r#"ˇstruct Row1.1;
10551                   struct Row1;
10552                   «ˇstr»uct Row22;
10553
10554                   struct ˇRow44;
10555                   struct Row5;
10556                   struct «Rˇ»ow66;ˇ
10557
10558                   «struˇ»ct Row88;
10559                   struct Row9;
10560                   struct Row1011;ˇ"#},
10561        vec![
10562            DiffHunkStatus::Modified,
10563            DiffHunkStatus::Modified,
10564            DiffHunkStatus::Modified,
10565            DiffHunkStatus::Modified,
10566            DiffHunkStatus::Modified,
10567            DiffHunkStatus::Modified,
10568        ],
10569        indoc! {r#"struct Row;
10570                   ˇstruct Row1;
10571                   struct Row2;
10572                   ˇ
10573                   struct Row4;
10574                   ˇstruct Row5;
10575                   struct Row6;
10576                   ˇ
10577                   struct Row8;
10578                   ˇstruct Row9;
10579                   struct Row10;ˇ"#},
10580        base_text,
10581        &mut cx,
10582    );
10583}
10584
10585#[gpui::test]
10586async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10587    init_test(cx, |_| {});
10588    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10589    let base_text = indoc! {r#"struct Row;
10590struct Row1;
10591struct Row2;
10592
10593struct Row4;
10594struct Row5;
10595struct Row6;
10596
10597struct Row8;
10598struct Row9;
10599struct Row10;"#};
10600
10601    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10602    assert_hunk_revert(
10603        indoc! {r#"struct Row;
10604                   struct Row2;
10605
10606                   ˇstruct Row4;
10607                   struct Row5;
10608                   struct Row6;
10609                   ˇ
10610                   struct Row8;
10611                   struct Row10;"#},
10612        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10613        indoc! {r#"struct Row;
10614                   struct Row2;
10615
10616                   ˇstruct Row4;
10617                   struct Row5;
10618                   struct Row6;
10619                   ˇ
10620                   struct Row8;
10621                   struct Row10;"#},
10622        base_text,
10623        &mut cx,
10624    );
10625    assert_hunk_revert(
10626        indoc! {r#"struct Row;
10627                   struct Row2;
10628
10629                   «ˇstruct Row4;
10630                   struct» Row5;
10631                   «struct Row6;
10632                   ˇ»
10633                   struct Row8;
10634                   struct Row10;"#},
10635        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10636        indoc! {r#"struct Row;
10637                   struct Row2;
10638
10639                   «ˇstruct Row4;
10640                   struct» Row5;
10641                   «struct Row6;
10642                   ˇ»
10643                   struct Row8;
10644                   struct Row10;"#},
10645        base_text,
10646        &mut cx,
10647    );
10648
10649    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10650    assert_hunk_revert(
10651        indoc! {r#"struct Row;
10652                   ˇstruct Row2;
10653
10654                   struct Row4;
10655                   struct Row5;
10656                   struct Row6;
10657
10658                   struct Row8;ˇ
10659                   struct Row10;"#},
10660        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10661        indoc! {r#"struct Row;
10662                   struct Row1;
10663                   ˇstruct Row2;
10664
10665                   struct Row4;
10666                   struct Row5;
10667                   struct Row6;
10668
10669                   struct Row8;ˇ
10670                   struct Row9;
10671                   struct Row10;"#},
10672        base_text,
10673        &mut cx,
10674    );
10675    assert_hunk_revert(
10676        indoc! {r#"struct Row;
10677                   struct Row2«ˇ;
10678                   struct Row4;
10679                   struct» Row5;
10680                   «struct Row6;
10681
10682                   struct Row8;ˇ»
10683                   struct Row10;"#},
10684        vec![
10685            DiffHunkStatus::Removed,
10686            DiffHunkStatus::Removed,
10687            DiffHunkStatus::Removed,
10688        ],
10689        indoc! {r#"struct Row;
10690                   struct Row1;
10691                   struct Row2«ˇ;
10692
10693                   struct Row4;
10694                   struct» Row5;
10695                   «struct Row6;
10696
10697                   struct Row8;ˇ»
10698                   struct Row9;
10699                   struct Row10;"#},
10700        base_text,
10701        &mut cx,
10702    );
10703}
10704
10705#[gpui::test]
10706async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10707    init_test(cx, |_| {});
10708
10709    let cols = 4;
10710    let rows = 10;
10711    let sample_text_1 = sample_text(rows, cols, 'a');
10712    assert_eq!(
10713        sample_text_1,
10714        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10715    );
10716    let sample_text_2 = sample_text(rows, cols, 'l');
10717    assert_eq!(
10718        sample_text_2,
10719        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10720    );
10721    let sample_text_3 = sample_text(rows, cols, 'v');
10722    assert_eq!(
10723        sample_text_3,
10724        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10725    );
10726
10727    fn diff_every_buffer_row(
10728        buffer: &Model<Buffer>,
10729        sample_text: String,
10730        cols: usize,
10731        cx: &mut gpui::TestAppContext,
10732    ) {
10733        // revert first character in each row, creating one large diff hunk per buffer
10734        let is_first_char = |offset: usize| offset % cols == 0;
10735        buffer.update(cx, |buffer, cx| {
10736            buffer.set_text(
10737                sample_text
10738                    .chars()
10739                    .enumerate()
10740                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10741                    .collect::<String>(),
10742                cx,
10743            );
10744            buffer.set_diff_base(Some(sample_text), cx);
10745        });
10746        cx.executor().run_until_parked();
10747    }
10748
10749    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10750    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10751
10752    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10753    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10754
10755    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10756    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10757
10758    let multibuffer = cx.new_model(|cx| {
10759        let mut multibuffer = MultiBuffer::new(ReadWrite);
10760        multibuffer.push_excerpts(
10761            buffer_1.clone(),
10762            [
10763                ExcerptRange {
10764                    context: Point::new(0, 0)..Point::new(3, 0),
10765                    primary: None,
10766                },
10767                ExcerptRange {
10768                    context: Point::new(5, 0)..Point::new(7, 0),
10769                    primary: None,
10770                },
10771                ExcerptRange {
10772                    context: Point::new(9, 0)..Point::new(10, 4),
10773                    primary: None,
10774                },
10775            ],
10776            cx,
10777        );
10778        multibuffer.push_excerpts(
10779            buffer_2.clone(),
10780            [
10781                ExcerptRange {
10782                    context: Point::new(0, 0)..Point::new(3, 0),
10783                    primary: None,
10784                },
10785                ExcerptRange {
10786                    context: Point::new(5, 0)..Point::new(7, 0),
10787                    primary: None,
10788                },
10789                ExcerptRange {
10790                    context: Point::new(9, 0)..Point::new(10, 4),
10791                    primary: None,
10792                },
10793            ],
10794            cx,
10795        );
10796        multibuffer.push_excerpts(
10797            buffer_3.clone(),
10798            [
10799                ExcerptRange {
10800                    context: Point::new(0, 0)..Point::new(3, 0),
10801                    primary: None,
10802                },
10803                ExcerptRange {
10804                    context: Point::new(5, 0)..Point::new(7, 0),
10805                    primary: None,
10806                },
10807                ExcerptRange {
10808                    context: Point::new(9, 0)..Point::new(10, 4),
10809                    primary: None,
10810                },
10811            ],
10812            cx,
10813        );
10814        multibuffer
10815    });
10816
10817    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10818    editor.update(cx, |editor, cx| {
10819        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");
10820        editor.select_all(&SelectAll, cx);
10821        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10822    });
10823    cx.executor().run_until_parked();
10824    // When all ranges are selected, all buffer hunks are reverted.
10825    editor.update(cx, |editor, cx| {
10826        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");
10827    });
10828    buffer_1.update(cx, |buffer, _| {
10829        assert_eq!(buffer.text(), sample_text_1);
10830    });
10831    buffer_2.update(cx, |buffer, _| {
10832        assert_eq!(buffer.text(), sample_text_2);
10833    });
10834    buffer_3.update(cx, |buffer, _| {
10835        assert_eq!(buffer.text(), sample_text_3);
10836    });
10837
10838    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10839    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10840    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10841    editor.update(cx, |editor, cx| {
10842        editor.change_selections(None, cx, |s| {
10843            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10844        });
10845        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10846    });
10847    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10848    // but not affect buffer_2 and its related excerpts.
10849    editor.update(cx, |editor, cx| {
10850        assert_eq!(
10851            editor.text(cx),
10852            "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"
10853        );
10854    });
10855    buffer_1.update(cx, |buffer, _| {
10856        assert_eq!(buffer.text(), sample_text_1);
10857    });
10858    buffer_2.update(cx, |buffer, _| {
10859        assert_eq!(
10860            buffer.text(),
10861            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10862        );
10863    });
10864    buffer_3.update(cx, |buffer, _| {
10865        assert_eq!(
10866            buffer.text(),
10867            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10868        );
10869    });
10870}
10871
10872#[gpui::test]
10873async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10874    init_test(cx, |_| {});
10875
10876    let cols = 4;
10877    let rows = 10;
10878    let sample_text_1 = sample_text(rows, cols, 'a');
10879    assert_eq!(
10880        sample_text_1,
10881        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10882    );
10883    let sample_text_2 = sample_text(rows, cols, 'l');
10884    assert_eq!(
10885        sample_text_2,
10886        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10887    );
10888    let sample_text_3 = sample_text(rows, cols, 'v');
10889    assert_eq!(
10890        sample_text_3,
10891        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10892    );
10893
10894    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10895    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10896    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10897
10898    let multi_buffer = cx.new_model(|cx| {
10899        let mut multibuffer = MultiBuffer::new(ReadWrite);
10900        multibuffer.push_excerpts(
10901            buffer_1.clone(),
10902            [
10903                ExcerptRange {
10904                    context: Point::new(0, 0)..Point::new(3, 0),
10905                    primary: None,
10906                },
10907                ExcerptRange {
10908                    context: Point::new(5, 0)..Point::new(7, 0),
10909                    primary: None,
10910                },
10911                ExcerptRange {
10912                    context: Point::new(9, 0)..Point::new(10, 4),
10913                    primary: None,
10914                },
10915            ],
10916            cx,
10917        );
10918        multibuffer.push_excerpts(
10919            buffer_2.clone(),
10920            [
10921                ExcerptRange {
10922                    context: Point::new(0, 0)..Point::new(3, 0),
10923                    primary: None,
10924                },
10925                ExcerptRange {
10926                    context: Point::new(5, 0)..Point::new(7, 0),
10927                    primary: None,
10928                },
10929                ExcerptRange {
10930                    context: Point::new(9, 0)..Point::new(10, 4),
10931                    primary: None,
10932                },
10933            ],
10934            cx,
10935        );
10936        multibuffer.push_excerpts(
10937            buffer_3.clone(),
10938            [
10939                ExcerptRange {
10940                    context: Point::new(0, 0)..Point::new(3, 0),
10941                    primary: None,
10942                },
10943                ExcerptRange {
10944                    context: Point::new(5, 0)..Point::new(7, 0),
10945                    primary: None,
10946                },
10947                ExcerptRange {
10948                    context: Point::new(9, 0)..Point::new(10, 4),
10949                    primary: None,
10950                },
10951            ],
10952            cx,
10953        );
10954        multibuffer
10955    });
10956
10957    let fs = FakeFs::new(cx.executor());
10958    fs.insert_tree(
10959        "/a",
10960        json!({
10961            "main.rs": sample_text_1,
10962            "other.rs": sample_text_2,
10963            "lib.rs": sample_text_3,
10964        }),
10965    )
10966    .await;
10967    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10968    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10969    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10970    let multi_buffer_editor = cx.new_view(|cx| {
10971        Editor::new(
10972            EditorMode::Full,
10973            multi_buffer,
10974            Some(project.clone()),
10975            true,
10976            cx,
10977        )
10978    });
10979    let multibuffer_item_id = workspace
10980        .update(cx, |workspace, cx| {
10981            assert!(
10982                workspace.active_item(cx).is_none(),
10983                "active item should be None before the first item is added"
10984            );
10985            workspace.add_item_to_active_pane(
10986                Box::new(multi_buffer_editor.clone()),
10987                None,
10988                true,
10989                cx,
10990            );
10991            let active_item = workspace
10992                .active_item(cx)
10993                .expect("should have an active item after adding the multi buffer");
10994            assert!(
10995                !active_item.is_singleton(cx),
10996                "A multi buffer was expected to active after adding"
10997            );
10998            active_item.item_id()
10999        })
11000        .unwrap();
11001    cx.executor().run_until_parked();
11002
11003    multi_buffer_editor.update(cx, |editor, cx| {
11004        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11005        editor.open_excerpts(&OpenExcerpts, cx);
11006    });
11007    cx.executor().run_until_parked();
11008    let first_item_id = workspace
11009        .update(cx, |workspace, cx| {
11010            let active_item = workspace
11011                .active_item(cx)
11012                .expect("should have an active item after navigating into the 1st buffer");
11013            let first_item_id = active_item.item_id();
11014            assert_ne!(
11015                first_item_id, multibuffer_item_id,
11016                "Should navigate into the 1st buffer and activate it"
11017            );
11018            assert!(
11019                active_item.is_singleton(cx),
11020                "New active item should be a singleton buffer"
11021            );
11022            assert_eq!(
11023                active_item
11024                    .act_as::<Editor>(cx)
11025                    .expect("should have navigated into an editor for the 1st buffer")
11026                    .read(cx)
11027                    .text(cx),
11028                sample_text_1
11029            );
11030
11031            workspace
11032                .go_back(workspace.active_pane().downgrade(), cx)
11033                .detach_and_log_err(cx);
11034
11035            first_item_id
11036        })
11037        .unwrap();
11038    cx.executor().run_until_parked();
11039    workspace
11040        .update(cx, |workspace, cx| {
11041            let active_item = workspace
11042                .active_item(cx)
11043                .expect("should have an active item after navigating back");
11044            assert_eq!(
11045                active_item.item_id(),
11046                multibuffer_item_id,
11047                "Should navigate back to the multi buffer"
11048            );
11049            assert!(!active_item.is_singleton(cx));
11050        })
11051        .unwrap();
11052
11053    multi_buffer_editor.update(cx, |editor, cx| {
11054        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11055            s.select_ranges(Some(39..40))
11056        });
11057        editor.open_excerpts(&OpenExcerpts, cx);
11058    });
11059    cx.executor().run_until_parked();
11060    let second_item_id = workspace
11061        .update(cx, |workspace, cx| {
11062            let active_item = workspace
11063                .active_item(cx)
11064                .expect("should have an active item after navigating into the 2nd buffer");
11065            let second_item_id = active_item.item_id();
11066            assert_ne!(
11067                second_item_id, multibuffer_item_id,
11068                "Should navigate away from the multibuffer"
11069            );
11070            assert_ne!(
11071                second_item_id, first_item_id,
11072                "Should navigate into the 2nd buffer and activate it"
11073            );
11074            assert!(
11075                active_item.is_singleton(cx),
11076                "New active item should be a singleton buffer"
11077            );
11078            assert_eq!(
11079                active_item
11080                    .act_as::<Editor>(cx)
11081                    .expect("should have navigated into an editor")
11082                    .read(cx)
11083                    .text(cx),
11084                sample_text_2
11085            );
11086
11087            workspace
11088                .go_back(workspace.active_pane().downgrade(), cx)
11089                .detach_and_log_err(cx);
11090
11091            second_item_id
11092        })
11093        .unwrap();
11094    cx.executor().run_until_parked();
11095    workspace
11096        .update(cx, |workspace, cx| {
11097            let active_item = workspace
11098                .active_item(cx)
11099                .expect("should have an active item after navigating back from the 2nd buffer");
11100            assert_eq!(
11101                active_item.item_id(),
11102                multibuffer_item_id,
11103                "Should navigate back from the 2nd buffer to the multi buffer"
11104            );
11105            assert!(!active_item.is_singleton(cx));
11106        })
11107        .unwrap();
11108
11109    multi_buffer_editor.update(cx, |editor, cx| {
11110        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11111            s.select_ranges(Some(60..70))
11112        });
11113        editor.open_excerpts(&OpenExcerpts, cx);
11114    });
11115    cx.executor().run_until_parked();
11116    workspace
11117        .update(cx, |workspace, cx| {
11118            let active_item = workspace
11119                .active_item(cx)
11120                .expect("should have an active item after navigating into the 3rd buffer");
11121            let third_item_id = active_item.item_id();
11122            assert_ne!(
11123                third_item_id, multibuffer_item_id,
11124                "Should navigate into the 3rd buffer and activate it"
11125            );
11126            assert_ne!(third_item_id, first_item_id);
11127            assert_ne!(third_item_id, second_item_id);
11128            assert!(
11129                active_item.is_singleton(cx),
11130                "New active item should be a singleton buffer"
11131            );
11132            assert_eq!(
11133                active_item
11134                    .act_as::<Editor>(cx)
11135                    .expect("should have navigated into an editor")
11136                    .read(cx)
11137                    .text(cx),
11138                sample_text_3
11139            );
11140
11141            workspace
11142                .go_back(workspace.active_pane().downgrade(), cx)
11143                .detach_and_log_err(cx);
11144        })
11145        .unwrap();
11146    cx.executor().run_until_parked();
11147    workspace
11148        .update(cx, |workspace, cx| {
11149            let active_item = workspace
11150                .active_item(cx)
11151                .expect("should have an active item after navigating back from the 3rd buffer");
11152            assert_eq!(
11153                active_item.item_id(),
11154                multibuffer_item_id,
11155                "Should navigate back from the 3rd buffer to the multi buffer"
11156            );
11157            assert!(!active_item.is_singleton(cx));
11158        })
11159        .unwrap();
11160}
11161
11162#[gpui::test]
11163async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11164    init_test(cx, |_| {});
11165
11166    let mut cx = EditorTestContext::new(cx).await;
11167
11168    let diff_base = r#"
11169        use some::mod;
11170
11171        const A: u32 = 42;
11172
11173        fn main() {
11174            println!("hello");
11175
11176            println!("world");
11177        }
11178        "#
11179    .unindent();
11180
11181    cx.set_state(
11182        &r#"
11183        use some::modified;
11184
11185        ˇ
11186        fn main() {
11187            println!("hello there");
11188
11189            println!("around the");
11190            println!("world");
11191        }
11192        "#
11193        .unindent(),
11194    );
11195
11196    cx.set_diff_base(Some(&diff_base));
11197    executor.run_until_parked();
11198
11199    cx.update_editor(|editor, cx| {
11200        editor.go_to_next_hunk(&GoToHunk, cx);
11201        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11202    });
11203    executor.run_until_parked();
11204    cx.assert_diff_hunks(
11205        r#"
11206          use some::modified;
11207
11208
11209          fn main() {
11210        -     println!("hello");
11211        +     println!("hello there");
11212
11213              println!("around the");
11214              println!("world");
11215          }
11216        "#
11217        .unindent(),
11218    );
11219
11220    cx.update_editor(|editor, cx| {
11221        for _ in 0..3 {
11222            editor.go_to_next_hunk(&GoToHunk, cx);
11223            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11224        }
11225    });
11226    executor.run_until_parked();
11227    cx.assert_editor_state(
11228        &r#"
11229        use some::modified;
11230
11231        ˇ
11232        fn main() {
11233            println!("hello there");
11234
11235            println!("around the");
11236            println!("world");
11237        }
11238        "#
11239        .unindent(),
11240    );
11241
11242    cx.assert_diff_hunks(
11243        r#"
11244        - use some::mod;
11245        + use some::modified;
11246
11247        - const A: u32 = 42;
11248
11249          fn main() {
11250        -     println!("hello");
11251        +     println!("hello there");
11252
11253        +     println!("around the");
11254              println!("world");
11255          }
11256        "#
11257        .unindent(),
11258    );
11259
11260    cx.update_editor(|editor, cx| {
11261        editor.cancel(&Cancel, cx);
11262    });
11263
11264    cx.assert_diff_hunks(
11265        r#"
11266          use some::modified;
11267
11268
11269          fn main() {
11270              println!("hello there");
11271
11272              println!("around the");
11273              println!("world");
11274          }
11275        "#
11276        .unindent(),
11277    );
11278}
11279
11280#[gpui::test]
11281async fn test_diff_base_change_with_expanded_diff_hunks(
11282    executor: BackgroundExecutor,
11283    cx: &mut gpui::TestAppContext,
11284) {
11285    init_test(cx, |_| {});
11286
11287    let mut cx = EditorTestContext::new(cx).await;
11288
11289    let diff_base = r#"
11290        use some::mod1;
11291        use some::mod2;
11292
11293        const A: u32 = 42;
11294        const B: u32 = 42;
11295        const C: u32 = 42;
11296
11297        fn main() {
11298            println!("hello");
11299
11300            println!("world");
11301        }
11302        "#
11303    .unindent();
11304
11305    cx.set_state(
11306        &r#"
11307        use some::mod2;
11308
11309        const A: u32 = 42;
11310        const C: u32 = 42;
11311
11312        fn main(ˇ) {
11313            //println!("hello");
11314
11315            println!("world");
11316            //
11317            //
11318        }
11319        "#
11320        .unindent(),
11321    );
11322
11323    cx.set_diff_base(Some(&diff_base));
11324    executor.run_until_parked();
11325
11326    cx.update_editor(|editor, cx| {
11327        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11328    });
11329    executor.run_until_parked();
11330    cx.assert_diff_hunks(
11331        r#"
11332        - use some::mod1;
11333          use some::mod2;
11334
11335          const A: u32 = 42;
11336        - const B: u32 = 42;
11337          const C: u32 = 42;
11338
11339          fn main() {
11340        -     println!("hello");
11341        +     //println!("hello");
11342
11343              println!("world");
11344        +     //
11345        +     //
11346          }
11347        "#
11348        .unindent(),
11349    );
11350
11351    cx.set_diff_base(Some("new diff base!"));
11352    executor.run_until_parked();
11353    cx.assert_diff_hunks(
11354        r#"
11355          use some::mod2;
11356
11357          const A: u32 = 42;
11358          const C: u32 = 42;
11359
11360          fn main() {
11361              //println!("hello");
11362
11363              println!("world");
11364              //
11365              //
11366          }
11367        "#
11368        .unindent(),
11369    );
11370
11371    cx.update_editor(|editor, cx| {
11372        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11373    });
11374    executor.run_until_parked();
11375    cx.assert_diff_hunks(
11376        r#"
11377        - new diff base!
11378        + use some::mod2;
11379        +
11380        + const A: u32 = 42;
11381        + const C: u32 = 42;
11382        +
11383        + fn main() {
11384        +     //println!("hello");
11385        +
11386        +     println!("world");
11387        +     //
11388        +     //
11389        + }
11390        "#
11391        .unindent(),
11392    );
11393}
11394
11395#[gpui::test]
11396async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11397    init_test(cx, |_| {});
11398
11399    let mut cx = EditorTestContext::new(cx).await;
11400
11401    let diff_base = r#"
11402        use some::mod1;
11403        use some::mod2;
11404
11405        const A: u32 = 42;
11406        const B: u32 = 42;
11407        const C: u32 = 42;
11408
11409        fn main() {
11410            println!("hello");
11411
11412            println!("world");
11413        }
11414
11415        fn another() {
11416            println!("another");
11417        }
11418
11419        fn another2() {
11420            println!("another2");
11421        }
11422        "#
11423    .unindent();
11424
11425    cx.set_state(
11426        &r#"
11427        «use some::mod2;
11428
11429        const A: u32 = 42;
11430        const C: u32 = 42;
11431
11432        fn main() {
11433            //println!("hello");
11434
11435            println!("world");
11436            //
11437            //ˇ»
11438        }
11439
11440        fn another() {
11441            println!("another");
11442            println!("another");
11443        }
11444
11445            println!("another2");
11446        }
11447        "#
11448        .unindent(),
11449    );
11450
11451    cx.set_diff_base(Some(&diff_base));
11452    executor.run_until_parked();
11453
11454    cx.update_editor(|editor, cx| {
11455        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11456    });
11457    executor.run_until_parked();
11458
11459    cx.assert_diff_hunks(
11460        r#"
11461        - use some::mod1;
11462          use some::mod2;
11463
11464          const A: u32 = 42;
11465        - const B: u32 = 42;
11466          const C: u32 = 42;
11467
11468          fn main() {
11469        -     println!("hello");
11470        +     //println!("hello");
11471
11472              println!("world");
11473        +     //
11474        +     //
11475          }
11476
11477          fn another() {
11478              println!("another");
11479        +     println!("another");
11480          }
11481
11482        - fn another2() {
11483              println!("another2");
11484          }
11485        "#
11486        .unindent(),
11487    );
11488
11489    // Fold across some of the diff hunks. They should no longer appear expanded.
11490    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11491    cx.executor().run_until_parked();
11492
11493    // Hunks are not shown if their position is within a fold
11494    cx.assert_diff_hunks(
11495        r#"
11496          use some::mod2;
11497
11498          const A: u32 = 42;
11499          const C: u32 = 42;
11500
11501          fn main() {
11502              //println!("hello");
11503
11504              println!("world");
11505              //
11506              //
11507          }
11508
11509          fn another() {
11510              println!("another");
11511        +     println!("another");
11512          }
11513
11514        - fn another2() {
11515              println!("another2");
11516          }
11517        "#
11518        .unindent(),
11519    );
11520
11521    cx.update_editor(|editor, cx| {
11522        editor.select_all(&SelectAll, cx);
11523        editor.unfold_lines(&UnfoldLines, cx);
11524    });
11525    cx.executor().run_until_parked();
11526
11527    // The deletions reappear when unfolding.
11528    cx.assert_diff_hunks(
11529        r#"
11530        - use some::mod1;
11531          use some::mod2;
11532
11533          const A: u32 = 42;
11534        - const B: u32 = 42;
11535          const C: u32 = 42;
11536
11537          fn main() {
11538        -     println!("hello");
11539        +     //println!("hello");
11540
11541              println!("world");
11542        +     //
11543        +     //
11544          }
11545
11546          fn another() {
11547              println!("another");
11548        +     println!("another");
11549          }
11550
11551        - fn another2() {
11552              println!("another2");
11553          }
11554        "#
11555        .unindent(),
11556    );
11557}
11558
11559#[gpui::test]
11560async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11561    init_test(cx, |_| {});
11562
11563    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11564    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11565    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11566    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11567    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11568    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11569
11570    let buffer_1 = cx.new_model(|cx| {
11571        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11572        buffer.set_diff_base(Some(file_1_old.into()), cx);
11573        buffer
11574    });
11575    let buffer_2 = cx.new_model(|cx| {
11576        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11577        buffer.set_diff_base(Some(file_2_old.into()), cx);
11578        buffer
11579    });
11580    let buffer_3 = cx.new_model(|cx| {
11581        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11582        buffer.set_diff_base(Some(file_3_old.into()), cx);
11583        buffer
11584    });
11585
11586    let multi_buffer = cx.new_model(|cx| {
11587        let mut multibuffer = MultiBuffer::new(ReadWrite);
11588        multibuffer.push_excerpts(
11589            buffer_1.clone(),
11590            [
11591                ExcerptRange {
11592                    context: Point::new(0, 0)..Point::new(3, 0),
11593                    primary: None,
11594                },
11595                ExcerptRange {
11596                    context: Point::new(5, 0)..Point::new(7, 0),
11597                    primary: None,
11598                },
11599                ExcerptRange {
11600                    context: Point::new(9, 0)..Point::new(10, 3),
11601                    primary: None,
11602                },
11603            ],
11604            cx,
11605        );
11606        multibuffer.push_excerpts(
11607            buffer_2.clone(),
11608            [
11609                ExcerptRange {
11610                    context: Point::new(0, 0)..Point::new(3, 0),
11611                    primary: None,
11612                },
11613                ExcerptRange {
11614                    context: Point::new(5, 0)..Point::new(7, 0),
11615                    primary: None,
11616                },
11617                ExcerptRange {
11618                    context: Point::new(9, 0)..Point::new(10, 3),
11619                    primary: None,
11620                },
11621            ],
11622            cx,
11623        );
11624        multibuffer.push_excerpts(
11625            buffer_3.clone(),
11626            [
11627                ExcerptRange {
11628                    context: Point::new(0, 0)..Point::new(3, 0),
11629                    primary: None,
11630                },
11631                ExcerptRange {
11632                    context: Point::new(5, 0)..Point::new(7, 0),
11633                    primary: None,
11634                },
11635                ExcerptRange {
11636                    context: Point::new(9, 0)..Point::new(10, 3),
11637                    primary: None,
11638                },
11639            ],
11640            cx,
11641        );
11642        multibuffer
11643    });
11644
11645    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11646    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11647    cx.run_until_parked();
11648
11649    cx.assert_editor_state(
11650        &"
11651            ˇaaa
11652            ccc
11653            ddd
11654
11655            ggg
11656            hhh
11657
11658
11659            lll
11660            mmm
11661            NNN
11662
11663            qqq
11664            rrr
11665
11666            uuu
11667            111
11668            222
11669            333
11670
11671            666
11672            777
11673
11674            000
11675            !!!"
11676        .unindent(),
11677    );
11678
11679    cx.update_editor(|editor, cx| {
11680        editor.select_all(&SelectAll, cx);
11681        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11682    });
11683    cx.executor().run_until_parked();
11684
11685    cx.assert_diff_hunks(
11686        "
11687            aaa
11688          - bbb
11689            ccc
11690            ddd
11691
11692            ggg
11693            hhh
11694
11695
11696            lll
11697            mmm
11698          - nnn
11699          + NNN
11700
11701            qqq
11702            rrr
11703
11704            uuu
11705            111
11706            222
11707            333
11708
11709          + 666
11710            777
11711
11712            000
11713            !!!"
11714        .unindent(),
11715    );
11716}
11717
11718#[gpui::test]
11719async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
11720    init_test(cx, |_| {});
11721
11722    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
11723    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
11724
11725    let buffer = cx.new_model(|cx| {
11726        let mut buffer = Buffer::local(text.to_string(), cx);
11727        buffer.set_diff_base(Some(base.into()), cx);
11728        buffer
11729    });
11730
11731    let multi_buffer = cx.new_model(|cx| {
11732        let mut multibuffer = MultiBuffer::new(ReadWrite);
11733        multibuffer.push_excerpts(
11734            buffer.clone(),
11735            [
11736                ExcerptRange {
11737                    context: Point::new(0, 0)..Point::new(2, 0),
11738                    primary: None,
11739                },
11740                ExcerptRange {
11741                    context: Point::new(5, 0)..Point::new(7, 0),
11742                    primary: None,
11743                },
11744            ],
11745            cx,
11746        );
11747        multibuffer
11748    });
11749
11750    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11751    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11752    cx.run_until_parked();
11753
11754    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
11755    cx.executor().run_until_parked();
11756
11757    cx.assert_diff_hunks(
11758        "
11759            aaa
11760          - bbb
11761          + BBB
11762
11763          - ddd
11764          - eee
11765          + EEE
11766            fff
11767        "
11768        .unindent(),
11769    );
11770}
11771
11772#[gpui::test]
11773async fn test_edits_around_expanded_insertion_hunks(
11774    executor: BackgroundExecutor,
11775    cx: &mut gpui::TestAppContext,
11776) {
11777    init_test(cx, |_| {});
11778
11779    let mut cx = EditorTestContext::new(cx).await;
11780
11781    let diff_base = r#"
11782        use some::mod1;
11783        use some::mod2;
11784
11785        const A: u32 = 42;
11786
11787        fn main() {
11788            println!("hello");
11789
11790            println!("world");
11791        }
11792        "#
11793    .unindent();
11794    executor.run_until_parked();
11795    cx.set_state(
11796        &r#"
11797        use some::mod1;
11798        use some::mod2;
11799
11800        const A: u32 = 42;
11801        const B: u32 = 42;
11802        const C: u32 = 42;
11803        ˇ
11804
11805        fn main() {
11806            println!("hello");
11807
11808            println!("world");
11809        }
11810        "#
11811        .unindent(),
11812    );
11813
11814    cx.set_diff_base(Some(&diff_base));
11815    executor.run_until_parked();
11816
11817    cx.update_editor(|editor, cx| {
11818        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11819    });
11820    executor.run_until_parked();
11821
11822    cx.assert_diff_hunks(
11823        r#"
11824        use some::mod1;
11825        use some::mod2;
11826
11827        const A: u32 = 42;
11828      + const B: u32 = 42;
11829      + const C: u32 = 42;
11830      +
11831
11832        fn main() {
11833            println!("hello");
11834
11835            println!("world");
11836        }
11837        "#
11838        .unindent(),
11839    );
11840
11841    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11842    executor.run_until_parked();
11843
11844    cx.assert_diff_hunks(
11845        r#"
11846        use some::mod1;
11847        use some::mod2;
11848
11849        const A: u32 = 42;
11850      + const B: u32 = 42;
11851      + const C: u32 = 42;
11852      + const D: u32 = 42;
11853      +
11854
11855        fn main() {
11856            println!("hello");
11857
11858            println!("world");
11859        }
11860        "#
11861        .unindent(),
11862    );
11863
11864    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11865    executor.run_until_parked();
11866
11867    cx.assert_diff_hunks(
11868        r#"
11869        use some::mod1;
11870        use some::mod2;
11871
11872        const A: u32 = 42;
11873      + const B: u32 = 42;
11874      + const C: u32 = 42;
11875      + const D: u32 = 42;
11876      + const E: u32 = 42;
11877      +
11878
11879        fn main() {
11880            println!("hello");
11881
11882            println!("world");
11883        }
11884        "#
11885        .unindent(),
11886    );
11887
11888    cx.update_editor(|editor, cx| {
11889        editor.delete_line(&DeleteLine, cx);
11890    });
11891    executor.run_until_parked();
11892
11893    cx.assert_diff_hunks(
11894        r#"
11895        use some::mod1;
11896        use some::mod2;
11897
11898        const A: u32 = 42;
11899      + const B: u32 = 42;
11900      + const C: u32 = 42;
11901      + const D: u32 = 42;
11902      + const E: u32 = 42;
11903
11904        fn main() {
11905            println!("hello");
11906
11907            println!("world");
11908        }
11909        "#
11910        .unindent(),
11911    );
11912
11913    cx.update_editor(|editor, cx| {
11914        editor.move_up(&MoveUp, cx);
11915        editor.delete_line(&DeleteLine, cx);
11916        editor.move_up(&MoveUp, cx);
11917        editor.delete_line(&DeleteLine, cx);
11918        editor.move_up(&MoveUp, cx);
11919        editor.delete_line(&DeleteLine, cx);
11920    });
11921    executor.run_until_parked();
11922    cx.assert_editor_state(
11923        &r#"
11924        use some::mod1;
11925        use some::mod2;
11926
11927        const A: u32 = 42;
11928        const B: u32 = 42;
11929        ˇ
11930        fn main() {
11931            println!("hello");
11932
11933            println!("world");
11934        }
11935        "#
11936        .unindent(),
11937    );
11938
11939    cx.assert_diff_hunks(
11940        r#"
11941        use some::mod1;
11942        use some::mod2;
11943
11944        const A: u32 = 42;
11945      + const B: u32 = 42;
11946
11947        fn main() {
11948            println!("hello");
11949
11950            println!("world");
11951        }
11952        "#
11953        .unindent(),
11954    );
11955
11956    cx.update_editor(|editor, cx| {
11957        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11958        editor.delete_line(&DeleteLine, cx);
11959    });
11960    executor.run_until_parked();
11961    cx.assert_diff_hunks(
11962        r#"
11963        use some::mod1;
11964      - use some::mod2;
11965      -
11966      - const A: u32 = 42;
11967
11968        fn main() {
11969            println!("hello");
11970
11971            println!("world");
11972        }
11973        "#
11974        .unindent(),
11975    );
11976}
11977
11978#[gpui::test]
11979async fn test_edits_around_expanded_deletion_hunks(
11980    executor: BackgroundExecutor,
11981    cx: &mut gpui::TestAppContext,
11982) {
11983    init_test(cx, |_| {});
11984
11985    let mut cx = EditorTestContext::new(cx).await;
11986
11987    let diff_base = r#"
11988        use some::mod1;
11989        use some::mod2;
11990
11991        const A: u32 = 42;
11992        const B: u32 = 42;
11993        const C: u32 = 42;
11994
11995
11996        fn main() {
11997            println!("hello");
11998
11999            println!("world");
12000        }
12001    "#
12002    .unindent();
12003    executor.run_until_parked();
12004    cx.set_state(
12005        &r#"
12006        use some::mod1;
12007        use some::mod2;
12008
12009        ˇconst B: u32 = 42;
12010        const C: u32 = 42;
12011
12012
12013        fn main() {
12014            println!("hello");
12015
12016            println!("world");
12017        }
12018        "#
12019        .unindent(),
12020    );
12021
12022    cx.set_diff_base(Some(&diff_base));
12023    executor.run_until_parked();
12024
12025    cx.update_editor(|editor, cx| {
12026        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12027    });
12028    executor.run_until_parked();
12029
12030    cx.assert_diff_hunks(
12031        r#"
12032        use some::mod1;
12033        use some::mod2;
12034
12035      - const A: u32 = 42;
12036        const B: u32 = 42;
12037        const C: u32 = 42;
12038
12039
12040        fn main() {
12041            println!("hello");
12042
12043            println!("world");
12044        }
12045        "#
12046        .unindent(),
12047    );
12048
12049    cx.update_editor(|editor, cx| {
12050        editor.delete_line(&DeleteLine, cx);
12051    });
12052    executor.run_until_parked();
12053    cx.assert_editor_state(
12054        &r#"
12055        use some::mod1;
12056        use some::mod2;
12057
12058        ˇconst C: u32 = 42;
12059
12060
12061        fn main() {
12062            println!("hello");
12063
12064            println!("world");
12065        }
12066        "#
12067        .unindent(),
12068    );
12069    cx.assert_diff_hunks(
12070        r#"
12071        use some::mod1;
12072        use some::mod2;
12073
12074      - const A: u32 = 42;
12075      - const B: u32 = 42;
12076        const C: u32 = 42;
12077
12078
12079        fn main() {
12080            println!("hello");
12081
12082            println!("world");
12083        }
12084        "#
12085        .unindent(),
12086    );
12087
12088    cx.update_editor(|editor, cx| {
12089        editor.delete_line(&DeleteLine, cx);
12090    });
12091    executor.run_until_parked();
12092    cx.assert_editor_state(
12093        &r#"
12094        use some::mod1;
12095        use some::mod2;
12096
12097        ˇ
12098
12099        fn main() {
12100            println!("hello");
12101
12102            println!("world");
12103        }
12104        "#
12105        .unindent(),
12106    );
12107    cx.assert_diff_hunks(
12108        r#"
12109        use some::mod1;
12110        use some::mod2;
12111
12112      - const A: u32 = 42;
12113      - const B: u32 = 42;
12114      - const C: u32 = 42;
12115
12116
12117        fn main() {
12118            println!("hello");
12119
12120            println!("world");
12121        }
12122        "#
12123        .unindent(),
12124    );
12125
12126    cx.update_editor(|editor, cx| {
12127        editor.handle_input("replacement", cx);
12128    });
12129    executor.run_until_parked();
12130    cx.assert_editor_state(
12131        &r#"
12132        use some::mod1;
12133        use some::mod2;
12134
12135        replacementˇ
12136
12137        fn main() {
12138            println!("hello");
12139
12140            println!("world");
12141        }
12142        "#
12143        .unindent(),
12144    );
12145    cx.assert_diff_hunks(
12146        r#"
12147        use some::mod1;
12148        use some::mod2;
12149
12150      - const A: u32 = 42;
12151      - const B: u32 = 42;
12152      - const C: u32 = 42;
12153      -
12154      + replacement
12155
12156        fn main() {
12157            println!("hello");
12158
12159            println!("world");
12160        }
12161        "#
12162        .unindent(),
12163    );
12164}
12165
12166#[gpui::test]
12167async fn test_edit_after_expanded_modification_hunk(
12168    executor: BackgroundExecutor,
12169    cx: &mut gpui::TestAppContext,
12170) {
12171    init_test(cx, |_| {});
12172
12173    let mut cx = EditorTestContext::new(cx).await;
12174
12175    let diff_base = r#"
12176        use some::mod1;
12177        use some::mod2;
12178
12179        const A: u32 = 42;
12180        const B: u32 = 42;
12181        const C: u32 = 42;
12182        const D: u32 = 42;
12183
12184
12185        fn main() {
12186            println!("hello");
12187
12188            println!("world");
12189        }"#
12190    .unindent();
12191
12192    cx.set_state(
12193        &r#"
12194        use some::mod1;
12195        use some::mod2;
12196
12197        const A: u32 = 42;
12198        const B: u32 = 42;
12199        const C: u32 = 43ˇ
12200        const D: u32 = 42;
12201
12202
12203        fn main() {
12204            println!("hello");
12205
12206            println!("world");
12207        }"#
12208        .unindent(),
12209    );
12210
12211    cx.set_diff_base(Some(&diff_base));
12212    executor.run_until_parked();
12213    cx.update_editor(|editor, cx| {
12214        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12215    });
12216    executor.run_until_parked();
12217
12218    cx.assert_diff_hunks(
12219        r#"
12220        use some::mod1;
12221        use some::mod2;
12222
12223        const A: u32 = 42;
12224        const B: u32 = 42;
12225      - const C: u32 = 42;
12226      + const C: u32 = 43
12227        const D: u32 = 42;
12228
12229
12230        fn main() {
12231            println!("hello");
12232
12233            println!("world");
12234        }"#
12235        .unindent(),
12236    );
12237
12238    cx.update_editor(|editor, cx| {
12239        editor.handle_input("\nnew_line\n", cx);
12240    });
12241    executor.run_until_parked();
12242
12243    cx.assert_diff_hunks(
12244        r#"
12245        use some::mod1;
12246        use some::mod2;
12247
12248        const A: u32 = 42;
12249        const B: u32 = 42;
12250      - const C: u32 = 42;
12251      + const C: u32 = 43
12252      + new_line
12253      +
12254        const D: u32 = 42;
12255
12256
12257        fn main() {
12258            println!("hello");
12259
12260            println!("world");
12261        }"#
12262        .unindent(),
12263    );
12264}
12265
12266async fn setup_indent_guides_editor(
12267    text: &str,
12268    cx: &mut gpui::TestAppContext,
12269) -> (BufferId, EditorTestContext) {
12270    init_test(cx, |_| {});
12271
12272    let mut cx = EditorTestContext::new(cx).await;
12273
12274    let buffer_id = cx.update_editor(|editor, cx| {
12275        editor.set_text(text, cx);
12276        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12277
12278        buffer_ids[0]
12279    });
12280
12281    (buffer_id, cx)
12282}
12283
12284fn assert_indent_guides(
12285    range: Range<u32>,
12286    expected: Vec<IndentGuide>,
12287    active_indices: Option<Vec<usize>>,
12288    cx: &mut EditorTestContext,
12289) {
12290    let indent_guides = cx.update_editor(|editor, cx| {
12291        let snapshot = editor.snapshot(cx).display_snapshot;
12292        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12293            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12294            true,
12295            &snapshot,
12296            cx,
12297        );
12298
12299        indent_guides.sort_by(|a, b| {
12300            a.depth.cmp(&b.depth).then(
12301                a.start_row
12302                    .cmp(&b.start_row)
12303                    .then(a.end_row.cmp(&b.end_row)),
12304            )
12305        });
12306        indent_guides
12307    });
12308
12309    if let Some(expected) = active_indices {
12310        let active_indices = cx.update_editor(|editor, cx| {
12311            let snapshot = editor.snapshot(cx).display_snapshot;
12312            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12313        });
12314
12315        assert_eq!(
12316            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12317            expected,
12318            "Active indent guide indices do not match"
12319        );
12320    }
12321
12322    let expected: Vec<_> = expected
12323        .into_iter()
12324        .map(|guide| MultiBufferIndentGuide {
12325            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12326            buffer: guide,
12327        })
12328        .collect();
12329
12330    assert_eq!(indent_guides, expected, "Indent guides do not match");
12331}
12332
12333fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12334    IndentGuide {
12335        buffer_id,
12336        start_row,
12337        end_row,
12338        depth,
12339        tab_size: 4,
12340        settings: IndentGuideSettings {
12341            enabled: true,
12342            line_width: 1,
12343            active_line_width: 1,
12344            ..Default::default()
12345        },
12346    }
12347}
12348
12349#[gpui::test]
12350async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12351    let (buffer_id, mut cx) = setup_indent_guides_editor(
12352        &"
12353    fn main() {
12354        let a = 1;
12355    }"
12356        .unindent(),
12357        cx,
12358    )
12359    .await;
12360
12361    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12362}
12363
12364#[gpui::test]
12365async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12366    let (buffer_id, mut cx) = setup_indent_guides_editor(
12367        &"
12368    fn main() {
12369        let a = 1;
12370        let b = 2;
12371    }"
12372        .unindent(),
12373        cx,
12374    )
12375    .await;
12376
12377    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12378}
12379
12380#[gpui::test]
12381async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12382    let (buffer_id, mut cx) = setup_indent_guides_editor(
12383        &"
12384    fn main() {
12385        let a = 1;
12386        if a == 3 {
12387            let b = 2;
12388        } else {
12389            let c = 3;
12390        }
12391    }"
12392        .unindent(),
12393        cx,
12394    )
12395    .await;
12396
12397    assert_indent_guides(
12398        0..8,
12399        vec![
12400            indent_guide(buffer_id, 1, 6, 0),
12401            indent_guide(buffer_id, 3, 3, 1),
12402            indent_guide(buffer_id, 5, 5, 1),
12403        ],
12404        None,
12405        &mut cx,
12406    );
12407}
12408
12409#[gpui::test]
12410async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12411    let (buffer_id, mut cx) = setup_indent_guides_editor(
12412        &"
12413    fn main() {
12414        let a = 1;
12415            let b = 2;
12416        let c = 3;
12417    }"
12418        .unindent(),
12419        cx,
12420    )
12421    .await;
12422
12423    assert_indent_guides(
12424        0..5,
12425        vec![
12426            indent_guide(buffer_id, 1, 3, 0),
12427            indent_guide(buffer_id, 2, 2, 1),
12428        ],
12429        None,
12430        &mut cx,
12431    );
12432}
12433
12434#[gpui::test]
12435async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12436    let (buffer_id, mut cx) = setup_indent_guides_editor(
12437        &"
12438        fn main() {
12439            let a = 1;
12440
12441            let c = 3;
12442        }"
12443        .unindent(),
12444        cx,
12445    )
12446    .await;
12447
12448    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12449}
12450
12451#[gpui::test]
12452async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12453    let (buffer_id, mut cx) = setup_indent_guides_editor(
12454        &"
12455        fn main() {
12456            let a = 1;
12457
12458            let c = 3;
12459
12460            if a == 3 {
12461                let b = 2;
12462            } else {
12463                let c = 3;
12464            }
12465        }"
12466        .unindent(),
12467        cx,
12468    )
12469    .await;
12470
12471    assert_indent_guides(
12472        0..11,
12473        vec![
12474            indent_guide(buffer_id, 1, 9, 0),
12475            indent_guide(buffer_id, 6, 6, 1),
12476            indent_guide(buffer_id, 8, 8, 1),
12477        ],
12478        None,
12479        &mut cx,
12480    );
12481}
12482
12483#[gpui::test]
12484async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12485    let (buffer_id, mut cx) = setup_indent_guides_editor(
12486        &"
12487        fn main() {
12488            let a = 1;
12489
12490            let c = 3;
12491
12492            if a == 3 {
12493                let b = 2;
12494            } else {
12495                let c = 3;
12496            }
12497        }"
12498        .unindent(),
12499        cx,
12500    )
12501    .await;
12502
12503    assert_indent_guides(
12504        1..11,
12505        vec![
12506            indent_guide(buffer_id, 1, 9, 0),
12507            indent_guide(buffer_id, 6, 6, 1),
12508            indent_guide(buffer_id, 8, 8, 1),
12509        ],
12510        None,
12511        &mut cx,
12512    );
12513}
12514
12515#[gpui::test]
12516async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12517    let (buffer_id, mut cx) = setup_indent_guides_editor(
12518        &"
12519        fn main() {
12520            let a = 1;
12521
12522            let c = 3;
12523
12524            if a == 3 {
12525                let b = 2;
12526            } else {
12527                let c = 3;
12528            }
12529        }"
12530        .unindent(),
12531        cx,
12532    )
12533    .await;
12534
12535    assert_indent_guides(
12536        1..10,
12537        vec![
12538            indent_guide(buffer_id, 1, 9, 0),
12539            indent_guide(buffer_id, 6, 6, 1),
12540            indent_guide(buffer_id, 8, 8, 1),
12541        ],
12542        None,
12543        &mut cx,
12544    );
12545}
12546
12547#[gpui::test]
12548async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12549    let (buffer_id, mut cx) = setup_indent_guides_editor(
12550        &"
12551        block1
12552            block2
12553                block3
12554                    block4
12555            block2
12556        block1
12557        block1"
12558            .unindent(),
12559        cx,
12560    )
12561    .await;
12562
12563    assert_indent_guides(
12564        1..10,
12565        vec![
12566            indent_guide(buffer_id, 1, 4, 0),
12567            indent_guide(buffer_id, 2, 3, 1),
12568            indent_guide(buffer_id, 3, 3, 2),
12569        ],
12570        None,
12571        &mut cx,
12572    );
12573}
12574
12575#[gpui::test]
12576async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12577    let (buffer_id, mut cx) = setup_indent_guides_editor(
12578        &"
12579        block1
12580            block2
12581                block3
12582
12583        block1
12584        block1"
12585            .unindent(),
12586        cx,
12587    )
12588    .await;
12589
12590    assert_indent_guides(
12591        0..6,
12592        vec![
12593            indent_guide(buffer_id, 1, 2, 0),
12594            indent_guide(buffer_id, 2, 2, 1),
12595        ],
12596        None,
12597        &mut cx,
12598    );
12599}
12600
12601#[gpui::test]
12602async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12603    let (buffer_id, mut cx) = setup_indent_guides_editor(
12604        &"
12605        block1
12606
12607
12608
12609            block2
12610        "
12611        .unindent(),
12612        cx,
12613    )
12614    .await;
12615
12616    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12617}
12618
12619#[gpui::test]
12620async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12621    let (buffer_id, mut cx) = setup_indent_guides_editor(
12622        &"
12623        def a:
12624        \tb = 3
12625        \tif True:
12626        \t\tc = 4
12627        \t\td = 5
12628        \tprint(b)
12629        "
12630        .unindent(),
12631        cx,
12632    )
12633    .await;
12634
12635    assert_indent_guides(
12636        0..6,
12637        vec![
12638            indent_guide(buffer_id, 1, 6, 0),
12639            indent_guide(buffer_id, 3, 4, 1),
12640        ],
12641        None,
12642        &mut cx,
12643    );
12644}
12645
12646#[gpui::test]
12647async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12648    let (buffer_id, mut cx) = setup_indent_guides_editor(
12649        &"
12650    fn main() {
12651        let a = 1;
12652    }"
12653        .unindent(),
12654        cx,
12655    )
12656    .await;
12657
12658    cx.update_editor(|editor, cx| {
12659        editor.change_selections(None, cx, |s| {
12660            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12661        });
12662    });
12663
12664    assert_indent_guides(
12665        0..3,
12666        vec![indent_guide(buffer_id, 1, 1, 0)],
12667        Some(vec![0]),
12668        &mut cx,
12669    );
12670}
12671
12672#[gpui::test]
12673async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12674    let (buffer_id, mut cx) = setup_indent_guides_editor(
12675        &"
12676    fn main() {
12677        if 1 == 2 {
12678            let a = 1;
12679        }
12680    }"
12681        .unindent(),
12682        cx,
12683    )
12684    .await;
12685
12686    cx.update_editor(|editor, cx| {
12687        editor.change_selections(None, cx, |s| {
12688            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12689        });
12690    });
12691
12692    assert_indent_guides(
12693        0..4,
12694        vec![
12695            indent_guide(buffer_id, 1, 3, 0),
12696            indent_guide(buffer_id, 2, 2, 1),
12697        ],
12698        Some(vec![1]),
12699        &mut cx,
12700    );
12701
12702    cx.update_editor(|editor, cx| {
12703        editor.change_selections(None, cx, |s| {
12704            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12705        });
12706    });
12707
12708    assert_indent_guides(
12709        0..4,
12710        vec![
12711            indent_guide(buffer_id, 1, 3, 0),
12712            indent_guide(buffer_id, 2, 2, 1),
12713        ],
12714        Some(vec![1]),
12715        &mut cx,
12716    );
12717
12718    cx.update_editor(|editor, cx| {
12719        editor.change_selections(None, cx, |s| {
12720            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12721        });
12722    });
12723
12724    assert_indent_guides(
12725        0..4,
12726        vec![
12727            indent_guide(buffer_id, 1, 3, 0),
12728            indent_guide(buffer_id, 2, 2, 1),
12729        ],
12730        Some(vec![0]),
12731        &mut cx,
12732    );
12733}
12734
12735#[gpui::test]
12736async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12737    let (buffer_id, mut cx) = setup_indent_guides_editor(
12738        &"
12739    fn main() {
12740        let a = 1;
12741
12742        let b = 2;
12743    }"
12744        .unindent(),
12745        cx,
12746    )
12747    .await;
12748
12749    cx.update_editor(|editor, cx| {
12750        editor.change_selections(None, cx, |s| {
12751            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12752        });
12753    });
12754
12755    assert_indent_guides(
12756        0..5,
12757        vec![indent_guide(buffer_id, 1, 3, 0)],
12758        Some(vec![0]),
12759        &mut cx,
12760    );
12761}
12762
12763#[gpui::test]
12764async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12765    let (buffer_id, mut cx) = setup_indent_guides_editor(
12766        &"
12767    def m:
12768        a = 1
12769        pass"
12770            .unindent(),
12771        cx,
12772    )
12773    .await;
12774
12775    cx.update_editor(|editor, cx| {
12776        editor.change_selections(None, cx, |s| {
12777            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12778        });
12779    });
12780
12781    assert_indent_guides(
12782        0..3,
12783        vec![indent_guide(buffer_id, 1, 2, 0)],
12784        Some(vec![0]),
12785        &mut cx,
12786    );
12787}
12788
12789#[gpui::test]
12790fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12791    init_test(cx, |_| {});
12792
12793    let editor = cx.add_window(|cx| {
12794        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12795        build_editor(buffer, cx)
12796    });
12797
12798    let render_args = Arc::new(Mutex::new(None));
12799    let snapshot = editor
12800        .update(cx, |editor, cx| {
12801            let snapshot = editor.buffer().read(cx).snapshot(cx);
12802            let range =
12803                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12804
12805            struct RenderArgs {
12806                row: MultiBufferRow,
12807                folded: bool,
12808                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12809            }
12810
12811            let crease = Crease::new(
12812                range,
12813                FoldPlaceholder::test(),
12814                {
12815                    let toggle_callback = render_args.clone();
12816                    move |row, folded, callback, _cx| {
12817                        *toggle_callback.lock() = Some(RenderArgs {
12818                            row,
12819                            folded,
12820                            callback,
12821                        });
12822                        div()
12823                    }
12824                },
12825                |_row, _folded, _cx| div(),
12826            );
12827
12828            editor.insert_creases(Some(crease), cx);
12829            let snapshot = editor.snapshot(cx);
12830            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12831            snapshot
12832        })
12833        .unwrap();
12834
12835    let render_args = render_args.lock().take().unwrap();
12836    assert_eq!(render_args.row, MultiBufferRow(1));
12837    assert!(!render_args.folded);
12838    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12839
12840    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12841        .unwrap();
12842    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12843    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12844
12845    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12846        .unwrap();
12847    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12848    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12849}
12850
12851#[gpui::test]
12852async fn test_input_text(cx: &mut gpui::TestAppContext) {
12853    init_test(cx, |_| {});
12854    let mut cx = EditorTestContext::new(cx).await;
12855
12856    cx.set_state(
12857        &r#"ˇone
12858        two
12859
12860        three
12861        fourˇ
12862        five
12863
12864        siˇx"#
12865            .unindent(),
12866    );
12867
12868    cx.dispatch_action(HandleInput(String::new()));
12869    cx.assert_editor_state(
12870        &r#"ˇone
12871        two
12872
12873        three
12874        fourˇ
12875        five
12876
12877        siˇx"#
12878            .unindent(),
12879    );
12880
12881    cx.dispatch_action(HandleInput("AAAA".to_string()));
12882    cx.assert_editor_state(
12883        &r#"AAAAˇone
12884        two
12885
12886        three
12887        fourAAAAˇ
12888        five
12889
12890        siAAAAˇx"#
12891            .unindent(),
12892    );
12893}
12894
12895#[gpui::test]
12896async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
12897    init_test(cx, |_| {});
12898
12899    let mut cx = EditorTestContext::new(cx).await;
12900    cx.set_state(
12901        r#"let foo = 1;
12902let foo = 2;
12903let foo = 3;
12904let fooˇ = 4;
12905let foo = 5;
12906let foo = 6;
12907let foo = 7;
12908let foo = 8;
12909let foo = 9;
12910let foo = 10;
12911let foo = 11;
12912let foo = 12;
12913let foo = 13;
12914let foo = 14;
12915let foo = 15;"#,
12916    );
12917
12918    cx.update_editor(|e, cx| {
12919        assert_eq!(
12920            e.next_scroll_position,
12921            NextScrollCursorCenterTopBottom::Center,
12922            "Default next scroll direction is center",
12923        );
12924
12925        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12926        assert_eq!(
12927            e.next_scroll_position,
12928            NextScrollCursorCenterTopBottom::Top,
12929            "After center, next scroll direction should be top",
12930        );
12931
12932        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12933        assert_eq!(
12934            e.next_scroll_position,
12935            NextScrollCursorCenterTopBottom::Bottom,
12936            "After top, next scroll direction should be bottom",
12937        );
12938
12939        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12940        assert_eq!(
12941            e.next_scroll_position,
12942            NextScrollCursorCenterTopBottom::Center,
12943            "After bottom, scrolling should start over",
12944        );
12945
12946        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12947        assert_eq!(
12948            e.next_scroll_position,
12949            NextScrollCursorCenterTopBottom::Top,
12950            "Scrolling continues if retriggered fast enough"
12951        );
12952    });
12953
12954    cx.executor()
12955        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
12956    cx.executor().run_until_parked();
12957    cx.update_editor(|e, _| {
12958        assert_eq!(
12959            e.next_scroll_position,
12960            NextScrollCursorCenterTopBottom::Center,
12961            "If scrolling is not triggered fast enough, it should reset"
12962        );
12963    });
12964}
12965
12966#[gpui::test]
12967async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
12968    init_test(cx, |_| {});
12969    let mut cx = EditorLspTestContext::new_rust(
12970        lsp::ServerCapabilities {
12971            definition_provider: Some(lsp::OneOf::Left(true)),
12972            references_provider: Some(lsp::OneOf::Left(true)),
12973            ..lsp::ServerCapabilities::default()
12974        },
12975        cx,
12976    )
12977    .await;
12978
12979    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
12980        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
12981            move |params, _| async move {
12982                if empty_go_to_definition {
12983                    Ok(None)
12984                } else {
12985                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
12986                        uri: params.text_document_position_params.text_document.uri,
12987                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
12988                    })))
12989                }
12990            },
12991        );
12992        let references =
12993            cx.lsp
12994                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
12995                    Ok(Some(vec![lsp::Location {
12996                        uri: params.text_document_position.text_document.uri,
12997                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
12998                    }]))
12999                });
13000        (go_to_definition, references)
13001    };
13002
13003    cx.set_state(
13004        &r#"fn one() {
13005            let mut a = ˇtwo();
13006        }
13007
13008        fn two() {}"#
13009            .unindent(),
13010    );
13011    set_up_lsp_handlers(false, &mut cx);
13012    let navigated = cx
13013        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13014        .await
13015        .expect("Failed to navigate to definition");
13016    assert_eq!(
13017        navigated,
13018        Navigated::Yes,
13019        "Should have navigated to definition from the GetDefinition response"
13020    );
13021    cx.assert_editor_state(
13022        &r#"fn one() {
13023            let mut a = two();
13024        }
13025
13026        fn «twoˇ»() {}"#
13027            .unindent(),
13028    );
13029
13030    let editors = cx.update_workspace(|workspace, cx| {
13031        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13032    });
13033    cx.update_editor(|_, test_editor_cx| {
13034        assert_eq!(
13035            editors.len(),
13036            1,
13037            "Initially, only one, test, editor should be open in the workspace"
13038        );
13039        assert_eq!(
13040            test_editor_cx.view(),
13041            editors.last().expect("Asserted len is 1")
13042        );
13043    });
13044
13045    set_up_lsp_handlers(true, &mut cx);
13046    let navigated = cx
13047        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13048        .await
13049        .expect("Failed to navigate to lookup references");
13050    assert_eq!(
13051        navigated,
13052        Navigated::Yes,
13053        "Should have navigated to references as a fallback after empty GoToDefinition response"
13054    );
13055    // We should not change the selections in the existing file,
13056    // if opening another milti buffer with the references
13057    cx.assert_editor_state(
13058        &r#"fn one() {
13059            let mut a = two();
13060        }
13061
13062        fn «twoˇ»() {}"#
13063            .unindent(),
13064    );
13065    let editors = cx.update_workspace(|workspace, cx| {
13066        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13067    });
13068    cx.update_editor(|_, test_editor_cx| {
13069        assert_eq!(
13070            editors.len(),
13071            2,
13072            "After falling back to references search, we open a new editor with the results"
13073        );
13074        let references_fallback_text = editors
13075            .into_iter()
13076            .find(|new_editor| new_editor != test_editor_cx.view())
13077            .expect("Should have one non-test editor now")
13078            .read(test_editor_cx)
13079            .text(test_editor_cx);
13080        assert_eq!(
13081            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13082            "Should use the range from the references response and not the GoToDefinition one"
13083        );
13084    });
13085}
13086
13087fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13088    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13089    point..point
13090}
13091
13092fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13093    let (text, ranges) = marked_text_ranges(marked_text, true);
13094    assert_eq!(view.text(cx), text);
13095    assert_eq!(
13096        view.selections.ranges(cx),
13097        ranges,
13098        "Assert selections are {}",
13099        marked_text
13100    );
13101}
13102
13103pub fn handle_signature_help_request(
13104    cx: &mut EditorLspTestContext,
13105    mocked_response: lsp::SignatureHelp,
13106) -> impl Future<Output = ()> {
13107    let mut request =
13108        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13109            let mocked_response = mocked_response.clone();
13110            async move { Ok(Some(mocked_response)) }
13111        });
13112
13113    async move {
13114        request.next().await;
13115    }
13116}
13117
13118/// Handle completion request passing a marked string specifying where the completion
13119/// should be triggered from using '|' character, what range should be replaced, and what completions
13120/// should be returned using '<' and '>' to delimit the range
13121pub fn handle_completion_request(
13122    cx: &mut EditorLspTestContext,
13123    marked_string: &str,
13124    completions: Vec<&'static str>,
13125    counter: Arc<AtomicUsize>,
13126) -> impl Future<Output = ()> {
13127    let complete_from_marker: TextRangeMarker = '|'.into();
13128    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13129    let (_, mut marked_ranges) = marked_text_ranges_by(
13130        marked_string,
13131        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13132    );
13133
13134    let complete_from_position =
13135        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13136    let replace_range =
13137        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13138
13139    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13140        let completions = completions.clone();
13141        counter.fetch_add(1, atomic::Ordering::Release);
13142        async move {
13143            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13144            assert_eq!(
13145                params.text_document_position.position,
13146                complete_from_position
13147            );
13148            Ok(Some(lsp::CompletionResponse::Array(
13149                completions
13150                    .iter()
13151                    .map(|completion_text| lsp::CompletionItem {
13152                        label: completion_text.to_string(),
13153                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13154                            range: replace_range,
13155                            new_text: completion_text.to_string(),
13156                        })),
13157                        ..Default::default()
13158                    })
13159                    .collect(),
13160            )))
13161        }
13162    });
13163
13164    async move {
13165        request.next().await;
13166    }
13167}
13168
13169fn handle_resolve_completion_request(
13170    cx: &mut EditorLspTestContext,
13171    edits: Option<Vec<(&'static str, &'static str)>>,
13172) -> impl Future<Output = ()> {
13173    let edits = edits.map(|edits| {
13174        edits
13175            .iter()
13176            .map(|(marked_string, new_text)| {
13177                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13178                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13179                lsp::TextEdit::new(replace_range, new_text.to_string())
13180            })
13181            .collect::<Vec<_>>()
13182    });
13183
13184    let mut request =
13185        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13186            let edits = edits.clone();
13187            async move {
13188                Ok(lsp::CompletionItem {
13189                    additional_text_edits: edits,
13190                    ..Default::default()
13191                })
13192            }
13193        });
13194
13195    async move {
13196        request.next().await;
13197    }
13198}
13199
13200pub(crate) fn update_test_language_settings(
13201    cx: &mut TestAppContext,
13202    f: impl Fn(&mut AllLanguageSettingsContent),
13203) {
13204    cx.update(|cx| {
13205        SettingsStore::update_global(cx, |store, cx| {
13206            store.update_user_settings::<AllLanguageSettings>(cx, f);
13207        });
13208    });
13209}
13210
13211pub(crate) fn update_test_project_settings(
13212    cx: &mut TestAppContext,
13213    f: impl Fn(&mut ProjectSettings),
13214) {
13215    cx.update(|cx| {
13216        SettingsStore::update_global(cx, |store, cx| {
13217            store.update_user_settings::<ProjectSettings>(cx, f);
13218        });
13219    });
13220}
13221
13222pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13223    cx.update(|cx| {
13224        assets::Assets.load_test_fonts(cx);
13225        let store = SettingsStore::test(cx);
13226        cx.set_global(store);
13227        theme::init(theme::LoadThemes::JustBase, cx);
13228        release_channel::init(SemanticVersion::default(), cx);
13229        client::init_settings(cx);
13230        language::init(cx);
13231        Project::init_settings(cx);
13232        workspace::init_settings(cx);
13233        crate::init(cx);
13234    });
13235
13236    update_test_language_settings(cx, f);
13237}
13238
13239pub(crate) fn rust_lang() -> Arc<Language> {
13240    Arc::new(Language::new(
13241        LanguageConfig {
13242            name: "Rust".into(),
13243            matcher: LanguageMatcher {
13244                path_suffixes: vec!["rs".to_string()],
13245                ..Default::default()
13246            },
13247            ..Default::default()
13248        },
13249        Some(tree_sitter_rust::LANGUAGE.into()),
13250    ))
13251}
13252
13253#[track_caller]
13254fn assert_hunk_revert(
13255    not_reverted_text_with_selections: &str,
13256    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13257    expected_reverted_text_with_selections: &str,
13258    base_text: &str,
13259    cx: &mut EditorLspTestContext,
13260) {
13261    cx.set_state(not_reverted_text_with_selections);
13262    cx.update_editor(|editor, cx| {
13263        editor
13264            .buffer()
13265            .read(cx)
13266            .as_singleton()
13267            .unwrap()
13268            .update(cx, |buffer, cx| {
13269                buffer.set_diff_base(Some(base_text.into()), cx);
13270            });
13271    });
13272    cx.executor().run_until_parked();
13273
13274    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13275        let snapshot = editor.buffer().read(cx).snapshot(cx);
13276        let reverted_hunk_statuses = snapshot
13277            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13278            .map(|hunk| hunk_status(&hunk))
13279            .collect::<Vec<_>>();
13280
13281        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13282        reverted_hunk_statuses
13283    });
13284    cx.executor().run_until_parked();
13285    cx.assert_editor_state(expected_reverted_text_with_selections);
13286    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13287}