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(
 7080                project.clone(),
 7081                FormatTrigger::Manual,
 7082                FormatTarget::Buffer,
 7083                cx,
 7084            )
 7085        })
 7086        .unwrap();
 7087    fake_server
 7088        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7089            assert_eq!(
 7090                params.text_document.uri,
 7091                lsp::Url::from_file_path("/file.rs").unwrap()
 7092            );
 7093            assert_eq!(params.options.tab_size, 4);
 7094            Ok(Some(vec![lsp::TextEdit::new(
 7095                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7096                ", ".to_string(),
 7097            )]))
 7098        })
 7099        .next()
 7100        .await;
 7101    cx.executor().start_waiting();
 7102    format.await;
 7103    assert_eq!(
 7104        editor.update(cx, |editor, cx| editor.text(cx)),
 7105        "one, two\nthree\n"
 7106    );
 7107
 7108    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7109    // Ensure we don't lock if formatting hangs.
 7110    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7111        assert_eq!(
 7112            params.text_document.uri,
 7113            lsp::Url::from_file_path("/file.rs").unwrap()
 7114        );
 7115        futures::future::pending::<()>().await;
 7116        unreachable!()
 7117    });
 7118    let format = editor
 7119        .update(cx, |editor, cx| {
 7120            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
 7121        })
 7122        .unwrap();
 7123    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7124    cx.executor().start_waiting();
 7125    format.await;
 7126    assert_eq!(
 7127        editor.update(cx, |editor, cx| editor.text(cx)),
 7128        "one\ntwo\nthree\n"
 7129    );
 7130}
 7131
 7132#[gpui::test]
 7133async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7134    init_test(cx, |_| {});
 7135
 7136    let mut cx = EditorLspTestContext::new_rust(
 7137        lsp::ServerCapabilities {
 7138            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7139            ..Default::default()
 7140        },
 7141        cx,
 7142    )
 7143    .await;
 7144
 7145    cx.set_state(indoc! {"
 7146        one.twoˇ
 7147    "});
 7148
 7149    // The format request takes a long time. When it completes, it inserts
 7150    // a newline and an indent before the `.`
 7151    cx.lsp
 7152        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7153            let executor = cx.background_executor().clone();
 7154            async move {
 7155                executor.timer(Duration::from_millis(100)).await;
 7156                Ok(Some(vec![lsp::TextEdit {
 7157                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7158                    new_text: "\n    ".into(),
 7159                }]))
 7160            }
 7161        });
 7162
 7163    // Submit a format request.
 7164    let format_1 = cx
 7165        .update_editor(|editor, cx| editor.format(&Format, cx))
 7166        .unwrap();
 7167    cx.executor().run_until_parked();
 7168
 7169    // Submit a second format request.
 7170    let format_2 = cx
 7171        .update_editor(|editor, cx| editor.format(&Format, cx))
 7172        .unwrap();
 7173    cx.executor().run_until_parked();
 7174
 7175    // Wait for both format requests to complete
 7176    cx.executor().advance_clock(Duration::from_millis(200));
 7177    cx.executor().start_waiting();
 7178    format_1.await.unwrap();
 7179    cx.executor().start_waiting();
 7180    format_2.await.unwrap();
 7181
 7182    // The formatting edits only happens once.
 7183    cx.assert_editor_state(indoc! {"
 7184        one
 7185            .twoˇ
 7186    "});
 7187}
 7188
 7189#[gpui::test]
 7190async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7191    init_test(cx, |settings| {
 7192        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7193    });
 7194
 7195    let mut cx = EditorLspTestContext::new_rust(
 7196        lsp::ServerCapabilities {
 7197            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7198            ..Default::default()
 7199        },
 7200        cx,
 7201    )
 7202    .await;
 7203
 7204    // Set up a buffer white some trailing whitespace and no trailing newline.
 7205    cx.set_state(
 7206        &[
 7207            "one ",   //
 7208            "twoˇ",   //
 7209            "three ", //
 7210            "four",   //
 7211        ]
 7212        .join("\n"),
 7213    );
 7214
 7215    // Submit a format request.
 7216    let format = cx
 7217        .update_editor(|editor, cx| editor.format(&Format, cx))
 7218        .unwrap();
 7219
 7220    // Record which buffer changes have been sent to the language server
 7221    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7222    cx.lsp
 7223        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7224            let buffer_changes = buffer_changes.clone();
 7225            move |params, _| {
 7226                buffer_changes.lock().extend(
 7227                    params
 7228                        .content_changes
 7229                        .into_iter()
 7230                        .map(|e| (e.range.unwrap(), e.text)),
 7231                );
 7232            }
 7233        });
 7234
 7235    // Handle formatting requests to the language server.
 7236    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7237        let buffer_changes = buffer_changes.clone();
 7238        move |_, _| {
 7239            // When formatting is requested, trailing whitespace has already been stripped,
 7240            // and the trailing newline has already been added.
 7241            assert_eq!(
 7242                &buffer_changes.lock()[1..],
 7243                &[
 7244                    (
 7245                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7246                        "".into()
 7247                    ),
 7248                    (
 7249                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7250                        "".into()
 7251                    ),
 7252                    (
 7253                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7254                        "\n".into()
 7255                    ),
 7256                ]
 7257            );
 7258
 7259            // Insert blank lines between each line of the buffer.
 7260            async move {
 7261                Ok(Some(vec![
 7262                    lsp::TextEdit {
 7263                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7264                        new_text: "\n".into(),
 7265                    },
 7266                    lsp::TextEdit {
 7267                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7268                        new_text: "\n".into(),
 7269                    },
 7270                ]))
 7271            }
 7272        }
 7273    });
 7274
 7275    // After formatting the buffer, the trailing whitespace is stripped,
 7276    // a newline is appended, and the edits provided by the language server
 7277    // have been applied.
 7278    format.await.unwrap();
 7279    cx.assert_editor_state(
 7280        &[
 7281            "one",   //
 7282            "",      //
 7283            "twoˇ",  //
 7284            "",      //
 7285            "three", //
 7286            "four",  //
 7287            "",      //
 7288        ]
 7289        .join("\n"),
 7290    );
 7291
 7292    // Undoing the formatting undoes the trailing whitespace removal, the
 7293    // trailing newline, and the LSP edits.
 7294    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7295    cx.assert_editor_state(
 7296        &[
 7297            "one ",   //
 7298            "twoˇ",   //
 7299            "three ", //
 7300            "four",   //
 7301        ]
 7302        .join("\n"),
 7303    );
 7304}
 7305
 7306#[gpui::test]
 7307async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7308    cx: &mut gpui::TestAppContext,
 7309) {
 7310    init_test(cx, |_| {});
 7311
 7312    cx.update(|cx| {
 7313        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7314            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7315                settings.auto_signature_help = Some(true);
 7316            });
 7317        });
 7318    });
 7319
 7320    let mut cx = EditorLspTestContext::new_rust(
 7321        lsp::ServerCapabilities {
 7322            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7323                ..Default::default()
 7324            }),
 7325            ..Default::default()
 7326        },
 7327        cx,
 7328    )
 7329    .await;
 7330
 7331    let language = Language::new(
 7332        LanguageConfig {
 7333            name: "Rust".into(),
 7334            brackets: BracketPairConfig {
 7335                pairs: vec![
 7336                    BracketPair {
 7337                        start: "{".to_string(),
 7338                        end: "}".to_string(),
 7339                        close: true,
 7340                        surround: true,
 7341                        newline: true,
 7342                    },
 7343                    BracketPair {
 7344                        start: "(".to_string(),
 7345                        end: ")".to_string(),
 7346                        close: true,
 7347                        surround: true,
 7348                        newline: true,
 7349                    },
 7350                    BracketPair {
 7351                        start: "/*".to_string(),
 7352                        end: " */".to_string(),
 7353                        close: true,
 7354                        surround: true,
 7355                        newline: true,
 7356                    },
 7357                    BracketPair {
 7358                        start: "[".to_string(),
 7359                        end: "]".to_string(),
 7360                        close: false,
 7361                        surround: false,
 7362                        newline: true,
 7363                    },
 7364                    BracketPair {
 7365                        start: "\"".to_string(),
 7366                        end: "\"".to_string(),
 7367                        close: true,
 7368                        surround: true,
 7369                        newline: false,
 7370                    },
 7371                    BracketPair {
 7372                        start: "<".to_string(),
 7373                        end: ">".to_string(),
 7374                        close: false,
 7375                        surround: true,
 7376                        newline: true,
 7377                    },
 7378                ],
 7379                ..Default::default()
 7380            },
 7381            autoclose_before: "})]".to_string(),
 7382            ..Default::default()
 7383        },
 7384        Some(tree_sitter_rust::LANGUAGE.into()),
 7385    );
 7386    let language = Arc::new(language);
 7387
 7388    cx.language_registry().add(language.clone());
 7389    cx.update_buffer(|buffer, cx| {
 7390        buffer.set_language(Some(language), cx);
 7391    });
 7392
 7393    cx.set_state(
 7394        &r#"
 7395            fn main() {
 7396                sampleˇ
 7397            }
 7398        "#
 7399        .unindent(),
 7400    );
 7401
 7402    cx.update_editor(|view, cx| {
 7403        view.handle_input("(", cx);
 7404    });
 7405    cx.assert_editor_state(
 7406        &"
 7407            fn main() {
 7408                sample(ˇ)
 7409            }
 7410        "
 7411        .unindent(),
 7412    );
 7413
 7414    let mocked_response = lsp::SignatureHelp {
 7415        signatures: vec![lsp::SignatureInformation {
 7416            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7417            documentation: None,
 7418            parameters: Some(vec![
 7419                lsp::ParameterInformation {
 7420                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7421                    documentation: None,
 7422                },
 7423                lsp::ParameterInformation {
 7424                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7425                    documentation: None,
 7426                },
 7427            ]),
 7428            active_parameter: None,
 7429        }],
 7430        active_signature: Some(0),
 7431        active_parameter: Some(0),
 7432    };
 7433    handle_signature_help_request(&mut cx, mocked_response).await;
 7434
 7435    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7436        .await;
 7437
 7438    cx.editor(|editor, _| {
 7439        let signature_help_state = editor.signature_help_state.popover().cloned();
 7440        assert!(signature_help_state.is_some());
 7441        let ParsedMarkdown {
 7442            text, highlights, ..
 7443        } = signature_help_state.unwrap().parsed_content;
 7444        assert_eq!(text, "param1: u8, param2: u8");
 7445        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7446    });
 7447}
 7448
 7449#[gpui::test]
 7450async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7451    init_test(cx, |_| {});
 7452
 7453    cx.update(|cx| {
 7454        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7455            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7456                settings.auto_signature_help = Some(false);
 7457                settings.show_signature_help_after_edits = Some(false);
 7458            });
 7459        });
 7460    });
 7461
 7462    let mut cx = EditorLspTestContext::new_rust(
 7463        lsp::ServerCapabilities {
 7464            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7465                ..Default::default()
 7466            }),
 7467            ..Default::default()
 7468        },
 7469        cx,
 7470    )
 7471    .await;
 7472
 7473    let language = Language::new(
 7474        LanguageConfig {
 7475            name: "Rust".into(),
 7476            brackets: BracketPairConfig {
 7477                pairs: vec![
 7478                    BracketPair {
 7479                        start: "{".to_string(),
 7480                        end: "}".to_string(),
 7481                        close: true,
 7482                        surround: true,
 7483                        newline: true,
 7484                    },
 7485                    BracketPair {
 7486                        start: "(".to_string(),
 7487                        end: ")".to_string(),
 7488                        close: true,
 7489                        surround: true,
 7490                        newline: true,
 7491                    },
 7492                    BracketPair {
 7493                        start: "/*".to_string(),
 7494                        end: " */".to_string(),
 7495                        close: true,
 7496                        surround: true,
 7497                        newline: true,
 7498                    },
 7499                    BracketPair {
 7500                        start: "[".to_string(),
 7501                        end: "]".to_string(),
 7502                        close: false,
 7503                        surround: false,
 7504                        newline: true,
 7505                    },
 7506                    BracketPair {
 7507                        start: "\"".to_string(),
 7508                        end: "\"".to_string(),
 7509                        close: true,
 7510                        surround: true,
 7511                        newline: false,
 7512                    },
 7513                    BracketPair {
 7514                        start: "<".to_string(),
 7515                        end: ">".to_string(),
 7516                        close: false,
 7517                        surround: true,
 7518                        newline: true,
 7519                    },
 7520                ],
 7521                ..Default::default()
 7522            },
 7523            autoclose_before: "})]".to_string(),
 7524            ..Default::default()
 7525        },
 7526        Some(tree_sitter_rust::LANGUAGE.into()),
 7527    );
 7528    let language = Arc::new(language);
 7529
 7530    cx.language_registry().add(language.clone());
 7531    cx.update_buffer(|buffer, cx| {
 7532        buffer.set_language(Some(language), cx);
 7533    });
 7534
 7535    // Ensure that signature_help is not called when no signature help is enabled.
 7536    cx.set_state(
 7537        &r#"
 7538            fn main() {
 7539                sampleˇ
 7540            }
 7541        "#
 7542        .unindent(),
 7543    );
 7544    cx.update_editor(|view, cx| {
 7545        view.handle_input("(", cx);
 7546    });
 7547    cx.assert_editor_state(
 7548        &"
 7549            fn main() {
 7550                sample(ˇ)
 7551            }
 7552        "
 7553        .unindent(),
 7554    );
 7555    cx.editor(|editor, _| {
 7556        assert!(editor.signature_help_state.task().is_none());
 7557    });
 7558
 7559    let mocked_response = lsp::SignatureHelp {
 7560        signatures: vec![lsp::SignatureInformation {
 7561            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7562            documentation: None,
 7563            parameters: Some(vec![
 7564                lsp::ParameterInformation {
 7565                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7566                    documentation: None,
 7567                },
 7568                lsp::ParameterInformation {
 7569                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7570                    documentation: None,
 7571                },
 7572            ]),
 7573            active_parameter: None,
 7574        }],
 7575        active_signature: Some(0),
 7576        active_parameter: Some(0),
 7577    };
 7578
 7579    // Ensure that signature_help is called when enabled afte edits
 7580    cx.update(|cx| {
 7581        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7582            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7583                settings.auto_signature_help = Some(false);
 7584                settings.show_signature_help_after_edits = Some(true);
 7585            });
 7586        });
 7587    });
 7588    cx.set_state(
 7589        &r#"
 7590            fn main() {
 7591                sampleˇ
 7592            }
 7593        "#
 7594        .unindent(),
 7595    );
 7596    cx.update_editor(|view, cx| {
 7597        view.handle_input("(", cx);
 7598    });
 7599    cx.assert_editor_state(
 7600        &"
 7601            fn main() {
 7602                sample(ˇ)
 7603            }
 7604        "
 7605        .unindent(),
 7606    );
 7607    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7608    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7609        .await;
 7610    cx.update_editor(|editor, _| {
 7611        let signature_help_state = editor.signature_help_state.popover().cloned();
 7612        assert!(signature_help_state.is_some());
 7613        let ParsedMarkdown {
 7614            text, highlights, ..
 7615        } = signature_help_state.unwrap().parsed_content;
 7616        assert_eq!(text, "param1: u8, param2: u8");
 7617        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7618        editor.signature_help_state = SignatureHelpState::default();
 7619    });
 7620
 7621    // Ensure that signature_help is called when auto signature help override is enabled
 7622    cx.update(|cx| {
 7623        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7624            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7625                settings.auto_signature_help = Some(true);
 7626                settings.show_signature_help_after_edits = Some(false);
 7627            });
 7628        });
 7629    });
 7630    cx.set_state(
 7631        &r#"
 7632            fn main() {
 7633                sampleˇ
 7634            }
 7635        "#
 7636        .unindent(),
 7637    );
 7638    cx.update_editor(|view, cx| {
 7639        view.handle_input("(", cx);
 7640    });
 7641    cx.assert_editor_state(
 7642        &"
 7643            fn main() {
 7644                sample(ˇ)
 7645            }
 7646        "
 7647        .unindent(),
 7648    );
 7649    handle_signature_help_request(&mut cx, mocked_response).await;
 7650    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7651        .await;
 7652    cx.editor(|editor, _| {
 7653        let signature_help_state = editor.signature_help_state.popover().cloned();
 7654        assert!(signature_help_state.is_some());
 7655        let ParsedMarkdown {
 7656            text, highlights, ..
 7657        } = signature_help_state.unwrap().parsed_content;
 7658        assert_eq!(text, "param1: u8, param2: u8");
 7659        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7660    });
 7661}
 7662
 7663#[gpui::test]
 7664async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7665    init_test(cx, |_| {});
 7666    cx.update(|cx| {
 7667        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7668            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7669                settings.auto_signature_help = Some(true);
 7670            });
 7671        });
 7672    });
 7673
 7674    let mut cx = EditorLspTestContext::new_rust(
 7675        lsp::ServerCapabilities {
 7676            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7677                ..Default::default()
 7678            }),
 7679            ..Default::default()
 7680        },
 7681        cx,
 7682    )
 7683    .await;
 7684
 7685    // A test that directly calls `show_signature_help`
 7686    cx.update_editor(|editor, cx| {
 7687        editor.show_signature_help(&ShowSignatureHelp, cx);
 7688    });
 7689
 7690    let mocked_response = lsp::SignatureHelp {
 7691        signatures: vec![lsp::SignatureInformation {
 7692            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7693            documentation: None,
 7694            parameters: Some(vec![
 7695                lsp::ParameterInformation {
 7696                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7697                    documentation: None,
 7698                },
 7699                lsp::ParameterInformation {
 7700                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7701                    documentation: None,
 7702                },
 7703            ]),
 7704            active_parameter: None,
 7705        }],
 7706        active_signature: Some(0),
 7707        active_parameter: Some(0),
 7708    };
 7709    handle_signature_help_request(&mut cx, mocked_response).await;
 7710
 7711    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7712        .await;
 7713
 7714    cx.editor(|editor, _| {
 7715        let signature_help_state = editor.signature_help_state.popover().cloned();
 7716        assert!(signature_help_state.is_some());
 7717        let ParsedMarkdown {
 7718            text, highlights, ..
 7719        } = signature_help_state.unwrap().parsed_content;
 7720        assert_eq!(text, "param1: u8, param2: u8");
 7721        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7722    });
 7723
 7724    // When exiting outside from inside the brackets, `signature_help` is closed.
 7725    cx.set_state(indoc! {"
 7726        fn main() {
 7727            sample(ˇ);
 7728        }
 7729
 7730        fn sample(param1: u8, param2: u8) {}
 7731    "});
 7732
 7733    cx.update_editor(|editor, cx| {
 7734        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7735    });
 7736
 7737    let mocked_response = lsp::SignatureHelp {
 7738        signatures: Vec::new(),
 7739        active_signature: None,
 7740        active_parameter: None,
 7741    };
 7742    handle_signature_help_request(&mut cx, mocked_response).await;
 7743
 7744    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7745        .await;
 7746
 7747    cx.editor(|editor, _| {
 7748        assert!(!editor.signature_help_state.is_shown());
 7749    });
 7750
 7751    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 7752    cx.set_state(indoc! {"
 7753        fn main() {
 7754            sample(ˇ);
 7755        }
 7756
 7757        fn sample(param1: u8, param2: u8) {}
 7758    "});
 7759
 7760    let mocked_response = lsp::SignatureHelp {
 7761        signatures: vec![lsp::SignatureInformation {
 7762            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7763            documentation: None,
 7764            parameters: Some(vec![
 7765                lsp::ParameterInformation {
 7766                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7767                    documentation: None,
 7768                },
 7769                lsp::ParameterInformation {
 7770                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7771                    documentation: None,
 7772                },
 7773            ]),
 7774            active_parameter: None,
 7775        }],
 7776        active_signature: Some(0),
 7777        active_parameter: Some(0),
 7778    };
 7779    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7780    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7781        .await;
 7782    cx.editor(|editor, _| {
 7783        assert!(editor.signature_help_state.is_shown());
 7784    });
 7785
 7786    // Restore the popover with more parameter input
 7787    cx.set_state(indoc! {"
 7788        fn main() {
 7789            sample(param1, param2ˇ);
 7790        }
 7791
 7792        fn sample(param1: u8, param2: u8) {}
 7793    "});
 7794
 7795    let mocked_response = lsp::SignatureHelp {
 7796        signatures: vec![lsp::SignatureInformation {
 7797            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7798            documentation: None,
 7799            parameters: Some(vec![
 7800                lsp::ParameterInformation {
 7801                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7802                    documentation: None,
 7803                },
 7804                lsp::ParameterInformation {
 7805                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7806                    documentation: None,
 7807                },
 7808            ]),
 7809            active_parameter: None,
 7810        }],
 7811        active_signature: Some(0),
 7812        active_parameter: Some(1),
 7813    };
 7814    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7815    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7816        .await;
 7817
 7818    // When selecting a range, the popover is gone.
 7819    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 7820    cx.update_editor(|editor, cx| {
 7821        editor.change_selections(None, cx, |s| {
 7822            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7823        })
 7824    });
 7825    cx.assert_editor_state(indoc! {"
 7826        fn main() {
 7827            sample(param1, «ˇparam2»);
 7828        }
 7829
 7830        fn sample(param1: u8, param2: u8) {}
 7831    "});
 7832    cx.editor(|editor, _| {
 7833        assert!(!editor.signature_help_state.is_shown());
 7834    });
 7835
 7836    // When unselecting again, the popover is back if within the brackets.
 7837    cx.update_editor(|editor, cx| {
 7838        editor.change_selections(None, cx, |s| {
 7839            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7840        })
 7841    });
 7842    cx.assert_editor_state(indoc! {"
 7843        fn main() {
 7844            sample(param1, ˇparam2);
 7845        }
 7846
 7847        fn sample(param1: u8, param2: u8) {}
 7848    "});
 7849    handle_signature_help_request(&mut cx, mocked_response).await;
 7850    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7851        .await;
 7852    cx.editor(|editor, _| {
 7853        assert!(editor.signature_help_state.is_shown());
 7854    });
 7855
 7856    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 7857    cx.update_editor(|editor, cx| {
 7858        editor.change_selections(None, cx, |s| {
 7859            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 7860            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7861        })
 7862    });
 7863    cx.assert_editor_state(indoc! {"
 7864        fn main() {
 7865            sample(param1, ˇparam2);
 7866        }
 7867
 7868        fn sample(param1: u8, param2: u8) {}
 7869    "});
 7870
 7871    let mocked_response = lsp::SignatureHelp {
 7872        signatures: vec![lsp::SignatureInformation {
 7873            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7874            documentation: None,
 7875            parameters: Some(vec![
 7876                lsp::ParameterInformation {
 7877                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7878                    documentation: None,
 7879                },
 7880                lsp::ParameterInformation {
 7881                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7882                    documentation: None,
 7883                },
 7884            ]),
 7885            active_parameter: None,
 7886        }],
 7887        active_signature: Some(0),
 7888        active_parameter: Some(1),
 7889    };
 7890    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7891    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7892        .await;
 7893    cx.update_editor(|editor, cx| {
 7894        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 7895    });
 7896    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 7897        .await;
 7898    cx.update_editor(|editor, cx| {
 7899        editor.change_selections(None, cx, |s| {
 7900            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 7901        })
 7902    });
 7903    cx.assert_editor_state(indoc! {"
 7904        fn main() {
 7905            sample(param1, «ˇparam2»);
 7906        }
 7907
 7908        fn sample(param1: u8, param2: u8) {}
 7909    "});
 7910    cx.update_editor(|editor, cx| {
 7911        editor.change_selections(None, cx, |s| {
 7912            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 7913        })
 7914    });
 7915    cx.assert_editor_state(indoc! {"
 7916        fn main() {
 7917            sample(param1, ˇparam2);
 7918        }
 7919
 7920        fn sample(param1: u8, param2: u8) {}
 7921    "});
 7922    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 7923        .await;
 7924}
 7925
 7926#[gpui::test]
 7927async fn test_completion(cx: &mut gpui::TestAppContext) {
 7928    init_test(cx, |_| {});
 7929
 7930    let mut cx = EditorLspTestContext::new_rust(
 7931        lsp::ServerCapabilities {
 7932            completion_provider: Some(lsp::CompletionOptions {
 7933                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 7934                resolve_provider: Some(true),
 7935                ..Default::default()
 7936            }),
 7937            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 7938            ..Default::default()
 7939        },
 7940        cx,
 7941    )
 7942    .await;
 7943    let counter = Arc::new(AtomicUsize::new(0));
 7944
 7945    cx.set_state(indoc! {"
 7946        oneˇ
 7947        two
 7948        three
 7949    "});
 7950    cx.simulate_keystroke(".");
 7951    handle_completion_request(
 7952        &mut cx,
 7953        indoc! {"
 7954            one.|<>
 7955            two
 7956            three
 7957        "},
 7958        vec!["first_completion", "second_completion"],
 7959        counter.clone(),
 7960    )
 7961    .await;
 7962    cx.condition(|editor, _| editor.context_menu_visible())
 7963        .await;
 7964    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 7965
 7966    let _handler = handle_signature_help_request(
 7967        &mut cx,
 7968        lsp::SignatureHelp {
 7969            signatures: vec![lsp::SignatureInformation {
 7970                label: "test signature".to_string(),
 7971                documentation: None,
 7972                parameters: Some(vec![lsp::ParameterInformation {
 7973                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 7974                    documentation: None,
 7975                }]),
 7976                active_parameter: None,
 7977            }],
 7978            active_signature: None,
 7979            active_parameter: None,
 7980        },
 7981    );
 7982    cx.update_editor(|editor, cx| {
 7983        assert!(
 7984            !editor.signature_help_state.is_shown(),
 7985            "No signature help was called for"
 7986        );
 7987        editor.show_signature_help(&ShowSignatureHelp, cx);
 7988    });
 7989    cx.run_until_parked();
 7990    cx.update_editor(|editor, _| {
 7991        assert!(
 7992            !editor.signature_help_state.is_shown(),
 7993            "No signature help should be shown when completions menu is open"
 7994        );
 7995    });
 7996
 7997    let apply_additional_edits = cx.update_editor(|editor, cx| {
 7998        editor.context_menu_next(&Default::default(), cx);
 7999        editor
 8000            .confirm_completion(&ConfirmCompletion::default(), cx)
 8001            .unwrap()
 8002    });
 8003    cx.assert_editor_state(indoc! {"
 8004        one.second_completionˇ
 8005        two
 8006        three
 8007    "});
 8008
 8009    handle_resolve_completion_request(
 8010        &mut cx,
 8011        Some(vec![
 8012            (
 8013                //This overlaps with the primary completion edit which is
 8014                //misbehavior from the LSP spec, test that we filter it out
 8015                indoc! {"
 8016                    one.second_ˇcompletion
 8017                    two
 8018                    threeˇ
 8019                "},
 8020                "overlapping additional edit",
 8021            ),
 8022            (
 8023                indoc! {"
 8024                    one.second_completion
 8025                    two
 8026                    threeˇ
 8027                "},
 8028                "\nadditional edit",
 8029            ),
 8030        ]),
 8031    )
 8032    .await;
 8033    apply_additional_edits.await.unwrap();
 8034    cx.assert_editor_state(indoc! {"
 8035        one.second_completionˇ
 8036        two
 8037        three
 8038        additional edit
 8039    "});
 8040
 8041    cx.set_state(indoc! {"
 8042        one.second_completion
 8043        twoˇ
 8044        threeˇ
 8045        additional edit
 8046    "});
 8047    cx.simulate_keystroke(" ");
 8048    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8049    cx.simulate_keystroke("s");
 8050    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8051
 8052    cx.assert_editor_state(indoc! {"
 8053        one.second_completion
 8054        two sˇ
 8055        three sˇ
 8056        additional edit
 8057    "});
 8058    handle_completion_request(
 8059        &mut cx,
 8060        indoc! {"
 8061            one.second_completion
 8062            two s
 8063            three <s|>
 8064            additional edit
 8065        "},
 8066        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8067        counter.clone(),
 8068    )
 8069    .await;
 8070    cx.condition(|editor, _| editor.context_menu_visible())
 8071        .await;
 8072    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8073
 8074    cx.simulate_keystroke("i");
 8075
 8076    handle_completion_request(
 8077        &mut cx,
 8078        indoc! {"
 8079            one.second_completion
 8080            two si
 8081            three <si|>
 8082            additional edit
 8083        "},
 8084        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8085        counter.clone(),
 8086    )
 8087    .await;
 8088    cx.condition(|editor, _| editor.context_menu_visible())
 8089        .await;
 8090    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8091
 8092    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8093        editor
 8094            .confirm_completion(&ConfirmCompletion::default(), cx)
 8095            .unwrap()
 8096    });
 8097    cx.assert_editor_state(indoc! {"
 8098        one.second_completion
 8099        two sixth_completionˇ
 8100        three sixth_completionˇ
 8101        additional edit
 8102    "});
 8103
 8104    handle_resolve_completion_request(&mut cx, None).await;
 8105    apply_additional_edits.await.unwrap();
 8106
 8107    cx.update(|cx| {
 8108        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8109            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8110                settings.show_completions_on_input = Some(false);
 8111            });
 8112        })
 8113    });
 8114    cx.set_state("editorˇ");
 8115    cx.simulate_keystroke(".");
 8116    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8117    cx.simulate_keystroke("c");
 8118    cx.simulate_keystroke("l");
 8119    cx.simulate_keystroke("o");
 8120    cx.assert_editor_state("editor.cloˇ");
 8121    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 8122    cx.update_editor(|editor, cx| {
 8123        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8124    });
 8125    handle_completion_request(
 8126        &mut cx,
 8127        "editor.<clo|>",
 8128        vec!["close", "clobber"],
 8129        counter.clone(),
 8130    )
 8131    .await;
 8132    cx.condition(|editor, _| editor.context_menu_visible())
 8133        .await;
 8134    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8135
 8136    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8137        editor
 8138            .confirm_completion(&ConfirmCompletion::default(), cx)
 8139            .unwrap()
 8140    });
 8141    cx.assert_editor_state("editor.closeˇ");
 8142    handle_resolve_completion_request(&mut cx, None).await;
 8143    apply_additional_edits.await.unwrap();
 8144}
 8145
 8146#[gpui::test]
 8147async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8148    init_test(cx, |_| {});
 8149    let mut cx = EditorLspTestContext::new_rust(
 8150        lsp::ServerCapabilities {
 8151            completion_provider: Some(lsp::CompletionOptions {
 8152                trigger_characters: Some(vec![".".to_string()]),
 8153                ..Default::default()
 8154            }),
 8155            ..Default::default()
 8156        },
 8157        cx,
 8158    )
 8159    .await;
 8160    cx.lsp
 8161        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8162            Ok(Some(lsp::CompletionResponse::Array(vec![
 8163                lsp::CompletionItem {
 8164                    label: "first".into(),
 8165                    ..Default::default()
 8166                },
 8167                lsp::CompletionItem {
 8168                    label: "last".into(),
 8169                    ..Default::default()
 8170                },
 8171            ])))
 8172        });
 8173    cx.set_state("variableˇ");
 8174    cx.simulate_keystroke(".");
 8175    cx.executor().run_until_parked();
 8176
 8177    cx.update_editor(|editor, _| {
 8178        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8179            assert_eq!(
 8180                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8181                &["first", "last"]
 8182            );
 8183        } else {
 8184            panic!("expected completion menu to be open");
 8185        }
 8186    });
 8187
 8188    cx.update_editor(|editor, cx| {
 8189        editor.move_page_down(&MovePageDown::default(), cx);
 8190        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8191            assert!(
 8192                menu.selected_item == 1,
 8193                "expected PageDown to select the last item from the context menu"
 8194            );
 8195        } else {
 8196            panic!("expected completion menu to stay open after PageDown");
 8197        }
 8198    });
 8199
 8200    cx.update_editor(|editor, cx| {
 8201        editor.move_page_up(&MovePageUp::default(), cx);
 8202        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8203            assert!(
 8204                menu.selected_item == 0,
 8205                "expected PageUp to select the first item from the context menu"
 8206            );
 8207        } else {
 8208            panic!("expected completion menu to stay open after PageUp");
 8209        }
 8210    });
 8211}
 8212
 8213#[gpui::test]
 8214async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8215    init_test(cx, |_| {});
 8216
 8217    let mut cx = EditorLspTestContext::new_rust(
 8218        lsp::ServerCapabilities {
 8219            completion_provider: Some(lsp::CompletionOptions {
 8220                trigger_characters: Some(vec![".".to_string()]),
 8221                resolve_provider: Some(true),
 8222                ..Default::default()
 8223            }),
 8224            ..Default::default()
 8225        },
 8226        cx,
 8227    )
 8228    .await;
 8229
 8230    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8231    cx.simulate_keystroke(".");
 8232    let completion_item = lsp::CompletionItem {
 8233        label: "Some".into(),
 8234        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8235        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8236        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8237            kind: lsp::MarkupKind::Markdown,
 8238            value: "```rust\nSome(2)\n```".to_string(),
 8239        })),
 8240        deprecated: Some(false),
 8241        sort_text: Some("Some".to_string()),
 8242        filter_text: Some("Some".to_string()),
 8243        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8244        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8245            range: lsp::Range {
 8246                start: lsp::Position {
 8247                    line: 0,
 8248                    character: 22,
 8249                },
 8250                end: lsp::Position {
 8251                    line: 0,
 8252                    character: 22,
 8253                },
 8254            },
 8255            new_text: "Some(2)".to_string(),
 8256        })),
 8257        additional_text_edits: Some(vec![lsp::TextEdit {
 8258            range: lsp::Range {
 8259                start: lsp::Position {
 8260                    line: 0,
 8261                    character: 20,
 8262                },
 8263                end: lsp::Position {
 8264                    line: 0,
 8265                    character: 22,
 8266                },
 8267            },
 8268            new_text: "".to_string(),
 8269        }]),
 8270        ..Default::default()
 8271    };
 8272
 8273    let closure_completion_item = completion_item.clone();
 8274    let counter = Arc::new(AtomicUsize::new(0));
 8275    let counter_clone = counter.clone();
 8276    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8277        let task_completion_item = closure_completion_item.clone();
 8278        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8279        async move {
 8280            Ok(Some(lsp::CompletionResponse::Array(vec![
 8281                task_completion_item,
 8282            ])))
 8283        }
 8284    });
 8285
 8286    cx.condition(|editor, _| editor.context_menu_visible())
 8287        .await;
 8288    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8289    assert!(request.next().await.is_some());
 8290    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8291
 8292    cx.simulate_keystroke("S");
 8293    cx.simulate_keystroke("o");
 8294    cx.simulate_keystroke("m");
 8295    cx.condition(|editor, _| editor.context_menu_visible())
 8296        .await;
 8297    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8298    assert!(request.next().await.is_some());
 8299    assert!(request.next().await.is_some());
 8300    assert!(request.next().await.is_some());
 8301    request.close();
 8302    assert!(request.next().await.is_none());
 8303    assert_eq!(
 8304        counter.load(atomic::Ordering::Acquire),
 8305        4,
 8306        "With the completions menu open, only one LSP request should happen per input"
 8307    );
 8308}
 8309
 8310#[gpui::test]
 8311async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8312    init_test(cx, |_| {});
 8313    let mut cx = EditorTestContext::new(cx).await;
 8314    let language = Arc::new(Language::new(
 8315        LanguageConfig {
 8316            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8317            ..Default::default()
 8318        },
 8319        Some(tree_sitter_rust::LANGUAGE.into()),
 8320    ));
 8321    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8322
 8323    // If multiple selections intersect a line, the line is only toggled once.
 8324    cx.set_state(indoc! {"
 8325        fn a() {
 8326            «//b();
 8327            ˇ»// «c();
 8328            //ˇ»  d();
 8329        }
 8330    "});
 8331
 8332    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8333
 8334    cx.assert_editor_state(indoc! {"
 8335        fn a() {
 8336            «b();
 8337            c();
 8338            ˇ» d();
 8339        }
 8340    "});
 8341
 8342    // The comment prefix is inserted at the same column for every line in a
 8343    // selection.
 8344    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8345
 8346    cx.assert_editor_state(indoc! {"
 8347        fn a() {
 8348            // «b();
 8349            // c();
 8350            ˇ»//  d();
 8351        }
 8352    "});
 8353
 8354    // If a selection ends at the beginning of a line, that line is not toggled.
 8355    cx.set_selections_state(indoc! {"
 8356        fn a() {
 8357            // b();
 8358            «// c();
 8359        ˇ»    //  d();
 8360        }
 8361    "});
 8362
 8363    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8364
 8365    cx.assert_editor_state(indoc! {"
 8366        fn a() {
 8367            // b();
 8368            «c();
 8369        ˇ»    //  d();
 8370        }
 8371    "});
 8372
 8373    // If a selection span a single line and is empty, the line is toggled.
 8374    cx.set_state(indoc! {"
 8375        fn a() {
 8376            a();
 8377            b();
 8378        ˇ
 8379        }
 8380    "});
 8381
 8382    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8383
 8384    cx.assert_editor_state(indoc! {"
 8385        fn a() {
 8386            a();
 8387            b();
 8388        //•ˇ
 8389        }
 8390    "});
 8391
 8392    // If a selection span multiple lines, empty lines are not toggled.
 8393    cx.set_state(indoc! {"
 8394        fn a() {
 8395            «a();
 8396
 8397            c();ˇ»
 8398        }
 8399    "});
 8400
 8401    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8402
 8403    cx.assert_editor_state(indoc! {"
 8404        fn a() {
 8405            // «a();
 8406
 8407            // c();ˇ»
 8408        }
 8409    "});
 8410
 8411    // If a selection includes multiple comment prefixes, all lines are uncommented.
 8412    cx.set_state(indoc! {"
 8413        fn a() {
 8414            «// a();
 8415            /// b();
 8416            //! c();ˇ»
 8417        }
 8418    "});
 8419
 8420    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8421
 8422    cx.assert_editor_state(indoc! {"
 8423        fn a() {
 8424            «a();
 8425            b();
 8426            c();ˇ»
 8427        }
 8428    "});
 8429}
 8430
 8431#[gpui::test]
 8432async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 8433    init_test(cx, |_| {});
 8434
 8435    let language = Arc::new(Language::new(
 8436        LanguageConfig {
 8437            line_comments: vec!["// ".into()],
 8438            ..Default::default()
 8439        },
 8440        Some(tree_sitter_rust::LANGUAGE.into()),
 8441    ));
 8442
 8443    let mut cx = EditorTestContext::new(cx).await;
 8444
 8445    cx.language_registry().add(language.clone());
 8446    cx.update_buffer(|buffer, cx| {
 8447        buffer.set_language(Some(language), cx);
 8448    });
 8449
 8450    let toggle_comments = &ToggleComments {
 8451        advance_downwards: true,
 8452    };
 8453
 8454    // Single cursor on one line -> advance
 8455    // Cursor moves horizontally 3 characters as well on non-blank line
 8456    cx.set_state(indoc!(
 8457        "fn a() {
 8458             ˇdog();
 8459             cat();
 8460        }"
 8461    ));
 8462    cx.update_editor(|editor, cx| {
 8463        editor.toggle_comments(toggle_comments, cx);
 8464    });
 8465    cx.assert_editor_state(indoc!(
 8466        "fn a() {
 8467             // dog();
 8468             catˇ();
 8469        }"
 8470    ));
 8471
 8472    // Single selection on one line -> don't advance
 8473    cx.set_state(indoc!(
 8474        "fn a() {
 8475             «dog()ˇ»;
 8476             cat();
 8477        }"
 8478    ));
 8479    cx.update_editor(|editor, cx| {
 8480        editor.toggle_comments(toggle_comments, cx);
 8481    });
 8482    cx.assert_editor_state(indoc!(
 8483        "fn a() {
 8484             // «dog()ˇ»;
 8485             cat();
 8486        }"
 8487    ));
 8488
 8489    // Multiple cursors on one line -> advance
 8490    cx.set_state(indoc!(
 8491        "fn a() {
 8492             ˇdˇog();
 8493             cat();
 8494        }"
 8495    ));
 8496    cx.update_editor(|editor, cx| {
 8497        editor.toggle_comments(toggle_comments, cx);
 8498    });
 8499    cx.assert_editor_state(indoc!(
 8500        "fn a() {
 8501             // dog();
 8502             catˇ(ˇ);
 8503        }"
 8504    ));
 8505
 8506    // Multiple cursors on one line, with selection -> don't advance
 8507    cx.set_state(indoc!(
 8508        "fn a() {
 8509             ˇdˇog«()ˇ»;
 8510             cat();
 8511        }"
 8512    ));
 8513    cx.update_editor(|editor, cx| {
 8514        editor.toggle_comments(toggle_comments, cx);
 8515    });
 8516    cx.assert_editor_state(indoc!(
 8517        "fn a() {
 8518             // ˇdˇog«()ˇ»;
 8519             cat();
 8520        }"
 8521    ));
 8522
 8523    // Single cursor on one line -> advance
 8524    // Cursor moves to column 0 on blank line
 8525    cx.set_state(indoc!(
 8526        "fn a() {
 8527             ˇdog();
 8528
 8529             cat();
 8530        }"
 8531    ));
 8532    cx.update_editor(|editor, cx| {
 8533        editor.toggle_comments(toggle_comments, cx);
 8534    });
 8535    cx.assert_editor_state(indoc!(
 8536        "fn a() {
 8537             // dog();
 8538        ˇ
 8539             cat();
 8540        }"
 8541    ));
 8542
 8543    // Single cursor on one line -> advance
 8544    // Cursor starts and ends at column 0
 8545    cx.set_state(indoc!(
 8546        "fn a() {
 8547         ˇ    dog();
 8548             cat();
 8549        }"
 8550    ));
 8551    cx.update_editor(|editor, cx| {
 8552        editor.toggle_comments(toggle_comments, cx);
 8553    });
 8554    cx.assert_editor_state(indoc!(
 8555        "fn a() {
 8556             // dog();
 8557         ˇ    cat();
 8558        }"
 8559    ));
 8560}
 8561
 8562#[gpui::test]
 8563async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 8564    init_test(cx, |_| {});
 8565
 8566    let mut cx = EditorTestContext::new(cx).await;
 8567
 8568    let html_language = Arc::new(
 8569        Language::new(
 8570            LanguageConfig {
 8571                name: "HTML".into(),
 8572                block_comment: Some(("<!-- ".into(), " -->".into())),
 8573                ..Default::default()
 8574            },
 8575            Some(tree_sitter_html::language()),
 8576        )
 8577        .with_injection_query(
 8578            r#"
 8579            (script_element
 8580                (raw_text) @content
 8581                (#set! "language" "javascript"))
 8582            "#,
 8583        )
 8584        .unwrap(),
 8585    );
 8586
 8587    let javascript_language = Arc::new(Language::new(
 8588        LanguageConfig {
 8589            name: "JavaScript".into(),
 8590            line_comments: vec!["// ".into()],
 8591            ..Default::default()
 8592        },
 8593        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 8594    ));
 8595
 8596    cx.language_registry().add(html_language.clone());
 8597    cx.language_registry().add(javascript_language.clone());
 8598    cx.update_buffer(|buffer, cx| {
 8599        buffer.set_language(Some(html_language), cx);
 8600    });
 8601
 8602    // Toggle comments for empty selections
 8603    cx.set_state(
 8604        &r#"
 8605            <p>A</p>ˇ
 8606            <p>B</p>ˇ
 8607            <p>C</p>ˇ
 8608        "#
 8609        .unindent(),
 8610    );
 8611    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8612    cx.assert_editor_state(
 8613        &r#"
 8614            <!-- <p>A</p>ˇ -->
 8615            <!-- <p>B</p>ˇ -->
 8616            <!-- <p>C</p>ˇ -->
 8617        "#
 8618        .unindent(),
 8619    );
 8620    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8621    cx.assert_editor_state(
 8622        &r#"
 8623            <p>A</p>ˇ
 8624            <p>B</p>ˇ
 8625            <p>C</p>ˇ
 8626        "#
 8627        .unindent(),
 8628    );
 8629
 8630    // Toggle comments for mixture of empty and non-empty selections, where
 8631    // multiple selections occupy a given line.
 8632    cx.set_state(
 8633        &r#"
 8634            <p>A«</p>
 8635            <p>ˇ»B</p>ˇ
 8636            <p>C«</p>
 8637            <p>ˇ»D</p>ˇ
 8638        "#
 8639        .unindent(),
 8640    );
 8641
 8642    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8643    cx.assert_editor_state(
 8644        &r#"
 8645            <!-- <p>A«</p>
 8646            <p>ˇ»B</p>ˇ -->
 8647            <!-- <p>C«</p>
 8648            <p>ˇ»D</p>ˇ -->
 8649        "#
 8650        .unindent(),
 8651    );
 8652    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8653    cx.assert_editor_state(
 8654        &r#"
 8655            <p>A«</p>
 8656            <p>ˇ»B</p>ˇ
 8657            <p>C«</p>
 8658            <p>ˇ»D</p>ˇ
 8659        "#
 8660        .unindent(),
 8661    );
 8662
 8663    // Toggle comments when different languages are active for different
 8664    // selections.
 8665    cx.set_state(
 8666        &r#"
 8667            ˇ<script>
 8668                ˇvar x = new Y();
 8669            ˇ</script>
 8670        "#
 8671        .unindent(),
 8672    );
 8673    cx.executor().run_until_parked();
 8674    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 8675    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 8676    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 8677    cx.assert_editor_state(
 8678        &r#"
 8679            <!-- ˇ<script> -->
 8680                // ˇvar x = new Y();
 8681            // ˇ</script>
 8682        "#
 8683        .unindent(),
 8684    );
 8685}
 8686
 8687#[gpui::test]
 8688fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 8689    init_test(cx, |_| {});
 8690
 8691    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8692    let multibuffer = cx.new_model(|cx| {
 8693        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8694        multibuffer.push_excerpts(
 8695            buffer.clone(),
 8696            [
 8697                ExcerptRange {
 8698                    context: Point::new(0, 0)..Point::new(0, 4),
 8699                    primary: None,
 8700                },
 8701                ExcerptRange {
 8702                    context: Point::new(1, 0)..Point::new(1, 4),
 8703                    primary: None,
 8704                },
 8705            ],
 8706            cx,
 8707        );
 8708        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 8709        multibuffer
 8710    });
 8711
 8712    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8713    view.update(cx, |view, cx| {
 8714        assert_eq!(view.text(cx), "aaaa\nbbbb");
 8715        view.change_selections(None, cx, |s| {
 8716            s.select_ranges([
 8717                Point::new(0, 0)..Point::new(0, 0),
 8718                Point::new(1, 0)..Point::new(1, 0),
 8719            ])
 8720        });
 8721
 8722        view.handle_input("X", cx);
 8723        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 8724        assert_eq!(
 8725            view.selections.ranges(cx),
 8726            [
 8727                Point::new(0, 1)..Point::new(0, 1),
 8728                Point::new(1, 1)..Point::new(1, 1),
 8729            ]
 8730        );
 8731
 8732        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 8733        view.change_selections(None, cx, |s| {
 8734            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 8735        });
 8736        view.backspace(&Default::default(), cx);
 8737        assert_eq!(view.text(cx), "Xa\nbbb");
 8738        assert_eq!(
 8739            view.selections.ranges(cx),
 8740            [Point::new(1, 0)..Point::new(1, 0)]
 8741        );
 8742
 8743        view.change_selections(None, cx, |s| {
 8744            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 8745        });
 8746        view.backspace(&Default::default(), cx);
 8747        assert_eq!(view.text(cx), "X\nbb");
 8748        assert_eq!(
 8749            view.selections.ranges(cx),
 8750            [Point::new(0, 1)..Point::new(0, 1)]
 8751        );
 8752    });
 8753}
 8754
 8755#[gpui::test]
 8756fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 8757    init_test(cx, |_| {});
 8758
 8759    let markers = vec![('[', ']').into(), ('(', ')').into()];
 8760    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 8761        indoc! {"
 8762            [aaaa
 8763            (bbbb]
 8764            cccc)",
 8765        },
 8766        markers.clone(),
 8767    );
 8768    let excerpt_ranges = markers.into_iter().map(|marker| {
 8769        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 8770        ExcerptRange {
 8771            context,
 8772            primary: None,
 8773        }
 8774    });
 8775    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 8776    let multibuffer = cx.new_model(|cx| {
 8777        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8778        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 8779        multibuffer
 8780    });
 8781
 8782    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 8783    view.update(cx, |view, cx| {
 8784        let (expected_text, selection_ranges) = marked_text_ranges(
 8785            indoc! {"
 8786                aaaa
 8787                bˇbbb
 8788                bˇbbˇb
 8789                cccc"
 8790            },
 8791            true,
 8792        );
 8793        assert_eq!(view.text(cx), expected_text);
 8794        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 8795
 8796        view.handle_input("X", cx);
 8797
 8798        let (expected_text, expected_selections) = marked_text_ranges(
 8799            indoc! {"
 8800                aaaa
 8801                bXˇbbXb
 8802                bXˇbbXˇb
 8803                cccc"
 8804            },
 8805            false,
 8806        );
 8807        assert_eq!(view.text(cx), expected_text);
 8808        assert_eq!(view.selections.ranges(cx), expected_selections);
 8809
 8810        view.newline(&Newline, cx);
 8811        let (expected_text, expected_selections) = marked_text_ranges(
 8812            indoc! {"
 8813                aaaa
 8814                bX
 8815                ˇbbX
 8816                b
 8817                bX
 8818                ˇbbX
 8819                ˇb
 8820                cccc"
 8821            },
 8822            false,
 8823        );
 8824        assert_eq!(view.text(cx), expected_text);
 8825        assert_eq!(view.selections.ranges(cx), expected_selections);
 8826    });
 8827}
 8828
 8829#[gpui::test]
 8830fn test_refresh_selections(cx: &mut TestAppContext) {
 8831    init_test(cx, |_| {});
 8832
 8833    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8834    let mut excerpt1_id = None;
 8835    let multibuffer = cx.new_model(|cx| {
 8836        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8837        excerpt1_id = multibuffer
 8838            .push_excerpts(
 8839                buffer.clone(),
 8840                [
 8841                    ExcerptRange {
 8842                        context: Point::new(0, 0)..Point::new(1, 4),
 8843                        primary: None,
 8844                    },
 8845                    ExcerptRange {
 8846                        context: Point::new(1, 0)..Point::new(2, 4),
 8847                        primary: None,
 8848                    },
 8849                ],
 8850                cx,
 8851            )
 8852            .into_iter()
 8853            .next();
 8854        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8855        multibuffer
 8856    });
 8857
 8858    let editor = cx.add_window(|cx| {
 8859        let mut editor = build_editor(multibuffer.clone(), cx);
 8860        let snapshot = editor.snapshot(cx);
 8861        editor.change_selections(None, cx, |s| {
 8862            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 8863        });
 8864        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 8865        assert_eq!(
 8866            editor.selections.ranges(cx),
 8867            [
 8868                Point::new(1, 3)..Point::new(1, 3),
 8869                Point::new(2, 1)..Point::new(2, 1),
 8870            ]
 8871        );
 8872        editor
 8873    });
 8874
 8875    // Refreshing selections is a no-op when excerpts haven't changed.
 8876    _ = editor.update(cx, |editor, cx| {
 8877        editor.change_selections(None, cx, |s| s.refresh());
 8878        assert_eq!(
 8879            editor.selections.ranges(cx),
 8880            [
 8881                Point::new(1, 3)..Point::new(1, 3),
 8882                Point::new(2, 1)..Point::new(2, 1),
 8883            ]
 8884        );
 8885    });
 8886
 8887    multibuffer.update(cx, |multibuffer, cx| {
 8888        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8889    });
 8890    _ = editor.update(cx, |editor, cx| {
 8891        // Removing an excerpt causes the first selection to become degenerate.
 8892        assert_eq!(
 8893            editor.selections.ranges(cx),
 8894            [
 8895                Point::new(0, 0)..Point::new(0, 0),
 8896                Point::new(0, 1)..Point::new(0, 1)
 8897            ]
 8898        );
 8899
 8900        // Refreshing selections will relocate the first selection to the original buffer
 8901        // location.
 8902        editor.change_selections(None, cx, |s| s.refresh());
 8903        assert_eq!(
 8904            editor.selections.ranges(cx),
 8905            [
 8906                Point::new(0, 1)..Point::new(0, 1),
 8907                Point::new(0, 3)..Point::new(0, 3)
 8908            ]
 8909        );
 8910        assert!(editor.selections.pending_anchor().is_some());
 8911    });
 8912}
 8913
 8914#[gpui::test]
 8915fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 8916    init_test(cx, |_| {});
 8917
 8918    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 8919    let mut excerpt1_id = None;
 8920    let multibuffer = cx.new_model(|cx| {
 8921        let mut multibuffer = MultiBuffer::new(ReadWrite);
 8922        excerpt1_id = multibuffer
 8923            .push_excerpts(
 8924                buffer.clone(),
 8925                [
 8926                    ExcerptRange {
 8927                        context: Point::new(0, 0)..Point::new(1, 4),
 8928                        primary: None,
 8929                    },
 8930                    ExcerptRange {
 8931                        context: Point::new(1, 0)..Point::new(2, 4),
 8932                        primary: None,
 8933                    },
 8934                ],
 8935                cx,
 8936            )
 8937            .into_iter()
 8938            .next();
 8939        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 8940        multibuffer
 8941    });
 8942
 8943    let editor = cx.add_window(|cx| {
 8944        let mut editor = build_editor(multibuffer.clone(), cx);
 8945        let snapshot = editor.snapshot(cx);
 8946        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 8947        assert_eq!(
 8948            editor.selections.ranges(cx),
 8949            [Point::new(1, 3)..Point::new(1, 3)]
 8950        );
 8951        editor
 8952    });
 8953
 8954    multibuffer.update(cx, |multibuffer, cx| {
 8955        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 8956    });
 8957    _ = editor.update(cx, |editor, cx| {
 8958        assert_eq!(
 8959            editor.selections.ranges(cx),
 8960            [Point::new(0, 0)..Point::new(0, 0)]
 8961        );
 8962
 8963        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 8964        editor.change_selections(None, cx, |s| s.refresh());
 8965        assert_eq!(
 8966            editor.selections.ranges(cx),
 8967            [Point::new(0, 3)..Point::new(0, 3)]
 8968        );
 8969        assert!(editor.selections.pending_anchor().is_some());
 8970    });
 8971}
 8972
 8973#[gpui::test]
 8974async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 8975    init_test(cx, |_| {});
 8976
 8977    let language = Arc::new(
 8978        Language::new(
 8979            LanguageConfig {
 8980                brackets: BracketPairConfig {
 8981                    pairs: vec![
 8982                        BracketPair {
 8983                            start: "{".to_string(),
 8984                            end: "}".to_string(),
 8985                            close: true,
 8986                            surround: true,
 8987                            newline: true,
 8988                        },
 8989                        BracketPair {
 8990                            start: "/* ".to_string(),
 8991                            end: " */".to_string(),
 8992                            close: true,
 8993                            surround: true,
 8994                            newline: true,
 8995                        },
 8996                    ],
 8997                    ..Default::default()
 8998                },
 8999                ..Default::default()
 9000            },
 9001            Some(tree_sitter_rust::LANGUAGE.into()),
 9002        )
 9003        .with_indents_query("")
 9004        .unwrap(),
 9005    );
 9006
 9007    let text = concat!(
 9008        "{   }\n",     //
 9009        "  x\n",       //
 9010        "  /*   */\n", //
 9011        "x\n",         //
 9012        "{{} }\n",     //
 9013    );
 9014
 9015    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9016    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9017    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9018    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9019        .await;
 9020
 9021    view.update(cx, |view, cx| {
 9022        view.change_selections(None, cx, |s| {
 9023            s.select_display_ranges([
 9024                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9025                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9026                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9027            ])
 9028        });
 9029        view.newline(&Newline, cx);
 9030
 9031        assert_eq!(
 9032            view.buffer().read(cx).read(cx).text(),
 9033            concat!(
 9034                "{ \n",    // Suppress rustfmt
 9035                "\n",      //
 9036                "}\n",     //
 9037                "  x\n",   //
 9038                "  /* \n", //
 9039                "  \n",    //
 9040                "  */\n",  //
 9041                "x\n",     //
 9042                "{{} \n",  //
 9043                "}\n",     //
 9044            )
 9045        );
 9046    });
 9047}
 9048
 9049#[gpui::test]
 9050fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9051    init_test(cx, |_| {});
 9052
 9053    let editor = cx.add_window(|cx| {
 9054        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9055        build_editor(buffer.clone(), cx)
 9056    });
 9057
 9058    _ = editor.update(cx, |editor, cx| {
 9059        struct Type1;
 9060        struct Type2;
 9061
 9062        let buffer = editor.buffer.read(cx).snapshot(cx);
 9063
 9064        let anchor_range =
 9065            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9066
 9067        editor.highlight_background::<Type1>(
 9068            &[
 9069                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9070                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9071                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9072                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9073            ],
 9074            |_| Hsla::red(),
 9075            cx,
 9076        );
 9077        editor.highlight_background::<Type2>(
 9078            &[
 9079                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9080                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9081                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9082                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9083            ],
 9084            |_| Hsla::green(),
 9085            cx,
 9086        );
 9087
 9088        let snapshot = editor.snapshot(cx);
 9089        let mut highlighted_ranges = editor.background_highlights_in_range(
 9090            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9091            &snapshot,
 9092            cx.theme().colors(),
 9093        );
 9094        // Enforce a consistent ordering based on color without relying on the ordering of the
 9095        // highlight's `TypeId` which is non-executor.
 9096        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9097        assert_eq!(
 9098            highlighted_ranges,
 9099            &[
 9100                (
 9101                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9102                    Hsla::red(),
 9103                ),
 9104                (
 9105                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9106                    Hsla::red(),
 9107                ),
 9108                (
 9109                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9110                    Hsla::green(),
 9111                ),
 9112                (
 9113                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9114                    Hsla::green(),
 9115                ),
 9116            ]
 9117        );
 9118        assert_eq!(
 9119            editor.background_highlights_in_range(
 9120                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9121                &snapshot,
 9122                cx.theme().colors(),
 9123            ),
 9124            &[(
 9125                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9126                Hsla::red(),
 9127            )]
 9128        );
 9129    });
 9130}
 9131
 9132#[gpui::test]
 9133async fn test_following(cx: &mut gpui::TestAppContext) {
 9134    init_test(cx, |_| {});
 9135
 9136    let fs = FakeFs::new(cx.executor());
 9137    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9138
 9139    let buffer = project.update(cx, |project, cx| {
 9140        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9141        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9142    });
 9143    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9144    let follower = cx.update(|cx| {
 9145        cx.open_window(
 9146            WindowOptions {
 9147                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9148                    gpui::Point::new(px(0.), px(0.)),
 9149                    gpui::Point::new(px(10.), px(80.)),
 9150                ))),
 9151                ..Default::default()
 9152            },
 9153            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9154        )
 9155        .unwrap()
 9156    });
 9157
 9158    let is_still_following = Rc::new(RefCell::new(true));
 9159    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9160    let pending_update = Rc::new(RefCell::new(None));
 9161    _ = follower.update(cx, {
 9162        let update = pending_update.clone();
 9163        let is_still_following = is_still_following.clone();
 9164        let follower_edit_event_count = follower_edit_event_count.clone();
 9165        |_, cx| {
 9166            cx.subscribe(
 9167                &leader.root_view(cx).unwrap(),
 9168                move |_, leader, event, cx| {
 9169                    leader
 9170                        .read(cx)
 9171                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9172                },
 9173            )
 9174            .detach();
 9175
 9176            cx.subscribe(
 9177                &follower.root_view(cx).unwrap(),
 9178                move |_, _, event: &EditorEvent, _cx| {
 9179                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9180                        *is_still_following.borrow_mut() = false;
 9181                    }
 9182
 9183                    if let EditorEvent::BufferEdited = event {
 9184                        *follower_edit_event_count.borrow_mut() += 1;
 9185                    }
 9186                },
 9187            )
 9188            .detach();
 9189        }
 9190    });
 9191
 9192    // Update the selections only
 9193    _ = leader.update(cx, |leader, cx| {
 9194        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9195    });
 9196    follower
 9197        .update(cx, |follower, cx| {
 9198            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9199        })
 9200        .unwrap()
 9201        .await
 9202        .unwrap();
 9203    _ = follower.update(cx, |follower, cx| {
 9204        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9205    });
 9206    assert!(*is_still_following.borrow());
 9207    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9208
 9209    // Update the scroll position only
 9210    _ = leader.update(cx, |leader, cx| {
 9211        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9212    });
 9213    follower
 9214        .update(cx, |follower, cx| {
 9215            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9216        })
 9217        .unwrap()
 9218        .await
 9219        .unwrap();
 9220    assert_eq!(
 9221        follower
 9222            .update(cx, |follower, cx| follower.scroll_position(cx))
 9223            .unwrap(),
 9224        gpui::Point::new(1.5, 3.5)
 9225    );
 9226    assert!(*is_still_following.borrow());
 9227    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9228
 9229    // Update the selections and scroll position. The follower's scroll position is updated
 9230    // via autoscroll, not via the leader's exact scroll position.
 9231    _ = leader.update(cx, |leader, cx| {
 9232        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9233        leader.request_autoscroll(Autoscroll::newest(), cx);
 9234        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9235    });
 9236    follower
 9237        .update(cx, |follower, cx| {
 9238            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9239        })
 9240        .unwrap()
 9241        .await
 9242        .unwrap();
 9243    _ = follower.update(cx, |follower, cx| {
 9244        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9245        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9246    });
 9247    assert!(*is_still_following.borrow());
 9248
 9249    // Creating a pending selection that precedes another selection
 9250    _ = leader.update(cx, |leader, cx| {
 9251        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9252        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9253    });
 9254    follower
 9255        .update(cx, |follower, cx| {
 9256            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9257        })
 9258        .unwrap()
 9259        .await
 9260        .unwrap();
 9261    _ = follower.update(cx, |follower, cx| {
 9262        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9263    });
 9264    assert!(*is_still_following.borrow());
 9265
 9266    // Extend the pending selection so that it surrounds another selection
 9267    _ = leader.update(cx, |leader, cx| {
 9268        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9269    });
 9270    follower
 9271        .update(cx, |follower, cx| {
 9272            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9273        })
 9274        .unwrap()
 9275        .await
 9276        .unwrap();
 9277    _ = follower.update(cx, |follower, cx| {
 9278        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 9279    });
 9280
 9281    // Scrolling locally breaks the follow
 9282    _ = follower.update(cx, |follower, cx| {
 9283        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 9284        follower.set_scroll_anchor(
 9285            ScrollAnchor {
 9286                anchor: top_anchor,
 9287                offset: gpui::Point::new(0.0, 0.5),
 9288            },
 9289            cx,
 9290        );
 9291    });
 9292    assert!(!(*is_still_following.borrow()));
 9293}
 9294
 9295#[gpui::test]
 9296async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 9297    init_test(cx, |_| {});
 9298
 9299    let fs = FakeFs::new(cx.executor());
 9300    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9301    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9302    let pane = workspace
 9303        .update(cx, |workspace, _| workspace.active_pane().clone())
 9304        .unwrap();
 9305
 9306    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9307
 9308    let leader = pane.update(cx, |_, cx| {
 9309        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
 9310        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 9311    });
 9312
 9313    // Start following the editor when it has no excerpts.
 9314    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9315    let follower_1 = cx
 9316        .update_window(*workspace.deref(), |_, cx| {
 9317            Editor::from_state_proto(
 9318                workspace.root_view(cx).unwrap(),
 9319                ViewId {
 9320                    creator: Default::default(),
 9321                    id: 0,
 9322                },
 9323                &mut state_message,
 9324                cx,
 9325            )
 9326        })
 9327        .unwrap()
 9328        .unwrap()
 9329        .await
 9330        .unwrap();
 9331
 9332    let update_message = Rc::new(RefCell::new(None));
 9333    follower_1.update(cx, {
 9334        let update = update_message.clone();
 9335        |_, cx| {
 9336            cx.subscribe(&leader, move |_, leader, event, cx| {
 9337                leader
 9338                    .read(cx)
 9339                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9340            })
 9341            .detach();
 9342        }
 9343    });
 9344
 9345    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 9346        (
 9347            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 9348            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 9349        )
 9350    });
 9351
 9352    // Insert some excerpts.
 9353    leader.update(cx, |leader, cx| {
 9354        leader.buffer.update(cx, |multibuffer, cx| {
 9355            let excerpt_ids = multibuffer.push_excerpts(
 9356                buffer_1.clone(),
 9357                [
 9358                    ExcerptRange {
 9359                        context: 1..6,
 9360                        primary: None,
 9361                    },
 9362                    ExcerptRange {
 9363                        context: 12..15,
 9364                        primary: None,
 9365                    },
 9366                    ExcerptRange {
 9367                        context: 0..3,
 9368                        primary: None,
 9369                    },
 9370                ],
 9371                cx,
 9372            );
 9373            multibuffer.insert_excerpts_after(
 9374                excerpt_ids[0],
 9375                buffer_2.clone(),
 9376                [
 9377                    ExcerptRange {
 9378                        context: 8..12,
 9379                        primary: None,
 9380                    },
 9381                    ExcerptRange {
 9382                        context: 0..6,
 9383                        primary: None,
 9384                    },
 9385                ],
 9386                cx,
 9387            );
 9388        });
 9389    });
 9390
 9391    // Apply the update of adding the excerpts.
 9392    follower_1
 9393        .update(cx, |follower, cx| {
 9394            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9395        })
 9396        .await
 9397        .unwrap();
 9398    assert_eq!(
 9399        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9400        leader.update(cx, |editor, cx| editor.text(cx))
 9401    );
 9402    update_message.borrow_mut().take();
 9403
 9404    // Start following separately after it already has excerpts.
 9405    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 9406    let follower_2 = cx
 9407        .update_window(*workspace.deref(), |_, cx| {
 9408            Editor::from_state_proto(
 9409                workspace.root_view(cx).unwrap().clone(),
 9410                ViewId {
 9411                    creator: Default::default(),
 9412                    id: 0,
 9413                },
 9414                &mut state_message,
 9415                cx,
 9416            )
 9417        })
 9418        .unwrap()
 9419        .unwrap()
 9420        .await
 9421        .unwrap();
 9422    assert_eq!(
 9423        follower_2.update(cx, |editor, cx| editor.text(cx)),
 9424        leader.update(cx, |editor, cx| editor.text(cx))
 9425    );
 9426
 9427    // Remove some excerpts.
 9428    leader.update(cx, |leader, cx| {
 9429        leader.buffer.update(cx, |multibuffer, cx| {
 9430            let excerpt_ids = multibuffer.excerpt_ids();
 9431            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 9432            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 9433        });
 9434    });
 9435
 9436    // Apply the update of removing the excerpts.
 9437    follower_1
 9438        .update(cx, |follower, cx| {
 9439            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9440        })
 9441        .await
 9442        .unwrap();
 9443    follower_2
 9444        .update(cx, |follower, cx| {
 9445            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 9446        })
 9447        .await
 9448        .unwrap();
 9449    update_message.borrow_mut().take();
 9450    assert_eq!(
 9451        follower_1.update(cx, |editor, cx| editor.text(cx)),
 9452        leader.update(cx, |editor, cx| editor.text(cx))
 9453    );
 9454}
 9455
 9456#[gpui::test]
 9457async fn go_to_prev_overlapping_diagnostic(
 9458    executor: BackgroundExecutor,
 9459    cx: &mut gpui::TestAppContext,
 9460) {
 9461    init_test(cx, |_| {});
 9462
 9463    let mut cx = EditorTestContext::new(cx).await;
 9464    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9465
 9466    cx.set_state(indoc! {"
 9467        ˇfn func(abc def: i32) -> u32 {
 9468        }
 9469    "});
 9470
 9471    cx.update(|cx| {
 9472        project.update(cx, |project, cx| {
 9473            project
 9474                .update_diagnostics(
 9475                    LanguageServerId(0),
 9476                    lsp::PublishDiagnosticsParams {
 9477                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9478                        version: None,
 9479                        diagnostics: vec![
 9480                            lsp::Diagnostic {
 9481                                range: lsp::Range::new(
 9482                                    lsp::Position::new(0, 11),
 9483                                    lsp::Position::new(0, 12),
 9484                                ),
 9485                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9486                                ..Default::default()
 9487                            },
 9488                            lsp::Diagnostic {
 9489                                range: lsp::Range::new(
 9490                                    lsp::Position::new(0, 12),
 9491                                    lsp::Position::new(0, 15),
 9492                                ),
 9493                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9494                                ..Default::default()
 9495                            },
 9496                            lsp::Diagnostic {
 9497                                range: lsp::Range::new(
 9498                                    lsp::Position::new(0, 25),
 9499                                    lsp::Position::new(0, 28),
 9500                                ),
 9501                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 9502                                ..Default::default()
 9503                            },
 9504                        ],
 9505                    },
 9506                    &[],
 9507                    cx,
 9508                )
 9509                .unwrap()
 9510        });
 9511    });
 9512
 9513    executor.run_until_parked();
 9514
 9515    cx.update_editor(|editor, cx| {
 9516        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9517    });
 9518
 9519    cx.assert_editor_state(indoc! {"
 9520        fn func(abc def: i32) -> ˇu32 {
 9521        }
 9522    "});
 9523
 9524    cx.update_editor(|editor, cx| {
 9525        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9526    });
 9527
 9528    cx.assert_editor_state(indoc! {"
 9529        fn func(abc ˇdef: i32) -> u32 {
 9530        }
 9531    "});
 9532
 9533    cx.update_editor(|editor, cx| {
 9534        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9535    });
 9536
 9537    cx.assert_editor_state(indoc! {"
 9538        fn func(abcˇ def: i32) -> u32 {
 9539        }
 9540    "});
 9541
 9542    cx.update_editor(|editor, cx| {
 9543        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 9544    });
 9545
 9546    cx.assert_editor_state(indoc! {"
 9547        fn func(abc def: i32) -> ˇu32 {
 9548        }
 9549    "});
 9550}
 9551
 9552#[gpui::test]
 9553async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
 9554    init_test(cx, |_| {});
 9555
 9556    let mut cx = EditorTestContext::new(cx).await;
 9557
 9558    cx.set_state(indoc! {"
 9559        fn func(abˇc def: i32) -> u32 {
 9560        }
 9561    "});
 9562    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 9563
 9564    cx.update(|cx| {
 9565        project.update(cx, |project, cx| {
 9566            project.update_diagnostics(
 9567                LanguageServerId(0),
 9568                lsp::PublishDiagnosticsParams {
 9569                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
 9570                    version: None,
 9571                    diagnostics: vec![lsp::Diagnostic {
 9572                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
 9573                        severity: Some(lsp::DiagnosticSeverity::ERROR),
 9574                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
 9575                        ..Default::default()
 9576                    }],
 9577                },
 9578                &[],
 9579                cx,
 9580            )
 9581        })
 9582    }).unwrap();
 9583    cx.run_until_parked();
 9584    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
 9585    cx.run_until_parked();
 9586    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
 9587}
 9588
 9589#[gpui::test]
 9590async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9591    init_test(cx, |_| {});
 9592
 9593    let mut cx = EditorTestContext::new(cx).await;
 9594
 9595    let diff_base = r#"
 9596        use some::mod;
 9597
 9598        const A: u32 = 42;
 9599
 9600        fn main() {
 9601            println!("hello");
 9602
 9603            println!("world");
 9604        }
 9605        "#
 9606    .unindent();
 9607
 9608    // Edits are modified, removed, modified, added
 9609    cx.set_state(
 9610        &r#"
 9611        use some::modified;
 9612
 9613        ˇ
 9614        fn main() {
 9615            println!("hello there");
 9616
 9617            println!("around the");
 9618            println!("world");
 9619        }
 9620        "#
 9621        .unindent(),
 9622    );
 9623
 9624    cx.set_diff_base(Some(&diff_base));
 9625    executor.run_until_parked();
 9626
 9627    cx.update_editor(|editor, cx| {
 9628        //Wrap around the bottom of the buffer
 9629        for _ in 0..3 {
 9630            editor.go_to_next_hunk(&GoToHunk, cx);
 9631        }
 9632    });
 9633
 9634    cx.assert_editor_state(
 9635        &r#"
 9636        ˇuse some::modified;
 9637
 9638
 9639        fn main() {
 9640            println!("hello there");
 9641
 9642            println!("around the");
 9643            println!("world");
 9644        }
 9645        "#
 9646        .unindent(),
 9647    );
 9648
 9649    cx.update_editor(|editor, cx| {
 9650        //Wrap around the top of the buffer
 9651        for _ in 0..2 {
 9652            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9653        }
 9654    });
 9655
 9656    cx.assert_editor_state(
 9657        &r#"
 9658        use some::modified;
 9659
 9660
 9661        fn main() {
 9662        ˇ    println!("hello there");
 9663
 9664            println!("around the");
 9665            println!("world");
 9666        }
 9667        "#
 9668        .unindent(),
 9669    );
 9670
 9671    cx.update_editor(|editor, cx| {
 9672        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9673    });
 9674
 9675    cx.assert_editor_state(
 9676        &r#"
 9677        use some::modified;
 9678
 9679        ˇ
 9680        fn main() {
 9681            println!("hello there");
 9682
 9683            println!("around the");
 9684            println!("world");
 9685        }
 9686        "#
 9687        .unindent(),
 9688    );
 9689
 9690    cx.update_editor(|editor, cx| {
 9691        for _ in 0..3 {
 9692            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 9693        }
 9694    });
 9695
 9696    cx.assert_editor_state(
 9697        &r#"
 9698        use some::modified;
 9699
 9700
 9701        fn main() {
 9702        ˇ    println!("hello there");
 9703
 9704            println!("around the");
 9705            println!("world");
 9706        }
 9707        "#
 9708        .unindent(),
 9709    );
 9710
 9711    cx.update_editor(|editor, cx| {
 9712        editor.fold(&Fold, cx);
 9713
 9714        //Make sure that the fold only gets one hunk
 9715        for _ in 0..4 {
 9716            editor.go_to_next_hunk(&GoToHunk, cx);
 9717        }
 9718    });
 9719
 9720    cx.assert_editor_state(
 9721        &r#"
 9722        ˇuse some::modified;
 9723
 9724
 9725        fn main() {
 9726            println!("hello there");
 9727
 9728            println!("around the");
 9729            println!("world");
 9730        }
 9731        "#
 9732        .unindent(),
 9733    );
 9734}
 9735
 9736#[test]
 9737fn test_split_words() {
 9738    fn split(text: &str) -> Vec<&str> {
 9739        split_words(text).collect()
 9740    }
 9741
 9742    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 9743    assert_eq!(split("hello_world"), &["hello_", "world"]);
 9744    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 9745    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 9746    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 9747    assert_eq!(split("helloworld"), &["helloworld"]);
 9748
 9749    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 9750}
 9751
 9752#[gpui::test]
 9753async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 9754    init_test(cx, |_| {});
 9755
 9756    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 9757    let mut assert = |before, after| {
 9758        let _state_context = cx.set_state(before);
 9759        cx.update_editor(|editor, cx| {
 9760            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 9761        });
 9762        cx.assert_editor_state(after);
 9763    };
 9764
 9765    // Outside bracket jumps to outside of matching bracket
 9766    assert("console.logˇ(var);", "console.log(var)ˇ;");
 9767    assert("console.log(var)ˇ;", "console.logˇ(var);");
 9768
 9769    // Inside bracket jumps to inside of matching bracket
 9770    assert("console.log(ˇvar);", "console.log(varˇ);");
 9771    assert("console.log(varˇ);", "console.log(ˇvar);");
 9772
 9773    // When outside a bracket and inside, favor jumping to the inside bracket
 9774    assert(
 9775        "console.log('foo', [1, 2, 3]ˇ);",
 9776        "console.log(ˇ'foo', [1, 2, 3]);",
 9777    );
 9778    assert(
 9779        "console.log(ˇ'foo', [1, 2, 3]);",
 9780        "console.log('foo', [1, 2, 3]ˇ);",
 9781    );
 9782
 9783    // Bias forward if two options are equally likely
 9784    assert(
 9785        "let result = curried_fun()ˇ();",
 9786        "let result = curried_fun()()ˇ;",
 9787    );
 9788
 9789    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 9790    assert(
 9791        indoc! {"
 9792            function test() {
 9793                console.log('test')ˇ
 9794            }"},
 9795        indoc! {"
 9796            function test() {
 9797                console.logˇ('test')
 9798            }"},
 9799    );
 9800}
 9801
 9802#[gpui::test]
 9803async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 9804    init_test(cx, |_| {});
 9805
 9806    let fs = FakeFs::new(cx.executor());
 9807    fs.insert_tree(
 9808        "/a",
 9809        json!({
 9810            "main.rs": "fn main() { let a = 5; }",
 9811            "other.rs": "// Test file",
 9812        }),
 9813    )
 9814    .await;
 9815    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9816
 9817    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9818    language_registry.add(Arc::new(Language::new(
 9819        LanguageConfig {
 9820            name: "Rust".into(),
 9821            matcher: LanguageMatcher {
 9822                path_suffixes: vec!["rs".to_string()],
 9823                ..Default::default()
 9824            },
 9825            brackets: BracketPairConfig {
 9826                pairs: vec![BracketPair {
 9827                    start: "{".to_string(),
 9828                    end: "}".to_string(),
 9829                    close: true,
 9830                    surround: true,
 9831                    newline: true,
 9832                }],
 9833                disabled_scopes_by_bracket_ix: Vec::new(),
 9834            },
 9835            ..Default::default()
 9836        },
 9837        Some(tree_sitter_rust::LANGUAGE.into()),
 9838    )));
 9839    let mut fake_servers = language_registry.register_fake_lsp(
 9840        "Rust",
 9841        FakeLspAdapter {
 9842            capabilities: lsp::ServerCapabilities {
 9843                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 9844                    first_trigger_character: "{".to_string(),
 9845                    more_trigger_character: None,
 9846                }),
 9847                ..Default::default()
 9848            },
 9849            ..Default::default()
 9850        },
 9851    );
 9852
 9853    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9854
 9855    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9856
 9857    let worktree_id = workspace
 9858        .update(cx, |workspace, cx| {
 9859            workspace.project().update(cx, |project, cx| {
 9860                project.worktrees(cx).next().unwrap().read(cx).id()
 9861            })
 9862        })
 9863        .unwrap();
 9864
 9865    let buffer = project
 9866        .update(cx, |project, cx| {
 9867            project.open_local_buffer("/a/main.rs", cx)
 9868        })
 9869        .await
 9870        .unwrap();
 9871    cx.executor().run_until_parked();
 9872    cx.executor().start_waiting();
 9873    let fake_server = fake_servers.next().await.unwrap();
 9874    let editor_handle = workspace
 9875        .update(cx, |workspace, cx| {
 9876            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 9877        })
 9878        .unwrap()
 9879        .await
 9880        .unwrap()
 9881        .downcast::<Editor>()
 9882        .unwrap();
 9883
 9884    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 9885        assert_eq!(
 9886            params.text_document_position.text_document.uri,
 9887            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 9888        );
 9889        assert_eq!(
 9890            params.text_document_position.position,
 9891            lsp::Position::new(0, 21),
 9892        );
 9893
 9894        Ok(Some(vec![lsp::TextEdit {
 9895            new_text: "]".to_string(),
 9896            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 9897        }]))
 9898    });
 9899
 9900    editor_handle.update(cx, |editor, cx| {
 9901        editor.focus(cx);
 9902        editor.change_selections(None, cx, |s| {
 9903            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 9904        });
 9905        editor.handle_input("{", cx);
 9906    });
 9907
 9908    cx.executor().run_until_parked();
 9909
 9910    buffer.update(cx, |buffer, _| {
 9911        assert_eq!(
 9912            buffer.text(),
 9913            "fn main() { let a = {5}; }",
 9914            "No extra braces from on type formatting should appear in the buffer"
 9915        )
 9916    });
 9917}
 9918
 9919#[gpui::test]
 9920async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 9921    init_test(cx, |_| {});
 9922
 9923    let fs = FakeFs::new(cx.executor());
 9924    fs.insert_tree(
 9925        "/a",
 9926        json!({
 9927            "main.rs": "fn main() { let a = 5; }",
 9928            "other.rs": "// Test file",
 9929        }),
 9930    )
 9931    .await;
 9932
 9933    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9934
 9935    let server_restarts = Arc::new(AtomicUsize::new(0));
 9936    let closure_restarts = Arc::clone(&server_restarts);
 9937    let language_server_name = "test language server";
 9938    let language_name: LanguageName = "Rust".into();
 9939
 9940    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9941    language_registry.add(Arc::new(Language::new(
 9942        LanguageConfig {
 9943            name: language_name.clone(),
 9944            matcher: LanguageMatcher {
 9945                path_suffixes: vec!["rs".to_string()],
 9946                ..Default::default()
 9947            },
 9948            ..Default::default()
 9949        },
 9950        Some(tree_sitter_rust::LANGUAGE.into()),
 9951    )));
 9952    let mut fake_servers = language_registry.register_fake_lsp(
 9953        "Rust",
 9954        FakeLspAdapter {
 9955            name: language_server_name,
 9956            initialization_options: Some(json!({
 9957                "testOptionValue": true
 9958            })),
 9959            initializer: Some(Box::new(move |fake_server| {
 9960                let task_restarts = Arc::clone(&closure_restarts);
 9961                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 9962                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 9963                    futures::future::ready(Ok(()))
 9964                });
 9965            })),
 9966            ..Default::default()
 9967        },
 9968    );
 9969
 9970    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9971    let _buffer = project
 9972        .update(cx, |project, cx| {
 9973            project.open_local_buffer("/a/main.rs", cx)
 9974        })
 9975        .await
 9976        .unwrap();
 9977    let _fake_server = fake_servers.next().await.unwrap();
 9978    update_test_language_settings(cx, |language_settings| {
 9979        language_settings.languages.insert(
 9980            language_name.clone(),
 9981            LanguageSettingsContent {
 9982                tab_size: NonZeroU32::new(8),
 9983                ..Default::default()
 9984            },
 9985        );
 9986    });
 9987    cx.executor().run_until_parked();
 9988    assert_eq!(
 9989        server_restarts.load(atomic::Ordering::Acquire),
 9990        0,
 9991        "Should not restart LSP server on an unrelated change"
 9992    );
 9993
 9994    update_test_project_settings(cx, |project_settings| {
 9995        project_settings.lsp.insert(
 9996            "Some other server name".into(),
 9997            LspSettings {
 9998                binary: None,
 9999                settings: None,
10000                initialization_options: Some(json!({
10001                    "some other init value": false
10002                })),
10003            },
10004        );
10005    });
10006    cx.executor().run_until_parked();
10007    assert_eq!(
10008        server_restarts.load(atomic::Ordering::Acquire),
10009        0,
10010        "Should not restart LSP server on an unrelated LSP settings change"
10011    );
10012
10013    update_test_project_settings(cx, |project_settings| {
10014        project_settings.lsp.insert(
10015            language_server_name.into(),
10016            LspSettings {
10017                binary: None,
10018                settings: None,
10019                initialization_options: Some(json!({
10020                    "anotherInitValue": false
10021                })),
10022            },
10023        );
10024    });
10025    cx.executor().run_until_parked();
10026    assert_eq!(
10027        server_restarts.load(atomic::Ordering::Acquire),
10028        1,
10029        "Should restart LSP server on a related LSP settings change"
10030    );
10031
10032    update_test_project_settings(cx, |project_settings| {
10033        project_settings.lsp.insert(
10034            language_server_name.into(),
10035            LspSettings {
10036                binary: None,
10037                settings: None,
10038                initialization_options: Some(json!({
10039                    "anotherInitValue": false
10040                })),
10041            },
10042        );
10043    });
10044    cx.executor().run_until_parked();
10045    assert_eq!(
10046        server_restarts.load(atomic::Ordering::Acquire),
10047        1,
10048        "Should not restart LSP server on a related LSP settings change that is the same"
10049    );
10050
10051    update_test_project_settings(cx, |project_settings| {
10052        project_settings.lsp.insert(
10053            language_server_name.into(),
10054            LspSettings {
10055                binary: None,
10056                settings: None,
10057                initialization_options: None,
10058            },
10059        );
10060    });
10061    cx.executor().run_until_parked();
10062    assert_eq!(
10063        server_restarts.load(atomic::Ordering::Acquire),
10064        2,
10065        "Should restart LSP server on another related LSP settings change"
10066    );
10067}
10068
10069#[gpui::test]
10070async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10071    init_test(cx, |_| {});
10072
10073    let mut cx = EditorLspTestContext::new_rust(
10074        lsp::ServerCapabilities {
10075            completion_provider: Some(lsp::CompletionOptions {
10076                trigger_characters: Some(vec![".".to_string()]),
10077                resolve_provider: Some(true),
10078                ..Default::default()
10079            }),
10080            ..Default::default()
10081        },
10082        cx,
10083    )
10084    .await;
10085
10086    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10087    cx.simulate_keystroke(".");
10088    let completion_item = lsp::CompletionItem {
10089        label: "some".into(),
10090        kind: Some(lsp::CompletionItemKind::SNIPPET),
10091        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10092        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10093            kind: lsp::MarkupKind::Markdown,
10094            value: "```rust\nSome(2)\n```".to_string(),
10095        })),
10096        deprecated: Some(false),
10097        sort_text: Some("fffffff2".to_string()),
10098        filter_text: Some("some".to_string()),
10099        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10100        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10101            range: lsp::Range {
10102                start: lsp::Position {
10103                    line: 0,
10104                    character: 22,
10105                },
10106                end: lsp::Position {
10107                    line: 0,
10108                    character: 22,
10109                },
10110            },
10111            new_text: "Some(2)".to_string(),
10112        })),
10113        additional_text_edits: Some(vec![lsp::TextEdit {
10114            range: lsp::Range {
10115                start: lsp::Position {
10116                    line: 0,
10117                    character: 20,
10118                },
10119                end: lsp::Position {
10120                    line: 0,
10121                    character: 22,
10122                },
10123            },
10124            new_text: "".to_string(),
10125        }]),
10126        ..Default::default()
10127    };
10128
10129    let closure_completion_item = completion_item.clone();
10130    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10131        let task_completion_item = closure_completion_item.clone();
10132        async move {
10133            Ok(Some(lsp::CompletionResponse::Array(vec![
10134                task_completion_item,
10135            ])))
10136        }
10137    });
10138
10139    request.next().await;
10140
10141    cx.condition(|editor, _| editor.context_menu_visible())
10142        .await;
10143    let apply_additional_edits = cx.update_editor(|editor, cx| {
10144        editor
10145            .confirm_completion(&ConfirmCompletion::default(), cx)
10146            .unwrap()
10147    });
10148    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10149
10150    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10151        let task_completion_item = completion_item.clone();
10152        async move { Ok(task_completion_item) }
10153    })
10154    .next()
10155    .await
10156    .unwrap();
10157    apply_additional_edits.await.unwrap();
10158    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10159}
10160
10161#[gpui::test]
10162async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
10163    init_test(cx, |_| {});
10164
10165    let mut cx = EditorLspTestContext::new(
10166        Language::new(
10167            LanguageConfig {
10168                matcher: LanguageMatcher {
10169                    path_suffixes: vec!["jsx".into()],
10170                    ..Default::default()
10171                },
10172                overrides: [(
10173                    "element".into(),
10174                    LanguageConfigOverride {
10175                        word_characters: Override::Set(['-'].into_iter().collect()),
10176                        ..Default::default()
10177                    },
10178                )]
10179                .into_iter()
10180                .collect(),
10181                ..Default::default()
10182            },
10183            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10184        )
10185        .with_override_query("(jsx_self_closing_element) @element")
10186        .unwrap(),
10187        lsp::ServerCapabilities {
10188            completion_provider: Some(lsp::CompletionOptions {
10189                trigger_characters: Some(vec![":".to_string()]),
10190                ..Default::default()
10191            }),
10192            ..Default::default()
10193        },
10194        cx,
10195    )
10196    .await;
10197
10198    cx.lsp
10199        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10200            Ok(Some(lsp::CompletionResponse::Array(vec![
10201                lsp::CompletionItem {
10202                    label: "bg-blue".into(),
10203                    ..Default::default()
10204                },
10205                lsp::CompletionItem {
10206                    label: "bg-red".into(),
10207                    ..Default::default()
10208                },
10209                lsp::CompletionItem {
10210                    label: "bg-yellow".into(),
10211                    ..Default::default()
10212                },
10213            ])))
10214        });
10215
10216    cx.set_state(r#"<p class="bgˇ" />"#);
10217
10218    // Trigger completion when typing a dash, because the dash is an extra
10219    // word character in the 'element' scope, which contains the cursor.
10220    cx.simulate_keystroke("-");
10221    cx.executor().run_until_parked();
10222    cx.update_editor(|editor, _| {
10223        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10224            assert_eq!(
10225                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10226                &["bg-red", "bg-blue", "bg-yellow"]
10227            );
10228        } else {
10229            panic!("expected completion menu to be open");
10230        }
10231    });
10232
10233    cx.simulate_keystroke("l");
10234    cx.executor().run_until_parked();
10235    cx.update_editor(|editor, _| {
10236        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10237            assert_eq!(
10238                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10239                &["bg-blue", "bg-yellow"]
10240            );
10241        } else {
10242            panic!("expected completion menu to be open");
10243        }
10244    });
10245
10246    // When filtering completions, consider the character after the '-' to
10247    // be the start of a subword.
10248    cx.set_state(r#"<p class="yelˇ" />"#);
10249    cx.simulate_keystroke("l");
10250    cx.executor().run_until_parked();
10251    cx.update_editor(|editor, _| {
10252        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
10253            assert_eq!(
10254                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
10255                &["bg-yellow"]
10256            );
10257        } else {
10258            panic!("expected completion menu to be open");
10259        }
10260    });
10261}
10262
10263#[gpui::test]
10264async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
10265    init_test(cx, |settings| {
10266        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
10267            FormatterList(vec![Formatter::Prettier].into()),
10268        ))
10269    });
10270
10271    let fs = FakeFs::new(cx.executor());
10272    fs.insert_file("/file.ts", Default::default()).await;
10273
10274    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
10275    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10276
10277    language_registry.add(Arc::new(Language::new(
10278        LanguageConfig {
10279            name: "TypeScript".into(),
10280            matcher: LanguageMatcher {
10281                path_suffixes: vec!["ts".to_string()],
10282                ..Default::default()
10283            },
10284            ..Default::default()
10285        },
10286        Some(tree_sitter_rust::LANGUAGE.into()),
10287    )));
10288    update_test_language_settings(cx, |settings| {
10289        settings.defaults.prettier = Some(PrettierSettings {
10290            allowed: true,
10291            ..PrettierSettings::default()
10292        });
10293    });
10294
10295    let test_plugin = "test_plugin";
10296    let _ = language_registry.register_fake_lsp(
10297        "TypeScript",
10298        FakeLspAdapter {
10299            prettier_plugins: vec![test_plugin],
10300            ..Default::default()
10301        },
10302    );
10303
10304    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
10305    let buffer = project
10306        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
10307        .await
10308        .unwrap();
10309
10310    let buffer_text = "one\ntwo\nthree\n";
10311    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
10312    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
10313    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
10314
10315    editor
10316        .update(cx, |editor, cx| {
10317            editor.perform_format(
10318                project.clone(),
10319                FormatTrigger::Manual,
10320                FormatTarget::Buffer,
10321                cx,
10322            )
10323        })
10324        .unwrap()
10325        .await;
10326    assert_eq!(
10327        editor.update(cx, |editor, cx| editor.text(cx)),
10328        buffer_text.to_string() + prettier_format_suffix,
10329        "Test prettier formatting was not applied to the original buffer text",
10330    );
10331
10332    update_test_language_settings(cx, |settings| {
10333        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
10334    });
10335    let format = editor.update(cx, |editor, cx| {
10336        editor.perform_format(
10337            project.clone(),
10338            FormatTrigger::Manual,
10339            FormatTarget::Buffer,
10340            cx,
10341        )
10342    });
10343    format.await.unwrap();
10344    assert_eq!(
10345        editor.update(cx, |editor, cx| editor.text(cx)),
10346        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
10347        "Autoformatting (via test prettier) was not applied to the original buffer text",
10348    );
10349}
10350
10351#[gpui::test]
10352async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
10353    init_test(cx, |_| {});
10354    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10355    let base_text = indoc! {r#"struct Row;
10356struct Row1;
10357struct Row2;
10358
10359struct Row4;
10360struct Row5;
10361struct Row6;
10362
10363struct Row8;
10364struct Row9;
10365struct Row10;"#};
10366
10367    // When addition hunks are not adjacent to carets, no hunk revert is performed
10368    assert_hunk_revert(
10369        indoc! {r#"struct Row;
10370                   struct Row1;
10371                   struct Row1.1;
10372                   struct Row1.2;
10373                   struct Row2;ˇ
10374
10375                   struct Row4;
10376                   struct Row5;
10377                   struct Row6;
10378
10379                   struct Row8;
10380                   ˇstruct Row9;
10381                   struct Row9.1;
10382                   struct Row9.2;
10383                   struct Row9.3;
10384                   struct Row10;"#},
10385        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10386        indoc! {r#"struct Row;
10387                   struct Row1;
10388                   struct Row1.1;
10389                   struct Row1.2;
10390                   struct Row2;ˇ
10391
10392                   struct Row4;
10393                   struct Row5;
10394                   struct Row6;
10395
10396                   struct Row8;
10397                   ˇstruct Row9;
10398                   struct Row9.1;
10399                   struct Row9.2;
10400                   struct Row9.3;
10401                   struct Row10;"#},
10402        base_text,
10403        &mut cx,
10404    );
10405    // Same for selections
10406    assert_hunk_revert(
10407        indoc! {r#"struct Row;
10408                   struct Row1;
10409                   struct Row2;
10410                   struct Row2.1;
10411                   struct Row2.2;
10412                   «ˇ
10413                   struct Row4;
10414                   struct» Row5;
10415                   «struct Row6;
10416                   ˇ»
10417                   struct Row9.1;
10418                   struct Row9.2;
10419                   struct Row9.3;
10420                   struct Row8;
10421                   struct Row9;
10422                   struct Row10;"#},
10423        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
10424        indoc! {r#"struct Row;
10425                   struct Row1;
10426                   struct Row2;
10427                   struct Row2.1;
10428                   struct Row2.2;
10429                   «ˇ
10430                   struct Row4;
10431                   struct» Row5;
10432                   «struct Row6;
10433                   ˇ»
10434                   struct Row9.1;
10435                   struct Row9.2;
10436                   struct Row9.3;
10437                   struct Row8;
10438                   struct Row9;
10439                   struct Row10;"#},
10440        base_text,
10441        &mut cx,
10442    );
10443
10444    // When carets and selections intersect the addition hunks, those are reverted.
10445    // Adjacent carets got merged.
10446    assert_hunk_revert(
10447        indoc! {r#"struct Row;
10448                   ˇ// something on the top
10449                   struct Row1;
10450                   struct Row2;
10451                   struct Roˇw3.1;
10452                   struct Row2.2;
10453                   struct Row2.3;ˇ
10454
10455                   struct Row4;
10456                   struct ˇRow5.1;
10457                   struct Row5.2;
10458                   struct «Rowˇ»5.3;
10459                   struct Row5;
10460                   struct Row6;
10461                   ˇ
10462                   struct Row9.1;
10463                   struct «Rowˇ»9.2;
10464                   struct «ˇRow»9.3;
10465                   struct Row8;
10466                   struct Row9;
10467                   «ˇ// something on bottom»
10468                   struct Row10;"#},
10469        vec![
10470            DiffHunkStatus::Added,
10471            DiffHunkStatus::Added,
10472            DiffHunkStatus::Added,
10473            DiffHunkStatus::Added,
10474            DiffHunkStatus::Added,
10475        ],
10476        indoc! {r#"struct Row;
10477                   ˇstruct Row1;
10478                   struct Row2;
10479                   ˇ
10480                   struct Row4;
10481                   ˇstruct Row5;
10482                   struct Row6;
10483                   ˇ
10484                   ˇstruct Row8;
10485                   struct Row9;
10486                   ˇstruct Row10;"#},
10487        base_text,
10488        &mut cx,
10489    );
10490}
10491
10492#[gpui::test]
10493async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
10494    init_test(cx, |_| {});
10495    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10496    let base_text = indoc! {r#"struct Row;
10497struct Row1;
10498struct Row2;
10499
10500struct Row4;
10501struct Row5;
10502struct Row6;
10503
10504struct Row8;
10505struct Row9;
10506struct Row10;"#};
10507
10508    // Modification hunks behave the same as the addition ones.
10509    assert_hunk_revert(
10510        indoc! {r#"struct Row;
10511                   struct Row1;
10512                   struct Row33;
10513                   ˇ
10514                   struct Row4;
10515                   struct Row5;
10516                   struct Row6;
10517                   ˇ
10518                   struct Row99;
10519                   struct Row9;
10520                   struct Row10;"#},
10521        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
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        base_text,
10534        &mut cx,
10535    );
10536    assert_hunk_revert(
10537        indoc! {r#"struct Row;
10538                   struct Row1;
10539                   struct Row33;
10540                   «ˇ
10541                   struct Row4;
10542                   struct» Row5;
10543                   «struct Row6;
10544                   ˇ»
10545                   struct Row99;
10546                   struct Row9;
10547                   struct Row10;"#},
10548        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
10549        indoc! {r#"struct Row;
10550                   struct Row1;
10551                   struct Row33;
10552                   «ˇ
10553                   struct Row4;
10554                   struct» Row5;
10555                   «struct Row6;
10556                   ˇ»
10557                   struct Row99;
10558                   struct Row9;
10559                   struct Row10;"#},
10560        base_text,
10561        &mut cx,
10562    );
10563
10564    assert_hunk_revert(
10565        indoc! {r#"ˇstruct Row1.1;
10566                   struct Row1;
10567                   «ˇstr»uct Row22;
10568
10569                   struct ˇRow44;
10570                   struct Row5;
10571                   struct «Rˇ»ow66;ˇ
10572
10573                   «struˇ»ct Row88;
10574                   struct Row9;
10575                   struct Row1011;ˇ"#},
10576        vec![
10577            DiffHunkStatus::Modified,
10578            DiffHunkStatus::Modified,
10579            DiffHunkStatus::Modified,
10580            DiffHunkStatus::Modified,
10581            DiffHunkStatus::Modified,
10582            DiffHunkStatus::Modified,
10583        ],
10584        indoc! {r#"struct Row;
10585                   ˇstruct Row1;
10586                   struct Row2;
10587                   ˇ
10588                   struct Row4;
10589                   ˇstruct Row5;
10590                   struct Row6;
10591                   ˇ
10592                   struct Row8;
10593                   ˇstruct Row9;
10594                   struct Row10;ˇ"#},
10595        base_text,
10596        &mut cx,
10597    );
10598}
10599
10600#[gpui::test]
10601async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
10602    init_test(cx, |_| {});
10603    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10604    let base_text = indoc! {r#"struct Row;
10605struct Row1;
10606struct Row2;
10607
10608struct Row4;
10609struct Row5;
10610struct Row6;
10611
10612struct Row8;
10613struct Row9;
10614struct Row10;"#};
10615
10616    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
10617    assert_hunk_revert(
10618        indoc! {r#"struct Row;
10619                   struct Row2;
10620
10621                   ˇstruct Row4;
10622                   struct Row5;
10623                   struct Row6;
10624                   ˇ
10625                   struct Row8;
10626                   struct Row10;"#},
10627        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10628        indoc! {r#"struct Row;
10629                   struct Row2;
10630
10631                   ˇstruct Row4;
10632                   struct Row5;
10633                   struct Row6;
10634                   ˇ
10635                   struct Row8;
10636                   struct Row10;"#},
10637        base_text,
10638        &mut cx,
10639    );
10640    assert_hunk_revert(
10641        indoc! {r#"struct Row;
10642                   struct Row2;
10643
10644                   «ˇstruct Row4;
10645                   struct» Row5;
10646                   «struct Row6;
10647                   ˇ»
10648                   struct Row8;
10649                   struct Row10;"#},
10650        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
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        base_text,
10661        &mut cx,
10662    );
10663
10664    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
10665    assert_hunk_revert(
10666        indoc! {r#"struct Row;
10667                   ˇstruct Row2;
10668
10669                   struct Row4;
10670                   struct Row5;
10671                   struct Row6;
10672
10673                   struct Row8;ˇ
10674                   struct Row10;"#},
10675        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
10676        indoc! {r#"struct Row;
10677                   struct Row1;
10678                   ˇstruct Row2;
10679
10680                   struct Row4;
10681                   struct Row5;
10682                   struct Row6;
10683
10684                   struct Row8;ˇ
10685                   struct Row9;
10686                   struct Row10;"#},
10687        base_text,
10688        &mut cx,
10689    );
10690    assert_hunk_revert(
10691        indoc! {r#"struct Row;
10692                   struct Row2«ˇ;
10693                   struct Row4;
10694                   struct» Row5;
10695                   «struct Row6;
10696
10697                   struct Row8;ˇ»
10698                   struct Row10;"#},
10699        vec![
10700            DiffHunkStatus::Removed,
10701            DiffHunkStatus::Removed,
10702            DiffHunkStatus::Removed,
10703        ],
10704        indoc! {r#"struct Row;
10705                   struct Row1;
10706                   struct Row2«ˇ;
10707
10708                   struct Row4;
10709                   struct» Row5;
10710                   «struct Row6;
10711
10712                   struct Row8;ˇ»
10713                   struct Row9;
10714                   struct Row10;"#},
10715        base_text,
10716        &mut cx,
10717    );
10718}
10719
10720#[gpui::test]
10721async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
10722    init_test(cx, |_| {});
10723
10724    let cols = 4;
10725    let rows = 10;
10726    let sample_text_1 = sample_text(rows, cols, 'a');
10727    assert_eq!(
10728        sample_text_1,
10729        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10730    );
10731    let sample_text_2 = sample_text(rows, cols, 'l');
10732    assert_eq!(
10733        sample_text_2,
10734        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10735    );
10736    let sample_text_3 = sample_text(rows, cols, 'v');
10737    assert_eq!(
10738        sample_text_3,
10739        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10740    );
10741
10742    fn diff_every_buffer_row(
10743        buffer: &Model<Buffer>,
10744        sample_text: String,
10745        cols: usize,
10746        cx: &mut gpui::TestAppContext,
10747    ) {
10748        // revert first character in each row, creating one large diff hunk per buffer
10749        let is_first_char = |offset: usize| offset % cols == 0;
10750        buffer.update(cx, |buffer, cx| {
10751            buffer.set_text(
10752                sample_text
10753                    .chars()
10754                    .enumerate()
10755                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
10756                    .collect::<String>(),
10757                cx,
10758            );
10759            buffer.set_diff_base(Some(sample_text), cx);
10760        });
10761        cx.executor().run_until_parked();
10762    }
10763
10764    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10765    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10766
10767    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10768    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10769
10770    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10771    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10772
10773    let multibuffer = cx.new_model(|cx| {
10774        let mut multibuffer = MultiBuffer::new(ReadWrite);
10775        multibuffer.push_excerpts(
10776            buffer_1.clone(),
10777            [
10778                ExcerptRange {
10779                    context: Point::new(0, 0)..Point::new(3, 0),
10780                    primary: None,
10781                },
10782                ExcerptRange {
10783                    context: Point::new(5, 0)..Point::new(7, 0),
10784                    primary: None,
10785                },
10786                ExcerptRange {
10787                    context: Point::new(9, 0)..Point::new(10, 4),
10788                    primary: None,
10789                },
10790            ],
10791            cx,
10792        );
10793        multibuffer.push_excerpts(
10794            buffer_2.clone(),
10795            [
10796                ExcerptRange {
10797                    context: Point::new(0, 0)..Point::new(3, 0),
10798                    primary: None,
10799                },
10800                ExcerptRange {
10801                    context: Point::new(5, 0)..Point::new(7, 0),
10802                    primary: None,
10803                },
10804                ExcerptRange {
10805                    context: Point::new(9, 0)..Point::new(10, 4),
10806                    primary: None,
10807                },
10808            ],
10809            cx,
10810        );
10811        multibuffer.push_excerpts(
10812            buffer_3.clone(),
10813            [
10814                ExcerptRange {
10815                    context: Point::new(0, 0)..Point::new(3, 0),
10816                    primary: None,
10817                },
10818                ExcerptRange {
10819                    context: Point::new(5, 0)..Point::new(7, 0),
10820                    primary: None,
10821                },
10822                ExcerptRange {
10823                    context: Point::new(9, 0)..Point::new(10, 4),
10824                    primary: None,
10825                },
10826            ],
10827            cx,
10828        );
10829        multibuffer
10830    });
10831
10832    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
10833    editor.update(cx, |editor, cx| {
10834        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");
10835        editor.select_all(&SelectAll, cx);
10836        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10837    });
10838    cx.executor().run_until_parked();
10839    // When all ranges are selected, all buffer hunks are reverted.
10840    editor.update(cx, |editor, cx| {
10841        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");
10842    });
10843    buffer_1.update(cx, |buffer, _| {
10844        assert_eq!(buffer.text(), sample_text_1);
10845    });
10846    buffer_2.update(cx, |buffer, _| {
10847        assert_eq!(buffer.text(), sample_text_2);
10848    });
10849    buffer_3.update(cx, |buffer, _| {
10850        assert_eq!(buffer.text(), sample_text_3);
10851    });
10852
10853    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
10854    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
10855    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
10856    editor.update(cx, |editor, cx| {
10857        editor.change_selections(None, cx, |s| {
10858            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
10859        });
10860        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
10861    });
10862    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
10863    // but not affect buffer_2 and its related excerpts.
10864    editor.update(cx, |editor, cx| {
10865        assert_eq!(
10866            editor.text(cx),
10867            "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"
10868        );
10869    });
10870    buffer_1.update(cx, |buffer, _| {
10871        assert_eq!(buffer.text(), sample_text_1);
10872    });
10873    buffer_2.update(cx, |buffer, _| {
10874        assert_eq!(
10875            buffer.text(),
10876            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
10877        );
10878    });
10879    buffer_3.update(cx, |buffer, _| {
10880        assert_eq!(
10881            buffer.text(),
10882            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
10883        );
10884    });
10885}
10886
10887#[gpui::test]
10888async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
10889    init_test(cx, |_| {});
10890
10891    let cols = 4;
10892    let rows = 10;
10893    let sample_text_1 = sample_text(rows, cols, 'a');
10894    assert_eq!(
10895        sample_text_1,
10896        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10897    );
10898    let sample_text_2 = sample_text(rows, cols, 'l');
10899    assert_eq!(
10900        sample_text_2,
10901        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10902    );
10903    let sample_text_3 = sample_text(rows, cols, 'v');
10904    assert_eq!(
10905        sample_text_3,
10906        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10907    );
10908
10909    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
10910    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
10911    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
10912
10913    let multi_buffer = cx.new_model(|cx| {
10914        let mut multibuffer = MultiBuffer::new(ReadWrite);
10915        multibuffer.push_excerpts(
10916            buffer_1.clone(),
10917            [
10918                ExcerptRange {
10919                    context: Point::new(0, 0)..Point::new(3, 0),
10920                    primary: None,
10921                },
10922                ExcerptRange {
10923                    context: Point::new(5, 0)..Point::new(7, 0),
10924                    primary: None,
10925                },
10926                ExcerptRange {
10927                    context: Point::new(9, 0)..Point::new(10, 4),
10928                    primary: None,
10929                },
10930            ],
10931            cx,
10932        );
10933        multibuffer.push_excerpts(
10934            buffer_2.clone(),
10935            [
10936                ExcerptRange {
10937                    context: Point::new(0, 0)..Point::new(3, 0),
10938                    primary: None,
10939                },
10940                ExcerptRange {
10941                    context: Point::new(5, 0)..Point::new(7, 0),
10942                    primary: None,
10943                },
10944                ExcerptRange {
10945                    context: Point::new(9, 0)..Point::new(10, 4),
10946                    primary: None,
10947                },
10948            ],
10949            cx,
10950        );
10951        multibuffer.push_excerpts(
10952            buffer_3.clone(),
10953            [
10954                ExcerptRange {
10955                    context: Point::new(0, 0)..Point::new(3, 0),
10956                    primary: None,
10957                },
10958                ExcerptRange {
10959                    context: Point::new(5, 0)..Point::new(7, 0),
10960                    primary: None,
10961                },
10962                ExcerptRange {
10963                    context: Point::new(9, 0)..Point::new(10, 4),
10964                    primary: None,
10965                },
10966            ],
10967            cx,
10968        );
10969        multibuffer
10970    });
10971
10972    let fs = FakeFs::new(cx.executor());
10973    fs.insert_tree(
10974        "/a",
10975        json!({
10976            "main.rs": sample_text_1,
10977            "other.rs": sample_text_2,
10978            "lib.rs": sample_text_3,
10979        }),
10980    )
10981    .await;
10982    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10983    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10984    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10985    let multi_buffer_editor = cx.new_view(|cx| {
10986        Editor::new(
10987            EditorMode::Full,
10988            multi_buffer,
10989            Some(project.clone()),
10990            true,
10991            cx,
10992        )
10993    });
10994    let multibuffer_item_id = workspace
10995        .update(cx, |workspace, cx| {
10996            assert!(
10997                workspace.active_item(cx).is_none(),
10998                "active item should be None before the first item is added"
10999            );
11000            workspace.add_item_to_active_pane(
11001                Box::new(multi_buffer_editor.clone()),
11002                None,
11003                true,
11004                cx,
11005            );
11006            let active_item = workspace
11007                .active_item(cx)
11008                .expect("should have an active item after adding the multi buffer");
11009            assert!(
11010                !active_item.is_singleton(cx),
11011                "A multi buffer was expected to active after adding"
11012            );
11013            active_item.item_id()
11014        })
11015        .unwrap();
11016    cx.executor().run_until_parked();
11017
11018    multi_buffer_editor.update(cx, |editor, cx| {
11019        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
11020        editor.open_excerpts(&OpenExcerpts, cx);
11021    });
11022    cx.executor().run_until_parked();
11023    let first_item_id = workspace
11024        .update(cx, |workspace, cx| {
11025            let active_item = workspace
11026                .active_item(cx)
11027                .expect("should have an active item after navigating into the 1st buffer");
11028            let first_item_id = active_item.item_id();
11029            assert_ne!(
11030                first_item_id, multibuffer_item_id,
11031                "Should navigate into the 1st buffer and activate it"
11032            );
11033            assert!(
11034                active_item.is_singleton(cx),
11035                "New active item should be a singleton buffer"
11036            );
11037            assert_eq!(
11038                active_item
11039                    .act_as::<Editor>(cx)
11040                    .expect("should have navigated into an editor for the 1st buffer")
11041                    .read(cx)
11042                    .text(cx),
11043                sample_text_1
11044            );
11045
11046            workspace
11047                .go_back(workspace.active_pane().downgrade(), cx)
11048                .detach_and_log_err(cx);
11049
11050            first_item_id
11051        })
11052        .unwrap();
11053    cx.executor().run_until_parked();
11054    workspace
11055        .update(cx, |workspace, cx| {
11056            let active_item = workspace
11057                .active_item(cx)
11058                .expect("should have an active item after navigating back");
11059            assert_eq!(
11060                active_item.item_id(),
11061                multibuffer_item_id,
11062                "Should navigate back to the multi buffer"
11063            );
11064            assert!(!active_item.is_singleton(cx));
11065        })
11066        .unwrap();
11067
11068    multi_buffer_editor.update(cx, |editor, cx| {
11069        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11070            s.select_ranges(Some(39..40))
11071        });
11072        editor.open_excerpts(&OpenExcerpts, cx);
11073    });
11074    cx.executor().run_until_parked();
11075    let second_item_id = workspace
11076        .update(cx, |workspace, cx| {
11077            let active_item = workspace
11078                .active_item(cx)
11079                .expect("should have an active item after navigating into the 2nd buffer");
11080            let second_item_id = active_item.item_id();
11081            assert_ne!(
11082                second_item_id, multibuffer_item_id,
11083                "Should navigate away from the multibuffer"
11084            );
11085            assert_ne!(
11086                second_item_id, first_item_id,
11087                "Should navigate into the 2nd buffer and activate it"
11088            );
11089            assert!(
11090                active_item.is_singleton(cx),
11091                "New active item should be a singleton buffer"
11092            );
11093            assert_eq!(
11094                active_item
11095                    .act_as::<Editor>(cx)
11096                    .expect("should have navigated into an editor")
11097                    .read(cx)
11098                    .text(cx),
11099                sample_text_2
11100            );
11101
11102            workspace
11103                .go_back(workspace.active_pane().downgrade(), cx)
11104                .detach_and_log_err(cx);
11105
11106            second_item_id
11107        })
11108        .unwrap();
11109    cx.executor().run_until_parked();
11110    workspace
11111        .update(cx, |workspace, cx| {
11112            let active_item = workspace
11113                .active_item(cx)
11114                .expect("should have an active item after navigating back from the 2nd buffer");
11115            assert_eq!(
11116                active_item.item_id(),
11117                multibuffer_item_id,
11118                "Should navigate back from the 2nd buffer to the multi buffer"
11119            );
11120            assert!(!active_item.is_singleton(cx));
11121        })
11122        .unwrap();
11123
11124    multi_buffer_editor.update(cx, |editor, cx| {
11125        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
11126            s.select_ranges(Some(60..70))
11127        });
11128        editor.open_excerpts(&OpenExcerpts, cx);
11129    });
11130    cx.executor().run_until_parked();
11131    workspace
11132        .update(cx, |workspace, cx| {
11133            let active_item = workspace
11134                .active_item(cx)
11135                .expect("should have an active item after navigating into the 3rd buffer");
11136            let third_item_id = active_item.item_id();
11137            assert_ne!(
11138                third_item_id, multibuffer_item_id,
11139                "Should navigate into the 3rd buffer and activate it"
11140            );
11141            assert_ne!(third_item_id, first_item_id);
11142            assert_ne!(third_item_id, second_item_id);
11143            assert!(
11144                active_item.is_singleton(cx),
11145                "New active item should be a singleton buffer"
11146            );
11147            assert_eq!(
11148                active_item
11149                    .act_as::<Editor>(cx)
11150                    .expect("should have navigated into an editor")
11151                    .read(cx)
11152                    .text(cx),
11153                sample_text_3
11154            );
11155
11156            workspace
11157                .go_back(workspace.active_pane().downgrade(), cx)
11158                .detach_and_log_err(cx);
11159        })
11160        .unwrap();
11161    cx.executor().run_until_parked();
11162    workspace
11163        .update(cx, |workspace, cx| {
11164            let active_item = workspace
11165                .active_item(cx)
11166                .expect("should have an active item after navigating back from the 3rd buffer");
11167            assert_eq!(
11168                active_item.item_id(),
11169                multibuffer_item_id,
11170                "Should navigate back from the 3rd buffer to the multi buffer"
11171            );
11172            assert!(!active_item.is_singleton(cx));
11173        })
11174        .unwrap();
11175}
11176
11177#[gpui::test]
11178async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11179    init_test(cx, |_| {});
11180
11181    let mut cx = EditorTestContext::new(cx).await;
11182
11183    let diff_base = r#"
11184        use some::mod;
11185
11186        const A: u32 = 42;
11187
11188        fn main() {
11189            println!("hello");
11190
11191            println!("world");
11192        }
11193        "#
11194    .unindent();
11195
11196    cx.set_state(
11197        &r#"
11198        use some::modified;
11199
11200        ˇ
11201        fn main() {
11202            println!("hello there");
11203
11204            println!("around the");
11205            println!("world");
11206        }
11207        "#
11208        .unindent(),
11209    );
11210
11211    cx.set_diff_base(Some(&diff_base));
11212    executor.run_until_parked();
11213
11214    cx.update_editor(|editor, cx| {
11215        editor.go_to_next_hunk(&GoToHunk, cx);
11216        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11217    });
11218    executor.run_until_parked();
11219    cx.assert_diff_hunks(
11220        r#"
11221          use some::modified;
11222
11223
11224          fn main() {
11225        -     println!("hello");
11226        +     println!("hello there");
11227
11228              println!("around the");
11229              println!("world");
11230          }
11231        "#
11232        .unindent(),
11233    );
11234
11235    cx.update_editor(|editor, cx| {
11236        for _ in 0..3 {
11237            editor.go_to_next_hunk(&GoToHunk, cx);
11238            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11239        }
11240    });
11241    executor.run_until_parked();
11242    cx.assert_editor_state(
11243        &r#"
11244        use some::modified;
11245
11246        ˇ
11247        fn main() {
11248            println!("hello there");
11249
11250            println!("around the");
11251            println!("world");
11252        }
11253        "#
11254        .unindent(),
11255    );
11256
11257    cx.assert_diff_hunks(
11258        r#"
11259        - use some::mod;
11260        + use some::modified;
11261
11262        - const A: u32 = 42;
11263
11264          fn main() {
11265        -     println!("hello");
11266        +     println!("hello there");
11267
11268        +     println!("around the");
11269              println!("world");
11270          }
11271        "#
11272        .unindent(),
11273    );
11274
11275    cx.update_editor(|editor, cx| {
11276        editor.cancel(&Cancel, cx);
11277    });
11278
11279    cx.assert_diff_hunks(
11280        r#"
11281          use some::modified;
11282
11283
11284          fn main() {
11285              println!("hello there");
11286
11287              println!("around the");
11288              println!("world");
11289          }
11290        "#
11291        .unindent(),
11292    );
11293}
11294
11295#[gpui::test]
11296async fn test_diff_base_change_with_expanded_diff_hunks(
11297    executor: BackgroundExecutor,
11298    cx: &mut gpui::TestAppContext,
11299) {
11300    init_test(cx, |_| {});
11301
11302    let mut cx = EditorTestContext::new(cx).await;
11303
11304    let diff_base = r#"
11305        use some::mod1;
11306        use some::mod2;
11307
11308        const A: u32 = 42;
11309        const B: u32 = 42;
11310        const C: u32 = 42;
11311
11312        fn main() {
11313            println!("hello");
11314
11315            println!("world");
11316        }
11317        "#
11318    .unindent();
11319
11320    cx.set_state(
11321        &r#"
11322        use some::mod2;
11323
11324        const A: u32 = 42;
11325        const C: u32 = 42;
11326
11327        fn main(ˇ) {
11328            //println!("hello");
11329
11330            println!("world");
11331            //
11332            //
11333        }
11334        "#
11335        .unindent(),
11336    );
11337
11338    cx.set_diff_base(Some(&diff_base));
11339    executor.run_until_parked();
11340
11341    cx.update_editor(|editor, cx| {
11342        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11343    });
11344    executor.run_until_parked();
11345    cx.assert_diff_hunks(
11346        r#"
11347        - use some::mod1;
11348          use some::mod2;
11349
11350          const A: u32 = 42;
11351        - const B: u32 = 42;
11352          const C: u32 = 42;
11353
11354          fn main() {
11355        -     println!("hello");
11356        +     //println!("hello");
11357
11358              println!("world");
11359        +     //
11360        +     //
11361          }
11362        "#
11363        .unindent(),
11364    );
11365
11366    cx.set_diff_base(Some("new diff base!"));
11367    executor.run_until_parked();
11368    cx.assert_diff_hunks(
11369        r#"
11370          use some::mod2;
11371
11372          const A: u32 = 42;
11373          const C: u32 = 42;
11374
11375          fn main() {
11376              //println!("hello");
11377
11378              println!("world");
11379              //
11380              //
11381          }
11382        "#
11383        .unindent(),
11384    );
11385
11386    cx.update_editor(|editor, cx| {
11387        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11388    });
11389    executor.run_until_parked();
11390    cx.assert_diff_hunks(
11391        r#"
11392        - new diff base!
11393        + use some::mod2;
11394        +
11395        + const A: u32 = 42;
11396        + const C: u32 = 42;
11397        +
11398        + fn main() {
11399        +     //println!("hello");
11400        +
11401        +     println!("world");
11402        +     //
11403        +     //
11404        + }
11405        "#
11406        .unindent(),
11407    );
11408}
11409
11410#[gpui::test]
11411async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
11412    init_test(cx, |_| {});
11413
11414    let mut cx = EditorTestContext::new(cx).await;
11415
11416    let diff_base = r#"
11417        use some::mod1;
11418        use some::mod2;
11419
11420        const A: u32 = 42;
11421        const B: u32 = 42;
11422        const C: u32 = 42;
11423
11424        fn main() {
11425            println!("hello");
11426
11427            println!("world");
11428        }
11429
11430        fn another() {
11431            println!("another");
11432        }
11433
11434        fn another2() {
11435            println!("another2");
11436        }
11437        "#
11438    .unindent();
11439
11440    cx.set_state(
11441        &r#"
11442        «use some::mod2;
11443
11444        const A: u32 = 42;
11445        const C: u32 = 42;
11446
11447        fn main() {
11448            //println!("hello");
11449
11450            println!("world");
11451            //
11452            //ˇ»
11453        }
11454
11455        fn another() {
11456            println!("another");
11457            println!("another");
11458        }
11459
11460            println!("another2");
11461        }
11462        "#
11463        .unindent(),
11464    );
11465
11466    cx.set_diff_base(Some(&diff_base));
11467    executor.run_until_parked();
11468
11469    cx.update_editor(|editor, cx| {
11470        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11471    });
11472    executor.run_until_parked();
11473
11474    cx.assert_diff_hunks(
11475        r#"
11476        - use some::mod1;
11477          use some::mod2;
11478
11479          const A: u32 = 42;
11480        - const B: u32 = 42;
11481          const C: u32 = 42;
11482
11483          fn main() {
11484        -     println!("hello");
11485        +     //println!("hello");
11486
11487              println!("world");
11488        +     //
11489        +     //
11490          }
11491
11492          fn another() {
11493              println!("another");
11494        +     println!("another");
11495          }
11496
11497        - fn another2() {
11498              println!("another2");
11499          }
11500        "#
11501        .unindent(),
11502    );
11503
11504    // Fold across some of the diff hunks. They should no longer appear expanded.
11505    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
11506    cx.executor().run_until_parked();
11507
11508    // Hunks are not shown if their position is within a fold
11509    cx.assert_diff_hunks(
11510        r#"
11511          use some::mod2;
11512
11513          const A: u32 = 42;
11514          const C: u32 = 42;
11515
11516          fn main() {
11517              //println!("hello");
11518
11519              println!("world");
11520              //
11521              //
11522          }
11523
11524          fn another() {
11525              println!("another");
11526        +     println!("another");
11527          }
11528
11529        - fn another2() {
11530              println!("another2");
11531          }
11532        "#
11533        .unindent(),
11534    );
11535
11536    cx.update_editor(|editor, cx| {
11537        editor.select_all(&SelectAll, cx);
11538        editor.unfold_lines(&UnfoldLines, cx);
11539    });
11540    cx.executor().run_until_parked();
11541
11542    // The deletions reappear when unfolding.
11543    cx.assert_diff_hunks(
11544        r#"
11545        - use some::mod1;
11546          use some::mod2;
11547
11548          const A: u32 = 42;
11549        - const B: u32 = 42;
11550          const C: u32 = 42;
11551
11552          fn main() {
11553        -     println!("hello");
11554        +     //println!("hello");
11555
11556              println!("world");
11557        +     //
11558        +     //
11559          }
11560
11561          fn another() {
11562              println!("another");
11563        +     println!("another");
11564          }
11565
11566        - fn another2() {
11567              println!("another2");
11568          }
11569        "#
11570        .unindent(),
11571    );
11572}
11573
11574#[gpui::test]
11575async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
11576    init_test(cx, |_| {});
11577
11578    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11579    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
11580    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11581    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
11582    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
11583    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
11584
11585    let buffer_1 = cx.new_model(|cx| {
11586        let mut buffer = Buffer::local(file_1_new.to_string(), cx);
11587        buffer.set_diff_base(Some(file_1_old.into()), cx);
11588        buffer
11589    });
11590    let buffer_2 = cx.new_model(|cx| {
11591        let mut buffer = Buffer::local(file_2_new.to_string(), cx);
11592        buffer.set_diff_base(Some(file_2_old.into()), cx);
11593        buffer
11594    });
11595    let buffer_3 = cx.new_model(|cx| {
11596        let mut buffer = Buffer::local(file_3_new.to_string(), cx);
11597        buffer.set_diff_base(Some(file_3_old.into()), cx);
11598        buffer
11599    });
11600
11601    let multi_buffer = cx.new_model(|cx| {
11602        let mut multibuffer = MultiBuffer::new(ReadWrite);
11603        multibuffer.push_excerpts(
11604            buffer_1.clone(),
11605            [
11606                ExcerptRange {
11607                    context: Point::new(0, 0)..Point::new(3, 0),
11608                    primary: None,
11609                },
11610                ExcerptRange {
11611                    context: Point::new(5, 0)..Point::new(7, 0),
11612                    primary: None,
11613                },
11614                ExcerptRange {
11615                    context: Point::new(9, 0)..Point::new(10, 3),
11616                    primary: None,
11617                },
11618            ],
11619            cx,
11620        );
11621        multibuffer.push_excerpts(
11622            buffer_2.clone(),
11623            [
11624                ExcerptRange {
11625                    context: Point::new(0, 0)..Point::new(3, 0),
11626                    primary: None,
11627                },
11628                ExcerptRange {
11629                    context: Point::new(5, 0)..Point::new(7, 0),
11630                    primary: None,
11631                },
11632                ExcerptRange {
11633                    context: Point::new(9, 0)..Point::new(10, 3),
11634                    primary: None,
11635                },
11636            ],
11637            cx,
11638        );
11639        multibuffer.push_excerpts(
11640            buffer_3.clone(),
11641            [
11642                ExcerptRange {
11643                    context: Point::new(0, 0)..Point::new(3, 0),
11644                    primary: None,
11645                },
11646                ExcerptRange {
11647                    context: Point::new(5, 0)..Point::new(7, 0),
11648                    primary: None,
11649                },
11650                ExcerptRange {
11651                    context: Point::new(9, 0)..Point::new(10, 3),
11652                    primary: None,
11653                },
11654            ],
11655            cx,
11656        );
11657        multibuffer
11658    });
11659
11660    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11661    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11662    cx.run_until_parked();
11663
11664    cx.assert_editor_state(
11665        &"
11666            ˇaaa
11667            ccc
11668            ddd
11669
11670            ggg
11671            hhh
11672
11673
11674            lll
11675            mmm
11676            NNN
11677
11678            qqq
11679            rrr
11680
11681            uuu
11682            111
11683            222
11684            333
11685
11686            666
11687            777
11688
11689            000
11690            !!!"
11691        .unindent(),
11692    );
11693
11694    cx.update_editor(|editor, cx| {
11695        editor.select_all(&SelectAll, cx);
11696        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
11697    });
11698    cx.executor().run_until_parked();
11699
11700    cx.assert_diff_hunks(
11701        "
11702            aaa
11703          - bbb
11704            ccc
11705            ddd
11706
11707            ggg
11708            hhh
11709
11710
11711            lll
11712            mmm
11713          - nnn
11714          + NNN
11715
11716            qqq
11717            rrr
11718
11719            uuu
11720            111
11721            222
11722            333
11723
11724          + 666
11725            777
11726
11727            000
11728            !!!"
11729        .unindent(),
11730    );
11731}
11732
11733#[gpui::test]
11734async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
11735    init_test(cx, |_| {});
11736
11737    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
11738    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
11739
11740    let buffer = cx.new_model(|cx| {
11741        let mut buffer = Buffer::local(text.to_string(), cx);
11742        buffer.set_diff_base(Some(base.into()), cx);
11743        buffer
11744    });
11745
11746    let multi_buffer = cx.new_model(|cx| {
11747        let mut multibuffer = MultiBuffer::new(ReadWrite);
11748        multibuffer.push_excerpts(
11749            buffer.clone(),
11750            [
11751                ExcerptRange {
11752                    context: Point::new(0, 0)..Point::new(2, 0),
11753                    primary: None,
11754                },
11755                ExcerptRange {
11756                    context: Point::new(5, 0)..Point::new(7, 0),
11757                    primary: None,
11758                },
11759            ],
11760            cx,
11761        );
11762        multibuffer
11763    });
11764
11765    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
11766    let mut cx = EditorTestContext::for_editor(editor, cx).await;
11767    cx.run_until_parked();
11768
11769    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
11770    cx.executor().run_until_parked();
11771
11772    cx.assert_diff_hunks(
11773        "
11774            aaa
11775          - bbb
11776          + BBB
11777
11778          - ddd
11779          - eee
11780          + EEE
11781            fff
11782        "
11783        .unindent(),
11784    );
11785}
11786
11787#[gpui::test]
11788async fn test_edits_around_expanded_insertion_hunks(
11789    executor: BackgroundExecutor,
11790    cx: &mut gpui::TestAppContext,
11791) {
11792    init_test(cx, |_| {});
11793
11794    let mut cx = EditorTestContext::new(cx).await;
11795
11796    let diff_base = r#"
11797        use some::mod1;
11798        use some::mod2;
11799
11800        const A: u32 = 42;
11801
11802        fn main() {
11803            println!("hello");
11804
11805            println!("world");
11806        }
11807        "#
11808    .unindent();
11809    executor.run_until_parked();
11810    cx.set_state(
11811        &r#"
11812        use some::mod1;
11813        use some::mod2;
11814
11815        const A: u32 = 42;
11816        const B: u32 = 42;
11817        const C: u32 = 42;
11818        ˇ
11819
11820        fn main() {
11821            println!("hello");
11822
11823            println!("world");
11824        }
11825        "#
11826        .unindent(),
11827    );
11828
11829    cx.set_diff_base(Some(&diff_base));
11830    executor.run_until_parked();
11831
11832    cx.update_editor(|editor, cx| {
11833        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11834    });
11835    executor.run_until_parked();
11836
11837    cx.assert_diff_hunks(
11838        r#"
11839        use some::mod1;
11840        use some::mod2;
11841
11842        const A: u32 = 42;
11843      + const B: u32 = 42;
11844      + const C: u32 = 42;
11845      +
11846
11847        fn main() {
11848            println!("hello");
11849
11850            println!("world");
11851        }
11852        "#
11853        .unindent(),
11854    );
11855
11856    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
11857    executor.run_until_parked();
11858
11859    cx.assert_diff_hunks(
11860        r#"
11861        use some::mod1;
11862        use some::mod2;
11863
11864        const A: u32 = 42;
11865      + const B: u32 = 42;
11866      + const C: u32 = 42;
11867      + const D: u32 = 42;
11868      +
11869
11870        fn main() {
11871            println!("hello");
11872
11873            println!("world");
11874        }
11875        "#
11876        .unindent(),
11877    );
11878
11879    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
11880    executor.run_until_parked();
11881
11882    cx.assert_diff_hunks(
11883        r#"
11884        use some::mod1;
11885        use some::mod2;
11886
11887        const A: u32 = 42;
11888      + const B: u32 = 42;
11889      + const C: u32 = 42;
11890      + const D: u32 = 42;
11891      + const E: u32 = 42;
11892      +
11893
11894        fn main() {
11895            println!("hello");
11896
11897            println!("world");
11898        }
11899        "#
11900        .unindent(),
11901    );
11902
11903    cx.update_editor(|editor, cx| {
11904        editor.delete_line(&DeleteLine, cx);
11905    });
11906    executor.run_until_parked();
11907
11908    cx.assert_diff_hunks(
11909        r#"
11910        use some::mod1;
11911        use some::mod2;
11912
11913        const A: u32 = 42;
11914      + const B: u32 = 42;
11915      + const C: u32 = 42;
11916      + const D: u32 = 42;
11917      + const E: u32 = 42;
11918
11919        fn main() {
11920            println!("hello");
11921
11922            println!("world");
11923        }
11924        "#
11925        .unindent(),
11926    );
11927
11928    cx.update_editor(|editor, cx| {
11929        editor.move_up(&MoveUp, cx);
11930        editor.delete_line(&DeleteLine, cx);
11931        editor.move_up(&MoveUp, cx);
11932        editor.delete_line(&DeleteLine, cx);
11933        editor.move_up(&MoveUp, cx);
11934        editor.delete_line(&DeleteLine, cx);
11935    });
11936    executor.run_until_parked();
11937    cx.assert_editor_state(
11938        &r#"
11939        use some::mod1;
11940        use some::mod2;
11941
11942        const A: u32 = 42;
11943        const B: u32 = 42;
11944        ˇ
11945        fn main() {
11946            println!("hello");
11947
11948            println!("world");
11949        }
11950        "#
11951        .unindent(),
11952    );
11953
11954    cx.assert_diff_hunks(
11955        r#"
11956        use some::mod1;
11957        use some::mod2;
11958
11959        const A: u32 = 42;
11960      + const B: u32 = 42;
11961
11962        fn main() {
11963            println!("hello");
11964
11965            println!("world");
11966        }
11967        "#
11968        .unindent(),
11969    );
11970
11971    cx.update_editor(|editor, cx| {
11972        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
11973        editor.delete_line(&DeleteLine, cx);
11974    });
11975    executor.run_until_parked();
11976    cx.assert_diff_hunks(
11977        r#"
11978        use some::mod1;
11979      - use some::mod2;
11980      -
11981      - const A: u32 = 42;
11982
11983        fn main() {
11984            println!("hello");
11985
11986            println!("world");
11987        }
11988        "#
11989        .unindent(),
11990    );
11991}
11992
11993#[gpui::test]
11994async fn test_edits_around_expanded_deletion_hunks(
11995    executor: BackgroundExecutor,
11996    cx: &mut gpui::TestAppContext,
11997) {
11998    init_test(cx, |_| {});
11999
12000    let mut cx = EditorTestContext::new(cx).await;
12001
12002    let diff_base = r#"
12003        use some::mod1;
12004        use some::mod2;
12005
12006        const A: u32 = 42;
12007        const B: u32 = 42;
12008        const C: u32 = 42;
12009
12010
12011        fn main() {
12012            println!("hello");
12013
12014            println!("world");
12015        }
12016    "#
12017    .unindent();
12018    executor.run_until_parked();
12019    cx.set_state(
12020        &r#"
12021        use some::mod1;
12022        use some::mod2;
12023
12024        ˇconst B: u32 = 42;
12025        const C: u32 = 42;
12026
12027
12028        fn main() {
12029            println!("hello");
12030
12031            println!("world");
12032        }
12033        "#
12034        .unindent(),
12035    );
12036
12037    cx.set_diff_base(Some(&diff_base));
12038    executor.run_until_parked();
12039
12040    cx.update_editor(|editor, cx| {
12041        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12042    });
12043    executor.run_until_parked();
12044
12045    cx.assert_diff_hunks(
12046        r#"
12047        use some::mod1;
12048        use some::mod2;
12049
12050      - const A: u32 = 42;
12051        const B: u32 = 42;
12052        const C: u32 = 42;
12053
12054
12055        fn main() {
12056            println!("hello");
12057
12058            println!("world");
12059        }
12060        "#
12061        .unindent(),
12062    );
12063
12064    cx.update_editor(|editor, cx| {
12065        editor.delete_line(&DeleteLine, cx);
12066    });
12067    executor.run_until_parked();
12068    cx.assert_editor_state(
12069        &r#"
12070        use some::mod1;
12071        use some::mod2;
12072
12073        ˇconst C: u32 = 42;
12074
12075
12076        fn main() {
12077            println!("hello");
12078
12079            println!("world");
12080        }
12081        "#
12082        .unindent(),
12083    );
12084    cx.assert_diff_hunks(
12085        r#"
12086        use some::mod1;
12087        use some::mod2;
12088
12089      - const A: u32 = 42;
12090      - const B: u32 = 42;
12091        const C: u32 = 42;
12092
12093
12094        fn main() {
12095            println!("hello");
12096
12097            println!("world");
12098        }
12099        "#
12100        .unindent(),
12101    );
12102
12103    cx.update_editor(|editor, cx| {
12104        editor.delete_line(&DeleteLine, cx);
12105    });
12106    executor.run_until_parked();
12107    cx.assert_editor_state(
12108        &r#"
12109        use some::mod1;
12110        use some::mod2;
12111
12112        ˇ
12113
12114        fn main() {
12115            println!("hello");
12116
12117            println!("world");
12118        }
12119        "#
12120        .unindent(),
12121    );
12122    cx.assert_diff_hunks(
12123        r#"
12124        use some::mod1;
12125        use some::mod2;
12126
12127      - const A: u32 = 42;
12128      - const B: u32 = 42;
12129      - const C: u32 = 42;
12130
12131
12132        fn main() {
12133            println!("hello");
12134
12135            println!("world");
12136        }
12137        "#
12138        .unindent(),
12139    );
12140
12141    cx.update_editor(|editor, cx| {
12142        editor.handle_input("replacement", cx);
12143    });
12144    executor.run_until_parked();
12145    cx.assert_editor_state(
12146        &r#"
12147        use some::mod1;
12148        use some::mod2;
12149
12150        replacementˇ
12151
12152        fn main() {
12153            println!("hello");
12154
12155            println!("world");
12156        }
12157        "#
12158        .unindent(),
12159    );
12160    cx.assert_diff_hunks(
12161        r#"
12162        use some::mod1;
12163        use some::mod2;
12164
12165      - const A: u32 = 42;
12166      - const B: u32 = 42;
12167      - const C: u32 = 42;
12168      -
12169      + replacement
12170
12171        fn main() {
12172            println!("hello");
12173
12174            println!("world");
12175        }
12176        "#
12177        .unindent(),
12178    );
12179}
12180
12181#[gpui::test]
12182async fn test_edit_after_expanded_modification_hunk(
12183    executor: BackgroundExecutor,
12184    cx: &mut gpui::TestAppContext,
12185) {
12186    init_test(cx, |_| {});
12187
12188    let mut cx = EditorTestContext::new(cx).await;
12189
12190    let diff_base = r#"
12191        use some::mod1;
12192        use some::mod2;
12193
12194        const A: u32 = 42;
12195        const B: u32 = 42;
12196        const C: u32 = 42;
12197        const D: u32 = 42;
12198
12199
12200        fn main() {
12201            println!("hello");
12202
12203            println!("world");
12204        }"#
12205    .unindent();
12206
12207    cx.set_state(
12208        &r#"
12209        use some::mod1;
12210        use some::mod2;
12211
12212        const A: u32 = 42;
12213        const B: u32 = 42;
12214        const C: u32 = 43ˇ
12215        const D: u32 = 42;
12216
12217
12218        fn main() {
12219            println!("hello");
12220
12221            println!("world");
12222        }"#
12223        .unindent(),
12224    );
12225
12226    cx.set_diff_base(Some(&diff_base));
12227    executor.run_until_parked();
12228    cx.update_editor(|editor, cx| {
12229        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12230    });
12231    executor.run_until_parked();
12232
12233    cx.assert_diff_hunks(
12234        r#"
12235        use some::mod1;
12236        use some::mod2;
12237
12238        const A: u32 = 42;
12239        const B: u32 = 42;
12240      - const C: u32 = 42;
12241      + const C: u32 = 43
12242        const D: u32 = 42;
12243
12244
12245        fn main() {
12246            println!("hello");
12247
12248            println!("world");
12249        }"#
12250        .unindent(),
12251    );
12252
12253    cx.update_editor(|editor, cx| {
12254        editor.handle_input("\nnew_line\n", cx);
12255    });
12256    executor.run_until_parked();
12257
12258    cx.assert_diff_hunks(
12259        r#"
12260        use some::mod1;
12261        use some::mod2;
12262
12263        const A: u32 = 42;
12264        const B: u32 = 42;
12265      - const C: u32 = 42;
12266      + const C: u32 = 43
12267      + new_line
12268      +
12269        const D: u32 = 42;
12270
12271
12272        fn main() {
12273            println!("hello");
12274
12275            println!("world");
12276        }"#
12277        .unindent(),
12278    );
12279}
12280
12281async fn setup_indent_guides_editor(
12282    text: &str,
12283    cx: &mut gpui::TestAppContext,
12284) -> (BufferId, EditorTestContext) {
12285    init_test(cx, |_| {});
12286
12287    let mut cx = EditorTestContext::new(cx).await;
12288
12289    let buffer_id = cx.update_editor(|editor, cx| {
12290        editor.set_text(text, cx);
12291        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
12292
12293        buffer_ids[0]
12294    });
12295
12296    (buffer_id, cx)
12297}
12298
12299fn assert_indent_guides(
12300    range: Range<u32>,
12301    expected: Vec<IndentGuide>,
12302    active_indices: Option<Vec<usize>>,
12303    cx: &mut EditorTestContext,
12304) {
12305    let indent_guides = cx.update_editor(|editor, cx| {
12306        let snapshot = editor.snapshot(cx).display_snapshot;
12307        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
12308            MultiBufferRow(range.start)..MultiBufferRow(range.end),
12309            true,
12310            &snapshot,
12311            cx,
12312        );
12313
12314        indent_guides.sort_by(|a, b| {
12315            a.depth.cmp(&b.depth).then(
12316                a.start_row
12317                    .cmp(&b.start_row)
12318                    .then(a.end_row.cmp(&b.end_row)),
12319            )
12320        });
12321        indent_guides
12322    });
12323
12324    if let Some(expected) = active_indices {
12325        let active_indices = cx.update_editor(|editor, cx| {
12326            let snapshot = editor.snapshot(cx).display_snapshot;
12327            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
12328        });
12329
12330        assert_eq!(
12331            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
12332            expected,
12333            "Active indent guide indices do not match"
12334        );
12335    }
12336
12337    let expected: Vec<_> = expected
12338        .into_iter()
12339        .map(|guide| MultiBufferIndentGuide {
12340            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
12341            buffer: guide,
12342        })
12343        .collect();
12344
12345    assert_eq!(indent_guides, expected, "Indent guides do not match");
12346}
12347
12348fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
12349    IndentGuide {
12350        buffer_id,
12351        start_row,
12352        end_row,
12353        depth,
12354        tab_size: 4,
12355        settings: IndentGuideSettings {
12356            enabled: true,
12357            line_width: 1,
12358            active_line_width: 1,
12359            ..Default::default()
12360        },
12361    }
12362}
12363
12364#[gpui::test]
12365async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12366    let (buffer_id, mut cx) = setup_indent_guides_editor(
12367        &"
12368    fn main() {
12369        let a = 1;
12370    }"
12371        .unindent(),
12372        cx,
12373    )
12374    .await;
12375
12376    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12377}
12378
12379#[gpui::test]
12380async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
12381    let (buffer_id, mut cx) = setup_indent_guides_editor(
12382        &"
12383    fn main() {
12384        let a = 1;
12385        let b = 2;
12386    }"
12387        .unindent(),
12388        cx,
12389    )
12390    .await;
12391
12392    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
12393}
12394
12395#[gpui::test]
12396async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
12397    let (buffer_id, mut cx) = setup_indent_guides_editor(
12398        &"
12399    fn main() {
12400        let a = 1;
12401        if a == 3 {
12402            let b = 2;
12403        } else {
12404            let c = 3;
12405        }
12406    }"
12407        .unindent(),
12408        cx,
12409    )
12410    .await;
12411
12412    assert_indent_guides(
12413        0..8,
12414        vec![
12415            indent_guide(buffer_id, 1, 6, 0),
12416            indent_guide(buffer_id, 3, 3, 1),
12417            indent_guide(buffer_id, 5, 5, 1),
12418        ],
12419        None,
12420        &mut cx,
12421    );
12422}
12423
12424#[gpui::test]
12425async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
12426    let (buffer_id, mut cx) = setup_indent_guides_editor(
12427        &"
12428    fn main() {
12429        let a = 1;
12430            let b = 2;
12431        let c = 3;
12432    }"
12433        .unindent(),
12434        cx,
12435    )
12436    .await;
12437
12438    assert_indent_guides(
12439        0..5,
12440        vec![
12441            indent_guide(buffer_id, 1, 3, 0),
12442            indent_guide(buffer_id, 2, 2, 1),
12443        ],
12444        None,
12445        &mut cx,
12446    );
12447}
12448
12449#[gpui::test]
12450async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
12451    let (buffer_id, mut cx) = setup_indent_guides_editor(
12452        &"
12453        fn main() {
12454            let a = 1;
12455
12456            let c = 3;
12457        }"
12458        .unindent(),
12459        cx,
12460    )
12461    .await;
12462
12463    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
12464}
12465
12466#[gpui::test]
12467async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
12468    let (buffer_id, mut cx) = setup_indent_guides_editor(
12469        &"
12470        fn main() {
12471            let a = 1;
12472
12473            let c = 3;
12474
12475            if a == 3 {
12476                let b = 2;
12477            } else {
12478                let c = 3;
12479            }
12480        }"
12481        .unindent(),
12482        cx,
12483    )
12484    .await;
12485
12486    assert_indent_guides(
12487        0..11,
12488        vec![
12489            indent_guide(buffer_id, 1, 9, 0),
12490            indent_guide(buffer_id, 6, 6, 1),
12491            indent_guide(buffer_id, 8, 8, 1),
12492        ],
12493        None,
12494        &mut cx,
12495    );
12496}
12497
12498#[gpui::test]
12499async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
12500    let (buffer_id, mut cx) = setup_indent_guides_editor(
12501        &"
12502        fn main() {
12503            let a = 1;
12504
12505            let c = 3;
12506
12507            if a == 3 {
12508                let b = 2;
12509            } else {
12510                let c = 3;
12511            }
12512        }"
12513        .unindent(),
12514        cx,
12515    )
12516    .await;
12517
12518    assert_indent_guides(
12519        1..11,
12520        vec![
12521            indent_guide(buffer_id, 1, 9, 0),
12522            indent_guide(buffer_id, 6, 6, 1),
12523            indent_guide(buffer_id, 8, 8, 1),
12524        ],
12525        None,
12526        &mut cx,
12527    );
12528}
12529
12530#[gpui::test]
12531async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
12532    let (buffer_id, mut cx) = setup_indent_guides_editor(
12533        &"
12534        fn main() {
12535            let a = 1;
12536
12537            let c = 3;
12538
12539            if a == 3 {
12540                let b = 2;
12541            } else {
12542                let c = 3;
12543            }
12544        }"
12545        .unindent(),
12546        cx,
12547    )
12548    .await;
12549
12550    assert_indent_guides(
12551        1..10,
12552        vec![
12553            indent_guide(buffer_id, 1, 9, 0),
12554            indent_guide(buffer_id, 6, 6, 1),
12555            indent_guide(buffer_id, 8, 8, 1),
12556        ],
12557        None,
12558        &mut cx,
12559    );
12560}
12561
12562#[gpui::test]
12563async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
12564    let (buffer_id, mut cx) = setup_indent_guides_editor(
12565        &"
12566        block1
12567            block2
12568                block3
12569                    block4
12570            block2
12571        block1
12572        block1"
12573            .unindent(),
12574        cx,
12575    )
12576    .await;
12577
12578    assert_indent_guides(
12579        1..10,
12580        vec![
12581            indent_guide(buffer_id, 1, 4, 0),
12582            indent_guide(buffer_id, 2, 3, 1),
12583            indent_guide(buffer_id, 3, 3, 2),
12584        ],
12585        None,
12586        &mut cx,
12587    );
12588}
12589
12590#[gpui::test]
12591async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
12592    let (buffer_id, mut cx) = setup_indent_guides_editor(
12593        &"
12594        block1
12595            block2
12596                block3
12597
12598        block1
12599        block1"
12600            .unindent(),
12601        cx,
12602    )
12603    .await;
12604
12605    assert_indent_guides(
12606        0..6,
12607        vec![
12608            indent_guide(buffer_id, 1, 2, 0),
12609            indent_guide(buffer_id, 2, 2, 1),
12610        ],
12611        None,
12612        &mut cx,
12613    );
12614}
12615
12616#[gpui::test]
12617async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
12618    let (buffer_id, mut cx) = setup_indent_guides_editor(
12619        &"
12620        block1
12621
12622
12623
12624            block2
12625        "
12626        .unindent(),
12627        cx,
12628    )
12629    .await;
12630
12631    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
12632}
12633
12634#[gpui::test]
12635async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
12636    let (buffer_id, mut cx) = setup_indent_guides_editor(
12637        &"
12638        def a:
12639        \tb = 3
12640        \tif True:
12641        \t\tc = 4
12642        \t\td = 5
12643        \tprint(b)
12644        "
12645        .unindent(),
12646        cx,
12647    )
12648    .await;
12649
12650    assert_indent_guides(
12651        0..6,
12652        vec![
12653            indent_guide(buffer_id, 1, 6, 0),
12654            indent_guide(buffer_id, 3, 4, 1),
12655        ],
12656        None,
12657        &mut cx,
12658    );
12659}
12660
12661#[gpui::test]
12662async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
12663    let (buffer_id, mut cx) = setup_indent_guides_editor(
12664        &"
12665    fn main() {
12666        let a = 1;
12667    }"
12668        .unindent(),
12669        cx,
12670    )
12671    .await;
12672
12673    cx.update_editor(|editor, cx| {
12674        editor.change_selections(None, cx, |s| {
12675            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12676        });
12677    });
12678
12679    assert_indent_guides(
12680        0..3,
12681        vec![indent_guide(buffer_id, 1, 1, 0)],
12682        Some(vec![0]),
12683        &mut cx,
12684    );
12685}
12686
12687#[gpui::test]
12688async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
12689    let (buffer_id, mut cx) = setup_indent_guides_editor(
12690        &"
12691    fn main() {
12692        if 1 == 2 {
12693            let a = 1;
12694        }
12695    }"
12696        .unindent(),
12697        cx,
12698    )
12699    .await;
12700
12701    cx.update_editor(|editor, cx| {
12702        editor.change_selections(None, cx, |s| {
12703            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12704        });
12705    });
12706
12707    assert_indent_guides(
12708        0..4,
12709        vec![
12710            indent_guide(buffer_id, 1, 3, 0),
12711            indent_guide(buffer_id, 2, 2, 1),
12712        ],
12713        Some(vec![1]),
12714        &mut cx,
12715    );
12716
12717    cx.update_editor(|editor, cx| {
12718        editor.change_selections(None, cx, |s| {
12719            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12720        });
12721    });
12722
12723    assert_indent_guides(
12724        0..4,
12725        vec![
12726            indent_guide(buffer_id, 1, 3, 0),
12727            indent_guide(buffer_id, 2, 2, 1),
12728        ],
12729        Some(vec![1]),
12730        &mut cx,
12731    );
12732
12733    cx.update_editor(|editor, cx| {
12734        editor.change_selections(None, cx, |s| {
12735            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
12736        });
12737    });
12738
12739    assert_indent_guides(
12740        0..4,
12741        vec![
12742            indent_guide(buffer_id, 1, 3, 0),
12743            indent_guide(buffer_id, 2, 2, 1),
12744        ],
12745        Some(vec![0]),
12746        &mut cx,
12747    );
12748}
12749
12750#[gpui::test]
12751async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
12752    let (buffer_id, mut cx) = setup_indent_guides_editor(
12753        &"
12754    fn main() {
12755        let a = 1;
12756
12757        let b = 2;
12758    }"
12759        .unindent(),
12760        cx,
12761    )
12762    .await;
12763
12764    cx.update_editor(|editor, cx| {
12765        editor.change_selections(None, cx, |s| {
12766            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
12767        });
12768    });
12769
12770    assert_indent_guides(
12771        0..5,
12772        vec![indent_guide(buffer_id, 1, 3, 0)],
12773        Some(vec![0]),
12774        &mut cx,
12775    );
12776}
12777
12778#[gpui::test]
12779async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
12780    let (buffer_id, mut cx) = setup_indent_guides_editor(
12781        &"
12782    def m:
12783        a = 1
12784        pass"
12785            .unindent(),
12786        cx,
12787    )
12788    .await;
12789
12790    cx.update_editor(|editor, cx| {
12791        editor.change_selections(None, cx, |s| {
12792            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
12793        });
12794    });
12795
12796    assert_indent_guides(
12797        0..3,
12798        vec![indent_guide(buffer_id, 1, 2, 0)],
12799        Some(vec![0]),
12800        &mut cx,
12801    );
12802}
12803
12804#[gpui::test]
12805fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
12806    init_test(cx, |_| {});
12807
12808    let editor = cx.add_window(|cx| {
12809        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
12810        build_editor(buffer, cx)
12811    });
12812
12813    let render_args = Arc::new(Mutex::new(None));
12814    let snapshot = editor
12815        .update(cx, |editor, cx| {
12816            let snapshot = editor.buffer().read(cx).snapshot(cx);
12817            let range =
12818                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12819
12820            struct RenderArgs {
12821                row: MultiBufferRow,
12822                folded: bool,
12823                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12824            }
12825
12826            let crease = Crease::new(
12827                range,
12828                FoldPlaceholder::test(),
12829                {
12830                    let toggle_callback = render_args.clone();
12831                    move |row, folded, callback, _cx| {
12832                        *toggle_callback.lock() = Some(RenderArgs {
12833                            row,
12834                            folded,
12835                            callback,
12836                        });
12837                        div()
12838                    }
12839                },
12840                |_row, _folded, _cx| div(),
12841            );
12842
12843            editor.insert_creases(Some(crease), cx);
12844            let snapshot = editor.snapshot(cx);
12845            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12846            snapshot
12847        })
12848        .unwrap();
12849
12850    let render_args = render_args.lock().take().unwrap();
12851    assert_eq!(render_args.row, MultiBufferRow(1));
12852    assert!(!render_args.folded);
12853    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12854
12855    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12856        .unwrap();
12857    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12858    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12859
12860    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12861        .unwrap();
12862    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12863    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12864}
12865
12866#[gpui::test]
12867async fn test_input_text(cx: &mut gpui::TestAppContext) {
12868    init_test(cx, |_| {});
12869    let mut cx = EditorTestContext::new(cx).await;
12870
12871    cx.set_state(
12872        &r#"ˇone
12873        two
12874
12875        three
12876        fourˇ
12877        five
12878
12879        siˇx"#
12880            .unindent(),
12881    );
12882
12883    cx.dispatch_action(HandleInput(String::new()));
12884    cx.assert_editor_state(
12885        &r#"ˇone
12886        two
12887
12888        three
12889        fourˇ
12890        five
12891
12892        siˇx"#
12893            .unindent(),
12894    );
12895
12896    cx.dispatch_action(HandleInput("AAAA".to_string()));
12897    cx.assert_editor_state(
12898        &r#"AAAAˇone
12899        two
12900
12901        three
12902        fourAAAAˇ
12903        five
12904
12905        siAAAAˇx"#
12906            .unindent(),
12907    );
12908}
12909
12910#[gpui::test]
12911async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
12912    init_test(cx, |_| {});
12913
12914    let mut cx = EditorTestContext::new(cx).await;
12915    cx.set_state(
12916        r#"let foo = 1;
12917let foo = 2;
12918let foo = 3;
12919let fooˇ = 4;
12920let foo = 5;
12921let foo = 6;
12922let foo = 7;
12923let foo = 8;
12924let foo = 9;
12925let foo = 10;
12926let foo = 11;
12927let foo = 12;
12928let foo = 13;
12929let foo = 14;
12930let foo = 15;"#,
12931    );
12932
12933    cx.update_editor(|e, cx| {
12934        assert_eq!(
12935            e.next_scroll_position,
12936            NextScrollCursorCenterTopBottom::Center,
12937            "Default next scroll direction is center",
12938        );
12939
12940        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12941        assert_eq!(
12942            e.next_scroll_position,
12943            NextScrollCursorCenterTopBottom::Top,
12944            "After center, next scroll direction should be top",
12945        );
12946
12947        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12948        assert_eq!(
12949            e.next_scroll_position,
12950            NextScrollCursorCenterTopBottom::Bottom,
12951            "After top, next scroll direction should be bottom",
12952        );
12953
12954        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12955        assert_eq!(
12956            e.next_scroll_position,
12957            NextScrollCursorCenterTopBottom::Center,
12958            "After bottom, scrolling should start over",
12959        );
12960
12961        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
12962        assert_eq!(
12963            e.next_scroll_position,
12964            NextScrollCursorCenterTopBottom::Top,
12965            "Scrolling continues if retriggered fast enough"
12966        );
12967    });
12968
12969    cx.executor()
12970        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
12971    cx.executor().run_until_parked();
12972    cx.update_editor(|e, _| {
12973        assert_eq!(
12974            e.next_scroll_position,
12975            NextScrollCursorCenterTopBottom::Center,
12976            "If scrolling is not triggered fast enough, it should reset"
12977        );
12978    });
12979}
12980
12981#[gpui::test]
12982async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
12983    init_test(cx, |_| {});
12984    let mut cx = EditorLspTestContext::new_rust(
12985        lsp::ServerCapabilities {
12986            definition_provider: Some(lsp::OneOf::Left(true)),
12987            references_provider: Some(lsp::OneOf::Left(true)),
12988            ..lsp::ServerCapabilities::default()
12989        },
12990        cx,
12991    )
12992    .await;
12993
12994    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
12995        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
12996            move |params, _| async move {
12997                if empty_go_to_definition {
12998                    Ok(None)
12999                } else {
13000                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
13001                        uri: params.text_document_position_params.text_document.uri,
13002                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
13003                    })))
13004                }
13005            },
13006        );
13007        let references =
13008            cx.lsp
13009                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
13010                    Ok(Some(vec![lsp::Location {
13011                        uri: params.text_document_position.text_document.uri,
13012                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
13013                    }]))
13014                });
13015        (go_to_definition, references)
13016    };
13017
13018    cx.set_state(
13019        &r#"fn one() {
13020            let mut a = ˇtwo();
13021        }
13022
13023        fn two() {}"#
13024            .unindent(),
13025    );
13026    set_up_lsp_handlers(false, &mut cx);
13027    let navigated = cx
13028        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13029        .await
13030        .expect("Failed to navigate to definition");
13031    assert_eq!(
13032        navigated,
13033        Navigated::Yes,
13034        "Should have navigated to definition from the GetDefinition response"
13035    );
13036    cx.assert_editor_state(
13037        &r#"fn one() {
13038            let mut a = two();
13039        }
13040
13041        fn «twoˇ»() {}"#
13042            .unindent(),
13043    );
13044
13045    let editors = cx.update_workspace(|workspace, cx| {
13046        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13047    });
13048    cx.update_editor(|_, test_editor_cx| {
13049        assert_eq!(
13050            editors.len(),
13051            1,
13052            "Initially, only one, test, editor should be open in the workspace"
13053        );
13054        assert_eq!(
13055            test_editor_cx.view(),
13056            editors.last().expect("Asserted len is 1")
13057        );
13058    });
13059
13060    set_up_lsp_handlers(true, &mut cx);
13061    let navigated = cx
13062        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
13063        .await
13064        .expect("Failed to navigate to lookup references");
13065    assert_eq!(
13066        navigated,
13067        Navigated::Yes,
13068        "Should have navigated to references as a fallback after empty GoToDefinition response"
13069    );
13070    // We should not change the selections in the existing file,
13071    // if opening another milti buffer with the references
13072    cx.assert_editor_state(
13073        &r#"fn one() {
13074            let mut a = two();
13075        }
13076
13077        fn «twoˇ»() {}"#
13078            .unindent(),
13079    );
13080    let editors = cx.update_workspace(|workspace, cx| {
13081        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
13082    });
13083    cx.update_editor(|_, test_editor_cx| {
13084        assert_eq!(
13085            editors.len(),
13086            2,
13087            "After falling back to references search, we open a new editor with the results"
13088        );
13089        let references_fallback_text = editors
13090            .into_iter()
13091            .find(|new_editor| new_editor != test_editor_cx.view())
13092            .expect("Should have one non-test editor now")
13093            .read(test_editor_cx)
13094            .text(test_editor_cx);
13095        assert_eq!(
13096            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
13097            "Should use the range from the references response and not the GoToDefinition one"
13098        );
13099    });
13100}
13101
13102fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
13103    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
13104    point..point
13105}
13106
13107fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
13108    let (text, ranges) = marked_text_ranges(marked_text, true);
13109    assert_eq!(view.text(cx), text);
13110    assert_eq!(
13111        view.selections.ranges(cx),
13112        ranges,
13113        "Assert selections are {}",
13114        marked_text
13115    );
13116}
13117
13118pub fn handle_signature_help_request(
13119    cx: &mut EditorLspTestContext,
13120    mocked_response: lsp::SignatureHelp,
13121) -> impl Future<Output = ()> {
13122    let mut request =
13123        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
13124            let mocked_response = mocked_response.clone();
13125            async move { Ok(Some(mocked_response)) }
13126        });
13127
13128    async move {
13129        request.next().await;
13130    }
13131}
13132
13133/// Handle completion request passing a marked string specifying where the completion
13134/// should be triggered from using '|' character, what range should be replaced, and what completions
13135/// should be returned using '<' and '>' to delimit the range
13136pub fn handle_completion_request(
13137    cx: &mut EditorLspTestContext,
13138    marked_string: &str,
13139    completions: Vec<&'static str>,
13140    counter: Arc<AtomicUsize>,
13141) -> impl Future<Output = ()> {
13142    let complete_from_marker: TextRangeMarker = '|'.into();
13143    let replace_range_marker: TextRangeMarker = ('<', '>').into();
13144    let (_, mut marked_ranges) = marked_text_ranges_by(
13145        marked_string,
13146        vec![complete_from_marker.clone(), replace_range_marker.clone()],
13147    );
13148
13149    let complete_from_position =
13150        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
13151    let replace_range =
13152        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
13153
13154    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
13155        let completions = completions.clone();
13156        counter.fetch_add(1, atomic::Ordering::Release);
13157        async move {
13158            assert_eq!(params.text_document_position.text_document.uri, url.clone());
13159            assert_eq!(
13160                params.text_document_position.position,
13161                complete_from_position
13162            );
13163            Ok(Some(lsp::CompletionResponse::Array(
13164                completions
13165                    .iter()
13166                    .map(|completion_text| lsp::CompletionItem {
13167                        label: completion_text.to_string(),
13168                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13169                            range: replace_range,
13170                            new_text: completion_text.to_string(),
13171                        })),
13172                        ..Default::default()
13173                    })
13174                    .collect(),
13175            )))
13176        }
13177    });
13178
13179    async move {
13180        request.next().await;
13181    }
13182}
13183
13184fn handle_resolve_completion_request(
13185    cx: &mut EditorLspTestContext,
13186    edits: Option<Vec<(&'static str, &'static str)>>,
13187) -> impl Future<Output = ()> {
13188    let edits = edits.map(|edits| {
13189        edits
13190            .iter()
13191            .map(|(marked_string, new_text)| {
13192                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
13193                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
13194                lsp::TextEdit::new(replace_range, new_text.to_string())
13195            })
13196            .collect::<Vec<_>>()
13197    });
13198
13199    let mut request =
13200        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13201            let edits = edits.clone();
13202            async move {
13203                Ok(lsp::CompletionItem {
13204                    additional_text_edits: edits,
13205                    ..Default::default()
13206                })
13207            }
13208        });
13209
13210    async move {
13211        request.next().await;
13212    }
13213}
13214
13215pub(crate) fn update_test_language_settings(
13216    cx: &mut TestAppContext,
13217    f: impl Fn(&mut AllLanguageSettingsContent),
13218) {
13219    cx.update(|cx| {
13220        SettingsStore::update_global(cx, |store, cx| {
13221            store.update_user_settings::<AllLanguageSettings>(cx, f);
13222        });
13223    });
13224}
13225
13226pub(crate) fn update_test_project_settings(
13227    cx: &mut TestAppContext,
13228    f: impl Fn(&mut ProjectSettings),
13229) {
13230    cx.update(|cx| {
13231        SettingsStore::update_global(cx, |store, cx| {
13232            store.update_user_settings::<ProjectSettings>(cx, f);
13233        });
13234    });
13235}
13236
13237pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
13238    cx.update(|cx| {
13239        assets::Assets.load_test_fonts(cx);
13240        let store = SettingsStore::test(cx);
13241        cx.set_global(store);
13242        theme::init(theme::LoadThemes::JustBase, cx);
13243        release_channel::init(SemanticVersion::default(), cx);
13244        client::init_settings(cx);
13245        language::init(cx);
13246        Project::init_settings(cx);
13247        workspace::init_settings(cx);
13248        crate::init(cx);
13249    });
13250
13251    update_test_language_settings(cx, f);
13252}
13253
13254pub(crate) fn rust_lang() -> Arc<Language> {
13255    Arc::new(Language::new(
13256        LanguageConfig {
13257            name: "Rust".into(),
13258            matcher: LanguageMatcher {
13259                path_suffixes: vec!["rs".to_string()],
13260                ..Default::default()
13261            },
13262            ..Default::default()
13263        },
13264        Some(tree_sitter_rust::LANGUAGE.into()),
13265    ))
13266}
13267
13268#[track_caller]
13269fn assert_hunk_revert(
13270    not_reverted_text_with_selections: &str,
13271    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
13272    expected_reverted_text_with_selections: &str,
13273    base_text: &str,
13274    cx: &mut EditorLspTestContext,
13275) {
13276    cx.set_state(not_reverted_text_with_selections);
13277    cx.update_editor(|editor, cx| {
13278        editor
13279            .buffer()
13280            .read(cx)
13281            .as_singleton()
13282            .unwrap()
13283            .update(cx, |buffer, cx| {
13284                buffer.set_diff_base(Some(base_text.into()), cx);
13285            });
13286    });
13287    cx.executor().run_until_parked();
13288
13289    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
13290        let snapshot = editor.buffer().read(cx).snapshot(cx);
13291        let reverted_hunk_statuses = snapshot
13292            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
13293            .map(|hunk| hunk_status(&hunk))
13294            .collect::<Vec<_>>();
13295
13296        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
13297        reverted_hunk_statuses
13298    });
13299    cx.executor().run_until_parked();
13300    cx.assert_editor_state(expected_reverted_text_with_selections);
13301    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
13302}