editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_hunks,
    6        editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
    7        expanded_hunks, expanded_hunks_background_highlights, select_ranges,
    8    },
    9    JoinLines,
   10};
   11use futures::StreamExt;
   12use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions};
   13use indoc::indoc;
   14use language::{
   15    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
   16    BracketPairConfig,
   17    Capability::ReadWrite,
   18    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override, Point,
   19};
   20use parking_lot::Mutex;
   21use project::project_settings::{LspSettings, ProjectSettings};
   22use project::FakeFs;
   23use serde_json::{self, json};
   24use std::sync::atomic;
   25use std::sync::atomic::AtomicUsize;
   26use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   27use unindent::Unindent;
   28use util::{
   29    assert_set_eq,
   30    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   31};
   32use workspace::{
   33    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   34    NavigationEntry, ViewId,
   35};
   36
   37#[gpui::test]
   38fn test_edit_events(cx: &mut TestAppContext) {
   39    init_test(cx, |_| {});
   40
   41    let buffer = cx.new_model(|cx| {
   42        let mut buffer = language::Buffer::local("123456", cx);
   43        buffer.set_group_interval(Duration::from_secs(1));
   44        buffer
   45    });
   46
   47    let events = Rc::new(RefCell::new(Vec::new()));
   48    let editor1 = cx.add_window({
   49        let events = events.clone();
   50        |cx| {
   51            let view = cx.view().clone();
   52            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
   53                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
   54                    events.borrow_mut().push(("editor1", event.clone()));
   55                }
   56            })
   57            .detach();
   58            Editor::for_buffer(buffer.clone(), None, cx)
   59        }
   60    });
   61
   62    let editor2 = cx.add_window({
   63        let events = events.clone();
   64        |cx| {
   65            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
   66                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
   67                    events.borrow_mut().push(("editor2", event.clone()));
   68                }
   69            })
   70            .detach();
   71            Editor::for_buffer(buffer.clone(), None, cx)
   72        }
   73    });
   74
   75    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   76
   77    // Mutating editor 1 will emit an `Edited` event only for that editor.
   78    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   79    assert_eq!(
   80        mem::take(&mut *events.borrow_mut()),
   81        [
   82            ("editor1", EditorEvent::Edited),
   83            ("editor1", EditorEvent::BufferEdited),
   84            ("editor2", EditorEvent::BufferEdited),
   85        ]
   86    );
   87
   88    // Mutating editor 2 will emit an `Edited` event only for that editor.
   89    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
   90    assert_eq!(
   91        mem::take(&mut *events.borrow_mut()),
   92        [
   93            ("editor2", EditorEvent::Edited),
   94            ("editor1", EditorEvent::BufferEdited),
   95            ("editor2", EditorEvent::BufferEdited),
   96        ]
   97    );
   98
   99    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  100    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  101    assert_eq!(
  102        mem::take(&mut *events.borrow_mut()),
  103        [
  104            ("editor1", EditorEvent::Edited),
  105            ("editor1", EditorEvent::BufferEdited),
  106            ("editor2", EditorEvent::BufferEdited),
  107        ]
  108    );
  109
  110    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", EditorEvent::Edited),
  116            ("editor1", EditorEvent::BufferEdited),
  117            ("editor2", EditorEvent::BufferEdited),
  118        ]
  119    );
  120
  121    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", EditorEvent::Edited),
  127            ("editor1", EditorEvent::BufferEdited),
  128            ("editor2", EditorEvent::BufferEdited),
  129        ]
  130    );
  131
  132    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  133    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor2", EditorEvent::Edited),
  138            ("editor1", EditorEvent::BufferEdited),
  139            ("editor2", EditorEvent::BufferEdited),
  140        ]
  141    );
  142
  143    // No event is emitted when the mutation is a no-op.
  144    _ = editor2.update(cx, |editor, cx| {
  145        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  146
  147        editor.backspace(&Backspace, cx);
  148    });
  149    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  150}
  151
  152#[gpui::test]
  153fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  154    init_test(cx, |_| {});
  155
  156    let mut now = Instant::now();
  157    let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
  158    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
  159    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  160    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  161
  162    _ = editor.update(cx, |editor, cx| {
  163        editor.start_transaction_at(now, cx);
  164        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  165
  166        editor.insert("cd", cx);
  167        editor.end_transaction_at(now, cx);
  168        assert_eq!(editor.text(cx), "12cd56");
  169        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  170
  171        editor.start_transaction_at(now, cx);
  172        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  173        editor.insert("e", cx);
  174        editor.end_transaction_at(now, cx);
  175        assert_eq!(editor.text(cx), "12cde6");
  176        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  177
  178        now += group_interval + Duration::from_millis(1);
  179        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  180
  181        // Simulate an edit in another editor
  182        _ = buffer.update(cx, |buffer, cx| {
  183            buffer.start_transaction_at(now, cx);
  184            buffer.edit([(0..1, "a")], None, cx);
  185            buffer.edit([(1..1, "b")], None, cx);
  186            buffer.end_transaction_at(now, cx);
  187        });
  188
  189        assert_eq!(editor.text(cx), "ab2cde6");
  190        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  191
  192        // Last transaction happened past the group interval in a different editor.
  193        // Undo it individually and don't restore selections.
  194        editor.undo(&Undo, cx);
  195        assert_eq!(editor.text(cx), "12cde6");
  196        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  197
  198        // First two transactions happened within the group interval in this editor.
  199        // Undo them together and restore selections.
  200        editor.undo(&Undo, cx);
  201        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  202        assert_eq!(editor.text(cx), "123456");
  203        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  204
  205        // Redo the first two transactions together.
  206        editor.redo(&Redo, cx);
  207        assert_eq!(editor.text(cx), "12cde6");
  208        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  209
  210        // Redo the last transaction on its own.
  211        editor.redo(&Redo, cx);
  212        assert_eq!(editor.text(cx), "ab2cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  214
  215        // Test empty transactions.
  216        editor.start_transaction_at(now, cx);
  217        editor.end_transaction_at(now, cx);
  218        editor.undo(&Undo, cx);
  219        assert_eq!(editor.text(cx), "12cde6");
  220    });
  221}
  222
  223#[gpui::test]
  224fn test_ime_composition(cx: &mut TestAppContext) {
  225    init_test(cx, |_| {});
  226
  227    let buffer = cx.new_model(|cx| {
  228        let mut buffer = language::Buffer::local("abcde", cx);
  229        // Ensure automatic grouping doesn't occur.
  230        buffer.set_group_interval(Duration::ZERO);
  231        buffer
  232    });
  233
  234    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  235    cx.add_window(|cx| {
  236        let mut editor = build_editor(buffer.clone(), cx);
  237
  238        // Start a new IME composition.
  239        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  240        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  241        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  242        assert_eq!(editor.text(cx), "äbcde");
  243        assert_eq!(
  244            editor.marked_text_ranges(cx),
  245            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  246        );
  247
  248        // Finalize IME composition.
  249        editor.replace_text_in_range(None, "ā", cx);
  250        assert_eq!(editor.text(cx), "ābcde");
  251        assert_eq!(editor.marked_text_ranges(cx), None);
  252
  253        // IME composition edits are grouped and are undone/redone at once.
  254        editor.undo(&Default::default(), cx);
  255        assert_eq!(editor.text(cx), "abcde");
  256        assert_eq!(editor.marked_text_ranges(cx), None);
  257        editor.redo(&Default::default(), cx);
  258        assert_eq!(editor.text(cx), "ābcde");
  259        assert_eq!(editor.marked_text_ranges(cx), None);
  260
  261        // Start a new IME composition.
  262        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  263        assert_eq!(
  264            editor.marked_text_ranges(cx),
  265            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  266        );
  267
  268        // Undoing during an IME composition cancels it.
  269        editor.undo(&Default::default(), cx);
  270        assert_eq!(editor.text(cx), "ābcde");
  271        assert_eq!(editor.marked_text_ranges(cx), None);
  272
  273        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  274        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  275        assert_eq!(editor.text(cx), "ābcdè");
  276        assert_eq!(
  277            editor.marked_text_ranges(cx),
  278            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  279        );
  280
  281        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  282        editor.replace_text_in_range(Some(4..999), "ę", cx);
  283        assert_eq!(editor.text(cx), "ābcdę");
  284        assert_eq!(editor.marked_text_ranges(cx), None);
  285
  286        // Start a new IME composition with multiple cursors.
  287        editor.change_selections(None, cx, |s| {
  288            s.select_ranges([
  289                OffsetUtf16(1)..OffsetUtf16(1),
  290                OffsetUtf16(3)..OffsetUtf16(3),
  291                OffsetUtf16(5)..OffsetUtf16(5),
  292            ])
  293        });
  294        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  295        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  296        assert_eq!(
  297            editor.marked_text_ranges(cx),
  298            Some(vec![
  299                OffsetUtf16(0)..OffsetUtf16(3),
  300                OffsetUtf16(4)..OffsetUtf16(7),
  301                OffsetUtf16(8)..OffsetUtf16(11)
  302            ])
  303        );
  304
  305        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  306        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  307        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  308        assert_eq!(
  309            editor.marked_text_ranges(cx),
  310            Some(vec![
  311                OffsetUtf16(1)..OffsetUtf16(2),
  312                OffsetUtf16(5)..OffsetUtf16(6),
  313                OffsetUtf16(9)..OffsetUtf16(10)
  314            ])
  315        );
  316
  317        // Finalize IME composition with multiple cursors.
  318        editor.replace_text_in_range(Some(9..10), "2", cx);
  319        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  320        assert_eq!(editor.marked_text_ranges(cx), None);
  321
  322        editor
  323    });
  324}
  325
  326#[gpui::test]
  327fn test_selection_with_mouse(cx: &mut TestAppContext) {
  328    init_test(cx, |_| {});
  329
  330    let editor = cx.add_window(|cx| {
  331        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  332        build_editor(buffer, cx)
  333    });
  334
  335    _ = editor.update(cx, |view, cx| {
  336        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  337    });
  338    assert_eq!(
  339        editor
  340            .update(cx, |view, cx| view.selections.display_ranges(cx))
  341            .unwrap(),
  342        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  343    );
  344
  345    _ = editor.update(cx, |view, cx| {
  346        view.update_selection(
  347            DisplayPoint::new(DisplayRow(3), 3),
  348            0,
  349            gpui::Point::<f32>::default(),
  350            cx,
  351        );
  352    });
  353
  354    assert_eq!(
  355        editor
  356            .update(cx, |view, cx| view.selections.display_ranges(cx))
  357            .unwrap(),
  358        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  359    );
  360
  361    _ = editor.update(cx, |view, cx| {
  362        view.update_selection(
  363            DisplayPoint::new(DisplayRow(1), 1),
  364            0,
  365            gpui::Point::<f32>::default(),
  366            cx,
  367        );
  368    });
  369
  370    assert_eq!(
  371        editor
  372            .update(cx, |view, cx| view.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  375    );
  376
  377    _ = editor.update(cx, |view, cx| {
  378        view.end_selection(cx);
  379        view.update_selection(
  380            DisplayPoint::new(DisplayRow(3), 3),
  381            0,
  382            gpui::Point::<f32>::default(),
  383            cx,
  384        );
  385    });
  386
  387    assert_eq!(
  388        editor
  389            .update(cx, |view, cx| view.selections.display_ranges(cx))
  390            .unwrap(),
  391        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  392    );
  393
  394    _ = editor.update(cx, |view, cx| {
  395        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  396        view.update_selection(
  397            DisplayPoint::new(DisplayRow(0), 0),
  398            0,
  399            gpui::Point::<f32>::default(),
  400            cx,
  401        );
  402    });
  403
  404    assert_eq!(
  405        editor
  406            .update(cx, |view, cx| view.selections.display_ranges(cx))
  407            .unwrap(),
  408        [
  409            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  410            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  411        ]
  412    );
  413
  414    _ = editor.update(cx, |view, cx| {
  415        view.end_selection(cx);
  416    });
  417
  418    assert_eq!(
  419        editor
  420            .update(cx, |view, cx| view.selections.display_ranges(cx))
  421            .unwrap(),
  422        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  423    );
  424}
  425
  426#[gpui::test]
  427fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  428    init_test(cx, |_| {});
  429
  430    let view = cx.add_window(|cx| {
  431        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  432        build_editor(buffer, cx)
  433    });
  434
  435    _ = view.update(cx, |view, cx| {
  436        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  437        assert_eq!(
  438            view.selections.display_ranges(cx),
  439            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  440        );
  441    });
  442
  443    _ = view.update(cx, |view, cx| {
  444        view.update_selection(
  445            DisplayPoint::new(DisplayRow(3), 3),
  446            0,
  447            gpui::Point::<f32>::default(),
  448            cx,
  449        );
  450        assert_eq!(
  451            view.selections.display_ranges(cx),
  452            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  453        );
  454    });
  455
  456    _ = view.update(cx, |view, cx| {
  457        view.cancel(&Cancel, cx);
  458        view.update_selection(
  459            DisplayPoint::new(DisplayRow(1), 1),
  460            0,
  461            gpui::Point::<f32>::default(),
  462            cx,
  463        );
  464        assert_eq!(
  465            view.selections.display_ranges(cx),
  466            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  467        );
  468    });
  469}
  470
  471#[gpui::test]
  472fn test_clone(cx: &mut TestAppContext) {
  473    init_test(cx, |_| {});
  474
  475    let (text, selection_ranges) = marked_text_ranges(
  476        indoc! {"
  477            one
  478            two
  479            threeˇ
  480            four
  481            fiveˇ
  482        "},
  483        true,
  484    );
  485
  486    let editor = cx.add_window(|cx| {
  487        let buffer = MultiBuffer::build_simple(&text, cx);
  488        build_editor(buffer, cx)
  489    });
  490
  491    _ = editor.update(cx, |editor, cx| {
  492        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  493        editor.fold_ranges(
  494            [
  495                Point::new(1, 0)..Point::new(2, 0),
  496                Point::new(3, 0)..Point::new(4, 0),
  497            ],
  498            true,
  499            cx,
  500        );
  501    });
  502
  503    let cloned_editor = editor
  504        .update(cx, |editor, cx| {
  505            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  506        })
  507        .unwrap();
  508
  509    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  510    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  511
  512    assert_eq!(
  513        cloned_editor
  514            .update(cx, |e, cx| e.display_text(cx))
  515            .unwrap(),
  516        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  517    );
  518    assert_eq!(
  519        cloned_snapshot
  520            .folds_in_range(0..text.len())
  521            .collect::<Vec<_>>(),
  522        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  523    );
  524    assert_set_eq!(
  525        cloned_editor
  526            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  527            .unwrap(),
  528        editor
  529            .update(cx, |editor, cx| editor.selections.ranges(cx))
  530            .unwrap()
  531    );
  532    assert_set_eq!(
  533        cloned_editor
  534            .update(cx, |e, cx| e.selections.display_ranges(cx))
  535            .unwrap(),
  536        editor
  537            .update(cx, |e, cx| e.selections.display_ranges(cx))
  538            .unwrap()
  539    );
  540}
  541
  542#[gpui::test]
  543async fn test_navigation_history(cx: &mut TestAppContext) {
  544    init_test(cx, |_| {});
  545
  546    use workspace::item::Item;
  547
  548    let fs = FakeFs::new(cx.executor());
  549    let project = Project::test(fs, [], cx).await;
  550    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  551    let pane = workspace
  552        .update(cx, |workspace, _| workspace.active_pane().clone())
  553        .unwrap();
  554
  555    _ = workspace.update(cx, |_v, cx| {
  556        cx.new_view(|cx| {
  557            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  558            let mut editor = build_editor(buffer.clone(), cx);
  559            let handle = cx.view();
  560            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  561
  562            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  563                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  564            }
  565
  566            // Move the cursor a small distance.
  567            // Nothing is added to the navigation history.
  568            editor.change_selections(None, cx, |s| {
  569                s.select_display_ranges([
  570                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  571                ])
  572            });
  573            editor.change_selections(None, cx, |s| {
  574                s.select_display_ranges([
  575                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  576                ])
  577            });
  578            assert!(pop_history(&mut editor, cx).is_none());
  579
  580            // Move the cursor a large distance.
  581            // The history can jump back to the previous position.
  582            editor.change_selections(None, cx, |s| {
  583                s.select_display_ranges([
  584                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  585                ])
  586            });
  587            let nav_entry = pop_history(&mut editor, cx).unwrap();
  588            editor.navigate(nav_entry.data.unwrap(), cx);
  589            assert_eq!(nav_entry.item.id(), cx.entity_id());
  590            assert_eq!(
  591                editor.selections.display_ranges(cx),
  592                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  593            );
  594            assert!(pop_history(&mut editor, cx).is_none());
  595
  596            // Move the cursor a small distance via the mouse.
  597            // Nothing is added to the navigation history.
  598            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  599            editor.end_selection(cx);
  600            assert_eq!(
  601                editor.selections.display_ranges(cx),
  602                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  603            );
  604            assert!(pop_history(&mut editor, cx).is_none());
  605
  606            // Move the cursor a large distance via the mouse.
  607            // The history can jump back to the previous position.
  608            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  609            editor.end_selection(cx);
  610            assert_eq!(
  611                editor.selections.display_ranges(cx),
  612                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  613            );
  614            let nav_entry = pop_history(&mut editor, cx).unwrap();
  615            editor.navigate(nav_entry.data.unwrap(), cx);
  616            assert_eq!(nav_entry.item.id(), cx.entity_id());
  617            assert_eq!(
  618                editor.selections.display_ranges(cx),
  619                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  620            );
  621            assert!(pop_history(&mut editor, cx).is_none());
  622
  623            // Set scroll position to check later
  624            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  625            let original_scroll_position = editor.scroll_manager.anchor();
  626
  627            // Jump to the end of the document and adjust scroll
  628            editor.move_to_end(&MoveToEnd, cx);
  629            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  630            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  631
  632            let nav_entry = pop_history(&mut editor, cx).unwrap();
  633            editor.navigate(nav_entry.data.unwrap(), cx);
  634            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  635
  636            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  637            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  638            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  639            let invalid_point = Point::new(9999, 0);
  640            editor.navigate(
  641                Box::new(NavigationData {
  642                    cursor_anchor: invalid_anchor,
  643                    cursor_position: invalid_point,
  644                    scroll_anchor: ScrollAnchor {
  645                        anchor: invalid_anchor,
  646                        offset: Default::default(),
  647                    },
  648                    scroll_top_row: invalid_point.row,
  649                }),
  650                cx,
  651            );
  652            assert_eq!(
  653                editor.selections.display_ranges(cx),
  654                &[editor.max_point(cx)..editor.max_point(cx)]
  655            );
  656            assert_eq!(
  657                editor.scroll_position(cx),
  658                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  659            );
  660
  661            editor
  662        })
  663    });
  664}
  665
  666#[gpui::test]
  667fn test_cancel(cx: &mut TestAppContext) {
  668    init_test(cx, |_| {});
  669
  670    let view = cx.add_window(|cx| {
  671        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  672        build_editor(buffer, cx)
  673    });
  674
  675    _ = view.update(cx, |view, cx| {
  676        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  677        view.update_selection(
  678            DisplayPoint::new(DisplayRow(1), 1),
  679            0,
  680            gpui::Point::<f32>::default(),
  681            cx,
  682        );
  683        view.end_selection(cx);
  684
  685        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  686        view.update_selection(
  687            DisplayPoint::new(DisplayRow(0), 3),
  688            0,
  689            gpui::Point::<f32>::default(),
  690            cx,
  691        );
  692        view.end_selection(cx);
  693        assert_eq!(
  694            view.selections.display_ranges(cx),
  695            [
  696                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  697                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  698            ]
  699        );
  700    });
  701
  702    _ = view.update(cx, |view, cx| {
  703        view.cancel(&Cancel, cx);
  704        assert_eq!(
  705            view.selections.display_ranges(cx),
  706            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  707        );
  708    });
  709
  710    _ = view.update(cx, |view, cx| {
  711        view.cancel(&Cancel, cx);
  712        assert_eq!(
  713            view.selections.display_ranges(cx),
  714            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  715        );
  716    });
  717}
  718
  719#[gpui::test]
  720fn test_fold_action(cx: &mut TestAppContext) {
  721    init_test(cx, |_| {});
  722
  723    let view = cx.add_window(|cx| {
  724        let buffer = MultiBuffer::build_simple(
  725            &"
  726                impl Foo {
  727                    // Hello!
  728
  729                    fn a() {
  730                        1
  731                    }
  732
  733                    fn b() {
  734                        2
  735                    }
  736
  737                    fn c() {
  738                        3
  739                    }
  740                }
  741            "
  742            .unindent(),
  743            cx,
  744        );
  745        build_editor(buffer.clone(), cx)
  746    });
  747
  748    _ = view.update(cx, |view, cx| {
  749        view.change_selections(None, cx, |s| {
  750            s.select_display_ranges([
  751                DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(12), 0)
  752            ]);
  753        });
  754        view.fold(&Fold, cx);
  755        assert_eq!(
  756            view.display_text(cx),
  757            "
  758                impl Foo {
  759                    // Hello!
  760
  761                    fn a() {
  762                        1
  763                    }
  764
  765                    fn b() {⋯
  766                    }
  767
  768                    fn c() {⋯
  769                    }
  770                }
  771            "
  772            .unindent(),
  773        );
  774
  775        view.fold(&Fold, cx);
  776        assert_eq!(
  777            view.display_text(cx),
  778            "
  779                impl Foo {⋯
  780                }
  781            "
  782            .unindent(),
  783        );
  784
  785        view.unfold_lines(&UnfoldLines, cx);
  786        assert_eq!(
  787            view.display_text(cx),
  788            "
  789                impl Foo {
  790                    // Hello!
  791
  792                    fn a() {
  793                        1
  794                    }
  795
  796                    fn b() {⋯
  797                    }
  798
  799                    fn c() {⋯
  800                    }
  801                }
  802            "
  803            .unindent(),
  804        );
  805
  806        view.unfold_lines(&UnfoldLines, cx);
  807        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  808    });
  809}
  810
  811#[gpui::test]
  812fn test_move_cursor(cx: &mut TestAppContext) {
  813    init_test(cx, |_| {});
  814
  815    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
  816    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  817
  818    _ = buffer.update(cx, |buffer, cx| {
  819        buffer.edit(
  820            vec![
  821                (Point::new(1, 0)..Point::new(1, 0), "\t"),
  822                (Point::new(1, 1)..Point::new(1, 1), "\t"),
  823            ],
  824            None,
  825            cx,
  826        );
  827    });
  828    _ = view.update(cx, |view, cx| {
  829        assert_eq!(
  830            view.selections.display_ranges(cx),
  831            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  832        );
  833
  834        view.move_down(&MoveDown, cx);
  835        assert_eq!(
  836            view.selections.display_ranges(cx),
  837            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
  838        );
  839
  840        view.move_right(&MoveRight, cx);
  841        assert_eq!(
  842            view.selections.display_ranges(cx),
  843            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
  844        );
  845
  846        view.move_left(&MoveLeft, cx);
  847        assert_eq!(
  848            view.selections.display_ranges(cx),
  849            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
  850        );
  851
  852        view.move_up(&MoveUp, cx);
  853        assert_eq!(
  854            view.selections.display_ranges(cx),
  855            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  856        );
  857
  858        view.move_to_end(&MoveToEnd, cx);
  859        assert_eq!(
  860            view.selections.display_ranges(cx),
  861            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
  862        );
  863
  864        view.move_to_beginning(&MoveToBeginning, cx);
  865        assert_eq!(
  866            view.selections.display_ranges(cx),
  867            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
  868        );
  869
  870        view.change_selections(None, cx, |s| {
  871            s.select_display_ranges([
  872                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
  873            ]);
  874        });
  875        view.select_to_beginning(&SelectToBeginning, cx);
  876        assert_eq!(
  877            view.selections.display_ranges(cx),
  878            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
  879        );
  880
  881        view.select_to_end(&SelectToEnd, cx);
  882        assert_eq!(
  883            view.selections.display_ranges(cx),
  884            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
  885        );
  886    });
  887}
  888
  889#[gpui::test]
  890fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
  891    init_test(cx, |_| {});
  892
  893    let view = cx.add_window(|cx| {
  894        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
  895        build_editor(buffer.clone(), cx)
  896    });
  897
  898    assert_eq!('ⓐ'.len_utf8(), 3);
  899    assert_eq!('α'.len_utf8(), 2);
  900
  901    _ = view.update(cx, |view, cx| {
  902        view.fold_ranges(
  903            vec![
  904                Point::new(0, 6)..Point::new(0, 12),
  905                Point::new(1, 2)..Point::new(1, 4),
  906                Point::new(2, 4)..Point::new(2, 8),
  907            ],
  908            true,
  909            cx,
  910        );
  911        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
  912
  913        view.move_right(&MoveRight, cx);
  914        assert_eq!(
  915            view.selections.display_ranges(cx),
  916            &[empty_range(0, "".len())]
  917        );
  918        view.move_right(&MoveRight, cx);
  919        assert_eq!(
  920            view.selections.display_ranges(cx),
  921            &[empty_range(0, "ⓐⓑ".len())]
  922        );
  923        view.move_right(&MoveRight, cx);
  924        assert_eq!(
  925            view.selections.display_ranges(cx),
  926            &[empty_range(0, "ⓐⓑ⋯".len())]
  927        );
  928
  929        view.move_down(&MoveDown, cx);
  930        assert_eq!(
  931            view.selections.display_ranges(cx),
  932            &[empty_range(1, "ab⋯e".len())]
  933        );
  934        view.move_left(&MoveLeft, cx);
  935        assert_eq!(
  936            view.selections.display_ranges(cx),
  937            &[empty_range(1, "ab⋯".len())]
  938        );
  939        view.move_left(&MoveLeft, cx);
  940        assert_eq!(
  941            view.selections.display_ranges(cx),
  942            &[empty_range(1, "ab".len())]
  943        );
  944        view.move_left(&MoveLeft, cx);
  945        assert_eq!(
  946            view.selections.display_ranges(cx),
  947            &[empty_range(1, "a".len())]
  948        );
  949
  950        view.move_down(&MoveDown, cx);
  951        assert_eq!(
  952            view.selections.display_ranges(cx),
  953            &[empty_range(2, "α".len())]
  954        );
  955        view.move_right(&MoveRight, cx);
  956        assert_eq!(
  957            view.selections.display_ranges(cx),
  958            &[empty_range(2, "αβ".len())]
  959        );
  960        view.move_right(&MoveRight, cx);
  961        assert_eq!(
  962            view.selections.display_ranges(cx),
  963            &[empty_range(2, "αβ⋯".len())]
  964        );
  965        view.move_right(&MoveRight, cx);
  966        assert_eq!(
  967            view.selections.display_ranges(cx),
  968            &[empty_range(2, "αβ⋯ε".len())]
  969        );
  970
  971        view.move_up(&MoveUp, cx);
  972        assert_eq!(
  973            view.selections.display_ranges(cx),
  974            &[empty_range(1, "ab⋯e".len())]
  975        );
  976        view.move_down(&MoveDown, cx);
  977        assert_eq!(
  978            view.selections.display_ranges(cx),
  979            &[empty_range(2, "αβ⋯ε".len())]
  980        );
  981        view.move_up(&MoveUp, cx);
  982        assert_eq!(
  983            view.selections.display_ranges(cx),
  984            &[empty_range(1, "ab⋯e".len())]
  985        );
  986
  987        view.move_up(&MoveUp, cx);
  988        assert_eq!(
  989            view.selections.display_ranges(cx),
  990            &[empty_range(0, "ⓐⓑ".len())]
  991        );
  992        view.move_left(&MoveLeft, cx);
  993        assert_eq!(
  994            view.selections.display_ranges(cx),
  995            &[empty_range(0, "".len())]
  996        );
  997        view.move_left(&MoveLeft, cx);
  998        assert_eq!(
  999            view.selections.display_ranges(cx),
 1000            &[empty_range(0, "".len())]
 1001        );
 1002    });
 1003}
 1004
 1005#[gpui::test]
 1006fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1007    init_test(cx, |_| {});
 1008
 1009    let view = cx.add_window(|cx| {
 1010        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1011        build_editor(buffer.clone(), cx)
 1012    });
 1013    _ = view.update(cx, |view, cx| {
 1014        view.change_selections(None, cx, |s| {
 1015            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1016        });
 1017        view.move_down(&MoveDown, cx);
 1018        assert_eq!(
 1019            view.selections.display_ranges(cx),
 1020            &[empty_range(1, "abcd".len())]
 1021        );
 1022
 1023        view.move_down(&MoveDown, cx);
 1024        assert_eq!(
 1025            view.selections.display_ranges(cx),
 1026            &[empty_range(2, "αβγ".len())]
 1027        );
 1028
 1029        view.move_down(&MoveDown, cx);
 1030        assert_eq!(
 1031            view.selections.display_ranges(cx),
 1032            &[empty_range(3, "abcd".len())]
 1033        );
 1034
 1035        view.move_down(&MoveDown, cx);
 1036        assert_eq!(
 1037            view.selections.display_ranges(cx),
 1038            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1039        );
 1040
 1041        view.move_up(&MoveUp, cx);
 1042        assert_eq!(
 1043            view.selections.display_ranges(cx),
 1044            &[empty_range(3, "abcd".len())]
 1045        );
 1046
 1047        view.move_up(&MoveUp, cx);
 1048        assert_eq!(
 1049            view.selections.display_ranges(cx),
 1050            &[empty_range(2, "αβγ".len())]
 1051        );
 1052    });
 1053}
 1054
 1055#[gpui::test]
 1056fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1057    init_test(cx, |_| {});
 1058    let move_to_beg = MoveToBeginningOfLine {
 1059        stop_at_soft_wraps: true,
 1060    };
 1061
 1062    let move_to_end = MoveToEndOfLine {
 1063        stop_at_soft_wraps: true,
 1064    };
 1065
 1066    let view = cx.add_window(|cx| {
 1067        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1068        build_editor(buffer, cx)
 1069    });
 1070    _ = view.update(cx, |view, cx| {
 1071        view.change_selections(None, cx, |s| {
 1072            s.select_display_ranges([
 1073                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1074                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1075            ]);
 1076        });
 1077    });
 1078
 1079    _ = view.update(cx, |view, cx| {
 1080        view.move_to_beginning_of_line(&move_to_beg, cx);
 1081        assert_eq!(
 1082            view.selections.display_ranges(cx),
 1083            &[
 1084                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1085                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1086            ]
 1087        );
 1088    });
 1089
 1090    _ = view.update(cx, |view, cx| {
 1091        view.move_to_beginning_of_line(&move_to_beg, cx);
 1092        assert_eq!(
 1093            view.selections.display_ranges(cx),
 1094            &[
 1095                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1096                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1097            ]
 1098        );
 1099    });
 1100
 1101    _ = view.update(cx, |view, cx| {
 1102        view.move_to_beginning_of_line(&move_to_beg, cx);
 1103        assert_eq!(
 1104            view.selections.display_ranges(cx),
 1105            &[
 1106                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1107                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1108            ]
 1109        );
 1110    });
 1111
 1112    _ = view.update(cx, |view, cx| {
 1113        view.move_to_end_of_line(&move_to_end, cx);
 1114        assert_eq!(
 1115            view.selections.display_ranges(cx),
 1116            &[
 1117                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1118                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1119            ]
 1120        );
 1121    });
 1122
 1123    // Moving to the end of line again is a no-op.
 1124    _ = view.update(cx, |view, cx| {
 1125        view.move_to_end_of_line(&move_to_end, cx);
 1126        assert_eq!(
 1127            view.selections.display_ranges(cx),
 1128            &[
 1129                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1130                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1131            ]
 1132        );
 1133    });
 1134
 1135    _ = view.update(cx, |view, cx| {
 1136        view.move_left(&MoveLeft, cx);
 1137        view.select_to_beginning_of_line(
 1138            &SelectToBeginningOfLine {
 1139                stop_at_soft_wraps: true,
 1140            },
 1141            cx,
 1142        );
 1143        assert_eq!(
 1144            view.selections.display_ranges(cx),
 1145            &[
 1146                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1147                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1148            ]
 1149        );
 1150    });
 1151
 1152    _ = view.update(cx, |view, cx| {
 1153        view.select_to_beginning_of_line(
 1154            &SelectToBeginningOfLine {
 1155                stop_at_soft_wraps: true,
 1156            },
 1157            cx,
 1158        );
 1159        assert_eq!(
 1160            view.selections.display_ranges(cx),
 1161            &[
 1162                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1163                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1164            ]
 1165        );
 1166    });
 1167
 1168    _ = view.update(cx, |view, cx| {
 1169        view.select_to_beginning_of_line(
 1170            &SelectToBeginningOfLine {
 1171                stop_at_soft_wraps: true,
 1172            },
 1173            cx,
 1174        );
 1175        assert_eq!(
 1176            view.selections.display_ranges(cx),
 1177            &[
 1178                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1179                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1180            ]
 1181        );
 1182    });
 1183
 1184    _ = view.update(cx, |view, cx| {
 1185        view.select_to_end_of_line(
 1186            &SelectToEndOfLine {
 1187                stop_at_soft_wraps: true,
 1188            },
 1189            cx,
 1190        );
 1191        assert_eq!(
 1192            view.selections.display_ranges(cx),
 1193            &[
 1194                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1195                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1196            ]
 1197        );
 1198    });
 1199
 1200    _ = view.update(cx, |view, cx| {
 1201        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1202        assert_eq!(view.display_text(cx), "ab\n  de");
 1203        assert_eq!(
 1204            view.selections.display_ranges(cx),
 1205            &[
 1206                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1207                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1208            ]
 1209        );
 1210    });
 1211
 1212    _ = view.update(cx, |view, cx| {
 1213        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1214        assert_eq!(view.display_text(cx), "\n");
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[
 1218                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1219                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1220            ]
 1221        );
 1222    });
 1223}
 1224
 1225#[gpui::test]
 1226fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1227    init_test(cx, |_| {});
 1228    let move_to_beg = MoveToBeginningOfLine {
 1229        stop_at_soft_wraps: false,
 1230    };
 1231
 1232    let move_to_end = MoveToEndOfLine {
 1233        stop_at_soft_wraps: false,
 1234    };
 1235
 1236    let view = cx.add_window(|cx| {
 1237        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1238        build_editor(buffer, cx)
 1239    });
 1240
 1241    _ = view.update(cx, |view, cx| {
 1242        view.set_wrap_width(Some(140.0.into()), cx);
 1243
 1244        // We expect the following lines after wrapping
 1245        // ```
 1246        // thequickbrownfox
 1247        // jumpedoverthelazydo
 1248        // gs
 1249        // ```
 1250        // The final `gs` was soft-wrapped onto a new line.
 1251        assert_eq!(
 1252            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1253            view.display_text(cx),
 1254        );
 1255
 1256        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1257        // Start the cursor at the `k` on the first line
 1258        view.change_selections(None, cx, |s| {
 1259            s.select_display_ranges([
 1260                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1261            ]);
 1262        });
 1263
 1264        // Moving to the beginning of the line should put us at the beginning of the line.
 1265        view.move_to_beginning_of_line(&move_to_beg, cx);
 1266        assert_eq!(
 1267            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1268            view.selections.display_ranges(cx)
 1269        );
 1270
 1271        // Moving to the end of the line should put us at the end of the line.
 1272        view.move_to_end_of_line(&move_to_end, cx);
 1273        assert_eq!(
 1274            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1275            view.selections.display_ranges(cx)
 1276        );
 1277
 1278        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1279        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1280        view.change_selections(None, cx, |s| {
 1281            s.select_display_ranges([
 1282                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1283            ]);
 1284        });
 1285
 1286        // Moving to the beginning of the line should put us at the start of the second line of
 1287        // display text, i.e., the `j`.
 1288        view.move_to_beginning_of_line(&move_to_beg, cx);
 1289        assert_eq!(
 1290            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1291            view.selections.display_ranges(cx)
 1292        );
 1293
 1294        // Moving to the beginning of the line again should be a no-op.
 1295        view.move_to_beginning_of_line(&move_to_beg, cx);
 1296        assert_eq!(
 1297            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1298            view.selections.display_ranges(cx)
 1299        );
 1300
 1301        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1302        // next display line.
 1303        view.move_to_end_of_line(&move_to_end, cx);
 1304        assert_eq!(
 1305            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1306            view.selections.display_ranges(cx)
 1307        );
 1308
 1309        // Moving to the end of the line again should be a no-op.
 1310        view.move_to_end_of_line(&move_to_end, cx);
 1311        assert_eq!(
 1312            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1313            view.selections.display_ranges(cx)
 1314        );
 1315    });
 1316}
 1317
 1318#[gpui::test]
 1319fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1320    init_test(cx, |_| {});
 1321
 1322    let view = cx.add_window(|cx| {
 1323        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1324        build_editor(buffer, cx)
 1325    });
 1326    _ = view.update(cx, |view, cx| {
 1327        view.change_selections(None, cx, |s| {
 1328            s.select_display_ranges([
 1329                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1330                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1331            ])
 1332        });
 1333
 1334        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1335        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1336
 1337        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1338        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1339
 1340        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1341        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1342
 1343        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1344        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1345
 1346        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1347        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1348
 1349        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1350        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1351
 1352        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1353        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1354
 1355        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1356        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1357
 1358        view.move_right(&MoveRight, cx);
 1359        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1360        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1361
 1362        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1363        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1364
 1365        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1366        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1367    });
 1368}
 1369
 1370#[gpui::test]
 1371fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1372    init_test(cx, |_| {});
 1373
 1374    let view = cx.add_window(|cx| {
 1375        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1376        build_editor(buffer, cx)
 1377    });
 1378
 1379    _ = view.update(cx, |view, cx| {
 1380        view.set_wrap_width(Some(140.0.into()), cx);
 1381        assert_eq!(
 1382            view.display_text(cx),
 1383            "use one::{\n    two::three::\n    four::five\n};"
 1384        );
 1385
 1386        view.change_selections(None, cx, |s| {
 1387            s.select_display_ranges([
 1388                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1389            ]);
 1390        });
 1391
 1392        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1393        assert_eq!(
 1394            view.selections.display_ranges(cx),
 1395            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1396        );
 1397
 1398        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1399        assert_eq!(
 1400            view.selections.display_ranges(cx),
 1401            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1402        );
 1403
 1404        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1405        assert_eq!(
 1406            view.selections.display_ranges(cx),
 1407            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1408        );
 1409
 1410        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1411        assert_eq!(
 1412            view.selections.display_ranges(cx),
 1413            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1414        );
 1415
 1416        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1417        assert_eq!(
 1418            view.selections.display_ranges(cx),
 1419            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1420        );
 1421
 1422        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1423        assert_eq!(
 1424            view.selections.display_ranges(cx),
 1425            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1426        );
 1427    });
 1428}
 1429
 1430#[gpui::test]
 1431async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1432    init_test(cx, |_| {});
 1433    let mut cx = EditorTestContext::new(cx).await;
 1434
 1435    let line_height = cx.editor(|editor, cx| {
 1436        editor
 1437            .style()
 1438            .unwrap()
 1439            .text
 1440            .line_height_in_pixels(cx.rem_size())
 1441    });
 1442    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1443
 1444    cx.set_state(
 1445        &r#"ˇone
 1446        two
 1447
 1448        three
 1449        fourˇ
 1450        five
 1451
 1452        six"#
 1453            .unindent(),
 1454    );
 1455
 1456    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1457    cx.assert_editor_state(
 1458        &r#"one
 1459        two
 1460        ˇ
 1461        three
 1462        four
 1463        five
 1464        ˇ
 1465        six"#
 1466            .unindent(),
 1467    );
 1468
 1469    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1470    cx.assert_editor_state(
 1471        &r#"one
 1472        two
 1473
 1474        three
 1475        four
 1476        five
 1477        ˇ
 1478        sixˇ"#
 1479            .unindent(),
 1480    );
 1481
 1482    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1483    cx.assert_editor_state(
 1484        &r#"one
 1485        two
 1486
 1487        three
 1488        four
 1489        five
 1490
 1491        sixˇ"#
 1492            .unindent(),
 1493    );
 1494
 1495    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1496    cx.assert_editor_state(
 1497        &r#"one
 1498        two
 1499
 1500        three
 1501        four
 1502        five
 1503        ˇ
 1504        six"#
 1505            .unindent(),
 1506    );
 1507
 1508    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1509    cx.assert_editor_state(
 1510        &r#"one
 1511        two
 1512        ˇ
 1513        three
 1514        four
 1515        five
 1516
 1517        six"#
 1518            .unindent(),
 1519    );
 1520
 1521    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1522    cx.assert_editor_state(
 1523        &r#"ˇone
 1524        two
 1525
 1526        three
 1527        four
 1528        five
 1529
 1530        six"#
 1531            .unindent(),
 1532    );
 1533}
 1534
 1535#[gpui::test]
 1536async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1537    init_test(cx, |_| {});
 1538    let mut cx = EditorTestContext::new(cx).await;
 1539    let line_height = cx.editor(|editor, cx| {
 1540        editor
 1541            .style()
 1542            .unwrap()
 1543            .text
 1544            .line_height_in_pixels(cx.rem_size())
 1545    });
 1546    let window = cx.window;
 1547    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1548
 1549    cx.set_state(
 1550        &r#"ˇone
 1551        two
 1552        three
 1553        four
 1554        five
 1555        six
 1556        seven
 1557        eight
 1558        nine
 1559        ten
 1560        "#,
 1561    );
 1562
 1563    cx.update_editor(|editor, cx| {
 1564        assert_eq!(
 1565            editor.snapshot(cx).scroll_position(),
 1566            gpui::Point::new(0., 0.)
 1567        );
 1568        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1569        assert_eq!(
 1570            editor.snapshot(cx).scroll_position(),
 1571            gpui::Point::new(0., 3.)
 1572        );
 1573        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1574        assert_eq!(
 1575            editor.snapshot(cx).scroll_position(),
 1576            gpui::Point::new(0., 6.)
 1577        );
 1578        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1579        assert_eq!(
 1580            editor.snapshot(cx).scroll_position(),
 1581            gpui::Point::new(0., 3.)
 1582        );
 1583
 1584        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 1585        assert_eq!(
 1586            editor.snapshot(cx).scroll_position(),
 1587            gpui::Point::new(0., 1.)
 1588        );
 1589        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 1590        assert_eq!(
 1591            editor.snapshot(cx).scroll_position(),
 1592            gpui::Point::new(0., 3.)
 1593        );
 1594    });
 1595}
 1596
 1597#[gpui::test]
 1598async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 1599    init_test(cx, |_| {});
 1600    let mut cx = EditorTestContext::new(cx).await;
 1601
 1602    let line_height = cx.update_editor(|editor, cx| {
 1603        editor.set_vertical_scroll_margin(2, cx);
 1604        editor
 1605            .style()
 1606            .unwrap()
 1607            .text
 1608            .line_height_in_pixels(cx.rem_size())
 1609    });
 1610    let window = cx.window;
 1611    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 1612
 1613    cx.set_state(
 1614        &r#"ˇone
 1615            two
 1616            three
 1617            four
 1618            five
 1619            six
 1620            seven
 1621            eight
 1622            nine
 1623            ten
 1624        "#,
 1625    );
 1626    cx.update_editor(|editor, cx| {
 1627        assert_eq!(
 1628            editor.snapshot(cx).scroll_position(),
 1629            gpui::Point::new(0., 0.0)
 1630        );
 1631    });
 1632
 1633    // Add a cursor below the visible area. Since both cursors cannot fit
 1634    // on screen, the editor autoscrolls to reveal the newest cursor, and
 1635    // allows the vertical scroll margin below that cursor.
 1636    cx.update_editor(|editor, cx| {
 1637        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1638            selections.select_ranges([
 1639                Point::new(0, 0)..Point::new(0, 0),
 1640                Point::new(6, 0)..Point::new(6, 0),
 1641            ]);
 1642        })
 1643    });
 1644    cx.update_editor(|editor, cx| {
 1645        assert_eq!(
 1646            editor.snapshot(cx).scroll_position(),
 1647            gpui::Point::new(0., 3.0)
 1648        );
 1649    });
 1650
 1651    // Move down. The editor cursor scrolls down to track the newest cursor.
 1652    cx.update_editor(|editor, cx| {
 1653        editor.move_down(&Default::default(), cx);
 1654    });
 1655    cx.update_editor(|editor, cx| {
 1656        assert_eq!(
 1657            editor.snapshot(cx).scroll_position(),
 1658            gpui::Point::new(0., 4.0)
 1659        );
 1660    });
 1661
 1662    // Add a cursor above the visible area. Since both cursors fit on screen,
 1663    // the editor scrolls to show both.
 1664    cx.update_editor(|editor, cx| {
 1665        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 1666            selections.select_ranges([
 1667                Point::new(1, 0)..Point::new(1, 0),
 1668                Point::new(6, 0)..Point::new(6, 0),
 1669            ]);
 1670        })
 1671    });
 1672    cx.update_editor(|editor, cx| {
 1673        assert_eq!(
 1674            editor.snapshot(cx).scroll_position(),
 1675            gpui::Point::new(0., 1.0)
 1676        );
 1677    });
 1678}
 1679
 1680#[gpui::test]
 1681async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1682    init_test(cx, |_| {});
 1683    let mut cx = EditorTestContext::new(cx).await;
 1684
 1685    let line_height = cx.editor(|editor, cx| {
 1686        editor
 1687            .style()
 1688            .unwrap()
 1689            .text
 1690            .line_height_in_pixels(cx.rem_size())
 1691    });
 1692    let window = cx.window;
 1693    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 1694    cx.set_state(
 1695        &r#"
 1696        ˇone
 1697        two
 1698        threeˇ
 1699        four
 1700        five
 1701        six
 1702        seven
 1703        eight
 1704        nine
 1705        ten
 1706        "#
 1707        .unindent(),
 1708    );
 1709
 1710    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1711    cx.assert_editor_state(
 1712        &r#"
 1713        one
 1714        two
 1715        three
 1716        ˇfour
 1717        five
 1718        sixˇ
 1719        seven
 1720        eight
 1721        nine
 1722        ten
 1723        "#
 1724        .unindent(),
 1725    );
 1726
 1727    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 1728    cx.assert_editor_state(
 1729        &r#"
 1730        one
 1731        two
 1732        three
 1733        four
 1734        five
 1735        six
 1736        ˇseven
 1737        eight
 1738        nineˇ
 1739        ten
 1740        "#
 1741        .unindent(),
 1742    );
 1743
 1744    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1745    cx.assert_editor_state(
 1746        &r#"
 1747        one
 1748        two
 1749        three
 1750        ˇfour
 1751        five
 1752        sixˇ
 1753        seven
 1754        eight
 1755        nine
 1756        ten
 1757        "#
 1758        .unindent(),
 1759    );
 1760
 1761    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 1762    cx.assert_editor_state(
 1763        &r#"
 1764        ˇone
 1765        two
 1766        threeˇ
 1767        four
 1768        five
 1769        six
 1770        seven
 1771        eight
 1772        nine
 1773        ten
 1774        "#
 1775        .unindent(),
 1776    );
 1777
 1778    // Test select collapsing
 1779    cx.update_editor(|editor, cx| {
 1780        editor.move_page_down(&MovePageDown::default(), cx);
 1781        editor.move_page_down(&MovePageDown::default(), cx);
 1782        editor.move_page_down(&MovePageDown::default(), cx);
 1783    });
 1784    cx.assert_editor_state(
 1785        &r#"
 1786        one
 1787        two
 1788        three
 1789        four
 1790        five
 1791        six
 1792        seven
 1793        eight
 1794        nine
 1795        ˇten
 1796        ˇ"#
 1797        .unindent(),
 1798    );
 1799}
 1800
 1801#[gpui::test]
 1802async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 1803    init_test(cx, |_| {});
 1804    let mut cx = EditorTestContext::new(cx).await;
 1805    cx.set_state("one «two threeˇ» four");
 1806    cx.update_editor(|editor, cx| {
 1807        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1808        assert_eq!(editor.text(cx), " four");
 1809    });
 1810}
 1811
 1812#[gpui::test]
 1813fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 1814    init_test(cx, |_| {});
 1815
 1816    let view = cx.add_window(|cx| {
 1817        let buffer = MultiBuffer::build_simple("one two three four", cx);
 1818        build_editor(buffer.clone(), cx)
 1819    });
 1820
 1821    _ = view.update(cx, |view, cx| {
 1822        view.change_selections(None, cx, |s| {
 1823            s.select_display_ranges([
 1824                // an empty selection - the preceding word fragment is deleted
 1825                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1826                // characters selected - they are deleted
 1827                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 1828            ])
 1829        });
 1830        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
 1831        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 1832    });
 1833
 1834    _ = view.update(cx, |view, cx| {
 1835        view.change_selections(None, cx, |s| {
 1836            s.select_display_ranges([
 1837                // an empty selection - the following word fragment is deleted
 1838                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1839                // characters selected - they are deleted
 1840                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 1841            ])
 1842        });
 1843        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
 1844        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 1845    });
 1846}
 1847
 1848#[gpui::test]
 1849fn test_newline(cx: &mut TestAppContext) {
 1850    init_test(cx, |_| {});
 1851
 1852    let view = cx.add_window(|cx| {
 1853        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 1854        build_editor(buffer.clone(), cx)
 1855    });
 1856
 1857    _ = view.update(cx, |view, cx| {
 1858        view.change_selections(None, cx, |s| {
 1859            s.select_display_ranges([
 1860                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1861                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1862                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 1863            ])
 1864        });
 1865
 1866        view.newline(&Newline, cx);
 1867        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 1868    });
 1869}
 1870
 1871#[gpui::test]
 1872fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 1873    init_test(cx, |_| {});
 1874
 1875    let editor = cx.add_window(|cx| {
 1876        let buffer = MultiBuffer::build_simple(
 1877            "
 1878                a
 1879                b(
 1880                    X
 1881                )
 1882                c(
 1883                    X
 1884                )
 1885            "
 1886            .unindent()
 1887            .as_str(),
 1888            cx,
 1889        );
 1890        let mut editor = build_editor(buffer.clone(), cx);
 1891        editor.change_selections(None, cx, |s| {
 1892            s.select_ranges([
 1893                Point::new(2, 4)..Point::new(2, 5),
 1894                Point::new(5, 4)..Point::new(5, 5),
 1895            ])
 1896        });
 1897        editor
 1898    });
 1899
 1900    _ = editor.update(cx, |editor, cx| {
 1901        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 1902        editor.buffer.update(cx, |buffer, cx| {
 1903            buffer.edit(
 1904                [
 1905                    (Point::new(1, 2)..Point::new(3, 0), ""),
 1906                    (Point::new(4, 2)..Point::new(6, 0), ""),
 1907                ],
 1908                None,
 1909                cx,
 1910            );
 1911            assert_eq!(
 1912                buffer.read(cx).text(),
 1913                "
 1914                    a
 1915                    b()
 1916                    c()
 1917                "
 1918                .unindent()
 1919            );
 1920        });
 1921        assert_eq!(
 1922            editor.selections.ranges(cx),
 1923            &[
 1924                Point::new(1, 2)..Point::new(1, 2),
 1925                Point::new(2, 2)..Point::new(2, 2),
 1926            ],
 1927        );
 1928
 1929        editor.newline(&Newline, cx);
 1930        assert_eq!(
 1931            editor.text(cx),
 1932            "
 1933                a
 1934                b(
 1935                )
 1936                c(
 1937                )
 1938            "
 1939            .unindent()
 1940        );
 1941
 1942        // The selections are moved after the inserted newlines
 1943        assert_eq!(
 1944            editor.selections.ranges(cx),
 1945            &[
 1946                Point::new(2, 0)..Point::new(2, 0),
 1947                Point::new(4, 0)..Point::new(4, 0),
 1948            ],
 1949        );
 1950    });
 1951}
 1952
 1953#[gpui::test]
 1954async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 1955    init_test(cx, |settings| {
 1956        settings.defaults.tab_size = NonZeroU32::new(4)
 1957    });
 1958
 1959    let language = Arc::new(
 1960        Language::new(
 1961            LanguageConfig::default(),
 1962            Some(tree_sitter_rust::language()),
 1963        )
 1964        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 1965        .unwrap(),
 1966    );
 1967
 1968    let mut cx = EditorTestContext::new(cx).await;
 1969    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 1970    cx.set_state(indoc! {"
 1971        const a: ˇA = (
 1972 1973                «const_functionˇ»(ˇ),
 1974                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 1975 1976        ˇ);ˇ
 1977    "});
 1978
 1979    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 1980    cx.assert_editor_state(indoc! {"
 1981        ˇ
 1982        const a: A = (
 1983            ˇ
 1984            (
 1985                ˇ
 1986                ˇ
 1987                const_function(),
 1988                ˇ
 1989                ˇ
 1990                ˇ
 1991                ˇ
 1992                something_else,
 1993                ˇ
 1994            )
 1995            ˇ
 1996            ˇ
 1997        );
 1998    "});
 1999}
 2000
 2001#[gpui::test]
 2002async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2003    init_test(cx, |settings| {
 2004        settings.defaults.tab_size = NonZeroU32::new(4)
 2005    });
 2006
 2007    let language = Arc::new(
 2008        Language::new(
 2009            LanguageConfig::default(),
 2010            Some(tree_sitter_rust::language()),
 2011        )
 2012        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2013        .unwrap(),
 2014    );
 2015
 2016    let mut cx = EditorTestContext::new(cx).await;
 2017    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2018    cx.set_state(indoc! {"
 2019        const a: ˇA = (
 2020 2021                «const_functionˇ»(ˇ),
 2022                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2023 2024        ˇ);ˇ
 2025    "});
 2026
 2027    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2028    cx.assert_editor_state(indoc! {"
 2029        const a: A = (
 2030            ˇ
 2031            (
 2032                ˇ
 2033                const_function(),
 2034                ˇ
 2035                ˇ
 2036                something_else,
 2037                ˇ
 2038                ˇ
 2039                ˇ
 2040                ˇ
 2041            )
 2042            ˇ
 2043        );
 2044        ˇ
 2045        ˇ
 2046    "});
 2047}
 2048
 2049#[gpui::test]
 2050async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2051    init_test(cx, |settings| {
 2052        settings.defaults.tab_size = NonZeroU32::new(4)
 2053    });
 2054
 2055    let language = Arc::new(Language::new(
 2056        LanguageConfig {
 2057            line_comments: vec!["//".into()],
 2058            ..LanguageConfig::default()
 2059        },
 2060        None,
 2061    ));
 2062    {
 2063        let mut cx = EditorTestContext::new(cx).await;
 2064        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2065        cx.set_state(indoc! {"
 2066        // Fooˇ
 2067    "});
 2068
 2069        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2070        cx.assert_editor_state(indoc! {"
 2071        // Foo
 2072        //ˇ
 2073    "});
 2074        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2075        cx.set_state(indoc! {"
 2076        ˇ// Foo
 2077    "});
 2078        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2079        cx.assert_editor_state(indoc! {"
 2080
 2081        ˇ// Foo
 2082    "});
 2083    }
 2084    // Ensure that comment continuations can be disabled.
 2085    update_test_language_settings(cx, |settings| {
 2086        settings.defaults.extend_comment_on_newline = Some(false);
 2087    });
 2088    let mut cx = EditorTestContext::new(cx).await;
 2089    cx.set_state(indoc! {"
 2090        // Fooˇ
 2091    "});
 2092    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2093    cx.assert_editor_state(indoc! {"
 2094        // Foo
 2095        ˇ
 2096    "});
 2097}
 2098
 2099#[gpui::test]
 2100fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2101    init_test(cx, |_| {});
 2102
 2103    let editor = cx.add_window(|cx| {
 2104        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2105        let mut editor = build_editor(buffer.clone(), cx);
 2106        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2107        editor
 2108    });
 2109
 2110    _ = editor.update(cx, |editor, cx| {
 2111        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2112        editor.buffer.update(cx, |buffer, cx| {
 2113            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2114            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2115        });
 2116        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2117
 2118        editor.insert("Z", cx);
 2119        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2120
 2121        // The selections are moved after the inserted characters
 2122        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2123    });
 2124}
 2125
 2126#[gpui::test]
 2127async fn test_tab(cx: &mut gpui::TestAppContext) {
 2128    init_test(cx, |settings| {
 2129        settings.defaults.tab_size = NonZeroU32::new(3)
 2130    });
 2131
 2132    let mut cx = EditorTestContext::new(cx).await;
 2133    cx.set_state(indoc! {"
 2134        ˇabˇc
 2135        ˇ🏀ˇ🏀ˇefg
 2136 2137    "});
 2138    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2139    cx.assert_editor_state(indoc! {"
 2140           ˇab ˇc
 2141           ˇ🏀  ˇ🏀  ˇefg
 2142        d  ˇ
 2143    "});
 2144
 2145    cx.set_state(indoc! {"
 2146        a
 2147        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2148    "});
 2149    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2150    cx.assert_editor_state(indoc! {"
 2151        a
 2152           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2153    "});
 2154}
 2155
 2156#[gpui::test]
 2157async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2158    init_test(cx, |_| {});
 2159
 2160    let mut cx = EditorTestContext::new(cx).await;
 2161    let language = Arc::new(
 2162        Language::new(
 2163            LanguageConfig::default(),
 2164            Some(tree_sitter_rust::language()),
 2165        )
 2166        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2167        .unwrap(),
 2168    );
 2169    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2170
 2171    // cursors that are already at the suggested indent level insert
 2172    // a soft tab. cursors that are to the left of the suggested indent
 2173    // auto-indent their line.
 2174    cx.set_state(indoc! {"
 2175        ˇ
 2176        const a: B = (
 2177            c(
 2178                d(
 2179        ˇ
 2180                )
 2181        ˇ
 2182        ˇ    )
 2183        );
 2184    "});
 2185    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2186    cx.assert_editor_state(indoc! {"
 2187            ˇ
 2188        const a: B = (
 2189            c(
 2190                d(
 2191                    ˇ
 2192                )
 2193                ˇ
 2194            ˇ)
 2195        );
 2196    "});
 2197
 2198    // handle auto-indent when there are multiple cursors on the same line
 2199    cx.set_state(indoc! {"
 2200        const a: B = (
 2201            c(
 2202        ˇ    ˇ
 2203        ˇ    )
 2204        );
 2205    "});
 2206    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2207    cx.assert_editor_state(indoc! {"
 2208        const a: B = (
 2209            c(
 2210                ˇ
 2211            ˇ)
 2212        );
 2213    "});
 2214}
 2215
 2216#[gpui::test]
 2217async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2218    init_test(cx, |settings| {
 2219        settings.defaults.tab_size = NonZeroU32::new(4)
 2220    });
 2221
 2222    let language = Arc::new(
 2223        Language::new(
 2224            LanguageConfig::default(),
 2225            Some(tree_sitter_rust::language()),
 2226        )
 2227        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2228        .unwrap(),
 2229    );
 2230
 2231    let mut cx = EditorTestContext::new(cx).await;
 2232    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2233    cx.set_state(indoc! {"
 2234        fn a() {
 2235            if b {
 2236        \t ˇc
 2237            }
 2238        }
 2239    "});
 2240
 2241    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2242    cx.assert_editor_state(indoc! {"
 2243        fn a() {
 2244            if b {
 2245                ˇc
 2246            }
 2247        }
 2248    "});
 2249}
 2250
 2251#[gpui::test]
 2252async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2253    init_test(cx, |settings| {
 2254        settings.defaults.tab_size = NonZeroU32::new(4);
 2255    });
 2256
 2257    let mut cx = EditorTestContext::new(cx).await;
 2258
 2259    cx.set_state(indoc! {"
 2260          «oneˇ» «twoˇ»
 2261        three
 2262         four
 2263    "});
 2264    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2265    cx.assert_editor_state(indoc! {"
 2266            «oneˇ» «twoˇ»
 2267        three
 2268         four
 2269    "});
 2270
 2271    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2272    cx.assert_editor_state(indoc! {"
 2273        «oneˇ» «twoˇ»
 2274        three
 2275         four
 2276    "});
 2277
 2278    // select across line ending
 2279    cx.set_state(indoc! {"
 2280        one two
 2281        t«hree
 2282        ˇ» four
 2283    "});
 2284    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2285    cx.assert_editor_state(indoc! {"
 2286        one two
 2287            t«hree
 2288        ˇ» four
 2289    "});
 2290
 2291    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2292    cx.assert_editor_state(indoc! {"
 2293        one two
 2294        t«hree
 2295        ˇ» four
 2296    "});
 2297
 2298    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2299    cx.set_state(indoc! {"
 2300        one two
 2301        ˇthree
 2302            four
 2303    "});
 2304    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2305    cx.assert_editor_state(indoc! {"
 2306        one two
 2307            ˇthree
 2308            four
 2309    "});
 2310
 2311    cx.set_state(indoc! {"
 2312        one two
 2313        ˇ    three
 2314            four
 2315    "});
 2316    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2317    cx.assert_editor_state(indoc! {"
 2318        one two
 2319        ˇthree
 2320            four
 2321    "});
 2322}
 2323
 2324#[gpui::test]
 2325async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2326    init_test(cx, |settings| {
 2327        settings.defaults.hard_tabs = Some(true);
 2328    });
 2329
 2330    let mut cx = EditorTestContext::new(cx).await;
 2331
 2332    // select two ranges on one line
 2333    cx.set_state(indoc! {"
 2334        «oneˇ» «twoˇ»
 2335        three
 2336        four
 2337    "});
 2338    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2339    cx.assert_editor_state(indoc! {"
 2340        \t«oneˇ» «twoˇ»
 2341        three
 2342        four
 2343    "});
 2344    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2345    cx.assert_editor_state(indoc! {"
 2346        \t\t«oneˇ» «twoˇ»
 2347        three
 2348        four
 2349    "});
 2350    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2351    cx.assert_editor_state(indoc! {"
 2352        \t«oneˇ» «twoˇ»
 2353        three
 2354        four
 2355    "});
 2356    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2357    cx.assert_editor_state(indoc! {"
 2358        «oneˇ» «twoˇ»
 2359        three
 2360        four
 2361    "});
 2362
 2363    // select across a line ending
 2364    cx.set_state(indoc! {"
 2365        one two
 2366        t«hree
 2367        ˇ»four
 2368    "});
 2369    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2370    cx.assert_editor_state(indoc! {"
 2371        one two
 2372        \tt«hree
 2373        ˇ»four
 2374    "});
 2375    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2376    cx.assert_editor_state(indoc! {"
 2377        one two
 2378        \t\tt«hree
 2379        ˇ»four
 2380    "});
 2381    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2382    cx.assert_editor_state(indoc! {"
 2383        one two
 2384        \tt«hree
 2385        ˇ»four
 2386    "});
 2387    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2388    cx.assert_editor_state(indoc! {"
 2389        one two
 2390        t«hree
 2391        ˇ»four
 2392    "});
 2393
 2394    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2395    cx.set_state(indoc! {"
 2396        one two
 2397        ˇthree
 2398        four
 2399    "});
 2400    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2401    cx.assert_editor_state(indoc! {"
 2402        one two
 2403        ˇthree
 2404        four
 2405    "});
 2406    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2407    cx.assert_editor_state(indoc! {"
 2408        one two
 2409        \tˇthree
 2410        four
 2411    "});
 2412    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2413    cx.assert_editor_state(indoc! {"
 2414        one two
 2415        ˇthree
 2416        four
 2417    "});
 2418}
 2419
 2420#[gpui::test]
 2421fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2422    init_test(cx, |settings| {
 2423        settings.languages.extend([
 2424            (
 2425                "TOML".into(),
 2426                LanguageSettingsContent {
 2427                    tab_size: NonZeroU32::new(2),
 2428                    ..Default::default()
 2429                },
 2430            ),
 2431            (
 2432                "Rust".into(),
 2433                LanguageSettingsContent {
 2434                    tab_size: NonZeroU32::new(4),
 2435                    ..Default::default()
 2436                },
 2437            ),
 2438        ]);
 2439    });
 2440
 2441    let toml_language = Arc::new(Language::new(
 2442        LanguageConfig {
 2443            name: "TOML".into(),
 2444            ..Default::default()
 2445        },
 2446        None,
 2447    ));
 2448    let rust_language = Arc::new(Language::new(
 2449        LanguageConfig {
 2450            name: "Rust".into(),
 2451            ..Default::default()
 2452        },
 2453        None,
 2454    ));
 2455
 2456    let toml_buffer =
 2457        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2458    let rust_buffer = cx.new_model(|cx| {
 2459        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2460    });
 2461    let multibuffer = cx.new_model(|cx| {
 2462        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 2463        multibuffer.push_excerpts(
 2464            toml_buffer.clone(),
 2465            [ExcerptRange {
 2466                context: Point::new(0, 0)..Point::new(2, 0),
 2467                primary: None,
 2468            }],
 2469            cx,
 2470        );
 2471        multibuffer.push_excerpts(
 2472            rust_buffer.clone(),
 2473            [ExcerptRange {
 2474                context: Point::new(0, 0)..Point::new(1, 0),
 2475                primary: None,
 2476            }],
 2477            cx,
 2478        );
 2479        multibuffer
 2480    });
 2481
 2482    cx.add_window(|cx| {
 2483        let mut editor = build_editor(multibuffer, cx);
 2484
 2485        assert_eq!(
 2486            editor.text(cx),
 2487            indoc! {"
 2488                a = 1
 2489                b = 2
 2490
 2491                const c: usize = 3;
 2492            "}
 2493        );
 2494
 2495        select_ranges(
 2496            &mut editor,
 2497            indoc! {"
 2498                «aˇ» = 1
 2499                b = 2
 2500
 2501                «const c:ˇ» usize = 3;
 2502            "},
 2503            cx,
 2504        );
 2505
 2506        editor.tab(&Tab, cx);
 2507        assert_text_with_selections(
 2508            &mut editor,
 2509            indoc! {"
 2510                  «aˇ» = 1
 2511                b = 2
 2512
 2513                    «const c:ˇ» usize = 3;
 2514            "},
 2515            cx,
 2516        );
 2517        editor.tab_prev(&TabPrev, cx);
 2518        assert_text_with_selections(
 2519            &mut editor,
 2520            indoc! {"
 2521                «aˇ» = 1
 2522                b = 2
 2523
 2524                «const c:ˇ» usize = 3;
 2525            "},
 2526            cx,
 2527        );
 2528
 2529        editor
 2530    });
 2531}
 2532
 2533#[gpui::test]
 2534async fn test_backspace(cx: &mut gpui::TestAppContext) {
 2535    init_test(cx, |_| {});
 2536
 2537    let mut cx = EditorTestContext::new(cx).await;
 2538
 2539    // Basic backspace
 2540    cx.set_state(indoc! {"
 2541        onˇe two three
 2542        fou«rˇ» five six
 2543        seven «ˇeight nine
 2544        »ten
 2545    "});
 2546    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2547    cx.assert_editor_state(indoc! {"
 2548        oˇe two three
 2549        fouˇ five six
 2550        seven ˇten
 2551    "});
 2552
 2553    // Test backspace inside and around indents
 2554    cx.set_state(indoc! {"
 2555        zero
 2556            ˇone
 2557                ˇtwo
 2558            ˇ ˇ ˇ  three
 2559        ˇ  ˇ  four
 2560    "});
 2561    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2562    cx.assert_editor_state(indoc! {"
 2563        zero
 2564        ˇone
 2565            ˇtwo
 2566        ˇ  threeˇ  four
 2567    "});
 2568
 2569    // Test backspace with line_mode set to true
 2570    cx.update_editor(|e, _| e.selections.line_mode = true);
 2571    cx.set_state(indoc! {"
 2572        The ˇquick ˇbrown
 2573        fox jumps over
 2574        the lazy dog
 2575        ˇThe qu«ick bˇ»rown"});
 2576    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2577    cx.assert_editor_state(indoc! {"
 2578        ˇfox jumps over
 2579        the lazy dogˇ"});
 2580}
 2581
 2582#[gpui::test]
 2583async fn test_delete(cx: &mut gpui::TestAppContext) {
 2584    init_test(cx, |_| {});
 2585
 2586    let mut cx = EditorTestContext::new(cx).await;
 2587    cx.set_state(indoc! {"
 2588        onˇe two three
 2589        fou«rˇ» five six
 2590        seven «ˇeight nine
 2591        »ten
 2592    "});
 2593    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 2594    cx.assert_editor_state(indoc! {"
 2595        onˇ two three
 2596        fouˇ five six
 2597        seven ˇten
 2598    "});
 2599
 2600    // Test backspace with line_mode set to true
 2601    cx.update_editor(|e, _| e.selections.line_mode = true);
 2602    cx.set_state(indoc! {"
 2603        The ˇquick ˇbrown
 2604        fox «ˇjum»ps over
 2605        the lazy dog
 2606        ˇThe qu«ick bˇ»rown"});
 2607    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 2608    cx.assert_editor_state("ˇthe lazy dogˇ");
 2609}
 2610
 2611#[gpui::test]
 2612fn test_delete_line(cx: &mut TestAppContext) {
 2613    init_test(cx, |_| {});
 2614
 2615    let view = cx.add_window(|cx| {
 2616        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2617        build_editor(buffer, cx)
 2618    });
 2619    _ = view.update(cx, |view, cx| {
 2620        view.change_selections(None, cx, |s| {
 2621            s.select_display_ranges([
 2622                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 2623                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 2624                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 2625            ])
 2626        });
 2627        view.delete_line(&DeleteLine, cx);
 2628        assert_eq!(view.display_text(cx), "ghi");
 2629        assert_eq!(
 2630            view.selections.display_ranges(cx),
 2631            vec![
 2632                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 2633                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 2634            ]
 2635        );
 2636    });
 2637
 2638    let view = cx.add_window(|cx| {
 2639        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 2640        build_editor(buffer, cx)
 2641    });
 2642    _ = view.update(cx, |view, cx| {
 2643        view.change_selections(None, cx, |s| {
 2644            s.select_display_ranges([
 2645                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 2646            ])
 2647        });
 2648        view.delete_line(&DeleteLine, cx);
 2649        assert_eq!(view.display_text(cx), "ghi\n");
 2650        assert_eq!(
 2651            view.selections.display_ranges(cx),
 2652            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 2653        );
 2654    });
 2655}
 2656
 2657#[gpui::test]
 2658fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 2659    init_test(cx, |_| {});
 2660
 2661    cx.add_window(|cx| {
 2662        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2663        let mut editor = build_editor(buffer.clone(), cx);
 2664        let buffer = buffer.read(cx).as_singleton().unwrap();
 2665
 2666        assert_eq!(
 2667            editor.selections.ranges::<Point>(cx),
 2668            &[Point::new(0, 0)..Point::new(0, 0)]
 2669        );
 2670
 2671        // When on single line, replace newline at end by space
 2672        editor.join_lines(&JoinLines, cx);
 2673        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2674        assert_eq!(
 2675            editor.selections.ranges::<Point>(cx),
 2676            &[Point::new(0, 3)..Point::new(0, 3)]
 2677        );
 2678
 2679        // When multiple lines are selected, remove newlines that are spanned by the selection
 2680        editor.change_selections(None, cx, |s| {
 2681            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 2682        });
 2683        editor.join_lines(&JoinLines, cx);
 2684        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 2685        assert_eq!(
 2686            editor.selections.ranges::<Point>(cx),
 2687            &[Point::new(0, 11)..Point::new(0, 11)]
 2688        );
 2689
 2690        // Undo should be transactional
 2691        editor.undo(&Undo, cx);
 2692        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 2693        assert_eq!(
 2694            editor.selections.ranges::<Point>(cx),
 2695            &[Point::new(0, 5)..Point::new(2, 2)]
 2696        );
 2697
 2698        // When joining an empty line don't insert a space
 2699        editor.change_selections(None, cx, |s| {
 2700            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 2701        });
 2702        editor.join_lines(&JoinLines, cx);
 2703        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 2704        assert_eq!(
 2705            editor.selections.ranges::<Point>(cx),
 2706            [Point::new(2, 3)..Point::new(2, 3)]
 2707        );
 2708
 2709        // We can remove trailing newlines
 2710        editor.join_lines(&JoinLines, cx);
 2711        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2712        assert_eq!(
 2713            editor.selections.ranges::<Point>(cx),
 2714            [Point::new(2, 3)..Point::new(2, 3)]
 2715        );
 2716
 2717        // We don't blow up on the last line
 2718        editor.join_lines(&JoinLines, cx);
 2719        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 2720        assert_eq!(
 2721            editor.selections.ranges::<Point>(cx),
 2722            [Point::new(2, 3)..Point::new(2, 3)]
 2723        );
 2724
 2725        // reset to test indentation
 2726        editor.buffer.update(cx, |buffer, cx| {
 2727            buffer.edit(
 2728                [
 2729                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 2730                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 2731                ],
 2732                None,
 2733                cx,
 2734            )
 2735        });
 2736
 2737        // We remove any leading spaces
 2738        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 2739        editor.change_selections(None, cx, |s| {
 2740            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 2741        });
 2742        editor.join_lines(&JoinLines, cx);
 2743        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 2744
 2745        // We don't insert a space for a line containing only spaces
 2746        editor.join_lines(&JoinLines, cx);
 2747        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 2748
 2749        // We ignore any leading tabs
 2750        editor.join_lines(&JoinLines, cx);
 2751        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 2752
 2753        editor
 2754    });
 2755}
 2756
 2757#[gpui::test]
 2758fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 2759    init_test(cx, |_| {});
 2760
 2761    cx.add_window(|cx| {
 2762        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 2763        let mut editor = build_editor(buffer.clone(), cx);
 2764        let buffer = buffer.read(cx).as_singleton().unwrap();
 2765
 2766        editor.change_selections(None, cx, |s| {
 2767            s.select_ranges([
 2768                Point::new(0, 2)..Point::new(1, 1),
 2769                Point::new(1, 2)..Point::new(1, 2),
 2770                Point::new(3, 1)..Point::new(3, 2),
 2771            ])
 2772        });
 2773
 2774        editor.join_lines(&JoinLines, cx);
 2775        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 2776
 2777        assert_eq!(
 2778            editor.selections.ranges::<Point>(cx),
 2779            [
 2780                Point::new(0, 7)..Point::new(0, 7),
 2781                Point::new(1, 3)..Point::new(1, 3)
 2782            ]
 2783        );
 2784        editor
 2785    });
 2786}
 2787
 2788#[gpui::test]
 2789async fn test_join_lines_with_git_diff_base(
 2790    executor: BackgroundExecutor,
 2791    cx: &mut gpui::TestAppContext,
 2792) {
 2793    init_test(cx, |_| {});
 2794
 2795    let mut cx = EditorTestContext::new(cx).await;
 2796
 2797    let diff_base = r#"
 2798        Line 0
 2799        Line 1
 2800        Line 2
 2801        Line 3
 2802        "#
 2803    .unindent();
 2804
 2805    cx.set_state(
 2806        &r#"
 2807        ˇLine 0
 2808        Line 1
 2809        Line 2
 2810        Line 3
 2811        "#
 2812        .unindent(),
 2813    );
 2814
 2815    cx.set_diff_base(Some(&diff_base));
 2816    executor.run_until_parked();
 2817
 2818    // Join lines
 2819    cx.update_editor(|editor, cx| {
 2820        editor.join_lines(&JoinLines, cx);
 2821    });
 2822    executor.run_until_parked();
 2823
 2824    cx.assert_editor_state(
 2825        &r#"
 2826        Line 0ˇ Line 1
 2827        Line 2
 2828        Line 3
 2829        "#
 2830        .unindent(),
 2831    );
 2832    // Join again
 2833    cx.update_editor(|editor, cx| {
 2834        editor.join_lines(&JoinLines, cx);
 2835    });
 2836    executor.run_until_parked();
 2837
 2838    cx.assert_editor_state(
 2839        &r#"
 2840        Line 0 Line 1ˇ Line 2
 2841        Line 3
 2842        "#
 2843        .unindent(),
 2844    );
 2845}
 2846
 2847#[gpui::test]
 2848async fn test_custom_newlines_cause_no_false_positive_diffs(
 2849    executor: BackgroundExecutor,
 2850    cx: &mut gpui::TestAppContext,
 2851) {
 2852    init_test(cx, |_| {});
 2853    let mut cx = EditorTestContext::new(cx).await;
 2854    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 2855    cx.set_diff_base(Some("Line 0\r\nLine 1\r\nLine 2\r\nLine 3"));
 2856    executor.run_until_parked();
 2857
 2858    cx.update_editor(|editor, cx| {
 2859        assert_eq!(
 2860            editor
 2861                .buffer()
 2862                .read(cx)
 2863                .snapshot(cx)
 2864                .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
 2865                .collect::<Vec<_>>(),
 2866            Vec::new(),
 2867            "Should not have any diffs for files with custom newlines"
 2868        );
 2869    });
 2870}
 2871
 2872#[gpui::test]
 2873async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 2874    init_test(cx, |_| {});
 2875
 2876    let mut cx = EditorTestContext::new(cx).await;
 2877
 2878    // Test sort_lines_case_insensitive()
 2879    cx.set_state(indoc! {"
 2880        «z
 2881        y
 2882        x
 2883        Z
 2884        Y
 2885        Xˇ»
 2886    "});
 2887    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889        «x
 2890        X
 2891        y
 2892        Y
 2893        z
 2894        Zˇ»
 2895    "});
 2896
 2897    // Test reverse_lines()
 2898    cx.set_state(indoc! {"
 2899        «5
 2900        4
 2901        3
 2902        2
 2903        1ˇ»
 2904    "});
 2905    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 2906    cx.assert_editor_state(indoc! {"
 2907        «1
 2908        2
 2909        3
 2910        4
 2911        5ˇ»
 2912    "});
 2913
 2914    // Skip testing shuffle_line()
 2915
 2916    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 2917    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 2918
 2919    // Don't manipulate when cursor is on single line, but expand the selection
 2920    cx.set_state(indoc! {"
 2921        ddˇdd
 2922        ccc
 2923        bb
 2924        a
 2925    "});
 2926    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2927    cx.assert_editor_state(indoc! {"
 2928        «ddddˇ»
 2929        ccc
 2930        bb
 2931        a
 2932    "});
 2933
 2934    // Basic manipulate case
 2935    // Start selection moves to column 0
 2936    // End of selection shrinks to fit shorter line
 2937    cx.set_state(indoc! {"
 2938        dd«d
 2939        ccc
 2940        bb
 2941        aaaaaˇ»
 2942    "});
 2943    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2944    cx.assert_editor_state(indoc! {"
 2945        «aaaaa
 2946        bb
 2947        ccc
 2948        dddˇ»
 2949    "});
 2950
 2951    // Manipulate case with newlines
 2952    cx.set_state(indoc! {"
 2953        dd«d
 2954        ccc
 2955
 2956        bb
 2957        aaaaa
 2958
 2959        ˇ»
 2960    "});
 2961    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 2962    cx.assert_editor_state(indoc! {"
 2963        «
 2964
 2965        aaaaa
 2966        bb
 2967        ccc
 2968        dddˇ»
 2969
 2970    "});
 2971
 2972    // Adding new line
 2973    cx.set_state(indoc! {"
 2974        aa«a
 2975        bbˇ»b
 2976    "});
 2977    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 2978    cx.assert_editor_state(indoc! {"
 2979        «aaa
 2980        bbb
 2981        added_lineˇ»
 2982    "});
 2983
 2984    // Removing line
 2985    cx.set_state(indoc! {"
 2986        aa«a
 2987        bbbˇ»
 2988    "});
 2989    cx.update_editor(|e, cx| {
 2990        e.manipulate_lines(cx, |lines| {
 2991            lines.pop();
 2992        })
 2993    });
 2994    cx.assert_editor_state(indoc! {"
 2995        «aaaˇ»
 2996    "});
 2997
 2998    // Removing all lines
 2999    cx.set_state(indoc! {"
 3000        aa«a
 3001        bbbˇ»
 3002    "});
 3003    cx.update_editor(|e, cx| {
 3004        e.manipulate_lines(cx, |lines| {
 3005            lines.drain(..);
 3006        })
 3007    });
 3008    cx.assert_editor_state(indoc! {"
 3009        ˇ
 3010    "});
 3011}
 3012
 3013#[gpui::test]
 3014async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3015    init_test(cx, |_| {});
 3016
 3017    let mut cx = EditorTestContext::new(cx).await;
 3018
 3019    // Consider continuous selection as single selection
 3020    cx.set_state(indoc! {"
 3021        Aaa«aa
 3022        cˇ»c«c
 3023        bb
 3024        aaaˇ»aa
 3025    "});
 3026    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3027    cx.assert_editor_state(indoc! {"
 3028        «Aaaaa
 3029        ccc
 3030        bb
 3031        aaaaaˇ»
 3032    "});
 3033
 3034    cx.set_state(indoc! {"
 3035        Aaa«aa
 3036        cˇ»c«c
 3037        bb
 3038        aaaˇ»aa
 3039    "});
 3040    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3041    cx.assert_editor_state(indoc! {"
 3042        «Aaaaa
 3043        ccc
 3044        bbˇ»
 3045    "});
 3046
 3047    // Consider non continuous selection as distinct dedup operations
 3048    cx.set_state(indoc! {"
 3049        «aaaaa
 3050        bb
 3051        aaaaa
 3052        aaaaaˇ»
 3053
 3054        aaa«aaˇ»
 3055    "});
 3056    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3057    cx.assert_editor_state(indoc! {"
 3058        «aaaaa
 3059        bbˇ»
 3060
 3061        «aaaaaˇ»
 3062    "});
 3063}
 3064
 3065#[gpui::test]
 3066async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3067    init_test(cx, |_| {});
 3068
 3069    let mut cx = EditorTestContext::new(cx).await;
 3070
 3071    cx.set_state(indoc! {"
 3072        «Aaa
 3073        aAa
 3074        Aaaˇ»
 3075    "});
 3076    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3077    cx.assert_editor_state(indoc! {"
 3078        «Aaa
 3079        aAaˇ»
 3080    "});
 3081
 3082    cx.set_state(indoc! {"
 3083        «Aaa
 3084        aAa
 3085        aaAˇ»
 3086    "});
 3087    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3088    cx.assert_editor_state(indoc! {"
 3089        «Aaaˇ»
 3090    "});
 3091}
 3092
 3093#[gpui::test]
 3094async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3095    init_test(cx, |_| {});
 3096
 3097    let mut cx = EditorTestContext::new(cx).await;
 3098
 3099    // Manipulate with multiple selections on a single line
 3100    cx.set_state(indoc! {"
 3101        dd«dd
 3102        cˇ»c«c
 3103        bb
 3104        aaaˇ»aa
 3105    "});
 3106    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3107    cx.assert_editor_state(indoc! {"
 3108        «aaaaa
 3109        bb
 3110        ccc
 3111        ddddˇ»
 3112    "});
 3113
 3114    // Manipulate with multiple disjoin selections
 3115    cx.set_state(indoc! {"
 3116 3117        4
 3118        3
 3119        2
 3120        1ˇ»
 3121
 3122        dd«dd
 3123        ccc
 3124        bb
 3125        aaaˇ»aa
 3126    "});
 3127    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        «1
 3130        2
 3131        3
 3132        4
 3133        5ˇ»
 3134
 3135        «aaaaa
 3136        bb
 3137        ccc
 3138        ddddˇ»
 3139    "});
 3140
 3141    // Adding lines on each selection
 3142    cx.set_state(indoc! {"
 3143 3144        1ˇ»
 3145
 3146        bb«bb
 3147        aaaˇ»aa
 3148    "});
 3149    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3150    cx.assert_editor_state(indoc! {"
 3151        «2
 3152        1
 3153        added lineˇ»
 3154
 3155        «bbbb
 3156        aaaaa
 3157        added lineˇ»
 3158    "});
 3159
 3160    // Removing lines on each selection
 3161    cx.set_state(indoc! {"
 3162 3163        1ˇ»
 3164
 3165        bb«bb
 3166        aaaˇ»aa
 3167    "});
 3168    cx.update_editor(|e, cx| {
 3169        e.manipulate_lines(cx, |lines| {
 3170            lines.pop();
 3171        })
 3172    });
 3173    cx.assert_editor_state(indoc! {"
 3174        «2ˇ»
 3175
 3176        «bbbbˇ»
 3177    "});
 3178}
 3179
 3180#[gpui::test]
 3181async fn test_manipulate_text(cx: &mut TestAppContext) {
 3182    init_test(cx, |_| {});
 3183
 3184    let mut cx = EditorTestContext::new(cx).await;
 3185
 3186    // Test convert_to_upper_case()
 3187    cx.set_state(indoc! {"
 3188        «hello worldˇ»
 3189    "});
 3190    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3191    cx.assert_editor_state(indoc! {"
 3192        «HELLO WORLDˇ»
 3193    "});
 3194
 3195    // Test convert_to_lower_case()
 3196    cx.set_state(indoc! {"
 3197        «HELLO WORLDˇ»
 3198    "});
 3199    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3200    cx.assert_editor_state(indoc! {"
 3201        «hello worldˇ»
 3202    "});
 3203
 3204    // Test multiple line, single selection case
 3205    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3206    cx.set_state(indoc! {"
 3207        «The quick brown
 3208        fox jumps over
 3209        the lazy dogˇ»
 3210    "});
 3211    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3212    cx.assert_editor_state(indoc! {"
 3213        «The Quick Brown
 3214        Fox Jumps Over
 3215        The Lazy Dogˇ»
 3216    "});
 3217
 3218    // Test multiple line, single selection case
 3219    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
 3220    cx.set_state(indoc! {"
 3221        «The quick brown
 3222        fox jumps over
 3223        the lazy dogˇ»
 3224    "});
 3225    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3226    cx.assert_editor_state(indoc! {"
 3227        «TheQuickBrown
 3228        FoxJumpsOver
 3229        TheLazyDogˇ»
 3230    "});
 3231
 3232    // From here on out, test more complex cases of manipulate_text()
 3233
 3234    // Test no selection case - should affect words cursors are in
 3235    // Cursor at beginning, middle, and end of word
 3236    cx.set_state(indoc! {"
 3237        ˇhello big beauˇtiful worldˇ
 3238    "});
 3239    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3240    cx.assert_editor_state(indoc! {"
 3241        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3242    "});
 3243
 3244    // Test multiple selections on a single line and across multiple lines
 3245    cx.set_state(indoc! {"
 3246        «Theˇ» quick «brown
 3247        foxˇ» jumps «overˇ»
 3248        the «lazyˇ» dog
 3249    "});
 3250    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        «THEˇ» quick «BROWN
 3253        FOXˇ» jumps «OVERˇ»
 3254        the «LAZYˇ» dog
 3255    "});
 3256
 3257    // Test case where text length grows
 3258    cx.set_state(indoc! {"
 3259        «tschüߡ»
 3260    "});
 3261    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3262    cx.assert_editor_state(indoc! {"
 3263        «TSCHÜSSˇ»
 3264    "});
 3265
 3266    // Test to make sure we don't crash when text shrinks
 3267    cx.set_state(indoc! {"
 3268        aaa_bbbˇ
 3269    "});
 3270    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3271    cx.assert_editor_state(indoc! {"
 3272        «aaaBbbˇ»
 3273    "});
 3274
 3275    // Test to make sure we all aware of the fact that each word can grow and shrink
 3276    // Final selections should be aware of this fact
 3277    cx.set_state(indoc! {"
 3278        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3279    "});
 3280    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3281    cx.assert_editor_state(indoc! {"
 3282        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3283    "});
 3284
 3285    cx.set_state(indoc! {"
 3286        «hElLo, WoRld!ˇ»
 3287    "});
 3288    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3289    cx.assert_editor_state(indoc! {"
 3290        «HeLlO, wOrLD!ˇ»
 3291    "});
 3292}
 3293
 3294#[gpui::test]
 3295fn test_duplicate_line(cx: &mut TestAppContext) {
 3296    init_test(cx, |_| {});
 3297
 3298    let view = cx.add_window(|cx| {
 3299        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3300        build_editor(buffer, cx)
 3301    });
 3302    _ = view.update(cx, |view, cx| {
 3303        view.change_selections(None, cx, |s| {
 3304            s.select_display_ranges([
 3305                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3306                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3307                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3308                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3309            ])
 3310        });
 3311        view.duplicate_line_down(&DuplicateLineDown, cx);
 3312        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3313        assert_eq!(
 3314            view.selections.display_ranges(cx),
 3315            vec![
 3316                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3317                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3318                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3319                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3320            ]
 3321        );
 3322    });
 3323
 3324    let view = cx.add_window(|cx| {
 3325        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3326        build_editor(buffer, cx)
 3327    });
 3328    _ = view.update(cx, |view, cx| {
 3329        view.change_selections(None, cx, |s| {
 3330            s.select_display_ranges([
 3331                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3332                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3333            ])
 3334        });
 3335        view.duplicate_line_down(&DuplicateLineDown, cx);
 3336        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3337        assert_eq!(
 3338            view.selections.display_ranges(cx),
 3339            vec![
 3340                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3341                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3342            ]
 3343        );
 3344    });
 3345
 3346    // With `move_upwards` the selections stay in place, except for
 3347    // the lines inserted above them
 3348    let view = cx.add_window(|cx| {
 3349        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3350        build_editor(buffer, cx)
 3351    });
 3352    _ = view.update(cx, |view, cx| {
 3353        view.change_selections(None, cx, |s| {
 3354            s.select_display_ranges([
 3355                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3356                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3357                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3358                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3359            ])
 3360        });
 3361        view.duplicate_line_up(&DuplicateLineUp, cx);
 3362        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3363        assert_eq!(
 3364            view.selections.display_ranges(cx),
 3365            vec![
 3366                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3367                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3368                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3369                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3370            ]
 3371        );
 3372    });
 3373
 3374    let view = cx.add_window(|cx| {
 3375        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3376        build_editor(buffer, cx)
 3377    });
 3378    _ = view.update(cx, |view, cx| {
 3379        view.change_selections(None, cx, |s| {
 3380            s.select_display_ranges([
 3381                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3383            ])
 3384        });
 3385        view.duplicate_line_up(&DuplicateLineUp, cx);
 3386        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3387        assert_eq!(
 3388            view.selections.display_ranges(cx),
 3389            vec![
 3390                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3391                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3392            ]
 3393        );
 3394    });
 3395}
 3396
 3397#[gpui::test]
 3398fn test_move_line_up_down(cx: &mut TestAppContext) {
 3399    init_test(cx, |_| {});
 3400
 3401    let view = cx.add_window(|cx| {
 3402        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3403        build_editor(buffer, cx)
 3404    });
 3405    _ = view.update(cx, |view, cx| {
 3406        view.fold_ranges(
 3407            vec![
 3408                Point::new(0, 2)..Point::new(1, 2),
 3409                Point::new(2, 3)..Point::new(4, 1),
 3410                Point::new(7, 0)..Point::new(8, 4),
 3411            ],
 3412            true,
 3413            cx,
 3414        );
 3415        view.change_selections(None, cx, |s| {
 3416            s.select_display_ranges([
 3417                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3418                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3419                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3420                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3421            ])
 3422        });
 3423        assert_eq!(
 3424            view.display_text(cx),
 3425            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3426        );
 3427
 3428        view.move_line_up(&MoveLineUp, cx);
 3429        assert_eq!(
 3430            view.display_text(cx),
 3431            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3432        );
 3433        assert_eq!(
 3434            view.selections.display_ranges(cx),
 3435            vec![
 3436                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3437                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3438                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3439                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3440            ]
 3441        );
 3442    });
 3443
 3444    _ = view.update(cx, |view, cx| {
 3445        view.move_line_down(&MoveLineDown, cx);
 3446        assert_eq!(
 3447            view.display_text(cx),
 3448            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3449        );
 3450        assert_eq!(
 3451            view.selections.display_ranges(cx),
 3452            vec![
 3453                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3454                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3455                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3456                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3457            ]
 3458        );
 3459    });
 3460
 3461    _ = view.update(cx, |view, cx| {
 3462        view.move_line_down(&MoveLineDown, cx);
 3463        assert_eq!(
 3464            view.display_text(cx),
 3465            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3466        );
 3467        assert_eq!(
 3468            view.selections.display_ranges(cx),
 3469            vec![
 3470                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3471                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3472                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3473                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3474            ]
 3475        );
 3476    });
 3477
 3478    _ = view.update(cx, |view, cx| {
 3479        view.move_line_up(&MoveLineUp, cx);
 3480        assert_eq!(
 3481            view.display_text(cx),
 3482            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 3483        );
 3484        assert_eq!(
 3485            view.selections.display_ranges(cx),
 3486            vec![
 3487                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3488                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3489                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3490                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3491            ]
 3492        );
 3493    });
 3494}
 3495
 3496#[gpui::test]
 3497fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 3498    init_test(cx, |_| {});
 3499
 3500    let editor = cx.add_window(|cx| {
 3501        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3502        build_editor(buffer, cx)
 3503    });
 3504    _ = editor.update(cx, |editor, cx| {
 3505        let snapshot = editor.buffer.read(cx).snapshot(cx);
 3506        editor.insert_blocks(
 3507            [BlockProperties {
 3508                style: BlockStyle::Fixed,
 3509                position: snapshot.anchor_after(Point::new(2, 0)),
 3510                disposition: BlockDisposition::Below,
 3511                height: 1,
 3512                render: Box::new(|_| div().into_any()),
 3513            }],
 3514            Some(Autoscroll::fit()),
 3515            cx,
 3516        );
 3517        editor.change_selections(None, cx, |s| {
 3518            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 3519        });
 3520        editor.move_line_down(&MoveLineDown, cx);
 3521    });
 3522}
 3523
 3524#[gpui::test]
 3525fn test_transpose(cx: &mut TestAppContext) {
 3526    init_test(cx, |_| {});
 3527
 3528    _ = cx.add_window(|cx| {
 3529        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 3530        editor.set_style(EditorStyle::default(), cx);
 3531        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 3532        editor.transpose(&Default::default(), cx);
 3533        assert_eq!(editor.text(cx), "bac");
 3534        assert_eq!(editor.selections.ranges(cx), [2..2]);
 3535
 3536        editor.transpose(&Default::default(), cx);
 3537        assert_eq!(editor.text(cx), "bca");
 3538        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3539
 3540        editor.transpose(&Default::default(), cx);
 3541        assert_eq!(editor.text(cx), "bac");
 3542        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3543
 3544        editor
 3545    });
 3546
 3547    _ = cx.add_window(|cx| {
 3548        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3549        editor.set_style(EditorStyle::default(), cx);
 3550        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 3551        editor.transpose(&Default::default(), cx);
 3552        assert_eq!(editor.text(cx), "acb\nde");
 3553        assert_eq!(editor.selections.ranges(cx), [3..3]);
 3554
 3555        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3556        editor.transpose(&Default::default(), cx);
 3557        assert_eq!(editor.text(cx), "acbd\ne");
 3558        assert_eq!(editor.selections.ranges(cx), [5..5]);
 3559
 3560        editor.transpose(&Default::default(), cx);
 3561        assert_eq!(editor.text(cx), "acbde\n");
 3562        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3563
 3564        editor.transpose(&Default::default(), cx);
 3565        assert_eq!(editor.text(cx), "acbd\ne");
 3566        assert_eq!(editor.selections.ranges(cx), [6..6]);
 3567
 3568        editor
 3569    });
 3570
 3571    _ = cx.add_window(|cx| {
 3572        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 3573        editor.set_style(EditorStyle::default(), cx);
 3574        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 3575        editor.transpose(&Default::default(), cx);
 3576        assert_eq!(editor.text(cx), "bacd\ne");
 3577        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 3578
 3579        editor.transpose(&Default::default(), cx);
 3580        assert_eq!(editor.text(cx), "bcade\n");
 3581        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 3582
 3583        editor.transpose(&Default::default(), cx);
 3584        assert_eq!(editor.text(cx), "bcda\ne");
 3585        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3586
 3587        editor.transpose(&Default::default(), cx);
 3588        assert_eq!(editor.text(cx), "bcade\n");
 3589        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 3590
 3591        editor.transpose(&Default::default(), cx);
 3592        assert_eq!(editor.text(cx), "bcaed\n");
 3593        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 3594
 3595        editor
 3596    });
 3597
 3598    _ = cx.add_window(|cx| {
 3599        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 3600        editor.set_style(EditorStyle::default(), cx);
 3601        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 3602        editor.transpose(&Default::default(), cx);
 3603        assert_eq!(editor.text(cx), "🏀🍐✋");
 3604        assert_eq!(editor.selections.ranges(cx), [8..8]);
 3605
 3606        editor.transpose(&Default::default(), cx);
 3607        assert_eq!(editor.text(cx), "🏀✋🍐");
 3608        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3609
 3610        editor.transpose(&Default::default(), cx);
 3611        assert_eq!(editor.text(cx), "🏀🍐✋");
 3612        assert_eq!(editor.selections.ranges(cx), [11..11]);
 3613
 3614        editor
 3615    });
 3616}
 3617
 3618#[gpui::test]
 3619async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 3620    init_test(cx, |_| {});
 3621
 3622    let mut cx = EditorTestContext::new(cx).await;
 3623
 3624    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 3625    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3626    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 3627
 3628    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 3629    cx.set_state("two ˇfour ˇsix ˇ");
 3630    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3631    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 3632
 3633    // Paste again but with only two cursors. Since the number of cursors doesn't
 3634    // match the number of slices in the clipboard, the entire clipboard text
 3635    // is pasted at each cursor.
 3636    cx.set_state("ˇtwo one✅ four three six five ˇ");
 3637    cx.update_editor(|e, cx| {
 3638        e.handle_input("( ", cx);
 3639        e.paste(&Paste, cx);
 3640        e.handle_input(") ", cx);
 3641    });
 3642    cx.assert_editor_state(
 3643        &([
 3644            "( one✅ ",
 3645            "three ",
 3646            "five ) ˇtwo one✅ four three six five ( one✅ ",
 3647            "three ",
 3648            "five ) ˇ",
 3649        ]
 3650        .join("\n")),
 3651    );
 3652
 3653    // Cut with three selections, one of which is full-line.
 3654    cx.set_state(indoc! {"
 3655        1«2ˇ»3
 3656        4ˇ567
 3657        «8ˇ»9"});
 3658    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3659    cx.assert_editor_state(indoc! {"
 3660        1ˇ3
 3661        ˇ9"});
 3662
 3663    // Paste with three selections, noticing how the copied selection that was full-line
 3664    // gets inserted before the second cursor.
 3665    cx.set_state(indoc! {"
 3666        1ˇ3
 3667 3668        «oˇ»ne"});
 3669    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3670    cx.assert_editor_state(indoc! {"
 3671        12ˇ3
 3672        4567
 3673 3674        8ˇne"});
 3675
 3676    // Copy with a single cursor only, which writes the whole line into the clipboard.
 3677    cx.set_state(indoc! {"
 3678        The quick brown
 3679        fox juˇmps over
 3680        the lazy dog"});
 3681    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 3682    assert_eq!(
 3683        cx.read_from_clipboard().map(|item| item.text().to_owned()),
 3684        Some("fox jumps over\n".to_owned())
 3685    );
 3686
 3687    // Paste with three selections, noticing how the copied full-line selection is inserted
 3688    // before the empty selections but replaces the selection that is non-empty.
 3689    cx.set_state(indoc! {"
 3690        Tˇhe quick brown
 3691        «foˇ»x jumps over
 3692        tˇhe lazy dog"});
 3693    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        fox jumps over
 3696        Tˇhe quick brown
 3697        fox jumps over
 3698        ˇx jumps over
 3699        fox jumps over
 3700        tˇhe lazy dog"});
 3701}
 3702
 3703#[gpui::test]
 3704async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 3705    init_test(cx, |_| {});
 3706
 3707    let mut cx = EditorTestContext::new(cx).await;
 3708    let language = Arc::new(Language::new(
 3709        LanguageConfig::default(),
 3710        Some(tree_sitter_rust::language()),
 3711    ));
 3712    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3713
 3714    // Cut an indented block, without the leading whitespace.
 3715    cx.set_state(indoc! {"
 3716        const a: B = (
 3717            c(),
 3718            «d(
 3719                e,
 3720                f
 3721            )ˇ»
 3722        );
 3723    "});
 3724    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3725    cx.assert_editor_state(indoc! {"
 3726        const a: B = (
 3727            c(),
 3728            ˇ
 3729        );
 3730    "});
 3731
 3732    // Paste it at the same position.
 3733    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3734    cx.assert_editor_state(indoc! {"
 3735        const a: B = (
 3736            c(),
 3737            d(
 3738                e,
 3739                f
 3740 3741        );
 3742    "});
 3743
 3744    // Paste it at a line with a lower indent level.
 3745    cx.set_state(indoc! {"
 3746        ˇ
 3747        const a: B = (
 3748            c(),
 3749        );
 3750    "});
 3751    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3752    cx.assert_editor_state(indoc! {"
 3753        d(
 3754            e,
 3755            f
 3756 3757        const a: B = (
 3758            c(),
 3759        );
 3760    "});
 3761
 3762    // Cut an indented block, with the leading whitespace.
 3763    cx.set_state(indoc! {"
 3764        const a: B = (
 3765            c(),
 3766        «    d(
 3767                e,
 3768                f
 3769            )
 3770        ˇ»);
 3771    "});
 3772    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 3773    cx.assert_editor_state(indoc! {"
 3774        const a: B = (
 3775            c(),
 3776        ˇ);
 3777    "});
 3778
 3779    // Paste it at the same position.
 3780    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3781    cx.assert_editor_state(indoc! {"
 3782        const a: B = (
 3783            c(),
 3784            d(
 3785                e,
 3786                f
 3787            )
 3788        ˇ);
 3789    "});
 3790
 3791    // Paste it at a line with a higher indent level.
 3792    cx.set_state(indoc! {"
 3793        const a: B = (
 3794            c(),
 3795            d(
 3796                e,
 3797 3798            )
 3799        );
 3800    "});
 3801    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 3802    cx.assert_editor_state(indoc! {"
 3803        const a: B = (
 3804            c(),
 3805            d(
 3806                e,
 3807                f    d(
 3808                    e,
 3809                    f
 3810                )
 3811        ˇ
 3812            )
 3813        );
 3814    "});
 3815}
 3816
 3817#[gpui::test]
 3818fn test_select_all(cx: &mut TestAppContext) {
 3819    init_test(cx, |_| {});
 3820
 3821    let view = cx.add_window(|cx| {
 3822        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 3823        build_editor(buffer, cx)
 3824    });
 3825    _ = view.update(cx, |view, cx| {
 3826        view.select_all(&SelectAll, cx);
 3827        assert_eq!(
 3828            view.selections.display_ranges(cx),
 3829            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 3830        );
 3831    });
 3832}
 3833
 3834#[gpui::test]
 3835fn test_select_line(cx: &mut TestAppContext) {
 3836    init_test(cx, |_| {});
 3837
 3838    let view = cx.add_window(|cx| {
 3839        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 3840        build_editor(buffer, cx)
 3841    });
 3842    _ = view.update(cx, |view, cx| {
 3843        view.change_selections(None, cx, |s| {
 3844            s.select_display_ranges([
 3845                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3846                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3847                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3848                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 3849            ])
 3850        });
 3851        view.select_line(&SelectLine, cx);
 3852        assert_eq!(
 3853            view.selections.display_ranges(cx),
 3854            vec![
 3855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3856                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 3857            ]
 3858        );
 3859    });
 3860
 3861    _ = view.update(cx, |view, cx| {
 3862        view.select_line(&SelectLine, cx);
 3863        assert_eq!(
 3864            view.selections.display_ranges(cx),
 3865            vec![
 3866                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3867                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 3868            ]
 3869        );
 3870    });
 3871
 3872    _ = view.update(cx, |view, cx| {
 3873        view.select_line(&SelectLine, cx);
 3874        assert_eq!(
 3875            view.selections.display_ranges(cx),
 3876            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 3877        );
 3878    });
 3879}
 3880
 3881#[gpui::test]
 3882fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 3883    init_test(cx, |_| {});
 3884
 3885    let view = cx.add_window(|cx| {
 3886        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 3887        build_editor(buffer, cx)
 3888    });
 3889    _ = view.update(cx, |view, cx| {
 3890        view.fold_ranges(
 3891            vec![
 3892                Point::new(0, 2)..Point::new(1, 2),
 3893                Point::new(2, 3)..Point::new(4, 1),
 3894                Point::new(7, 0)..Point::new(8, 4),
 3895            ],
 3896            true,
 3897            cx,
 3898        );
 3899        view.change_selections(None, cx, |s| {
 3900            s.select_display_ranges([
 3901                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3902                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3903                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3904                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 3905            ])
 3906        });
 3907        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 3908    });
 3909
 3910    _ = view.update(cx, |view, cx| {
 3911        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 3912        assert_eq!(
 3913            view.display_text(cx),
 3914            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 3915        );
 3916        assert_eq!(
 3917            view.selections.display_ranges(cx),
 3918            [
 3919                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3920                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3921                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3922                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 3923            ]
 3924        );
 3925    });
 3926
 3927    _ = view.update(cx, |view, cx| {
 3928        view.change_selections(None, cx, |s| {
 3929            s.select_display_ranges([
 3930                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3931            ])
 3932        });
 3933        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 3934        assert_eq!(
 3935            view.display_text(cx),
 3936            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 3937        );
 3938        assert_eq!(
 3939            view.selections.display_ranges(cx),
 3940            [
 3941                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 3942                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 3943                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 3944                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 3945                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 3946                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 3947                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 3948                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 3949            ]
 3950        );
 3951    });
 3952}
 3953
 3954#[gpui::test]
 3955async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 3956    init_test(cx, |_| {});
 3957
 3958    let mut cx = EditorTestContext::new(cx).await;
 3959
 3960    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 3961    cx.set_state(indoc!(
 3962        r#"abc
 3963           defˇghi
 3964
 3965           jk
 3966           nlmo
 3967           "#
 3968    ));
 3969
 3970    cx.update_editor(|editor, cx| {
 3971        editor.add_selection_above(&Default::default(), cx);
 3972    });
 3973
 3974    cx.assert_editor_state(indoc!(
 3975        r#"abcˇ
 3976           defˇghi
 3977
 3978           jk
 3979           nlmo
 3980           "#
 3981    ));
 3982
 3983    cx.update_editor(|editor, cx| {
 3984        editor.add_selection_above(&Default::default(), cx);
 3985    });
 3986
 3987    cx.assert_editor_state(indoc!(
 3988        r#"abcˇ
 3989            defˇghi
 3990
 3991            jk
 3992            nlmo
 3993            "#
 3994    ));
 3995
 3996    cx.update_editor(|view, cx| {
 3997        view.add_selection_below(&Default::default(), cx);
 3998    });
 3999
 4000    cx.assert_editor_state(indoc!(
 4001        r#"abc
 4002           defˇghi
 4003
 4004           jk
 4005           nlmo
 4006           "#
 4007    ));
 4008
 4009    cx.update_editor(|view, cx| {
 4010        view.undo_selection(&Default::default(), cx);
 4011    });
 4012
 4013    cx.assert_editor_state(indoc!(
 4014        r#"abcˇ
 4015           defˇghi
 4016
 4017           jk
 4018           nlmo
 4019           "#
 4020    ));
 4021
 4022    cx.update_editor(|view, cx| {
 4023        view.redo_selection(&Default::default(), cx);
 4024    });
 4025
 4026    cx.assert_editor_state(indoc!(
 4027        r#"abc
 4028           defˇghi
 4029
 4030           jk
 4031           nlmo
 4032           "#
 4033    ));
 4034
 4035    cx.update_editor(|view, cx| {
 4036        view.add_selection_below(&Default::default(), cx);
 4037    });
 4038
 4039    cx.assert_editor_state(indoc!(
 4040        r#"abc
 4041           defˇghi
 4042
 4043           jk
 4044           nlmˇo
 4045           "#
 4046    ));
 4047
 4048    cx.update_editor(|view, cx| {
 4049        view.add_selection_below(&Default::default(), cx);
 4050    });
 4051
 4052    cx.assert_editor_state(indoc!(
 4053        r#"abc
 4054           defˇghi
 4055
 4056           jk
 4057           nlmˇo
 4058           "#
 4059    ));
 4060
 4061    // change selections
 4062    cx.set_state(indoc!(
 4063        r#"abc
 4064           def«ˇg»hi
 4065
 4066           jk
 4067           nlmo
 4068           "#
 4069    ));
 4070
 4071    cx.update_editor(|view, cx| {
 4072        view.add_selection_below(&Default::default(), cx);
 4073    });
 4074
 4075    cx.assert_editor_state(indoc!(
 4076        r#"abc
 4077           def«ˇg»hi
 4078
 4079           jk
 4080           nlm«ˇo»
 4081           "#
 4082    ));
 4083
 4084    cx.update_editor(|view, cx| {
 4085        view.add_selection_below(&Default::default(), cx);
 4086    });
 4087
 4088    cx.assert_editor_state(indoc!(
 4089        r#"abc
 4090           def«ˇg»hi
 4091
 4092           jk
 4093           nlm«ˇo»
 4094           "#
 4095    ));
 4096
 4097    cx.update_editor(|view, cx| {
 4098        view.add_selection_above(&Default::default(), cx);
 4099    });
 4100
 4101    cx.assert_editor_state(indoc!(
 4102        r#"abc
 4103           def«ˇg»hi
 4104
 4105           jk
 4106           nlmo
 4107           "#
 4108    ));
 4109
 4110    cx.update_editor(|view, cx| {
 4111        view.add_selection_above(&Default::default(), cx);
 4112    });
 4113
 4114    cx.assert_editor_state(indoc!(
 4115        r#"abc
 4116           def«ˇg»hi
 4117
 4118           jk
 4119           nlmo
 4120           "#
 4121    ));
 4122
 4123    // Change selections again
 4124    cx.set_state(indoc!(
 4125        r#"a«bc
 4126           defgˇ»hi
 4127
 4128           jk
 4129           nlmo
 4130           "#
 4131    ));
 4132
 4133    cx.update_editor(|view, cx| {
 4134        view.add_selection_below(&Default::default(), cx);
 4135    });
 4136
 4137    cx.assert_editor_state(indoc!(
 4138        r#"a«bcˇ»
 4139           d«efgˇ»hi
 4140
 4141           j«kˇ»
 4142           nlmo
 4143           "#
 4144    ));
 4145
 4146    cx.update_editor(|view, cx| {
 4147        view.add_selection_below(&Default::default(), cx);
 4148    });
 4149    cx.assert_editor_state(indoc!(
 4150        r#"a«bcˇ»
 4151           d«efgˇ»hi
 4152
 4153           j«kˇ»
 4154           n«lmoˇ»
 4155           "#
 4156    ));
 4157    cx.update_editor(|view, cx| {
 4158        view.add_selection_above(&Default::default(), cx);
 4159    });
 4160
 4161    cx.assert_editor_state(indoc!(
 4162        r#"a«bcˇ»
 4163           d«efgˇ»hi
 4164
 4165           j«kˇ»
 4166           nlmo
 4167           "#
 4168    ));
 4169
 4170    // Change selections again
 4171    cx.set_state(indoc!(
 4172        r#"abc
 4173           d«ˇefghi
 4174
 4175           jk
 4176           nlm»o
 4177           "#
 4178    ));
 4179
 4180    cx.update_editor(|view, cx| {
 4181        view.add_selection_above(&Default::default(), cx);
 4182    });
 4183
 4184    cx.assert_editor_state(indoc!(
 4185        r#"a«ˇbc»
 4186           d«ˇef»ghi
 4187
 4188           j«ˇk»
 4189           n«ˇlm»o
 4190           "#
 4191    ));
 4192
 4193    cx.update_editor(|view, cx| {
 4194        view.add_selection_below(&Default::default(), cx);
 4195    });
 4196
 4197    cx.assert_editor_state(indoc!(
 4198        r#"abc
 4199           d«ˇef»ghi
 4200
 4201           j«ˇk»
 4202           n«ˇlm»o
 4203           "#
 4204    ));
 4205}
 4206
 4207#[gpui::test]
 4208async fn test_select_next(cx: &mut gpui::TestAppContext) {
 4209    init_test(cx, |_| {});
 4210
 4211    let mut cx = EditorTestContext::new(cx).await;
 4212    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4213
 4214    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4215        .unwrap();
 4216    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4217
 4218    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4219        .unwrap();
 4220    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4221
 4222    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4223    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4224
 4225    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4226    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 4227
 4228    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4229        .unwrap();
 4230    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4231
 4232    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4233        .unwrap();
 4234    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4235}
 4236
 4237#[gpui::test]
 4238async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 4239    init_test(cx, |_| {});
 4240
 4241    let mut cx = EditorTestContext::new(cx).await;
 4242    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4243
 4244    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 4245        .unwrap();
 4246    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 4247}
 4248
 4249#[gpui::test]
 4250async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4251    init_test(cx, |_| {});
 4252
 4253    let mut cx = EditorTestContext::new(cx).await;
 4254    cx.set_state(
 4255        r#"let foo = 2;
 4256lˇet foo = 2;
 4257let fooˇ = 2;
 4258let foo = 2;
 4259let foo = ˇ2;"#,
 4260    );
 4261
 4262    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4263        .unwrap();
 4264    cx.assert_editor_state(
 4265        r#"let foo = 2;
 4266«letˇ» foo = 2;
 4267let «fooˇ» = 2;
 4268let foo = 2;
 4269let foo = «2ˇ»;"#,
 4270    );
 4271
 4272    // noop for multiple selections with different contents
 4273    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 4274        .unwrap();
 4275    cx.assert_editor_state(
 4276        r#"let foo = 2;
 4277«letˇ» foo = 2;
 4278let «fooˇ» = 2;
 4279let foo = 2;
 4280let foo = «2ˇ»;"#,
 4281    );
 4282}
 4283
 4284#[gpui::test]
 4285async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 4286    init_test(cx, |_| {});
 4287
 4288    let mut cx = EditorTestContext::new_multibuffer(
 4289        cx,
 4290        [
 4291            indoc! {
 4292                "aaa\n«bbb\nccc\n»ddd"
 4293            },
 4294            indoc! {
 4295                "aaa\n«bbb\nccc\n»ddd"
 4296            },
 4297        ],
 4298    );
 4299
 4300    cx.assert_editor_state(indoc! {"
 4301        ˇbbb
 4302        ccc
 4303
 4304        bbb
 4305        ccc
 4306        "});
 4307    cx.dispatch_action(SelectPrevious::default());
 4308    cx.assert_editor_state(indoc! {"
 4309                «bbbˇ»
 4310                ccc
 4311
 4312                bbb
 4313                ccc
 4314                "});
 4315    cx.dispatch_action(SelectPrevious::default());
 4316    cx.assert_editor_state(indoc! {"
 4317                «bbbˇ»
 4318                ccc
 4319
 4320                «bbbˇ»
 4321                ccc
 4322                "});
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 4327    init_test(cx, |_| {});
 4328
 4329    let mut cx = EditorTestContext::new(cx).await;
 4330    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 4331
 4332    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4333        .unwrap();
 4334    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4335
 4336    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4337        .unwrap();
 4338    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4339
 4340    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4341    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 4342
 4343    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4344    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 4345
 4346    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4347        .unwrap();
 4348    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 4349
 4350    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4351        .unwrap();
 4352    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 4353
 4354    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4355        .unwrap();
 4356    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4357}
 4358
 4359#[gpui::test]
 4360async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 4361    init_test(cx, |_| {});
 4362
 4363    let mut cx = EditorTestContext::new(cx).await;
 4364    cx.set_state(
 4365        r#"let foo = 2;
 4366lˇet foo = 2;
 4367let fooˇ = 2;
 4368let foo = 2;
 4369let foo = ˇ2;"#,
 4370    );
 4371
 4372    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4373        .unwrap();
 4374    cx.assert_editor_state(
 4375        r#"let foo = 2;
 4376«letˇ» foo = 2;
 4377let «fooˇ» = 2;
 4378let foo = 2;
 4379let foo = «2ˇ»;"#,
 4380    );
 4381
 4382    // noop for multiple selections with different contents
 4383    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4384        .unwrap();
 4385    cx.assert_editor_state(
 4386        r#"let foo = 2;
 4387«letˇ» foo = 2;
 4388let «fooˇ» = 2;
 4389let foo = 2;
 4390let foo = «2ˇ»;"#,
 4391    );
 4392}
 4393
 4394#[gpui::test]
 4395async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 4396    init_test(cx, |_| {});
 4397
 4398    let mut cx = EditorTestContext::new(cx).await;
 4399    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 4400
 4401    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4402        .unwrap();
 4403    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4404
 4405    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4406        .unwrap();
 4407    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4408
 4409    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 4410    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 4411
 4412    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 4413    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 4414
 4415    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4416        .unwrap();
 4417    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 4418
 4419    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 4420        .unwrap();
 4421    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 4422}
 4423
 4424#[gpui::test]
 4425async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 4426    init_test(cx, |_| {});
 4427
 4428    let language = Arc::new(Language::new(
 4429        LanguageConfig::default(),
 4430        Some(tree_sitter_rust::language()),
 4431    ));
 4432
 4433    let text = r#"
 4434        use mod1::mod2::{mod3, mod4};
 4435
 4436        fn fn_1(param1: bool, param2: &str) {
 4437            let var1 = "text";
 4438        }
 4439    "#
 4440    .unindent();
 4441
 4442    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4443    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4444    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4445
 4446    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 4447        .await;
 4448
 4449    _ = view.update(cx, |view, cx| {
 4450        view.change_selections(None, cx, |s| {
 4451            s.select_display_ranges([
 4452                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4453                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4454                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4455            ]);
 4456        });
 4457        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4458    });
 4459    assert_eq!(
 4460        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
 4461        &[
 4462            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4463            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4464            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4465        ]
 4466    );
 4467
 4468    _ = view.update(cx, |view, cx| {
 4469        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4470    });
 4471    assert_eq!(
 4472        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4473        &[
 4474            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4475            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4476        ]
 4477    );
 4478
 4479    _ = view.update(cx, |view, cx| {
 4480        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4481    });
 4482    assert_eq!(
 4483        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4484        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4485    );
 4486
 4487    // Trying to expand the selected syntax node one more time has no effect.
 4488    _ = view.update(cx, |view, cx| {
 4489        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4490    });
 4491    assert_eq!(
 4492        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4493        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 4494    );
 4495
 4496    _ = view.update(cx, |view, cx| {
 4497        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4498    });
 4499    assert_eq!(
 4500        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4501        &[
 4502            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4503            DisplayPoint::new(DisplayRow(4), 1)..DisplayPoint::new(DisplayRow(2), 0),
 4504        ]
 4505    );
 4506
 4507    _ = view.update(cx, |view, cx| {
 4508        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4509    });
 4510    assert_eq!(
 4511        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4512        &[
 4513            DisplayPoint::new(DisplayRow(0), 23)..DisplayPoint::new(DisplayRow(0), 27),
 4514            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4515            DisplayPoint::new(DisplayRow(3), 15)..DisplayPoint::new(DisplayRow(3), 21),
 4516        ]
 4517    );
 4518
 4519    _ = view.update(cx, |view, cx| {
 4520        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4521    });
 4522    assert_eq!(
 4523        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4524        &[
 4525            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4526            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4527            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4528        ]
 4529    );
 4530
 4531    // Trying to shrink the selected syntax node one more time has no effect.
 4532    _ = view.update(cx, |view, cx| {
 4533        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 4534    });
 4535    assert_eq!(
 4536        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4537        &[
 4538            DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 4539            DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 4540            DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 4541        ]
 4542    );
 4543
 4544    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 4545    // a fold.
 4546    _ = view.update(cx, |view, cx| {
 4547        view.fold_ranges(
 4548            vec![
 4549                Point::new(0, 21)..Point::new(0, 24),
 4550                Point::new(3, 20)..Point::new(3, 22),
 4551            ],
 4552            true,
 4553            cx,
 4554        );
 4555        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4556    });
 4557    assert_eq!(
 4558        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4559        &[
 4560            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4561            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4562            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4563        ]
 4564    );
 4565}
 4566
 4567#[gpui::test]
 4568async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4569    init_test(cx, |_| {});
 4570
 4571    let language = Arc::new(
 4572        Language::new(
 4573            LanguageConfig {
 4574                brackets: BracketPairConfig {
 4575                    pairs: vec![
 4576                        BracketPair {
 4577                            start: "{".to_string(),
 4578                            end: "}".to_string(),
 4579                            close: false,
 4580                            newline: true,
 4581                        },
 4582                        BracketPair {
 4583                            start: "(".to_string(),
 4584                            end: ")".to_string(),
 4585                            close: false,
 4586                            newline: true,
 4587                        },
 4588                    ],
 4589                    ..Default::default()
 4590                },
 4591                ..Default::default()
 4592            },
 4593            Some(tree_sitter_rust::language()),
 4594        )
 4595        .with_indents_query(
 4596            r#"
 4597                (_ "(" ")" @end) @indent
 4598                (_ "{" "}" @end) @indent
 4599            "#,
 4600        )
 4601        .unwrap(),
 4602    );
 4603
 4604    let text = "fn a() {}";
 4605
 4606    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4607    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4608    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4609    editor
 4610        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4611        .await;
 4612
 4613    _ = editor.update(cx, |editor, cx| {
 4614        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4615        editor.newline(&Newline, cx);
 4616        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4617        assert_eq!(
 4618            editor.selections.ranges(cx),
 4619            &[
 4620                Point::new(1, 4)..Point::new(1, 4),
 4621                Point::new(3, 4)..Point::new(3, 4),
 4622                Point::new(5, 0)..Point::new(5, 0)
 4623            ]
 4624        );
 4625    });
 4626}
 4627
 4628#[gpui::test]
 4629async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
 4630    init_test(cx, |_| {});
 4631
 4632    let mut cx = EditorTestContext::new(cx).await;
 4633
 4634    let language = Arc::new(Language::new(
 4635        LanguageConfig {
 4636            brackets: BracketPairConfig {
 4637                pairs: vec![
 4638                    BracketPair {
 4639                        start: "{".to_string(),
 4640                        end: "}".to_string(),
 4641                        close: true,
 4642                        newline: true,
 4643                    },
 4644                    BracketPair {
 4645                        start: "(".to_string(),
 4646                        end: ")".to_string(),
 4647                        close: true,
 4648                        newline: true,
 4649                    },
 4650                    BracketPair {
 4651                        start: "/*".to_string(),
 4652                        end: " */".to_string(),
 4653                        close: true,
 4654                        newline: true,
 4655                    },
 4656                    BracketPair {
 4657                        start: "[".to_string(),
 4658                        end: "]".to_string(),
 4659                        close: false,
 4660                        newline: true,
 4661                    },
 4662                    BracketPair {
 4663                        start: "\"".to_string(),
 4664                        end: "\"".to_string(),
 4665                        close: true,
 4666                        newline: false,
 4667                    },
 4668                ],
 4669                ..Default::default()
 4670            },
 4671            autoclose_before: "})]".to_string(),
 4672            ..Default::default()
 4673        },
 4674        Some(tree_sitter_rust::language()),
 4675    ));
 4676
 4677    cx.language_registry().add(language.clone());
 4678    cx.update_buffer(|buffer, cx| {
 4679        buffer.set_language(Some(language), cx);
 4680    });
 4681
 4682    cx.set_state(
 4683        &r#"
 4684            🏀ˇ
 4685            εˇ
 4686            ❤️ˇ
 4687        "#
 4688        .unindent(),
 4689    );
 4690
 4691    // autoclose multiple nested brackets at multiple cursors
 4692    cx.update_editor(|view, cx| {
 4693        view.handle_input("{", cx);
 4694        view.handle_input("{", cx);
 4695        view.handle_input("{", cx);
 4696    });
 4697    cx.assert_editor_state(
 4698        &"
 4699            🏀{{{ˇ}}}
 4700            ε{{{ˇ}}}
 4701            ❤️{{{ˇ}}}
 4702        "
 4703        .unindent(),
 4704    );
 4705
 4706    // insert a different closing bracket
 4707    cx.update_editor(|view, cx| {
 4708        view.handle_input(")", cx);
 4709    });
 4710    cx.assert_editor_state(
 4711        &"
 4712            🏀{{{)ˇ}}}
 4713            ε{{{)ˇ}}}
 4714            ❤️{{{)ˇ}}}
 4715        "
 4716        .unindent(),
 4717    );
 4718
 4719    // skip over the auto-closed brackets when typing a closing bracket
 4720    cx.update_editor(|view, cx| {
 4721        view.move_right(&MoveRight, cx);
 4722        view.handle_input("}", cx);
 4723        view.handle_input("}", cx);
 4724        view.handle_input("}", cx);
 4725    });
 4726    cx.assert_editor_state(
 4727        &"
 4728            🏀{{{)}}}}ˇ
 4729            ε{{{)}}}}ˇ
 4730            ❤️{{{)}}}}ˇ
 4731        "
 4732        .unindent(),
 4733    );
 4734
 4735    // autoclose multi-character pairs
 4736    cx.set_state(
 4737        &"
 4738            ˇ
 4739            ˇ
 4740        "
 4741        .unindent(),
 4742    );
 4743    cx.update_editor(|view, cx| {
 4744        view.handle_input("/", cx);
 4745        view.handle_input("*", cx);
 4746    });
 4747    cx.assert_editor_state(
 4748        &"
 4749            /*ˇ */
 4750            /*ˇ */
 4751        "
 4752        .unindent(),
 4753    );
 4754
 4755    // one cursor autocloses a multi-character pair, one cursor
 4756    // does not autoclose.
 4757    cx.set_state(
 4758        &"
 4759 4760            ˇ
 4761        "
 4762        .unindent(),
 4763    );
 4764    cx.update_editor(|view, cx| view.handle_input("*", cx));
 4765    cx.assert_editor_state(
 4766        &"
 4767            /*ˇ */
 4768 4769        "
 4770        .unindent(),
 4771    );
 4772
 4773    // Don't autoclose if the next character isn't whitespace and isn't
 4774    // listed in the language's "autoclose_before" section.
 4775    cx.set_state("ˇa b");
 4776    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4777    cx.assert_editor_state("{ˇa b");
 4778
 4779    // Don't autoclose if `close` is false for the bracket pair
 4780    cx.set_state("ˇ");
 4781    cx.update_editor(|view, cx| view.handle_input("[", cx));
 4782    cx.assert_editor_state("");
 4783
 4784    // Surround with brackets if text is selected
 4785    cx.set_state("«aˇ» b");
 4786    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4787    cx.assert_editor_state("{«aˇ»} b");
 4788
 4789    // Autclose pair where the start and end characters are the same
 4790    cx.set_state("");
 4791    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4792    cx.assert_editor_state("a\"ˇ\"");
 4793    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4794    cx.assert_editor_state("a\"\"ˇ");
 4795}
 4796
 4797#[gpui::test]
 4798async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 4799    init_test(cx, |settings| {
 4800        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 4801    });
 4802
 4803    let mut cx = EditorTestContext::new(cx).await;
 4804
 4805    let language = Arc::new(Language::new(
 4806        LanguageConfig {
 4807            brackets: BracketPairConfig {
 4808                pairs: vec![
 4809                    BracketPair {
 4810                        start: "{".to_string(),
 4811                        end: "}".to_string(),
 4812                        close: true,
 4813                        newline: true,
 4814                    },
 4815                    BracketPair {
 4816                        start: "(".to_string(),
 4817                        end: ")".to_string(),
 4818                        close: true,
 4819                        newline: true,
 4820                    },
 4821                    BracketPair {
 4822                        start: "[".to_string(),
 4823                        end: "]".to_string(),
 4824                        close: false,
 4825                        newline: true,
 4826                    },
 4827                ],
 4828                ..Default::default()
 4829            },
 4830            autoclose_before: "})]".to_string(),
 4831            ..Default::default()
 4832        },
 4833        Some(tree_sitter_rust::language()),
 4834    ));
 4835
 4836    cx.language_registry().add(language.clone());
 4837    cx.update_buffer(|buffer, cx| {
 4838        buffer.set_language(Some(language), cx);
 4839    });
 4840
 4841    cx.set_state(
 4842        &"
 4843            ˇ
 4844            ˇ
 4845            ˇ
 4846        "
 4847        .unindent(),
 4848    );
 4849
 4850    // ensure only matching closing brackets are skipped over
 4851    cx.update_editor(|view, cx| {
 4852        view.handle_input("}", cx);
 4853        view.move_left(&MoveLeft, cx);
 4854        view.handle_input(")", cx);
 4855        view.move_left(&MoveLeft, cx);
 4856    });
 4857    cx.assert_editor_state(
 4858        &"
 4859            ˇ)}
 4860            ˇ)}
 4861            ˇ)}
 4862        "
 4863        .unindent(),
 4864    );
 4865
 4866    // skip-over closing brackets at multiple cursors
 4867    cx.update_editor(|view, cx| {
 4868        view.handle_input(")", cx);
 4869        view.handle_input("}", cx);
 4870    });
 4871    cx.assert_editor_state(
 4872        &"
 4873            )}ˇ
 4874            )}ˇ
 4875            )}ˇ
 4876        "
 4877        .unindent(),
 4878    );
 4879
 4880    // ignore non-close brackets
 4881    cx.update_editor(|view, cx| {
 4882        view.handle_input("]", cx);
 4883        view.move_left(&MoveLeft, cx);
 4884        view.handle_input("]", cx);
 4885    });
 4886    cx.assert_editor_state(
 4887        &"
 4888            )}]ˇ]
 4889            )}]ˇ]
 4890            )}]ˇ]
 4891        "
 4892        .unindent(),
 4893    );
 4894}
 4895
 4896#[gpui::test]
 4897async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 4898    init_test(cx, |_| {});
 4899
 4900    let mut cx = EditorTestContext::new(cx).await;
 4901
 4902    let html_language = Arc::new(
 4903        Language::new(
 4904            LanguageConfig {
 4905                name: "HTML".into(),
 4906                brackets: BracketPairConfig {
 4907                    pairs: vec![
 4908                        BracketPair {
 4909                            start: "<".into(),
 4910                            end: ">".into(),
 4911                            close: true,
 4912                            ..Default::default()
 4913                        },
 4914                        BracketPair {
 4915                            start: "{".into(),
 4916                            end: "}".into(),
 4917                            close: true,
 4918                            ..Default::default()
 4919                        },
 4920                        BracketPair {
 4921                            start: "(".into(),
 4922                            end: ")".into(),
 4923                            close: true,
 4924                            ..Default::default()
 4925                        },
 4926                    ],
 4927                    ..Default::default()
 4928                },
 4929                autoclose_before: "})]>".into(),
 4930                ..Default::default()
 4931            },
 4932            Some(tree_sitter_html::language()),
 4933        )
 4934        .with_injection_query(
 4935            r#"
 4936            (script_element
 4937                (raw_text) @content
 4938                (#set! "language" "javascript"))
 4939            "#,
 4940        )
 4941        .unwrap(),
 4942    );
 4943
 4944    let javascript_language = Arc::new(Language::new(
 4945        LanguageConfig {
 4946            name: "JavaScript".into(),
 4947            brackets: BracketPairConfig {
 4948                pairs: vec![
 4949                    BracketPair {
 4950                        start: "/*".into(),
 4951                        end: " */".into(),
 4952                        close: true,
 4953                        ..Default::default()
 4954                    },
 4955                    BracketPair {
 4956                        start: "{".into(),
 4957                        end: "}".into(),
 4958                        close: true,
 4959                        ..Default::default()
 4960                    },
 4961                    BracketPair {
 4962                        start: "(".into(),
 4963                        end: ")".into(),
 4964                        close: true,
 4965                        ..Default::default()
 4966                    },
 4967                ],
 4968                ..Default::default()
 4969            },
 4970            autoclose_before: "})]>".into(),
 4971            ..Default::default()
 4972        },
 4973        Some(tree_sitter_typescript::language_tsx()),
 4974    ));
 4975
 4976    cx.language_registry().add(html_language.clone());
 4977    cx.language_registry().add(javascript_language.clone());
 4978
 4979    cx.update_buffer(|buffer, cx| {
 4980        buffer.set_language(Some(html_language), cx);
 4981    });
 4982
 4983    cx.set_state(
 4984        &r#"
 4985            <body>ˇ
 4986                <script>
 4987                    var x = 1;ˇ
 4988                </script>
 4989            </body>ˇ
 4990        "#
 4991        .unindent(),
 4992    );
 4993
 4994    // Precondition: different languages are active at different locations.
 4995    cx.update_editor(|editor, cx| {
 4996        let snapshot = editor.snapshot(cx);
 4997        let cursors = editor.selections.ranges::<usize>(cx);
 4998        let languages = cursors
 4999            .iter()
 5000            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5001            .collect::<Vec<_>>();
 5002        assert_eq!(
 5003            languages,
 5004            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5005        );
 5006    });
 5007
 5008    // Angle brackets autoclose in HTML, but not JavaScript.
 5009    cx.update_editor(|editor, cx| {
 5010        editor.handle_input("<", cx);
 5011        editor.handle_input("a", cx);
 5012    });
 5013    cx.assert_editor_state(
 5014        &r#"
 5015            <body><aˇ>
 5016                <script>
 5017                    var x = 1;<aˇ
 5018                </script>
 5019            </body><aˇ>
 5020        "#
 5021        .unindent(),
 5022    );
 5023
 5024    // Curly braces and parens autoclose in both HTML and JavaScript.
 5025    cx.update_editor(|editor, cx| {
 5026        editor.handle_input(" b=", cx);
 5027        editor.handle_input("{", cx);
 5028        editor.handle_input("c", cx);
 5029        editor.handle_input("(", cx);
 5030    });
 5031    cx.assert_editor_state(
 5032        &r#"
 5033            <body><a b={c(ˇ)}>
 5034                <script>
 5035                    var x = 1;<a b={c(ˇ)}
 5036                </script>
 5037            </body><a b={c(ˇ)}>
 5038        "#
 5039        .unindent(),
 5040    );
 5041
 5042    // Brackets that were already autoclosed are skipped.
 5043    cx.update_editor(|editor, cx| {
 5044        editor.handle_input(")", cx);
 5045        editor.handle_input("d", cx);
 5046        editor.handle_input("}", cx);
 5047    });
 5048    cx.assert_editor_state(
 5049        &r#"
 5050            <body><a b={c()d}ˇ>
 5051                <script>
 5052                    var x = 1;<a b={c()d}ˇ
 5053                </script>
 5054            </body><a b={c()d}ˇ>
 5055        "#
 5056        .unindent(),
 5057    );
 5058    cx.update_editor(|editor, cx| {
 5059        editor.handle_input(">", cx);
 5060    });
 5061    cx.assert_editor_state(
 5062        &r#"
 5063            <body><a b={c()d}>ˇ
 5064                <script>
 5065                    var x = 1;<a b={c()d}>ˇ
 5066                </script>
 5067            </body><a b={c()d}>ˇ
 5068        "#
 5069        .unindent(),
 5070    );
 5071
 5072    // Reset
 5073    cx.set_state(
 5074        &r#"
 5075            <body>ˇ
 5076                <script>
 5077                    var x = 1;ˇ
 5078                </script>
 5079            </body>ˇ
 5080        "#
 5081        .unindent(),
 5082    );
 5083
 5084    cx.update_editor(|editor, cx| {
 5085        editor.handle_input("<", cx);
 5086    });
 5087    cx.assert_editor_state(
 5088        &r#"
 5089            <body><ˇ>
 5090                <script>
 5091                    var x = 1;<ˇ
 5092                </script>
 5093            </body><ˇ>
 5094        "#
 5095        .unindent(),
 5096    );
 5097
 5098    // When backspacing, the closing angle brackets are removed.
 5099    cx.update_editor(|editor, cx| {
 5100        editor.backspace(&Backspace, cx);
 5101    });
 5102    cx.assert_editor_state(
 5103        &r#"
 5104            <body>ˇ
 5105                <script>
 5106                    var x = 1;ˇ
 5107                </script>
 5108            </body>ˇ
 5109        "#
 5110        .unindent(),
 5111    );
 5112
 5113    // Block comments autoclose in JavaScript, but not HTML.
 5114    cx.update_editor(|editor, cx| {
 5115        editor.handle_input("/", cx);
 5116        editor.handle_input("*", cx);
 5117    });
 5118    cx.assert_editor_state(
 5119        &r#"
 5120            <body>/*ˇ
 5121                <script>
 5122                    var x = 1;/*ˇ */
 5123                </script>
 5124            </body>/*ˇ
 5125        "#
 5126        .unindent(),
 5127    );
 5128}
 5129
 5130#[gpui::test]
 5131async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5132    init_test(cx, |_| {});
 5133
 5134    let mut cx = EditorTestContext::new(cx).await;
 5135
 5136    let rust_language = Arc::new(
 5137        Language::new(
 5138            LanguageConfig {
 5139                name: "Rust".into(),
 5140                brackets: serde_json::from_value(json!([
 5141                    { "start": "{", "end": "}", "close": true, "newline": true },
 5142                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5143                ]))
 5144                .unwrap(),
 5145                autoclose_before: "})]>".into(),
 5146                ..Default::default()
 5147            },
 5148            Some(tree_sitter_rust::language()),
 5149        )
 5150        .with_override_query("(string_literal) @string")
 5151        .unwrap(),
 5152    );
 5153
 5154    cx.language_registry().add(rust_language.clone());
 5155    cx.update_buffer(|buffer, cx| {
 5156        buffer.set_language(Some(rust_language), cx);
 5157    });
 5158
 5159    cx.set_state(
 5160        &r#"
 5161            let x = ˇ
 5162        "#
 5163        .unindent(),
 5164    );
 5165
 5166    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5167    cx.update_editor(|editor, cx| {
 5168        editor.handle_input("\"", cx);
 5169    });
 5170    cx.assert_editor_state(
 5171        &r#"
 5172            let x = "ˇ"
 5173        "#
 5174        .unindent(),
 5175    );
 5176
 5177    // Inserting another quotation mark. The cursor moves across the existing
 5178    // automatically-inserted quotation mark.
 5179    cx.update_editor(|editor, cx| {
 5180        editor.handle_input("\"", cx);
 5181    });
 5182    cx.assert_editor_state(
 5183        &r#"
 5184            let x = ""ˇ
 5185        "#
 5186        .unindent(),
 5187    );
 5188
 5189    // Reset
 5190    cx.set_state(
 5191        &r#"
 5192            let x = ˇ
 5193        "#
 5194        .unindent(),
 5195    );
 5196
 5197    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5198    cx.update_editor(|editor, cx| {
 5199        editor.handle_input("\"", cx);
 5200        editor.handle_input(" ", cx);
 5201        editor.move_left(&Default::default(), cx);
 5202        editor.handle_input("\\", cx);
 5203        editor.handle_input("\"", cx);
 5204    });
 5205    cx.assert_editor_state(
 5206        &r#"
 5207            let x = "\"ˇ "
 5208        "#
 5209        .unindent(),
 5210    );
 5211
 5212    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5213    // mark. Nothing is inserted.
 5214    cx.update_editor(|editor, cx| {
 5215        editor.move_right(&Default::default(), cx);
 5216        editor.handle_input("\"", cx);
 5217    });
 5218    cx.assert_editor_state(
 5219        &r#"
 5220            let x = "\" "ˇ
 5221        "#
 5222        .unindent(),
 5223    );
 5224}
 5225
 5226#[gpui::test]
 5227async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5228    init_test(cx, |_| {});
 5229
 5230    let language = Arc::new(Language::new(
 5231        LanguageConfig {
 5232            brackets: BracketPairConfig {
 5233                pairs: vec![
 5234                    BracketPair {
 5235                        start: "{".to_string(),
 5236                        end: "}".to_string(),
 5237                        close: true,
 5238                        newline: true,
 5239                    },
 5240                    BracketPair {
 5241                        start: "/* ".to_string(),
 5242                        end: "*/".to_string(),
 5243                        close: true,
 5244                        ..Default::default()
 5245                    },
 5246                ],
 5247                ..Default::default()
 5248            },
 5249            ..Default::default()
 5250        },
 5251        Some(tree_sitter_rust::language()),
 5252    ));
 5253
 5254    let text = r#"
 5255        a
 5256        b
 5257        c
 5258    "#
 5259    .unindent();
 5260
 5261    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5262    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5263    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5264    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5265        .await;
 5266
 5267    _ = view.update(cx, |view, cx| {
 5268        view.change_selections(None, cx, |s| {
 5269            s.select_display_ranges([
 5270                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5271                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5272                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5273            ])
 5274        });
 5275
 5276        view.handle_input("{", cx);
 5277        view.handle_input("{", cx);
 5278        view.handle_input("{", cx);
 5279        assert_eq!(
 5280            view.text(cx),
 5281            "
 5282                {{{a}}}
 5283                {{{b}}}
 5284                {{{c}}}
 5285            "
 5286            .unindent()
 5287        );
 5288        assert_eq!(
 5289            view.selections.display_ranges(cx),
 5290            [
 5291                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5292                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5293                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5294            ]
 5295        );
 5296
 5297        view.undo(&Undo, cx);
 5298        view.undo(&Undo, cx);
 5299        view.undo(&Undo, cx);
 5300        assert_eq!(
 5301            view.text(cx),
 5302            "
 5303                a
 5304                b
 5305                c
 5306            "
 5307            .unindent()
 5308        );
 5309        assert_eq!(
 5310            view.selections.display_ranges(cx),
 5311            [
 5312                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5313                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5314                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5315            ]
 5316        );
 5317
 5318        // Ensure inserting the first character of a multi-byte bracket pair
 5319        // doesn't surround the selections with the bracket.
 5320        view.handle_input("/", cx);
 5321        assert_eq!(
 5322            view.text(cx),
 5323            "
 5324                /
 5325                /
 5326                /
 5327            "
 5328            .unindent()
 5329        );
 5330        assert_eq!(
 5331            view.selections.display_ranges(cx),
 5332            [
 5333                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5334                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5335                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5336            ]
 5337        );
 5338
 5339        view.undo(&Undo, cx);
 5340        assert_eq!(
 5341            view.text(cx),
 5342            "
 5343                a
 5344                b
 5345                c
 5346            "
 5347            .unindent()
 5348        );
 5349        assert_eq!(
 5350            view.selections.display_ranges(cx),
 5351            [
 5352                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5353                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5354                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5355            ]
 5356        );
 5357
 5358        // Ensure inserting the last character of a multi-byte bracket pair
 5359        // doesn't surround the selections with the bracket.
 5360        view.handle_input("*", cx);
 5361        assert_eq!(
 5362            view.text(cx),
 5363            "
 5364                *
 5365                *
 5366                *
 5367            "
 5368            .unindent()
 5369        );
 5370        assert_eq!(
 5371            view.selections.display_ranges(cx),
 5372            [
 5373                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5374                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5375                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5376            ]
 5377        );
 5378    });
 5379}
 5380
 5381#[gpui::test]
 5382async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5383    init_test(cx, |_| {});
 5384
 5385    let language = Arc::new(Language::new(
 5386        LanguageConfig {
 5387            brackets: BracketPairConfig {
 5388                pairs: vec![BracketPair {
 5389                    start: "{".to_string(),
 5390                    end: "}".to_string(),
 5391                    close: true,
 5392                    newline: true,
 5393                }],
 5394                ..Default::default()
 5395            },
 5396            autoclose_before: "}".to_string(),
 5397            ..Default::default()
 5398        },
 5399        Some(tree_sitter_rust::language()),
 5400    ));
 5401
 5402    let text = r#"
 5403        a
 5404        b
 5405        c
 5406    "#
 5407    .unindent();
 5408
 5409    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5410    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5411    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5412    editor
 5413        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5414        .await;
 5415
 5416    _ = editor.update(cx, |editor, cx| {
 5417        editor.change_selections(None, cx, |s| {
 5418            s.select_ranges([
 5419                Point::new(0, 1)..Point::new(0, 1),
 5420                Point::new(1, 1)..Point::new(1, 1),
 5421                Point::new(2, 1)..Point::new(2, 1),
 5422            ])
 5423        });
 5424
 5425        editor.handle_input("{", cx);
 5426        editor.handle_input("{", cx);
 5427        editor.handle_input("_", cx);
 5428        assert_eq!(
 5429            editor.text(cx),
 5430            "
 5431                a{{_}}
 5432                b{{_}}
 5433                c{{_}}
 5434            "
 5435            .unindent()
 5436        );
 5437        assert_eq!(
 5438            editor.selections.ranges::<Point>(cx),
 5439            [
 5440                Point::new(0, 4)..Point::new(0, 4),
 5441                Point::new(1, 4)..Point::new(1, 4),
 5442                Point::new(2, 4)..Point::new(2, 4)
 5443            ]
 5444        );
 5445
 5446        editor.backspace(&Default::default(), cx);
 5447        editor.backspace(&Default::default(), cx);
 5448        assert_eq!(
 5449            editor.text(cx),
 5450            "
 5451                a{}
 5452                b{}
 5453                c{}
 5454            "
 5455            .unindent()
 5456        );
 5457        assert_eq!(
 5458            editor.selections.ranges::<Point>(cx),
 5459            [
 5460                Point::new(0, 2)..Point::new(0, 2),
 5461                Point::new(1, 2)..Point::new(1, 2),
 5462                Point::new(2, 2)..Point::new(2, 2)
 5463            ]
 5464        );
 5465
 5466        editor.delete_to_previous_word_start(&Default::default(), cx);
 5467        assert_eq!(
 5468            editor.text(cx),
 5469            "
 5470                a
 5471                b
 5472                c
 5473            "
 5474            .unindent()
 5475        );
 5476        assert_eq!(
 5477            editor.selections.ranges::<Point>(cx),
 5478            [
 5479                Point::new(0, 1)..Point::new(0, 1),
 5480                Point::new(1, 1)..Point::new(1, 1),
 5481                Point::new(2, 1)..Point::new(2, 1)
 5482            ]
 5483        );
 5484    });
 5485}
 5486
 5487#[gpui::test]
 5488async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5489    init_test(cx, |settings| {
 5490        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5491    });
 5492
 5493    let mut cx = EditorTestContext::new(cx).await;
 5494
 5495    let language = Arc::new(Language::new(
 5496        LanguageConfig {
 5497            brackets: BracketPairConfig {
 5498                pairs: vec![
 5499                    BracketPair {
 5500                        start: "{".to_string(),
 5501                        end: "}".to_string(),
 5502                        close: true,
 5503                        newline: true,
 5504                    },
 5505                    BracketPair {
 5506                        start: "(".to_string(),
 5507                        end: ")".to_string(),
 5508                        close: true,
 5509                        newline: true,
 5510                    },
 5511                    BracketPair {
 5512                        start: "[".to_string(),
 5513                        end: "]".to_string(),
 5514                        close: false,
 5515                        newline: true,
 5516                    },
 5517                ],
 5518                ..Default::default()
 5519            },
 5520            autoclose_before: "})]".to_string(),
 5521            ..Default::default()
 5522        },
 5523        Some(tree_sitter_rust::language()),
 5524    ));
 5525
 5526    cx.language_registry().add(language.clone());
 5527    cx.update_buffer(|buffer, cx| {
 5528        buffer.set_language(Some(language), cx);
 5529    });
 5530
 5531    cx.set_state(
 5532        &"
 5533            {(ˇ)}
 5534            [[ˇ]]
 5535            {(ˇ)}
 5536        "
 5537        .unindent(),
 5538    );
 5539
 5540    cx.update_editor(|view, cx| {
 5541        view.backspace(&Default::default(), cx);
 5542        view.backspace(&Default::default(), cx);
 5543    });
 5544
 5545    cx.assert_editor_state(
 5546        &"
 5547            ˇ
 5548            ˇ]]
 5549            ˇ
 5550        "
 5551        .unindent(),
 5552    );
 5553
 5554    cx.update_editor(|view, cx| {
 5555        view.handle_input("{", cx);
 5556        view.handle_input("{", cx);
 5557        view.move_right(&MoveRight, cx);
 5558        view.move_right(&MoveRight, cx);
 5559        view.move_left(&MoveLeft, cx);
 5560        view.move_left(&MoveLeft, cx);
 5561        view.backspace(&Default::default(), cx);
 5562    });
 5563
 5564    cx.assert_editor_state(
 5565        &"
 5566            {ˇ}
 5567            {ˇ}]]
 5568            {ˇ}
 5569        "
 5570        .unindent(),
 5571    );
 5572
 5573    cx.update_editor(|view, cx| {
 5574        view.backspace(&Default::default(), cx);
 5575    });
 5576
 5577    cx.assert_editor_state(
 5578        &"
 5579            ˇ
 5580            ˇ]]
 5581            ˇ
 5582        "
 5583        .unindent(),
 5584    );
 5585}
 5586
 5587#[gpui::test]
 5588async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5589    init_test(cx, |_| {});
 5590
 5591    let language = Arc::new(Language::new(
 5592        LanguageConfig::default(),
 5593        Some(tree_sitter_rust::language()),
 5594    ));
 5595
 5596    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5597    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5598    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5599    editor
 5600        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5601        .await;
 5602
 5603    _ = editor.update(cx, |editor, cx| {
 5604        editor.set_auto_replace_emoji_shortcode(true);
 5605
 5606        editor.handle_input("Hello ", cx);
 5607        editor.handle_input(":wave", cx);
 5608        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5609
 5610        editor.handle_input(":", cx);
 5611        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5612
 5613        editor.handle_input(" :smile", cx);
 5614        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5615
 5616        editor.handle_input(":", cx);
 5617        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5618
 5619        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5620        editor.handle_input(":wave", cx);
 5621        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5622
 5623        editor.handle_input(":", cx);
 5624        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5625
 5626        editor.handle_input(":1", cx);
 5627        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5628
 5629        editor.handle_input(":", cx);
 5630        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5631
 5632        // Ensure shortcode does not get replaced when it is part of a word
 5633        editor.handle_input(" Test:wave", cx);
 5634        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5635
 5636        editor.handle_input(":", cx);
 5637        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5638
 5639        editor.set_auto_replace_emoji_shortcode(false);
 5640
 5641        // Ensure shortcode does not get replaced when auto replace is off
 5642        editor.handle_input(" :wave", cx);
 5643        assert_eq!(
 5644            editor.text(cx),
 5645            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5646        );
 5647
 5648        editor.handle_input(":", cx);
 5649        assert_eq!(
 5650            editor.text(cx),
 5651            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5652        );
 5653    });
 5654}
 5655
 5656#[gpui::test]
 5657async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5658    init_test(cx, |_| {});
 5659
 5660    let (text, insertion_ranges) = marked_text_ranges(
 5661        indoc! {"
 5662            a.ˇ b
 5663            a.ˇ b
 5664            a.ˇ b
 5665        "},
 5666        false,
 5667    );
 5668
 5669    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5670    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5671
 5672    _ = editor.update(cx, |editor, cx| {
 5673        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5674
 5675        editor
 5676            .insert_snippet(&insertion_ranges, snippet, cx)
 5677            .unwrap();
 5678
 5679        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5680            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5681            assert_eq!(editor.text(cx), expected_text);
 5682            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5683        }
 5684
 5685        assert(
 5686            editor,
 5687            cx,
 5688            indoc! {"
 5689                a.f(«one», two, «three») b
 5690                a.f(«one», two, «three») b
 5691                a.f(«one», two, «three») b
 5692            "},
 5693        );
 5694
 5695        // Can't move earlier than the first tab stop
 5696        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 5697        assert(
 5698            editor,
 5699            cx,
 5700            indoc! {"
 5701                a.f(«one», two, «three») b
 5702                a.f(«one», two, «three») b
 5703                a.f(«one», two, «three») b
 5704            "},
 5705        );
 5706
 5707        assert!(editor.move_to_next_snippet_tabstop(cx));
 5708        assert(
 5709            editor,
 5710            cx,
 5711            indoc! {"
 5712                a.f(one, «two», three) b
 5713                a.f(one, «two», three) b
 5714                a.f(one, «two», three) b
 5715            "},
 5716        );
 5717
 5718        editor.move_to_prev_snippet_tabstop(cx);
 5719        assert(
 5720            editor,
 5721            cx,
 5722            indoc! {"
 5723                a.f(«one», two, «three») b
 5724                a.f(«one», two, «three») b
 5725                a.f(«one», two, «three») b
 5726            "},
 5727        );
 5728
 5729        assert!(editor.move_to_next_snippet_tabstop(cx));
 5730        assert(
 5731            editor,
 5732            cx,
 5733            indoc! {"
 5734                a.f(one, «two», three) b
 5735                a.f(one, «two», three) b
 5736                a.f(one, «two», three) b
 5737            "},
 5738        );
 5739        assert!(editor.move_to_next_snippet_tabstop(cx));
 5740        assert(
 5741            editor,
 5742            cx,
 5743            indoc! {"
 5744                a.f(one, two, three)ˇ b
 5745                a.f(one, two, three)ˇ b
 5746                a.f(one, two, three)ˇ b
 5747            "},
 5748        );
 5749
 5750        // As soon as the last tab stop is reached, snippet state is gone
 5751        editor.move_to_prev_snippet_tabstop(cx);
 5752        assert(
 5753            editor,
 5754            cx,
 5755            indoc! {"
 5756                a.f(one, two, three)ˇ b
 5757                a.f(one, two, three)ˇ b
 5758                a.f(one, two, three)ˇ b
 5759            "},
 5760        );
 5761    });
 5762}
 5763
 5764#[gpui::test]
 5765async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 5766    init_test(cx, |_| {});
 5767
 5768    let fs = FakeFs::new(cx.executor());
 5769    fs.insert_file("/file.rs", Default::default()).await;
 5770
 5771    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 5772
 5773    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5774    language_registry.add(rust_lang());
 5775    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5776        "Rust",
 5777        FakeLspAdapter {
 5778            capabilities: lsp::ServerCapabilities {
 5779                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5780                ..Default::default()
 5781            },
 5782            ..Default::default()
 5783        },
 5784    );
 5785
 5786    let buffer = project
 5787        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 5788        .await
 5789        .unwrap();
 5790
 5791    cx.executor().start_waiting();
 5792    let fake_server = fake_servers.next().await.unwrap();
 5793
 5794    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5795    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5796    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5797    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5798
 5799    let save = editor
 5800        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5801        .unwrap();
 5802    fake_server
 5803        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5804            assert_eq!(
 5805                params.text_document.uri,
 5806                lsp::Url::from_file_path("/file.rs").unwrap()
 5807            );
 5808            assert_eq!(params.options.tab_size, 4);
 5809            Ok(Some(vec![lsp::TextEdit::new(
 5810                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 5811                ", ".to_string(),
 5812            )]))
 5813        })
 5814        .next()
 5815        .await;
 5816    cx.executor().start_waiting();
 5817    save.await;
 5818
 5819    assert_eq!(
 5820        editor.update(cx, |editor, cx| editor.text(cx)),
 5821        "one, two\nthree\n"
 5822    );
 5823    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5824
 5825    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5826    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5827
 5828    // Ensure we can still save even if formatting hangs.
 5829    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5830        assert_eq!(
 5831            params.text_document.uri,
 5832            lsp::Url::from_file_path("/file.rs").unwrap()
 5833        );
 5834        futures::future::pending::<()>().await;
 5835        unreachable!()
 5836    });
 5837    let save = editor
 5838        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5839        .unwrap();
 5840    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 5841    cx.executor().start_waiting();
 5842    save.await;
 5843    assert_eq!(
 5844        editor.update(cx, |editor, cx| editor.text(cx)),
 5845        "one\ntwo\nthree\n"
 5846    );
 5847    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5848
 5849    // For non-dirty buffer, no formatting request should be sent
 5850    let save = editor
 5851        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5852        .unwrap();
 5853    let _pending_format_request = fake_server
 5854        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 5855            panic!("Should not be invoked on non-dirty buffer");
 5856        })
 5857        .next();
 5858    cx.executor().start_waiting();
 5859    save.await;
 5860
 5861    // Set rust language override and assert overridden tabsize is sent to language server
 5862    update_test_language_settings(cx, |settings| {
 5863        settings.languages.insert(
 5864            "Rust".into(),
 5865            LanguageSettingsContent {
 5866                tab_size: NonZeroU32::new(8),
 5867                ..Default::default()
 5868            },
 5869        );
 5870    });
 5871
 5872    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 5873    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5874    let save = editor
 5875        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5876        .unwrap();
 5877    fake_server
 5878        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5879            assert_eq!(
 5880                params.text_document.uri,
 5881                lsp::Url::from_file_path("/file.rs").unwrap()
 5882            );
 5883            assert_eq!(params.options.tab_size, 8);
 5884            Ok(Some(vec![]))
 5885        })
 5886        .next()
 5887        .await;
 5888    cx.executor().start_waiting();
 5889    save.await;
 5890}
 5891
 5892#[gpui::test]
 5893async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 5894    init_test(cx, |_| {});
 5895
 5896    let cols = 4;
 5897    let rows = 10;
 5898    let sample_text_1 = sample_text(rows, cols, 'a');
 5899    assert_eq!(
 5900        sample_text_1,
 5901        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 5902    );
 5903    let sample_text_2 = sample_text(rows, cols, 'l');
 5904    assert_eq!(
 5905        sample_text_2,
 5906        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 5907    );
 5908    let sample_text_3 = sample_text(rows, cols, 'v');
 5909    assert_eq!(
 5910        sample_text_3,
 5911        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 5912    );
 5913
 5914    let fs = FakeFs::new(cx.executor());
 5915    fs.insert_tree(
 5916        "/a",
 5917        json!({
 5918            "main.rs": sample_text_1,
 5919            "other.rs": sample_text_2,
 5920            "lib.rs": sample_text_3,
 5921        }),
 5922    )
 5923    .await;
 5924
 5925    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 5926    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 5927    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 5928
 5929    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5930    language_registry.add(rust_lang());
 5931    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5932        "Rust",
 5933        FakeLspAdapter {
 5934            capabilities: lsp::ServerCapabilities {
 5935                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5936                ..Default::default()
 5937            },
 5938            ..Default::default()
 5939        },
 5940    );
 5941
 5942    let worktree = project.update(cx, |project, _| {
 5943        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 5944        assert_eq!(worktrees.len(), 1);
 5945        worktrees.pop().unwrap()
 5946    });
 5947    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 5948
 5949    let buffer_1 = project
 5950        .update(cx, |project, cx| {
 5951            project.open_buffer((worktree_id, "main.rs"), cx)
 5952        })
 5953        .await
 5954        .unwrap();
 5955    let buffer_2 = project
 5956        .update(cx, |project, cx| {
 5957            project.open_buffer((worktree_id, "other.rs"), cx)
 5958        })
 5959        .await
 5960        .unwrap();
 5961    let buffer_3 = project
 5962        .update(cx, |project, cx| {
 5963            project.open_buffer((worktree_id, "lib.rs"), cx)
 5964        })
 5965        .await
 5966        .unwrap();
 5967
 5968    let multi_buffer = cx.new_model(|cx| {
 5969        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 5970        multi_buffer.push_excerpts(
 5971            buffer_1.clone(),
 5972            [
 5973                ExcerptRange {
 5974                    context: Point::new(0, 0)..Point::new(3, 0),
 5975                    primary: None,
 5976                },
 5977                ExcerptRange {
 5978                    context: Point::new(5, 0)..Point::new(7, 0),
 5979                    primary: None,
 5980                },
 5981                ExcerptRange {
 5982                    context: Point::new(9, 0)..Point::new(10, 4),
 5983                    primary: None,
 5984                },
 5985            ],
 5986            cx,
 5987        );
 5988        multi_buffer.push_excerpts(
 5989            buffer_2.clone(),
 5990            [
 5991                ExcerptRange {
 5992                    context: Point::new(0, 0)..Point::new(3, 0),
 5993                    primary: None,
 5994                },
 5995                ExcerptRange {
 5996                    context: Point::new(5, 0)..Point::new(7, 0),
 5997                    primary: None,
 5998                },
 5999                ExcerptRange {
 6000                    context: Point::new(9, 0)..Point::new(10, 4),
 6001                    primary: None,
 6002                },
 6003            ],
 6004            cx,
 6005        );
 6006        multi_buffer.push_excerpts(
 6007            buffer_3.clone(),
 6008            [
 6009                ExcerptRange {
 6010                    context: Point::new(0, 0)..Point::new(3, 0),
 6011                    primary: None,
 6012                },
 6013                ExcerptRange {
 6014                    context: Point::new(5, 0)..Point::new(7, 0),
 6015                    primary: None,
 6016                },
 6017                ExcerptRange {
 6018                    context: Point::new(9, 0)..Point::new(10, 4),
 6019                    primary: None,
 6020                },
 6021            ],
 6022            cx,
 6023        );
 6024        multi_buffer
 6025    });
 6026    let multi_buffer_editor =
 6027        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 6028
 6029    multi_buffer_editor.update(cx, |editor, cx| {
 6030        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6031        editor.insert("|one|two|three|", cx);
 6032    });
 6033    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6034    multi_buffer_editor.update(cx, |editor, cx| {
 6035        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6036            s.select_ranges(Some(60..70))
 6037        });
 6038        editor.insert("|four|five|six|", cx);
 6039    });
 6040    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6041
 6042    // First two buffers should be edited, but not the third one.
 6043    assert_eq!(
 6044        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6045        "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}",
 6046    );
 6047    buffer_1.update(cx, |buffer, _| {
 6048        assert!(buffer.is_dirty());
 6049        assert_eq!(
 6050            buffer.text(),
 6051            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6052        )
 6053    });
 6054    buffer_2.update(cx, |buffer, _| {
 6055        assert!(buffer.is_dirty());
 6056        assert_eq!(
 6057            buffer.text(),
 6058            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6059        )
 6060    });
 6061    buffer_3.update(cx, |buffer, _| {
 6062        assert!(!buffer.is_dirty());
 6063        assert_eq!(buffer.text(), sample_text_3,)
 6064    });
 6065
 6066    cx.executor().start_waiting();
 6067    let save = multi_buffer_editor
 6068        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6069        .unwrap();
 6070
 6071    let fake_server = fake_servers.next().await.unwrap();
 6072    fake_server
 6073        .server
 6074        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6075            Ok(Some(vec![lsp::TextEdit::new(
 6076                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6077                format!("[{} formatted]", params.text_document.uri),
 6078            )]))
 6079        })
 6080        .detach();
 6081    save.await;
 6082
 6083    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6084    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6085    assert_eq!(
 6086        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6087        "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}",
 6088    );
 6089    buffer_1.update(cx, |buffer, _| {
 6090        assert!(!buffer.is_dirty());
 6091        assert_eq!(
 6092            buffer.text(),
 6093            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6094        )
 6095    });
 6096    buffer_2.update(cx, |buffer, _| {
 6097        assert!(!buffer.is_dirty());
 6098        assert_eq!(
 6099            buffer.text(),
 6100            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6101        )
 6102    });
 6103    buffer_3.update(cx, |buffer, _| {
 6104        assert!(!buffer.is_dirty());
 6105        assert_eq!(buffer.text(), sample_text_3,)
 6106    });
 6107}
 6108
 6109#[gpui::test]
 6110async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6111    init_test(cx, |_| {});
 6112
 6113    let fs = FakeFs::new(cx.executor());
 6114    fs.insert_file("/file.rs", Default::default()).await;
 6115
 6116    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6117
 6118    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6119    language_registry.add(rust_lang());
 6120    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6121        "Rust",
 6122        FakeLspAdapter {
 6123            capabilities: lsp::ServerCapabilities {
 6124                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6125                ..Default::default()
 6126            },
 6127            ..Default::default()
 6128        },
 6129    );
 6130
 6131    let buffer = project
 6132        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6133        .await
 6134        .unwrap();
 6135
 6136    cx.executor().start_waiting();
 6137    let fake_server = fake_servers.next().await.unwrap();
 6138
 6139    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6140    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6141    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6142    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6143
 6144    let save = editor
 6145        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6146        .unwrap();
 6147    fake_server
 6148        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6149            assert_eq!(
 6150                params.text_document.uri,
 6151                lsp::Url::from_file_path("/file.rs").unwrap()
 6152            );
 6153            assert_eq!(params.options.tab_size, 4);
 6154            Ok(Some(vec![lsp::TextEdit::new(
 6155                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6156                ", ".to_string(),
 6157            )]))
 6158        })
 6159        .next()
 6160        .await;
 6161    cx.executor().start_waiting();
 6162    save.await;
 6163    assert_eq!(
 6164        editor.update(cx, |editor, cx| editor.text(cx)),
 6165        "one, two\nthree\n"
 6166    );
 6167    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6168
 6169    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6170    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6171
 6172    // Ensure we can still save even if formatting hangs.
 6173    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6174        move |params, _| async move {
 6175            assert_eq!(
 6176                params.text_document.uri,
 6177                lsp::Url::from_file_path("/file.rs").unwrap()
 6178            );
 6179            futures::future::pending::<()>().await;
 6180            unreachable!()
 6181        },
 6182    );
 6183    let save = editor
 6184        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6185        .unwrap();
 6186    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6187    cx.executor().start_waiting();
 6188    save.await;
 6189    assert_eq!(
 6190        editor.update(cx, |editor, cx| editor.text(cx)),
 6191        "one\ntwo\nthree\n"
 6192    );
 6193    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6194
 6195    // For non-dirty buffer, no formatting request should be sent
 6196    let save = editor
 6197        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6198        .unwrap();
 6199    let _pending_format_request = fake_server
 6200        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6201            panic!("Should not be invoked on non-dirty buffer");
 6202        })
 6203        .next();
 6204    cx.executor().start_waiting();
 6205    save.await;
 6206
 6207    // Set Rust language override and assert overridden tabsize is sent to language server
 6208    update_test_language_settings(cx, |settings| {
 6209        settings.languages.insert(
 6210            "Rust".into(),
 6211            LanguageSettingsContent {
 6212                tab_size: NonZeroU32::new(8),
 6213                ..Default::default()
 6214            },
 6215        );
 6216    });
 6217
 6218    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6219    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6220    let save = editor
 6221        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6222        .unwrap();
 6223    fake_server
 6224        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6225            assert_eq!(
 6226                params.text_document.uri,
 6227                lsp::Url::from_file_path("/file.rs").unwrap()
 6228            );
 6229            assert_eq!(params.options.tab_size, 8);
 6230            Ok(Some(vec![]))
 6231        })
 6232        .next()
 6233        .await;
 6234    cx.executor().start_waiting();
 6235    save.await;
 6236}
 6237
 6238#[gpui::test]
 6239async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6240    init_test(cx, |settings| {
 6241        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6242    });
 6243
 6244    let fs = FakeFs::new(cx.executor());
 6245    fs.insert_file("/file.rs", Default::default()).await;
 6246
 6247    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6248
 6249    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6250    language_registry.add(Arc::new(Language::new(
 6251        LanguageConfig {
 6252            name: "Rust".into(),
 6253            matcher: LanguageMatcher {
 6254                path_suffixes: vec!["rs".to_string()],
 6255                ..Default::default()
 6256            },
 6257            // Enable Prettier formatting for the same buffer, and ensure
 6258            // LSP is called instead of Prettier.
 6259            prettier_parser_name: Some("test_parser".to_string()),
 6260            ..Default::default()
 6261        },
 6262        Some(tree_sitter_rust::language()),
 6263    )));
 6264    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6265        "Rust",
 6266        FakeLspAdapter {
 6267            capabilities: lsp::ServerCapabilities {
 6268                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6269                ..Default::default()
 6270            },
 6271            ..Default::default()
 6272        },
 6273    );
 6274
 6275    let buffer = project
 6276        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6277        .await
 6278        .unwrap();
 6279
 6280    cx.executor().start_waiting();
 6281    let fake_server = fake_servers.next().await.unwrap();
 6282
 6283    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6284    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6285    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6286
 6287    let format = editor
 6288        .update(cx, |editor, cx| {
 6289            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6290        })
 6291        .unwrap();
 6292    fake_server
 6293        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6294            assert_eq!(
 6295                params.text_document.uri,
 6296                lsp::Url::from_file_path("/file.rs").unwrap()
 6297            );
 6298            assert_eq!(params.options.tab_size, 4);
 6299            Ok(Some(vec![lsp::TextEdit::new(
 6300                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6301                ", ".to_string(),
 6302            )]))
 6303        })
 6304        .next()
 6305        .await;
 6306    cx.executor().start_waiting();
 6307    format.await;
 6308    assert_eq!(
 6309        editor.update(cx, |editor, cx| editor.text(cx)),
 6310        "one, two\nthree\n"
 6311    );
 6312
 6313    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6314    // Ensure we don't lock if formatting hangs.
 6315    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6316        assert_eq!(
 6317            params.text_document.uri,
 6318            lsp::Url::from_file_path("/file.rs").unwrap()
 6319        );
 6320        futures::future::pending::<()>().await;
 6321        unreachable!()
 6322    });
 6323    let format = editor
 6324        .update(cx, |editor, cx| {
 6325            editor.perform_format(project, FormatTrigger::Manual, cx)
 6326        })
 6327        .unwrap();
 6328    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6329    cx.executor().start_waiting();
 6330    format.await;
 6331    assert_eq!(
 6332        editor.update(cx, |editor, cx| editor.text(cx)),
 6333        "one\ntwo\nthree\n"
 6334    );
 6335}
 6336
 6337#[gpui::test]
 6338async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6339    init_test(cx, |_| {});
 6340
 6341    let mut cx = EditorLspTestContext::new_rust(
 6342        lsp::ServerCapabilities {
 6343            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6344            ..Default::default()
 6345        },
 6346        cx,
 6347    )
 6348    .await;
 6349
 6350    cx.set_state(indoc! {"
 6351        one.twoˇ
 6352    "});
 6353
 6354    // The format request takes a long time. When it completes, it inserts
 6355    // a newline and an indent before the `.`
 6356    cx.lsp
 6357        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6358            let executor = cx.background_executor().clone();
 6359            async move {
 6360                executor.timer(Duration::from_millis(100)).await;
 6361                Ok(Some(vec![lsp::TextEdit {
 6362                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6363                    new_text: "\n    ".into(),
 6364                }]))
 6365            }
 6366        });
 6367
 6368    // Submit a format request.
 6369    let format_1 = cx
 6370        .update_editor(|editor, cx| editor.format(&Format, cx))
 6371        .unwrap();
 6372    cx.executor().run_until_parked();
 6373
 6374    // Submit a second format request.
 6375    let format_2 = cx
 6376        .update_editor(|editor, cx| editor.format(&Format, cx))
 6377        .unwrap();
 6378    cx.executor().run_until_parked();
 6379
 6380    // Wait for both format requests to complete
 6381    cx.executor().advance_clock(Duration::from_millis(200));
 6382    cx.executor().start_waiting();
 6383    format_1.await.unwrap();
 6384    cx.executor().start_waiting();
 6385    format_2.await.unwrap();
 6386
 6387    // The formatting edits only happens once.
 6388    cx.assert_editor_state(indoc! {"
 6389        one
 6390            .twoˇ
 6391    "});
 6392}
 6393
 6394#[gpui::test]
 6395async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6396    init_test(cx, |settings| {
 6397        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6398    });
 6399
 6400    let mut cx = EditorLspTestContext::new_rust(
 6401        lsp::ServerCapabilities {
 6402            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6403            ..Default::default()
 6404        },
 6405        cx,
 6406    )
 6407    .await;
 6408
 6409    // Set up a buffer white some trailing whitespace and no trailing newline.
 6410    cx.set_state(
 6411        &[
 6412            "one ",   //
 6413            "twoˇ",   //
 6414            "three ", //
 6415            "four",   //
 6416        ]
 6417        .join("\n"),
 6418    );
 6419
 6420    // Submit a format request.
 6421    let format = cx
 6422        .update_editor(|editor, cx| editor.format(&Format, cx))
 6423        .unwrap();
 6424
 6425    // Record which buffer changes have been sent to the language server
 6426    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6427    cx.lsp
 6428        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6429            let buffer_changes = buffer_changes.clone();
 6430            move |params, _| {
 6431                buffer_changes.lock().extend(
 6432                    params
 6433                        .content_changes
 6434                        .into_iter()
 6435                        .map(|e| (e.range.unwrap(), e.text)),
 6436                );
 6437            }
 6438        });
 6439
 6440    // Handle formatting requests to the language server.
 6441    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6442        let buffer_changes = buffer_changes.clone();
 6443        move |_, _| {
 6444            // When formatting is requested, trailing whitespace has already been stripped,
 6445            // and the trailing newline has already been added.
 6446            assert_eq!(
 6447                &buffer_changes.lock()[1..],
 6448                &[
 6449                    (
 6450                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6451                        "".into()
 6452                    ),
 6453                    (
 6454                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6455                        "".into()
 6456                    ),
 6457                    (
 6458                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6459                        "\n".into()
 6460                    ),
 6461                ]
 6462            );
 6463
 6464            // Insert blank lines between each line of the buffer.
 6465            async move {
 6466                Ok(Some(vec![
 6467                    lsp::TextEdit {
 6468                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6469                        new_text: "\n".into(),
 6470                    },
 6471                    lsp::TextEdit {
 6472                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6473                        new_text: "\n".into(),
 6474                    },
 6475                ]))
 6476            }
 6477        }
 6478    });
 6479
 6480    // After formatting the buffer, the trailing whitespace is stripped,
 6481    // a newline is appended, and the edits provided by the language server
 6482    // have been applied.
 6483    format.await.unwrap();
 6484    cx.assert_editor_state(
 6485        &[
 6486            "one",   //
 6487            "",      //
 6488            "twoˇ",  //
 6489            "",      //
 6490            "three", //
 6491            "four",  //
 6492            "",      //
 6493        ]
 6494        .join("\n"),
 6495    );
 6496
 6497    // Undoing the formatting undoes the trailing whitespace removal, the
 6498    // trailing newline, and the LSP edits.
 6499    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6500    cx.assert_editor_state(
 6501        &[
 6502            "one ",   //
 6503            "twoˇ",   //
 6504            "three ", //
 6505            "four",   //
 6506        ]
 6507        .join("\n"),
 6508    );
 6509}
 6510
 6511#[gpui::test]
 6512async fn test_completion(cx: &mut gpui::TestAppContext) {
 6513    init_test(cx, |_| {});
 6514
 6515    let mut cx = EditorLspTestContext::new_rust(
 6516        lsp::ServerCapabilities {
 6517            completion_provider: Some(lsp::CompletionOptions {
 6518                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 6519                resolve_provider: Some(true),
 6520                ..Default::default()
 6521            }),
 6522            ..Default::default()
 6523        },
 6524        cx,
 6525    )
 6526    .await;
 6527
 6528    cx.set_state(indoc! {"
 6529        oneˇ
 6530        two
 6531        three
 6532    "});
 6533    cx.simulate_keystroke(".");
 6534    handle_completion_request(
 6535        &mut cx,
 6536        indoc! {"
 6537            one.|<>
 6538            two
 6539            three
 6540        "},
 6541        vec!["first_completion", "second_completion"],
 6542    )
 6543    .await;
 6544    cx.condition(|editor, _| editor.context_menu_visible())
 6545        .await;
 6546    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6547        editor.context_menu_next(&Default::default(), cx);
 6548        editor
 6549            .confirm_completion(&ConfirmCompletion::default(), cx)
 6550            .unwrap()
 6551    });
 6552    cx.assert_editor_state(indoc! {"
 6553        one.second_completionˇ
 6554        two
 6555        three
 6556    "});
 6557
 6558    handle_resolve_completion_request(
 6559        &mut cx,
 6560        Some(vec![
 6561            (
 6562                //This overlaps with the primary completion edit which is
 6563                //misbehavior from the LSP spec, test that we filter it out
 6564                indoc! {"
 6565                    one.second_ˇcompletion
 6566                    two
 6567                    threeˇ
 6568                "},
 6569                "overlapping additional edit",
 6570            ),
 6571            (
 6572                indoc! {"
 6573                    one.second_completion
 6574                    two
 6575                    threeˇ
 6576                "},
 6577                "\nadditional edit",
 6578            ),
 6579        ]),
 6580    )
 6581    .await;
 6582    apply_additional_edits.await.unwrap();
 6583    cx.assert_editor_state(indoc! {"
 6584        one.second_completionˇ
 6585        two
 6586        three
 6587        additional edit
 6588    "});
 6589
 6590    cx.set_state(indoc! {"
 6591        one.second_completion
 6592        twoˇ
 6593        threeˇ
 6594        additional edit
 6595    "});
 6596    cx.simulate_keystroke(" ");
 6597    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6598    cx.simulate_keystroke("s");
 6599    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6600
 6601    cx.assert_editor_state(indoc! {"
 6602        one.second_completion
 6603        two sˇ
 6604        three sˇ
 6605        additional edit
 6606    "});
 6607    handle_completion_request(
 6608        &mut cx,
 6609        indoc! {"
 6610            one.second_completion
 6611            two s
 6612            three <s|>
 6613            additional edit
 6614        "},
 6615        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6616    )
 6617    .await;
 6618    cx.condition(|editor, _| editor.context_menu_visible())
 6619        .await;
 6620
 6621    cx.simulate_keystroke("i");
 6622
 6623    handle_completion_request(
 6624        &mut cx,
 6625        indoc! {"
 6626            one.second_completion
 6627            two si
 6628            three <si|>
 6629            additional edit
 6630        "},
 6631        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6632    )
 6633    .await;
 6634    cx.condition(|editor, _| editor.context_menu_visible())
 6635        .await;
 6636
 6637    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6638        editor
 6639            .confirm_completion(&ConfirmCompletion::default(), cx)
 6640            .unwrap()
 6641    });
 6642    cx.assert_editor_state(indoc! {"
 6643        one.second_completion
 6644        two sixth_completionˇ
 6645        three sixth_completionˇ
 6646        additional edit
 6647    "});
 6648
 6649    handle_resolve_completion_request(&mut cx, None).await;
 6650    apply_additional_edits.await.unwrap();
 6651
 6652    _ = cx.update(|cx| {
 6653        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6654            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6655                settings.show_completions_on_input = Some(false);
 6656            });
 6657        })
 6658    });
 6659    cx.set_state("editorˇ");
 6660    cx.simulate_keystroke(".");
 6661    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6662    cx.simulate_keystroke("c");
 6663    cx.simulate_keystroke("l");
 6664    cx.simulate_keystroke("o");
 6665    cx.assert_editor_state("editor.cloˇ");
 6666    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6667    cx.update_editor(|editor, cx| {
 6668        editor.show_completions(&ShowCompletions, cx);
 6669    });
 6670    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
 6671    cx.condition(|editor, _| editor.context_menu_visible())
 6672        .await;
 6673    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6674        editor
 6675            .confirm_completion(&ConfirmCompletion::default(), cx)
 6676            .unwrap()
 6677    });
 6678    cx.assert_editor_state("editor.closeˇ");
 6679    handle_resolve_completion_request(&mut cx, None).await;
 6680    apply_additional_edits.await.unwrap();
 6681}
 6682
 6683#[gpui::test]
 6684async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 6685    init_test(cx, |_| {});
 6686    let mut cx = EditorTestContext::new(cx).await;
 6687    let language = Arc::new(Language::new(
 6688        LanguageConfig {
 6689            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 6690            ..Default::default()
 6691        },
 6692        Some(tree_sitter_rust::language()),
 6693    ));
 6694    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 6695
 6696    // If multiple selections intersect a line, the line is only toggled once.
 6697    cx.set_state(indoc! {"
 6698        fn a() {
 6699            «//b();
 6700            ˇ»// «c();
 6701            //ˇ»  d();
 6702        }
 6703    "});
 6704
 6705    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6706
 6707    cx.assert_editor_state(indoc! {"
 6708        fn a() {
 6709            «b();
 6710            c();
 6711            ˇ» d();
 6712        }
 6713    "});
 6714
 6715    // The comment prefix is inserted at the same column for every line in a
 6716    // selection.
 6717    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6718
 6719    cx.assert_editor_state(indoc! {"
 6720        fn a() {
 6721            // «b();
 6722            // c();
 6723            ˇ»//  d();
 6724        }
 6725    "});
 6726
 6727    // If a selection ends at the beginning of a line, that line is not toggled.
 6728    cx.set_selections_state(indoc! {"
 6729        fn a() {
 6730            // b();
 6731            «// c();
 6732        ˇ»    //  d();
 6733        }
 6734    "});
 6735
 6736    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6737
 6738    cx.assert_editor_state(indoc! {"
 6739        fn a() {
 6740            // b();
 6741            «c();
 6742        ˇ»    //  d();
 6743        }
 6744    "});
 6745
 6746    // If a selection span a single line and is empty, the line is toggled.
 6747    cx.set_state(indoc! {"
 6748        fn a() {
 6749            a();
 6750            b();
 6751        ˇ
 6752        }
 6753    "});
 6754
 6755    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6756
 6757    cx.assert_editor_state(indoc! {"
 6758        fn a() {
 6759            a();
 6760            b();
 6761        //•ˇ
 6762        }
 6763    "});
 6764
 6765    // If a selection span multiple lines, empty lines are not toggled.
 6766    cx.set_state(indoc! {"
 6767        fn a() {
 6768            «a();
 6769
 6770            c();ˇ»
 6771        }
 6772    "});
 6773
 6774    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6775
 6776    cx.assert_editor_state(indoc! {"
 6777        fn a() {
 6778            // «a();
 6779
 6780            // c();ˇ»
 6781        }
 6782    "});
 6783
 6784    // If a selection includes multiple comment prefixes, all lines are uncommented.
 6785    cx.set_state(indoc! {"
 6786        fn a() {
 6787            «// a();
 6788            /// b();
 6789            //! c();ˇ»
 6790        }
 6791    "});
 6792
 6793    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6794
 6795    cx.assert_editor_state(indoc! {"
 6796        fn a() {
 6797            «a();
 6798            b();
 6799            c();ˇ»
 6800        }
 6801    "});
 6802}
 6803
 6804#[gpui::test]
 6805async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 6806    init_test(cx, |_| {});
 6807
 6808    let language = Arc::new(Language::new(
 6809        LanguageConfig {
 6810            line_comments: vec!["// ".into()],
 6811            ..Default::default()
 6812        },
 6813        Some(tree_sitter_rust::language()),
 6814    ));
 6815
 6816    let mut cx = EditorTestContext::new(cx).await;
 6817
 6818    cx.language_registry().add(language.clone());
 6819    cx.update_buffer(|buffer, cx| {
 6820        buffer.set_language(Some(language), cx);
 6821    });
 6822
 6823    let toggle_comments = &ToggleComments {
 6824        advance_downwards: true,
 6825    };
 6826
 6827    // Single cursor on one line -> advance
 6828    // Cursor moves horizontally 3 characters as well on non-blank line
 6829    cx.set_state(indoc!(
 6830        "fn a() {
 6831             ˇdog();
 6832             cat();
 6833        }"
 6834    ));
 6835    cx.update_editor(|editor, cx| {
 6836        editor.toggle_comments(toggle_comments, cx);
 6837    });
 6838    cx.assert_editor_state(indoc!(
 6839        "fn a() {
 6840             // dog();
 6841             catˇ();
 6842        }"
 6843    ));
 6844
 6845    // Single selection on one line -> don't advance
 6846    cx.set_state(indoc!(
 6847        "fn a() {
 6848             «dog()ˇ»;
 6849             cat();
 6850        }"
 6851    ));
 6852    cx.update_editor(|editor, cx| {
 6853        editor.toggle_comments(toggle_comments, cx);
 6854    });
 6855    cx.assert_editor_state(indoc!(
 6856        "fn a() {
 6857             // «dog()ˇ»;
 6858             cat();
 6859        }"
 6860    ));
 6861
 6862    // Multiple cursors on one line -> advance
 6863    cx.set_state(indoc!(
 6864        "fn a() {
 6865             ˇdˇog();
 6866             cat();
 6867        }"
 6868    ));
 6869    cx.update_editor(|editor, cx| {
 6870        editor.toggle_comments(toggle_comments, cx);
 6871    });
 6872    cx.assert_editor_state(indoc!(
 6873        "fn a() {
 6874             // dog();
 6875             catˇ(ˇ);
 6876        }"
 6877    ));
 6878
 6879    // Multiple cursors on one line, with selection -> don't advance
 6880    cx.set_state(indoc!(
 6881        "fn a() {
 6882             ˇdˇog«()ˇ»;
 6883             cat();
 6884        }"
 6885    ));
 6886    cx.update_editor(|editor, cx| {
 6887        editor.toggle_comments(toggle_comments, cx);
 6888    });
 6889    cx.assert_editor_state(indoc!(
 6890        "fn a() {
 6891             // ˇdˇog«()ˇ»;
 6892             cat();
 6893        }"
 6894    ));
 6895
 6896    // Single cursor on one line -> advance
 6897    // Cursor moves to column 0 on blank line
 6898    cx.set_state(indoc!(
 6899        "fn a() {
 6900             ˇdog();
 6901
 6902             cat();
 6903        }"
 6904    ));
 6905    cx.update_editor(|editor, cx| {
 6906        editor.toggle_comments(toggle_comments, cx);
 6907    });
 6908    cx.assert_editor_state(indoc!(
 6909        "fn a() {
 6910             // dog();
 6911        ˇ
 6912             cat();
 6913        }"
 6914    ));
 6915
 6916    // Single cursor on one line -> advance
 6917    // Cursor starts and ends at column 0
 6918    cx.set_state(indoc!(
 6919        "fn a() {
 6920         ˇ    dog();
 6921             cat();
 6922        }"
 6923    ));
 6924    cx.update_editor(|editor, cx| {
 6925        editor.toggle_comments(toggle_comments, cx);
 6926    });
 6927    cx.assert_editor_state(indoc!(
 6928        "fn a() {
 6929             // dog();
 6930         ˇ    cat();
 6931        }"
 6932    ));
 6933}
 6934
 6935#[gpui::test]
 6936async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 6937    init_test(cx, |_| {});
 6938
 6939    let mut cx = EditorTestContext::new(cx).await;
 6940
 6941    let html_language = Arc::new(
 6942        Language::new(
 6943            LanguageConfig {
 6944                name: "HTML".into(),
 6945                block_comment: Some(("<!-- ".into(), " -->".into())),
 6946                ..Default::default()
 6947            },
 6948            Some(tree_sitter_html::language()),
 6949        )
 6950        .with_injection_query(
 6951            r#"
 6952            (script_element
 6953                (raw_text) @content
 6954                (#set! "language" "javascript"))
 6955            "#,
 6956        )
 6957        .unwrap(),
 6958    );
 6959
 6960    let javascript_language = Arc::new(Language::new(
 6961        LanguageConfig {
 6962            name: "JavaScript".into(),
 6963            line_comments: vec!["// ".into()],
 6964            ..Default::default()
 6965        },
 6966        Some(tree_sitter_typescript::language_tsx()),
 6967    ));
 6968
 6969    cx.language_registry().add(html_language.clone());
 6970    cx.language_registry().add(javascript_language.clone());
 6971    cx.update_buffer(|buffer, cx| {
 6972        buffer.set_language(Some(html_language), cx);
 6973    });
 6974
 6975    // Toggle comments for empty selections
 6976    cx.set_state(
 6977        &r#"
 6978            <p>A</p>ˇ
 6979            <p>B</p>ˇ
 6980            <p>C</p>ˇ
 6981        "#
 6982        .unindent(),
 6983    );
 6984    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 6985    cx.assert_editor_state(
 6986        &r#"
 6987            <!-- <p>A</p>ˇ -->
 6988            <!-- <p>B</p>ˇ -->
 6989            <!-- <p>C</p>ˇ -->
 6990        "#
 6991        .unindent(),
 6992    );
 6993    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 6994    cx.assert_editor_state(
 6995        &r#"
 6996            <p>A</p>ˇ
 6997            <p>B</p>ˇ
 6998            <p>C</p>ˇ
 6999        "#
 7000        .unindent(),
 7001    );
 7002
 7003    // Toggle comments for mixture of empty and non-empty selections, where
 7004    // multiple selections occupy a given line.
 7005    cx.set_state(
 7006        &r#"
 7007            <p>A«</p>
 7008            <p>ˇ»B</p>ˇ
 7009            <p>C«</p>
 7010            <p>ˇ»D</p>ˇ
 7011        "#
 7012        .unindent(),
 7013    );
 7014
 7015    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7016    cx.assert_editor_state(
 7017        &r#"
 7018            <!-- <p>A«</p>
 7019            <p>ˇ»B</p>ˇ -->
 7020            <!-- <p>C«</p>
 7021            <p>ˇ»D</p>ˇ -->
 7022        "#
 7023        .unindent(),
 7024    );
 7025    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7026    cx.assert_editor_state(
 7027        &r#"
 7028            <p>A«</p>
 7029            <p>ˇ»B</p>ˇ
 7030            <p>C«</p>
 7031            <p>ˇ»D</p>ˇ
 7032        "#
 7033        .unindent(),
 7034    );
 7035
 7036    // Toggle comments when different languages are active for different
 7037    // selections.
 7038    cx.set_state(
 7039        &r#"
 7040            ˇ<script>
 7041                ˇvar x = new Y();
 7042            ˇ</script>
 7043        "#
 7044        .unindent(),
 7045    );
 7046    cx.executor().run_until_parked();
 7047    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7048    cx.assert_editor_state(
 7049        &r#"
 7050            <!-- ˇ<script> -->
 7051                // ˇvar x = new Y();
 7052            <!-- ˇ</script> -->
 7053        "#
 7054        .unindent(),
 7055    );
 7056}
 7057
 7058#[gpui::test]
 7059fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 7060    init_test(cx, |_| {});
 7061
 7062    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7063    let multibuffer = cx.new_model(|cx| {
 7064        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7065        multibuffer.push_excerpts(
 7066            buffer.clone(),
 7067            [
 7068                ExcerptRange {
 7069                    context: Point::new(0, 0)..Point::new(0, 4),
 7070                    primary: None,
 7071                },
 7072                ExcerptRange {
 7073                    context: Point::new(1, 0)..Point::new(1, 4),
 7074                    primary: None,
 7075                },
 7076            ],
 7077            cx,
 7078        );
 7079        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 7080        multibuffer
 7081    });
 7082
 7083    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7084    _ = view.update(cx, |view, cx| {
 7085        assert_eq!(view.text(cx), "aaaa\nbbbb");
 7086        view.change_selections(None, cx, |s| {
 7087            s.select_ranges([
 7088                Point::new(0, 0)..Point::new(0, 0),
 7089                Point::new(1, 0)..Point::new(1, 0),
 7090            ])
 7091        });
 7092
 7093        view.handle_input("X", cx);
 7094        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 7095        assert_eq!(
 7096            view.selections.ranges(cx),
 7097            [
 7098                Point::new(0, 1)..Point::new(0, 1),
 7099                Point::new(1, 1)..Point::new(1, 1),
 7100            ]
 7101        );
 7102
 7103        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 7104        view.change_selections(None, cx, |s| {
 7105            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 7106        });
 7107        view.backspace(&Default::default(), cx);
 7108        assert_eq!(view.text(cx), "Xa\nbbb");
 7109        assert_eq!(
 7110            view.selections.ranges(cx),
 7111            [Point::new(1, 0)..Point::new(1, 0)]
 7112        );
 7113
 7114        view.change_selections(None, cx, |s| {
 7115            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 7116        });
 7117        view.backspace(&Default::default(), cx);
 7118        assert_eq!(view.text(cx), "X\nbb");
 7119        assert_eq!(
 7120            view.selections.ranges(cx),
 7121            [Point::new(0, 1)..Point::new(0, 1)]
 7122        );
 7123    });
 7124}
 7125
 7126#[gpui::test]
 7127fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 7128    init_test(cx, |_| {});
 7129
 7130    let markers = vec![('[', ']').into(), ('(', ')').into()];
 7131    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 7132        indoc! {"
 7133            [aaaa
 7134            (bbbb]
 7135            cccc)",
 7136        },
 7137        markers.clone(),
 7138    );
 7139    let excerpt_ranges = markers.into_iter().map(|marker| {
 7140        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 7141        ExcerptRange {
 7142            context,
 7143            primary: None,
 7144        }
 7145    });
 7146    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 7147    let multibuffer = cx.new_model(|cx| {
 7148        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7149        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 7150        multibuffer
 7151    });
 7152
 7153    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7154    _ = view.update(cx, |view, cx| {
 7155        let (expected_text, selection_ranges) = marked_text_ranges(
 7156            indoc! {"
 7157                aaaa
 7158                bˇbbb
 7159                bˇbbˇb
 7160                cccc"
 7161            },
 7162            true,
 7163        );
 7164        assert_eq!(view.text(cx), expected_text);
 7165        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 7166
 7167        view.handle_input("X", cx);
 7168
 7169        let (expected_text, expected_selections) = marked_text_ranges(
 7170            indoc! {"
 7171                aaaa
 7172                bXˇbbXb
 7173                bXˇbbXˇb
 7174                cccc"
 7175            },
 7176            false,
 7177        );
 7178        assert_eq!(view.text(cx), expected_text);
 7179        assert_eq!(view.selections.ranges(cx), expected_selections);
 7180
 7181        view.newline(&Newline, cx);
 7182        let (expected_text, expected_selections) = marked_text_ranges(
 7183            indoc! {"
 7184                aaaa
 7185                bX
 7186                ˇbbX
 7187                b
 7188                bX
 7189                ˇbbX
 7190                ˇb
 7191                cccc"
 7192            },
 7193            false,
 7194        );
 7195        assert_eq!(view.text(cx), expected_text);
 7196        assert_eq!(view.selections.ranges(cx), expected_selections);
 7197    });
 7198}
 7199
 7200#[gpui::test]
 7201fn test_refresh_selections(cx: &mut TestAppContext) {
 7202    init_test(cx, |_| {});
 7203
 7204    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7205    let mut excerpt1_id = None;
 7206    let multibuffer = cx.new_model(|cx| {
 7207        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7208        excerpt1_id = multibuffer
 7209            .push_excerpts(
 7210                buffer.clone(),
 7211                [
 7212                    ExcerptRange {
 7213                        context: Point::new(0, 0)..Point::new(1, 4),
 7214                        primary: None,
 7215                    },
 7216                    ExcerptRange {
 7217                        context: Point::new(1, 0)..Point::new(2, 4),
 7218                        primary: None,
 7219                    },
 7220                ],
 7221                cx,
 7222            )
 7223            .into_iter()
 7224            .next();
 7225        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7226        multibuffer
 7227    });
 7228
 7229    let editor = cx.add_window(|cx| {
 7230        let mut editor = build_editor(multibuffer.clone(), cx);
 7231        let snapshot = editor.snapshot(cx);
 7232        editor.change_selections(None, cx, |s| {
 7233            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 7234        });
 7235        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 7236        assert_eq!(
 7237            editor.selections.ranges(cx),
 7238            [
 7239                Point::new(1, 3)..Point::new(1, 3),
 7240                Point::new(2, 1)..Point::new(2, 1),
 7241            ]
 7242        );
 7243        editor
 7244    });
 7245
 7246    // Refreshing selections is a no-op when excerpts haven't changed.
 7247    _ = editor.update(cx, |editor, cx| {
 7248        editor.change_selections(None, cx, |s| s.refresh());
 7249        assert_eq!(
 7250            editor.selections.ranges(cx),
 7251            [
 7252                Point::new(1, 3)..Point::new(1, 3),
 7253                Point::new(2, 1)..Point::new(2, 1),
 7254            ]
 7255        );
 7256    });
 7257
 7258    _ = multibuffer.update(cx, |multibuffer, cx| {
 7259        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7260    });
 7261    _ = editor.update(cx, |editor, cx| {
 7262        // Removing an excerpt causes the first selection to become degenerate.
 7263        assert_eq!(
 7264            editor.selections.ranges(cx),
 7265            [
 7266                Point::new(0, 0)..Point::new(0, 0),
 7267                Point::new(0, 1)..Point::new(0, 1)
 7268            ]
 7269        );
 7270
 7271        // Refreshing selections will relocate the first selection to the original buffer
 7272        // location.
 7273        editor.change_selections(None, cx, |s| s.refresh());
 7274        assert_eq!(
 7275            editor.selections.ranges(cx),
 7276            [
 7277                Point::new(0, 1)..Point::new(0, 1),
 7278                Point::new(0, 3)..Point::new(0, 3)
 7279            ]
 7280        );
 7281        assert!(editor.selections.pending_anchor().is_some());
 7282    });
 7283}
 7284
 7285#[gpui::test]
 7286fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 7287    init_test(cx, |_| {});
 7288
 7289    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7290    let mut excerpt1_id = None;
 7291    let multibuffer = cx.new_model(|cx| {
 7292        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7293        excerpt1_id = multibuffer
 7294            .push_excerpts(
 7295                buffer.clone(),
 7296                [
 7297                    ExcerptRange {
 7298                        context: Point::new(0, 0)..Point::new(1, 4),
 7299                        primary: None,
 7300                    },
 7301                    ExcerptRange {
 7302                        context: Point::new(1, 0)..Point::new(2, 4),
 7303                        primary: None,
 7304                    },
 7305                ],
 7306                cx,
 7307            )
 7308            .into_iter()
 7309            .next();
 7310        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7311        multibuffer
 7312    });
 7313
 7314    let editor = cx.add_window(|cx| {
 7315        let mut editor = build_editor(multibuffer.clone(), cx);
 7316        let snapshot = editor.snapshot(cx);
 7317        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 7318        assert_eq!(
 7319            editor.selections.ranges(cx),
 7320            [Point::new(1, 3)..Point::new(1, 3)]
 7321        );
 7322        editor
 7323    });
 7324
 7325    _ = multibuffer.update(cx, |multibuffer, cx| {
 7326        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7327    });
 7328    _ = editor.update(cx, |editor, cx| {
 7329        assert_eq!(
 7330            editor.selections.ranges(cx),
 7331            [Point::new(0, 0)..Point::new(0, 0)]
 7332        );
 7333
 7334        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 7335        editor.change_selections(None, cx, |s| s.refresh());
 7336        assert_eq!(
 7337            editor.selections.ranges(cx),
 7338            [Point::new(0, 3)..Point::new(0, 3)]
 7339        );
 7340        assert!(editor.selections.pending_anchor().is_some());
 7341    });
 7342}
 7343
 7344#[gpui::test]
 7345async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 7346    init_test(cx, |_| {});
 7347
 7348    let language = Arc::new(
 7349        Language::new(
 7350            LanguageConfig {
 7351                brackets: BracketPairConfig {
 7352                    pairs: vec![
 7353                        BracketPair {
 7354                            start: "{".to_string(),
 7355                            end: "}".to_string(),
 7356                            close: true,
 7357                            newline: true,
 7358                        },
 7359                        BracketPair {
 7360                            start: "/* ".to_string(),
 7361                            end: " */".to_string(),
 7362                            close: true,
 7363                            newline: true,
 7364                        },
 7365                    ],
 7366                    ..Default::default()
 7367                },
 7368                ..Default::default()
 7369            },
 7370            Some(tree_sitter_rust::language()),
 7371        )
 7372        .with_indents_query("")
 7373        .unwrap(),
 7374    );
 7375
 7376    let text = concat!(
 7377        "{   }\n",     //
 7378        "  x\n",       //
 7379        "  /*   */\n", //
 7380        "x\n",         //
 7381        "{{} }\n",     //
 7382    );
 7383
 7384    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 7385    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7386    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7387    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 7388        .await;
 7389
 7390    _ = view.update(cx, |view, cx| {
 7391        view.change_selections(None, cx, |s| {
 7392            s.select_display_ranges([
 7393                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 7394                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 7395                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 7396            ])
 7397        });
 7398        view.newline(&Newline, cx);
 7399
 7400        assert_eq!(
 7401            view.buffer().read(cx).read(cx).text(),
 7402            concat!(
 7403                "{ \n",    // Suppress rustfmt
 7404                "\n",      //
 7405                "}\n",     //
 7406                "  x\n",   //
 7407                "  /* \n", //
 7408                "  \n",    //
 7409                "  */\n",  //
 7410                "x\n",     //
 7411                "{{} \n",  //
 7412                "}\n",     //
 7413            )
 7414        );
 7415    });
 7416}
 7417
 7418#[gpui::test]
 7419fn test_highlighted_ranges(cx: &mut TestAppContext) {
 7420    init_test(cx, |_| {});
 7421
 7422    let editor = cx.add_window(|cx| {
 7423        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 7424        build_editor(buffer.clone(), cx)
 7425    });
 7426
 7427    _ = editor.update(cx, |editor, cx| {
 7428        struct Type1;
 7429        struct Type2;
 7430
 7431        let buffer = editor.buffer.read(cx).snapshot(cx);
 7432
 7433        let anchor_range =
 7434            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 7435
 7436        editor.highlight_background::<Type1>(
 7437            &[
 7438                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 7439                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 7440                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 7441                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 7442            ],
 7443            |_| Hsla::red(),
 7444            cx,
 7445        );
 7446        editor.highlight_background::<Type2>(
 7447            &[
 7448                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 7449                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 7450                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 7451                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 7452            ],
 7453            |_| Hsla::green(),
 7454            cx,
 7455        );
 7456
 7457        let snapshot = editor.snapshot(cx);
 7458        let mut highlighted_ranges = editor.background_highlights_in_range(
 7459            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 7460            &snapshot,
 7461            cx.theme().colors(),
 7462        );
 7463        // Enforce a consistent ordering based on color without relying on the ordering of the
 7464        // highlight's `TypeId` which is non-executor.
 7465        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 7466        assert_eq!(
 7467            highlighted_ranges,
 7468            &[
 7469                (
 7470                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 7471                    Hsla::red(),
 7472                ),
 7473                (
 7474                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7475                    Hsla::red(),
 7476                ),
 7477                (
 7478                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 7479                    Hsla::green(),
 7480                ),
 7481                (
 7482                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 7483                    Hsla::green(),
 7484                ),
 7485            ]
 7486        );
 7487        assert_eq!(
 7488            editor.background_highlights_in_range(
 7489                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 7490                &snapshot,
 7491                cx.theme().colors(),
 7492            ),
 7493            &[(
 7494                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7495                Hsla::red(),
 7496            )]
 7497        );
 7498    });
 7499}
 7500
 7501#[gpui::test]
 7502async fn test_following(cx: &mut gpui::TestAppContext) {
 7503    init_test(cx, |_| {});
 7504
 7505    let fs = FakeFs::new(cx.executor());
 7506    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7507
 7508    let buffer = project.update(cx, |project, cx| {
 7509        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 7510        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 7511    });
 7512    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 7513    let follower = cx.update(|cx| {
 7514        cx.open_window(
 7515            WindowOptions {
 7516                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 7517                    gpui::Point::new(0.into(), 0.into()),
 7518                    gpui::Point::new(10.into(), 80.into()),
 7519                ))),
 7520                ..Default::default()
 7521            },
 7522            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 7523        )
 7524    });
 7525
 7526    let is_still_following = Rc::new(RefCell::new(true));
 7527    let follower_edit_event_count = Rc::new(RefCell::new(0));
 7528    let pending_update = Rc::new(RefCell::new(None));
 7529    _ = follower.update(cx, {
 7530        let update = pending_update.clone();
 7531        let is_still_following = is_still_following.clone();
 7532        let follower_edit_event_count = follower_edit_event_count.clone();
 7533        |_, cx| {
 7534            cx.subscribe(
 7535                &leader.root_view(cx).unwrap(),
 7536                move |_, leader, event, cx| {
 7537                    leader
 7538                        .read(cx)
 7539                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7540                },
 7541            )
 7542            .detach();
 7543
 7544            cx.subscribe(
 7545                &follower.root_view(cx).unwrap(),
 7546                move |_, _, event: &EditorEvent, _cx| {
 7547                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 7548                        *is_still_following.borrow_mut() = false;
 7549                    }
 7550
 7551                    if let EditorEvent::BufferEdited = event {
 7552                        *follower_edit_event_count.borrow_mut() += 1;
 7553                    }
 7554                },
 7555            )
 7556            .detach();
 7557        }
 7558    });
 7559
 7560    // Update the selections only
 7561    _ = leader.update(cx, |leader, cx| {
 7562        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7563    });
 7564    follower
 7565        .update(cx, |follower, cx| {
 7566            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7567        })
 7568        .unwrap()
 7569        .await
 7570        .unwrap();
 7571    _ = follower.update(cx, |follower, cx| {
 7572        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 7573    });
 7574    assert_eq!(*is_still_following.borrow(), true);
 7575    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7576
 7577    // Update the scroll position only
 7578    _ = leader.update(cx, |leader, cx| {
 7579        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7580    });
 7581    follower
 7582        .update(cx, |follower, cx| {
 7583            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7584        })
 7585        .unwrap()
 7586        .await
 7587        .unwrap();
 7588    assert_eq!(
 7589        follower
 7590            .update(cx, |follower, cx| follower.scroll_position(cx))
 7591            .unwrap(),
 7592        gpui::Point::new(1.5, 3.5)
 7593    );
 7594    assert_eq!(*is_still_following.borrow(), true);
 7595    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7596
 7597    // Update the selections and scroll position. The follower's scroll position is updated
 7598    // via autoscroll, not via the leader's exact scroll position.
 7599    _ = leader.update(cx, |leader, cx| {
 7600        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7601        leader.request_autoscroll(Autoscroll::newest(), cx);
 7602        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7603    });
 7604    follower
 7605        .update(cx, |follower, cx| {
 7606            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7607        })
 7608        .unwrap()
 7609        .await
 7610        .unwrap();
 7611    _ = follower.update(cx, |follower, cx| {
 7612        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 7613        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 7614    });
 7615    assert_eq!(*is_still_following.borrow(), true);
 7616
 7617    // Creating a pending selection that precedes another selection
 7618    _ = leader.update(cx, |leader, cx| {
 7619        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7620        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 7621    });
 7622    follower
 7623        .update(cx, |follower, cx| {
 7624            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7625        })
 7626        .unwrap()
 7627        .await
 7628        .unwrap();
 7629    _ = follower.update(cx, |follower, cx| {
 7630        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 7631    });
 7632    assert_eq!(*is_still_following.borrow(), true);
 7633
 7634    // Extend the pending selection so that it surrounds another selection
 7635    _ = leader.update(cx, |leader, cx| {
 7636        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 7637    });
 7638    follower
 7639        .update(cx, |follower, cx| {
 7640            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7641        })
 7642        .unwrap()
 7643        .await
 7644        .unwrap();
 7645    _ = follower.update(cx, |follower, cx| {
 7646        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 7647    });
 7648
 7649    // Scrolling locally breaks the follow
 7650    _ = follower.update(cx, |follower, cx| {
 7651        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 7652        follower.set_scroll_anchor(
 7653            ScrollAnchor {
 7654                anchor: top_anchor,
 7655                offset: gpui::Point::new(0.0, 0.5),
 7656            },
 7657            cx,
 7658        );
 7659    });
 7660    assert_eq!(*is_still_following.borrow(), false);
 7661}
 7662
 7663#[gpui::test]
 7664async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 7665    init_test(cx, |_| {});
 7666
 7667    let fs = FakeFs::new(cx.executor());
 7668    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7669    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7670    let pane = workspace
 7671        .update(cx, |workspace, _| workspace.active_pane().clone())
 7672        .unwrap();
 7673
 7674    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7675
 7676    let leader = pane.update(cx, |_, cx| {
 7677        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 7678        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 7679    });
 7680
 7681    // Start following the editor when it has no excerpts.
 7682    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7683    let follower_1 = cx
 7684        .update_window(*workspace.deref(), |_, cx| {
 7685            Editor::from_state_proto(
 7686                pane.clone(),
 7687                workspace.root_view(cx).unwrap(),
 7688                ViewId {
 7689                    creator: Default::default(),
 7690                    id: 0,
 7691                },
 7692                &mut state_message,
 7693                cx,
 7694            )
 7695        })
 7696        .unwrap()
 7697        .unwrap()
 7698        .await
 7699        .unwrap();
 7700
 7701    let update_message = Rc::new(RefCell::new(None));
 7702    follower_1.update(cx, {
 7703        let update = update_message.clone();
 7704        |_, cx| {
 7705            cx.subscribe(&leader, move |_, leader, event, cx| {
 7706                leader
 7707                    .read(cx)
 7708                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7709            })
 7710            .detach();
 7711        }
 7712    });
 7713
 7714    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 7715        (
 7716            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 7717            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 7718        )
 7719    });
 7720
 7721    // Insert some excerpts.
 7722    _ = leader.update(cx, |leader, cx| {
 7723        leader.buffer.update(cx, |multibuffer, cx| {
 7724            let excerpt_ids = multibuffer.push_excerpts(
 7725                buffer_1.clone(),
 7726                [
 7727                    ExcerptRange {
 7728                        context: 1..6,
 7729                        primary: None,
 7730                    },
 7731                    ExcerptRange {
 7732                        context: 12..15,
 7733                        primary: None,
 7734                    },
 7735                    ExcerptRange {
 7736                        context: 0..3,
 7737                        primary: None,
 7738                    },
 7739                ],
 7740                cx,
 7741            );
 7742            multibuffer.insert_excerpts_after(
 7743                excerpt_ids[0],
 7744                buffer_2.clone(),
 7745                [
 7746                    ExcerptRange {
 7747                        context: 8..12,
 7748                        primary: None,
 7749                    },
 7750                    ExcerptRange {
 7751                        context: 0..6,
 7752                        primary: None,
 7753                    },
 7754                ],
 7755                cx,
 7756            );
 7757        });
 7758    });
 7759
 7760    // Apply the update of adding the excerpts.
 7761    follower_1
 7762        .update(cx, |follower, cx| {
 7763            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7764        })
 7765        .await
 7766        .unwrap();
 7767    assert_eq!(
 7768        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7769        leader.update(cx, |editor, cx| editor.text(cx))
 7770    );
 7771    update_message.borrow_mut().take();
 7772
 7773    // Start following separately after it already has excerpts.
 7774    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7775    let follower_2 = cx
 7776        .update_window(*workspace.deref(), |_, cx| {
 7777            Editor::from_state_proto(
 7778                pane.clone(),
 7779                workspace.root_view(cx).unwrap().clone(),
 7780                ViewId {
 7781                    creator: Default::default(),
 7782                    id: 0,
 7783                },
 7784                &mut state_message,
 7785                cx,
 7786            )
 7787        })
 7788        .unwrap()
 7789        .unwrap()
 7790        .await
 7791        .unwrap();
 7792    assert_eq!(
 7793        follower_2.update(cx, |editor, cx| editor.text(cx)),
 7794        leader.update(cx, |editor, cx| editor.text(cx))
 7795    );
 7796
 7797    // Remove some excerpts.
 7798    _ = leader.update(cx, |leader, cx| {
 7799        leader.buffer.update(cx, |multibuffer, cx| {
 7800            let excerpt_ids = multibuffer.excerpt_ids();
 7801            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 7802            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 7803        });
 7804    });
 7805
 7806    // Apply the update of removing the excerpts.
 7807    follower_1
 7808        .update(cx, |follower, cx| {
 7809            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7810        })
 7811        .await
 7812        .unwrap();
 7813    follower_2
 7814        .update(cx, |follower, cx| {
 7815            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7816        })
 7817        .await
 7818        .unwrap();
 7819    update_message.borrow_mut().take();
 7820    assert_eq!(
 7821        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7822        leader.update(cx, |editor, cx| editor.text(cx))
 7823    );
 7824}
 7825
 7826#[gpui::test]
 7827async fn go_to_prev_overlapping_diagnostic(
 7828    executor: BackgroundExecutor,
 7829    cx: &mut gpui::TestAppContext,
 7830) {
 7831    init_test(cx, |_| {});
 7832
 7833    let mut cx = EditorTestContext::new(cx).await;
 7834    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 7835
 7836    cx.set_state(indoc! {"
 7837        ˇfn func(abc def: i32) -> u32 {
 7838        }
 7839    "});
 7840
 7841    _ = cx.update(|cx| {
 7842        _ = project.update(cx, |project, cx| {
 7843            project
 7844                .update_diagnostics(
 7845                    LanguageServerId(0),
 7846                    lsp::PublishDiagnosticsParams {
 7847                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 7848                        version: None,
 7849                        diagnostics: vec![
 7850                            lsp::Diagnostic {
 7851                                range: lsp::Range::new(
 7852                                    lsp::Position::new(0, 11),
 7853                                    lsp::Position::new(0, 12),
 7854                                ),
 7855                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7856                                ..Default::default()
 7857                            },
 7858                            lsp::Diagnostic {
 7859                                range: lsp::Range::new(
 7860                                    lsp::Position::new(0, 12),
 7861                                    lsp::Position::new(0, 15),
 7862                                ),
 7863                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7864                                ..Default::default()
 7865                            },
 7866                            lsp::Diagnostic {
 7867                                range: lsp::Range::new(
 7868                                    lsp::Position::new(0, 25),
 7869                                    lsp::Position::new(0, 28),
 7870                                ),
 7871                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7872                                ..Default::default()
 7873                            },
 7874                        ],
 7875                    },
 7876                    &[],
 7877                    cx,
 7878                )
 7879                .unwrap()
 7880        });
 7881    });
 7882
 7883    executor.run_until_parked();
 7884
 7885    cx.update_editor(|editor, cx| {
 7886        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7887    });
 7888
 7889    cx.assert_editor_state(indoc! {"
 7890        fn func(abc def: i32) -> ˇu32 {
 7891        }
 7892    "});
 7893
 7894    cx.update_editor(|editor, cx| {
 7895        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7896    });
 7897
 7898    cx.assert_editor_state(indoc! {"
 7899        fn func(abc ˇdef: i32) -> u32 {
 7900        }
 7901    "});
 7902
 7903    cx.update_editor(|editor, cx| {
 7904        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7905    });
 7906
 7907    cx.assert_editor_state(indoc! {"
 7908        fn func(abcˇ def: i32) -> u32 {
 7909        }
 7910    "});
 7911
 7912    cx.update_editor(|editor, cx| {
 7913        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 7914    });
 7915
 7916    cx.assert_editor_state(indoc! {"
 7917        fn func(abc def: i32) -> ˇu32 {
 7918        }
 7919    "});
 7920}
 7921
 7922#[gpui::test]
 7923async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 7924    init_test(cx, |_| {});
 7925
 7926    let mut cx = EditorTestContext::new(cx).await;
 7927
 7928    let diff_base = r#"
 7929        use some::mod;
 7930
 7931        const A: u32 = 42;
 7932
 7933        fn main() {
 7934            println!("hello");
 7935
 7936            println!("world");
 7937        }
 7938        "#
 7939    .unindent();
 7940
 7941    // Edits are modified, removed, modified, added
 7942    cx.set_state(
 7943        &r#"
 7944        use some::modified;
 7945
 7946        ˇ
 7947        fn main() {
 7948            println!("hello there");
 7949
 7950            println!("around the");
 7951            println!("world");
 7952        }
 7953        "#
 7954        .unindent(),
 7955    );
 7956
 7957    cx.set_diff_base(Some(&diff_base));
 7958    executor.run_until_parked();
 7959
 7960    cx.update_editor(|editor, cx| {
 7961        //Wrap around the bottom of the buffer
 7962        for _ in 0..3 {
 7963            editor.go_to_hunk(&GoToHunk, cx);
 7964        }
 7965    });
 7966
 7967    cx.assert_editor_state(
 7968        &r#"
 7969        ˇuse some::modified;
 7970
 7971
 7972        fn main() {
 7973            println!("hello there");
 7974
 7975            println!("around the");
 7976            println!("world");
 7977        }
 7978        "#
 7979        .unindent(),
 7980    );
 7981
 7982    cx.update_editor(|editor, cx| {
 7983        //Wrap around the top of the buffer
 7984        for _ in 0..2 {
 7985            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 7986        }
 7987    });
 7988
 7989    cx.assert_editor_state(
 7990        &r#"
 7991        use some::modified;
 7992
 7993
 7994        fn main() {
 7995        ˇ    println!("hello there");
 7996
 7997            println!("around the");
 7998            println!("world");
 7999        }
 8000        "#
 8001        .unindent(),
 8002    );
 8003
 8004    cx.update_editor(|editor, cx| {
 8005        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8006    });
 8007
 8008    cx.assert_editor_state(
 8009        &r#"
 8010        use some::modified;
 8011
 8012        ˇ
 8013        fn main() {
 8014            println!("hello there");
 8015
 8016            println!("around the");
 8017            println!("world");
 8018        }
 8019        "#
 8020        .unindent(),
 8021    );
 8022
 8023    cx.update_editor(|editor, cx| {
 8024        for _ in 0..3 {
 8025            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8026        }
 8027    });
 8028
 8029    cx.assert_editor_state(
 8030        &r#"
 8031        use some::modified;
 8032
 8033
 8034        fn main() {
 8035        ˇ    println!("hello there");
 8036
 8037            println!("around the");
 8038            println!("world");
 8039        }
 8040        "#
 8041        .unindent(),
 8042    );
 8043
 8044    cx.update_editor(|editor, cx| {
 8045        editor.fold(&Fold, cx);
 8046
 8047        //Make sure that the fold only gets one hunk
 8048        for _ in 0..4 {
 8049            editor.go_to_hunk(&GoToHunk, cx);
 8050        }
 8051    });
 8052
 8053    cx.assert_editor_state(
 8054        &r#"
 8055        ˇuse some::modified;
 8056
 8057
 8058        fn main() {
 8059            println!("hello there");
 8060
 8061            println!("around the");
 8062            println!("world");
 8063        }
 8064        "#
 8065        .unindent(),
 8066    );
 8067}
 8068
 8069#[test]
 8070fn test_split_words() {
 8071    fn split(text: &str) -> Vec<&str> {
 8072        split_words(text).collect()
 8073    }
 8074
 8075    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 8076    assert_eq!(split("hello_world"), &["hello_", "world"]);
 8077    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 8078    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 8079    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 8080    assert_eq!(split("helloworld"), &["helloworld"]);
 8081
 8082    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 8083}
 8084
 8085#[gpui::test]
 8086async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 8087    init_test(cx, |_| {});
 8088
 8089    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 8090    let mut assert = |before, after| {
 8091        let _state_context = cx.set_state(before);
 8092        cx.update_editor(|editor, cx| {
 8093            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 8094        });
 8095        cx.assert_editor_state(after);
 8096    };
 8097
 8098    // Outside bracket jumps to outside of matching bracket
 8099    assert("console.logˇ(var);", "console.log(var)ˇ;");
 8100    assert("console.log(var)ˇ;", "console.logˇ(var);");
 8101
 8102    // Inside bracket jumps to inside of matching bracket
 8103    assert("console.log(ˇvar);", "console.log(varˇ);");
 8104    assert("console.log(varˇ);", "console.log(ˇvar);");
 8105
 8106    // When outside a bracket and inside, favor jumping to the inside bracket
 8107    assert(
 8108        "console.log('foo', [1, 2, 3]ˇ);",
 8109        "console.log(ˇ'foo', [1, 2, 3]);",
 8110    );
 8111    assert(
 8112        "console.log(ˇ'foo', [1, 2, 3]);",
 8113        "console.log('foo', [1, 2, 3]ˇ);",
 8114    );
 8115
 8116    // Bias forward if two options are equally likely
 8117    assert(
 8118        "let result = curried_fun()ˇ();",
 8119        "let result = curried_fun()()ˇ;",
 8120    );
 8121
 8122    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 8123    assert(
 8124        indoc! {"
 8125            function test() {
 8126                console.log('test')ˇ
 8127            }"},
 8128        indoc! {"
 8129            function test() {
 8130                console.logˇ('test')
 8131            }"},
 8132    );
 8133}
 8134
 8135#[gpui::test]
 8136async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 8137    init_test(cx, |_| {});
 8138
 8139    let fs = FakeFs::new(cx.executor());
 8140    fs.insert_tree(
 8141        "/a",
 8142        json!({
 8143            "main.rs": "fn main() { let a = 5; }",
 8144            "other.rs": "// Test file",
 8145        }),
 8146    )
 8147    .await;
 8148    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8149
 8150    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8151    language_registry.add(Arc::new(Language::new(
 8152        LanguageConfig {
 8153            name: "Rust".into(),
 8154            matcher: LanguageMatcher {
 8155                path_suffixes: vec!["rs".to_string()],
 8156                ..Default::default()
 8157            },
 8158            brackets: BracketPairConfig {
 8159                pairs: vec![BracketPair {
 8160                    start: "{".to_string(),
 8161                    end: "}".to_string(),
 8162                    close: true,
 8163                    newline: true,
 8164                }],
 8165                disabled_scopes_by_bracket_ix: Vec::new(),
 8166            },
 8167            ..Default::default()
 8168        },
 8169        Some(tree_sitter_rust::language()),
 8170    )));
 8171    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8172        "Rust",
 8173        FakeLspAdapter {
 8174            capabilities: lsp::ServerCapabilities {
 8175                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 8176                    first_trigger_character: "{".to_string(),
 8177                    more_trigger_character: None,
 8178                }),
 8179                ..Default::default()
 8180            },
 8181            ..Default::default()
 8182        },
 8183    );
 8184
 8185    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8186
 8187    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8188
 8189    let worktree_id = workspace
 8190        .update(cx, |workspace, cx| {
 8191            workspace.project().update(cx, |project, cx| {
 8192                project.worktrees().next().unwrap().read(cx).id()
 8193            })
 8194        })
 8195        .unwrap();
 8196
 8197    let buffer = project
 8198        .update(cx, |project, cx| {
 8199            project.open_local_buffer("/a/main.rs", cx)
 8200        })
 8201        .await
 8202        .unwrap();
 8203    cx.executor().run_until_parked();
 8204    cx.executor().start_waiting();
 8205    let fake_server = fake_servers.next().await.unwrap();
 8206    let editor_handle = workspace
 8207        .update(cx, |workspace, cx| {
 8208            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 8209        })
 8210        .unwrap()
 8211        .await
 8212        .unwrap()
 8213        .downcast::<Editor>()
 8214        .unwrap();
 8215
 8216    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 8217        assert_eq!(
 8218            params.text_document_position.text_document.uri,
 8219            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 8220        );
 8221        assert_eq!(
 8222            params.text_document_position.position,
 8223            lsp::Position::new(0, 21),
 8224        );
 8225
 8226        Ok(Some(vec![lsp::TextEdit {
 8227            new_text: "]".to_string(),
 8228            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 8229        }]))
 8230    });
 8231
 8232    editor_handle.update(cx, |editor, cx| {
 8233        editor.focus(cx);
 8234        editor.change_selections(None, cx, |s| {
 8235            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 8236        });
 8237        editor.handle_input("{", cx);
 8238    });
 8239
 8240    cx.executor().run_until_parked();
 8241
 8242    _ = buffer.update(cx, |buffer, _| {
 8243        assert_eq!(
 8244            buffer.text(),
 8245            "fn main() { let a = {5}; }",
 8246            "No extra braces from on type formatting should appear in the buffer"
 8247        )
 8248    });
 8249}
 8250
 8251#[gpui::test]
 8252async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 8253    init_test(cx, |_| {});
 8254
 8255    let fs = FakeFs::new(cx.executor());
 8256    fs.insert_tree(
 8257        "/a",
 8258        json!({
 8259            "main.rs": "fn main() { let a = 5; }",
 8260            "other.rs": "// Test file",
 8261        }),
 8262    )
 8263    .await;
 8264
 8265    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8266
 8267    let server_restarts = Arc::new(AtomicUsize::new(0));
 8268    let closure_restarts = Arc::clone(&server_restarts);
 8269    let language_server_name = "test language server";
 8270    let language_name: Arc<str> = "Rust".into();
 8271
 8272    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8273    language_registry.add(Arc::new(Language::new(
 8274        LanguageConfig {
 8275            name: Arc::clone(&language_name),
 8276            matcher: LanguageMatcher {
 8277                path_suffixes: vec!["rs".to_string()],
 8278                ..Default::default()
 8279            },
 8280            ..Default::default()
 8281        },
 8282        Some(tree_sitter_rust::language()),
 8283    )));
 8284    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8285        "Rust",
 8286        FakeLspAdapter {
 8287            name: language_server_name,
 8288            initialization_options: Some(json!({
 8289                "testOptionValue": true
 8290            })),
 8291            initializer: Some(Box::new(move |fake_server| {
 8292                let task_restarts = Arc::clone(&closure_restarts);
 8293                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 8294                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 8295                    futures::future::ready(Ok(()))
 8296                });
 8297            })),
 8298            ..Default::default()
 8299        },
 8300    );
 8301
 8302    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8303    let _buffer = project
 8304        .update(cx, |project, cx| {
 8305            project.open_local_buffer("/a/main.rs", cx)
 8306        })
 8307        .await
 8308        .unwrap();
 8309    let _fake_server = fake_servers.next().await.unwrap();
 8310    update_test_language_settings(cx, |language_settings| {
 8311        language_settings.languages.insert(
 8312            Arc::clone(&language_name),
 8313            LanguageSettingsContent {
 8314                tab_size: NonZeroU32::new(8),
 8315                ..Default::default()
 8316            },
 8317        );
 8318    });
 8319    cx.executor().run_until_parked();
 8320    assert_eq!(
 8321        server_restarts.load(atomic::Ordering::Acquire),
 8322        0,
 8323        "Should not restart LSP server on an unrelated change"
 8324    );
 8325
 8326    update_test_project_settings(cx, |project_settings| {
 8327        project_settings.lsp.insert(
 8328            "Some other server name".into(),
 8329            LspSettings {
 8330                binary: None,
 8331                settings: None,
 8332                initialization_options: Some(json!({
 8333                    "some other init value": false
 8334                })),
 8335            },
 8336        );
 8337    });
 8338    cx.executor().run_until_parked();
 8339    assert_eq!(
 8340        server_restarts.load(atomic::Ordering::Acquire),
 8341        0,
 8342        "Should not restart LSP server on an unrelated LSP settings change"
 8343    );
 8344
 8345    update_test_project_settings(cx, |project_settings| {
 8346        project_settings.lsp.insert(
 8347            language_server_name.into(),
 8348            LspSettings {
 8349                binary: None,
 8350                settings: None,
 8351                initialization_options: Some(json!({
 8352                    "anotherInitValue": false
 8353                })),
 8354            },
 8355        );
 8356    });
 8357    cx.executor().run_until_parked();
 8358    assert_eq!(
 8359        server_restarts.load(atomic::Ordering::Acquire),
 8360        1,
 8361        "Should restart LSP server on a related LSP settings change"
 8362    );
 8363
 8364    update_test_project_settings(cx, |project_settings| {
 8365        project_settings.lsp.insert(
 8366            language_server_name.into(),
 8367            LspSettings {
 8368                binary: None,
 8369                settings: None,
 8370                initialization_options: Some(json!({
 8371                    "anotherInitValue": false
 8372                })),
 8373            },
 8374        );
 8375    });
 8376    cx.executor().run_until_parked();
 8377    assert_eq!(
 8378        server_restarts.load(atomic::Ordering::Acquire),
 8379        1,
 8380        "Should not restart LSP server on a related LSP settings change that is the same"
 8381    );
 8382
 8383    update_test_project_settings(cx, |project_settings| {
 8384        project_settings.lsp.insert(
 8385            language_server_name.into(),
 8386            LspSettings {
 8387                binary: None,
 8388                settings: None,
 8389                initialization_options: None,
 8390            },
 8391        );
 8392    });
 8393    cx.executor().run_until_parked();
 8394    assert_eq!(
 8395        server_restarts.load(atomic::Ordering::Acquire),
 8396        2,
 8397        "Should restart LSP server on another related LSP settings change"
 8398    );
 8399}
 8400
 8401#[gpui::test]
 8402async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 8403    init_test(cx, |_| {});
 8404
 8405    let mut cx = EditorLspTestContext::new_rust(
 8406        lsp::ServerCapabilities {
 8407            completion_provider: Some(lsp::CompletionOptions {
 8408                trigger_characters: Some(vec![".".to_string()]),
 8409                resolve_provider: Some(true),
 8410                ..Default::default()
 8411            }),
 8412            ..Default::default()
 8413        },
 8414        cx,
 8415    )
 8416    .await;
 8417
 8418    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8419    cx.simulate_keystroke(".");
 8420    let completion_item = lsp::CompletionItem {
 8421        label: "some".into(),
 8422        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8423        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8424        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8425            kind: lsp::MarkupKind::Markdown,
 8426            value: "```rust\nSome(2)\n```".to_string(),
 8427        })),
 8428        deprecated: Some(false),
 8429        sort_text: Some("fffffff2".to_string()),
 8430        filter_text: Some("some".to_string()),
 8431        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8432        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8433            range: lsp::Range {
 8434                start: lsp::Position {
 8435                    line: 0,
 8436                    character: 22,
 8437                },
 8438                end: lsp::Position {
 8439                    line: 0,
 8440                    character: 22,
 8441                },
 8442            },
 8443            new_text: "Some(2)".to_string(),
 8444        })),
 8445        additional_text_edits: Some(vec![lsp::TextEdit {
 8446            range: lsp::Range {
 8447                start: lsp::Position {
 8448                    line: 0,
 8449                    character: 20,
 8450                },
 8451                end: lsp::Position {
 8452                    line: 0,
 8453                    character: 22,
 8454                },
 8455            },
 8456            new_text: "".to_string(),
 8457        }]),
 8458        ..Default::default()
 8459    };
 8460
 8461    let closure_completion_item = completion_item.clone();
 8462    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8463        let task_completion_item = closure_completion_item.clone();
 8464        async move {
 8465            Ok(Some(lsp::CompletionResponse::Array(vec![
 8466                task_completion_item,
 8467            ])))
 8468        }
 8469    });
 8470
 8471    request.next().await;
 8472
 8473    cx.condition(|editor, _| editor.context_menu_visible())
 8474        .await;
 8475    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8476        editor
 8477            .confirm_completion(&ConfirmCompletion::default(), cx)
 8478            .unwrap()
 8479    });
 8480    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 8481
 8482    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 8483        let task_completion_item = completion_item.clone();
 8484        async move { Ok(task_completion_item) }
 8485    })
 8486    .next()
 8487    .await
 8488    .unwrap();
 8489    apply_additional_edits.await.unwrap();
 8490    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 8491}
 8492
 8493#[gpui::test]
 8494async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 8495    init_test(cx, |_| {});
 8496
 8497    let mut cx = EditorLspTestContext::new(
 8498        Language::new(
 8499            LanguageConfig {
 8500                matcher: LanguageMatcher {
 8501                    path_suffixes: vec!["jsx".into()],
 8502                    ..Default::default()
 8503                },
 8504                overrides: [(
 8505                    "element".into(),
 8506                    LanguageConfigOverride {
 8507                        word_characters: Override::Set(['-'].into_iter().collect()),
 8508                        ..Default::default()
 8509                    },
 8510                )]
 8511                .into_iter()
 8512                .collect(),
 8513                ..Default::default()
 8514            },
 8515            Some(tree_sitter_typescript::language_tsx()),
 8516        )
 8517        .with_override_query("(jsx_self_closing_element) @element")
 8518        .unwrap(),
 8519        lsp::ServerCapabilities {
 8520            completion_provider: Some(lsp::CompletionOptions {
 8521                trigger_characters: Some(vec![":".to_string()]),
 8522                ..Default::default()
 8523            }),
 8524            ..Default::default()
 8525        },
 8526        cx,
 8527    )
 8528    .await;
 8529
 8530    cx.lsp
 8531        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8532            Ok(Some(lsp::CompletionResponse::Array(vec![
 8533                lsp::CompletionItem {
 8534                    label: "bg-blue".into(),
 8535                    ..Default::default()
 8536                },
 8537                lsp::CompletionItem {
 8538                    label: "bg-red".into(),
 8539                    ..Default::default()
 8540                },
 8541                lsp::CompletionItem {
 8542                    label: "bg-yellow".into(),
 8543                    ..Default::default()
 8544                },
 8545            ])))
 8546        });
 8547
 8548    cx.set_state(r#"<p class="bgˇ" />"#);
 8549
 8550    // Trigger completion when typing a dash, because the dash is an extra
 8551    // word character in the 'element' scope, which contains the cursor.
 8552    cx.simulate_keystroke("-");
 8553    cx.executor().run_until_parked();
 8554    cx.update_editor(|editor, _| {
 8555        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8556            assert_eq!(
 8557                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8558                &["bg-red", "bg-blue", "bg-yellow"]
 8559            );
 8560        } else {
 8561            panic!("expected completion menu to be open");
 8562        }
 8563    });
 8564
 8565    cx.simulate_keystroke("l");
 8566    cx.executor().run_until_parked();
 8567    cx.update_editor(|editor, _| {
 8568        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8569            assert_eq!(
 8570                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8571                &["bg-blue", "bg-yellow"]
 8572            );
 8573        } else {
 8574            panic!("expected completion menu to be open");
 8575        }
 8576    });
 8577
 8578    // When filtering completions, consider the character after the '-' to
 8579    // be the start of a subword.
 8580    cx.set_state(r#"<p class="yelˇ" />"#);
 8581    cx.simulate_keystroke("l");
 8582    cx.executor().run_until_parked();
 8583    cx.update_editor(|editor, _| {
 8584        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8585            assert_eq!(
 8586                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8587                &["bg-yellow"]
 8588            );
 8589        } else {
 8590            panic!("expected completion menu to be open");
 8591        }
 8592    });
 8593}
 8594
 8595#[gpui::test]
 8596async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 8597    init_test(cx, |settings| {
 8598        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 8599    });
 8600
 8601    let fs = FakeFs::new(cx.executor());
 8602    fs.insert_file("/file.rs", Default::default()).await;
 8603
 8604    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 8605    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8606
 8607    language_registry.add(Arc::new(Language::new(
 8608        LanguageConfig {
 8609            name: "Rust".into(),
 8610            matcher: LanguageMatcher {
 8611                path_suffixes: vec!["rs".to_string()],
 8612                ..Default::default()
 8613            },
 8614            prettier_parser_name: Some("test_parser".to_string()),
 8615            ..Default::default()
 8616        },
 8617        Some(tree_sitter_rust::language()),
 8618    )));
 8619
 8620    let test_plugin = "test_plugin";
 8621    let _ = language_registry.register_fake_lsp_adapter(
 8622        "Rust",
 8623        FakeLspAdapter {
 8624            prettier_plugins: vec![test_plugin],
 8625            ..Default::default()
 8626        },
 8627    );
 8628
 8629    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 8630    let buffer = project
 8631        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 8632        .await
 8633        .unwrap();
 8634
 8635    let buffer_text = "one\ntwo\nthree\n";
 8636    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8637    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8638    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 8639
 8640    editor
 8641        .update(cx, |editor, cx| {
 8642            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8643        })
 8644        .unwrap()
 8645        .await;
 8646    assert_eq!(
 8647        editor.update(cx, |editor, cx| editor.text(cx)),
 8648        buffer_text.to_string() + prettier_format_suffix,
 8649        "Test prettier formatting was not applied to the original buffer text",
 8650    );
 8651
 8652    update_test_language_settings(cx, |settings| {
 8653        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 8654    });
 8655    let format = editor.update(cx, |editor, cx| {
 8656        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8657    });
 8658    format.await.unwrap();
 8659    assert_eq!(
 8660        editor.update(cx, |editor, cx| editor.text(cx)),
 8661        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 8662        "Autoformatting (via test prettier) was not applied to the original buffer text",
 8663    );
 8664}
 8665
 8666#[gpui::test]
 8667async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 8668    init_test(cx, |_| {});
 8669    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8670    let base_text = indoc! {r#"struct Row;
 8671struct Row1;
 8672struct Row2;
 8673
 8674struct Row4;
 8675struct Row5;
 8676struct Row6;
 8677
 8678struct Row8;
 8679struct Row9;
 8680struct Row10;"#};
 8681
 8682    // When addition hunks are not adjacent to carets, no hunk revert is performed
 8683    assert_hunk_revert(
 8684        indoc! {r#"struct Row;
 8685                   struct Row1;
 8686                   struct Row1.1;
 8687                   struct Row1.2;
 8688                   struct Row2;ˇ
 8689
 8690                   struct Row4;
 8691                   struct Row5;
 8692                   struct Row6;
 8693
 8694                   struct Row8;
 8695                   ˇstruct Row9;
 8696                   struct Row9.1;
 8697                   struct Row9.2;
 8698                   struct Row9.3;
 8699                   struct Row10;"#},
 8700        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8701        indoc! {r#"struct Row;
 8702                   struct Row1;
 8703                   struct Row1.1;
 8704                   struct Row1.2;
 8705                   struct Row2;ˇ
 8706
 8707                   struct Row4;
 8708                   struct Row5;
 8709                   struct Row6;
 8710
 8711                   struct Row8;
 8712                   ˇstruct Row9;
 8713                   struct Row9.1;
 8714                   struct Row9.2;
 8715                   struct Row9.3;
 8716                   struct Row10;"#},
 8717        base_text,
 8718        &mut cx,
 8719    );
 8720    // Same for selections
 8721    assert_hunk_revert(
 8722        indoc! {r#"struct Row;
 8723                   struct Row1;
 8724                   struct Row2;
 8725                   struct Row2.1;
 8726                   struct Row2.2;
 8727                   «ˇ
 8728                   struct Row4;
 8729                   struct» Row5;
 8730                   «struct Row6;
 8731                   ˇ»
 8732                   struct Row9.1;
 8733                   struct Row9.2;
 8734                   struct Row9.3;
 8735                   struct Row8;
 8736                   struct Row9;
 8737                   struct Row10;"#},
 8738        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8739        indoc! {r#"struct Row;
 8740                   struct Row1;
 8741                   struct Row2;
 8742                   struct Row2.1;
 8743                   struct Row2.2;
 8744                   «ˇ
 8745                   struct Row4;
 8746                   struct» Row5;
 8747                   «struct Row6;
 8748                   ˇ»
 8749                   struct Row9.1;
 8750                   struct Row9.2;
 8751                   struct Row9.3;
 8752                   struct Row8;
 8753                   struct Row9;
 8754                   struct Row10;"#},
 8755        base_text,
 8756        &mut cx,
 8757    );
 8758
 8759    // When carets and selections intersect the addition hunks, those are reverted.
 8760    // Adjacent carets got merged.
 8761    assert_hunk_revert(
 8762        indoc! {r#"struct Row;
 8763                   ˇ// something on the top
 8764                   struct Row1;
 8765                   struct Row2;
 8766                   struct Roˇw3.1;
 8767                   struct Row2.2;
 8768                   struct Row2.3;ˇ
 8769
 8770                   struct Row4;
 8771                   struct ˇRow5.1;
 8772                   struct Row5.2;
 8773                   struct «Rowˇ»5.3;
 8774                   struct Row5;
 8775                   struct Row6;
 8776                   ˇ
 8777                   struct Row9.1;
 8778                   struct «Rowˇ»9.2;
 8779                   struct «ˇRow»9.3;
 8780                   struct Row8;
 8781                   struct Row9;
 8782                   «ˇ// something on bottom»
 8783                   struct Row10;"#},
 8784        vec![
 8785            DiffHunkStatus::Added,
 8786            DiffHunkStatus::Added,
 8787            DiffHunkStatus::Added,
 8788            DiffHunkStatus::Added,
 8789            DiffHunkStatus::Added,
 8790        ],
 8791        indoc! {r#"struct Row;
 8792                   ˇstruct Row1;
 8793                   struct Row2;
 8794                   ˇ
 8795                   struct Row4;
 8796                   ˇstruct Row5;
 8797                   struct Row6;
 8798                   ˇ
 8799                   ˇstruct Row8;
 8800                   struct Row9;
 8801                   ˇstruct Row10;"#},
 8802        base_text,
 8803        &mut cx,
 8804    );
 8805}
 8806
 8807#[gpui::test]
 8808async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 8809    init_test(cx, |_| {});
 8810    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8811    let base_text = indoc! {r#"struct Row;
 8812struct Row1;
 8813struct Row2;
 8814
 8815struct Row4;
 8816struct Row5;
 8817struct Row6;
 8818
 8819struct Row8;
 8820struct Row9;
 8821struct Row10;"#};
 8822
 8823    // Modification hunks behave the same as the addition ones.
 8824    assert_hunk_revert(
 8825        indoc! {r#"struct Row;
 8826                   struct Row1;
 8827                   struct Row33;
 8828                   ˇ
 8829                   struct Row4;
 8830                   struct Row5;
 8831                   struct Row6;
 8832                   ˇ
 8833                   struct Row99;
 8834                   struct Row9;
 8835                   struct Row10;"#},
 8836        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8837        indoc! {r#"struct Row;
 8838                   struct Row1;
 8839                   struct Row33;
 8840                   ˇ
 8841                   struct Row4;
 8842                   struct Row5;
 8843                   struct Row6;
 8844                   ˇ
 8845                   struct Row99;
 8846                   struct Row9;
 8847                   struct Row10;"#},
 8848        base_text,
 8849        &mut cx,
 8850    );
 8851    assert_hunk_revert(
 8852        indoc! {r#"struct Row;
 8853                   struct Row1;
 8854                   struct Row33;
 8855                   «ˇ
 8856                   struct Row4;
 8857                   struct» Row5;
 8858                   «struct Row6;
 8859                   ˇ»
 8860                   struct Row99;
 8861                   struct Row9;
 8862                   struct Row10;"#},
 8863        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8864        indoc! {r#"struct Row;
 8865                   struct Row1;
 8866                   struct Row33;
 8867                   «ˇ
 8868                   struct Row4;
 8869                   struct» Row5;
 8870                   «struct Row6;
 8871                   ˇ»
 8872                   struct Row99;
 8873                   struct Row9;
 8874                   struct Row10;"#},
 8875        base_text,
 8876        &mut cx,
 8877    );
 8878
 8879    assert_hunk_revert(
 8880        indoc! {r#"ˇstruct Row1.1;
 8881                   struct Row1;
 8882                   «ˇstr»uct Row22;
 8883
 8884                   struct ˇRow44;
 8885                   struct Row5;
 8886                   struct «Rˇ»ow66;ˇ
 8887
 8888                   «struˇ»ct Row88;
 8889                   struct Row9;
 8890                   struct Row1011;ˇ"#},
 8891        vec![
 8892            DiffHunkStatus::Modified,
 8893            DiffHunkStatus::Modified,
 8894            DiffHunkStatus::Modified,
 8895            DiffHunkStatus::Modified,
 8896            DiffHunkStatus::Modified,
 8897            DiffHunkStatus::Modified,
 8898        ],
 8899        indoc! {r#"struct Row;
 8900                   ˇstruct Row1;
 8901                   struct Row2;
 8902                   ˇ
 8903                   struct Row4;
 8904                   ˇstruct Row5;
 8905                   struct Row6;
 8906                   ˇ
 8907                   struct Row8;
 8908                   ˇstruct Row9;
 8909                   struct Row10;ˇ"#},
 8910        base_text,
 8911        &mut cx,
 8912    );
 8913}
 8914
 8915#[gpui::test]
 8916async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
 8917    init_test(cx, |_| {});
 8918    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8919    let base_text = indoc! {r#"struct Row;
 8920struct Row1;
 8921struct Row2;
 8922
 8923struct Row4;
 8924struct Row5;
 8925struct Row6;
 8926
 8927struct Row8;
 8928struct Row9;
 8929struct Row10;"#};
 8930
 8931    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
 8932    assert_hunk_revert(
 8933        indoc! {r#"struct Row;
 8934                   struct Row2;
 8935
 8936                   ˇstruct Row4;
 8937                   struct Row5;
 8938                   struct Row6;
 8939                   ˇ
 8940                   struct Row8;
 8941                   struct Row10;"#},
 8942        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 8943        indoc! {r#"struct Row;
 8944                   struct Row2;
 8945
 8946                   ˇstruct Row4;
 8947                   struct Row5;
 8948                   struct Row6;
 8949                   ˇ
 8950                   struct Row8;
 8951                   struct Row10;"#},
 8952        base_text,
 8953        &mut cx,
 8954    );
 8955    assert_hunk_revert(
 8956        indoc! {r#"struct Row;
 8957                   struct Row2;
 8958
 8959                   «ˇstruct Row4;
 8960                   struct» Row5;
 8961                   «struct Row6;
 8962                   ˇ»
 8963                   struct Row8;
 8964                   struct Row10;"#},
 8965        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 8966        indoc! {r#"struct Row;
 8967                   struct Row2;
 8968
 8969                   «ˇstruct Row4;
 8970                   struct» Row5;
 8971                   «struct Row6;
 8972                   ˇ»
 8973                   struct Row8;
 8974                   struct Row10;"#},
 8975        base_text,
 8976        &mut cx,
 8977    );
 8978
 8979    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
 8980    assert_hunk_revert(
 8981        indoc! {r#"struct Row;
 8982                   ˇstruct Row2;
 8983
 8984                   struct Row4;
 8985                   struct Row5;
 8986                   struct Row6;
 8987
 8988                   struct Row8;ˇ
 8989                   struct Row10;"#},
 8990        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 8991        indoc! {r#"struct Row;
 8992                   struct Row1;
 8993                   ˇstruct Row2;
 8994
 8995                   struct Row4;
 8996                   struct Row5;
 8997                   struct Row6;
 8998
 8999                   struct Row8;ˇ
 9000                   struct Row9;
 9001                   struct Row10;"#},
 9002        base_text,
 9003        &mut cx,
 9004    );
 9005    assert_hunk_revert(
 9006        indoc! {r#"struct Row;
 9007                   struct Row2«ˇ;
 9008                   struct Row4;
 9009                   struct» Row5;
 9010                   «struct Row6;
 9011
 9012                   struct Row8;ˇ»
 9013                   struct Row10;"#},
 9014        vec![
 9015            DiffHunkStatus::Removed,
 9016            DiffHunkStatus::Removed,
 9017            DiffHunkStatus::Removed,
 9018        ],
 9019        indoc! {r#"struct Row;
 9020                   struct Row1;
 9021                   struct Row2«ˇ;
 9022
 9023                   struct Row4;
 9024                   struct» Row5;
 9025                   «struct Row6;
 9026
 9027                   struct Row8;ˇ»
 9028                   struct Row9;
 9029                   struct Row10;"#},
 9030        base_text,
 9031        &mut cx,
 9032    );
 9033}
 9034
 9035#[gpui::test]
 9036async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
 9037    init_test(cx, |_| {});
 9038
 9039    let cols = 4;
 9040    let rows = 10;
 9041    let sample_text_1 = sample_text(rows, cols, 'a');
 9042    assert_eq!(
 9043        sample_text_1,
 9044        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9045    );
 9046    let sample_text_2 = sample_text(rows, cols, 'l');
 9047    assert_eq!(
 9048        sample_text_2,
 9049        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9050    );
 9051    let sample_text_3 = sample_text(rows, cols, 'v');
 9052    assert_eq!(
 9053        sample_text_3,
 9054        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9055    );
 9056
 9057    fn diff_every_buffer_row(
 9058        buffer: &Model<Buffer>,
 9059        sample_text: String,
 9060        cols: usize,
 9061        cx: &mut gpui::TestAppContext,
 9062    ) {
 9063        // revert first character in each row, creating one large diff hunk per buffer
 9064        let is_first_char = |offset: usize| offset % cols == 0;
 9065        buffer.update(cx, |buffer, cx| {
 9066            buffer.set_text(
 9067                sample_text
 9068                    .chars()
 9069                    .enumerate()
 9070                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
 9071                    .collect::<String>(),
 9072                cx,
 9073            );
 9074            buffer.set_diff_base(Some(sample_text), cx);
 9075        });
 9076        cx.executor().run_until_parked();
 9077    }
 9078
 9079    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9080    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9081
 9082    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9083    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9084
 9085    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9086    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9087
 9088    let multibuffer = cx.new_model(|cx| {
 9089        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9090        multibuffer.push_excerpts(
 9091            buffer_1.clone(),
 9092            [
 9093                ExcerptRange {
 9094                    context: Point::new(0, 0)..Point::new(3, 0),
 9095                    primary: None,
 9096                },
 9097                ExcerptRange {
 9098                    context: Point::new(5, 0)..Point::new(7, 0),
 9099                    primary: None,
 9100                },
 9101                ExcerptRange {
 9102                    context: Point::new(9, 0)..Point::new(10, 4),
 9103                    primary: None,
 9104                },
 9105            ],
 9106            cx,
 9107        );
 9108        multibuffer.push_excerpts(
 9109            buffer_2.clone(),
 9110            [
 9111                ExcerptRange {
 9112                    context: Point::new(0, 0)..Point::new(3, 0),
 9113                    primary: None,
 9114                },
 9115                ExcerptRange {
 9116                    context: Point::new(5, 0)..Point::new(7, 0),
 9117                    primary: None,
 9118                },
 9119                ExcerptRange {
 9120                    context: Point::new(9, 0)..Point::new(10, 4),
 9121                    primary: None,
 9122                },
 9123            ],
 9124            cx,
 9125        );
 9126        multibuffer.push_excerpts(
 9127            buffer_3.clone(),
 9128            [
 9129                ExcerptRange {
 9130                    context: Point::new(0, 0)..Point::new(3, 0),
 9131                    primary: None,
 9132                },
 9133                ExcerptRange {
 9134                    context: Point::new(5, 0)..Point::new(7, 0),
 9135                    primary: None,
 9136                },
 9137                ExcerptRange {
 9138                    context: Point::new(9, 0)..Point::new(10, 4),
 9139                    primary: None,
 9140                },
 9141            ],
 9142            cx,
 9143        );
 9144        multibuffer
 9145    });
 9146
 9147    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9148    editor.update(cx, |editor, cx| {
 9149        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");
 9150        editor.select_all(&SelectAll, cx);
 9151        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9152    });
 9153    cx.executor().run_until_parked();
 9154    // When all ranges are selected, all buffer hunks are reverted.
 9155    editor.update(cx, |editor, cx| {
 9156        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");
 9157    });
 9158    buffer_1.update(cx, |buffer, _| {
 9159        assert_eq!(buffer.text(), sample_text_1);
 9160    });
 9161    buffer_2.update(cx, |buffer, _| {
 9162        assert_eq!(buffer.text(), sample_text_2);
 9163    });
 9164    buffer_3.update(cx, |buffer, _| {
 9165        assert_eq!(buffer.text(), sample_text_3);
 9166    });
 9167
 9168    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9169    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9170    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9171    editor.update(cx, |editor, cx| {
 9172        editor.change_selections(None, cx, |s| {
 9173            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
 9174        });
 9175        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9176    });
 9177    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
 9178    // but not affect buffer_2 and its related excerpts.
 9179    editor.update(cx, |editor, cx| {
 9180        assert_eq!(
 9181            editor.text(cx),
 9182            "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"
 9183        );
 9184    });
 9185    buffer_1.update(cx, |buffer, _| {
 9186        assert_eq!(buffer.text(), sample_text_1);
 9187    });
 9188    buffer_2.update(cx, |buffer, _| {
 9189        assert_eq!(
 9190            buffer.text(),
 9191            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
 9192        );
 9193    });
 9194    buffer_3.update(cx, |buffer, _| {
 9195        assert_eq!(
 9196            buffer.text(),
 9197            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
 9198        );
 9199    });
 9200}
 9201
 9202#[gpui::test]
 9203async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 9204    init_test(cx, |_| {});
 9205
 9206    let cols = 4;
 9207    let rows = 10;
 9208    let sample_text_1 = sample_text(rows, cols, 'a');
 9209    assert_eq!(
 9210        sample_text_1,
 9211        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9212    );
 9213    let sample_text_2 = sample_text(rows, cols, 'l');
 9214    assert_eq!(
 9215        sample_text_2,
 9216        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9217    );
 9218    let sample_text_3 = sample_text(rows, cols, 'v');
 9219    assert_eq!(
 9220        sample_text_3,
 9221        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9222    );
 9223
 9224    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9225    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9226    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9227
 9228    let multi_buffer = cx.new_model(|cx| {
 9229        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9230        multibuffer.push_excerpts(
 9231            buffer_1.clone(),
 9232            [
 9233                ExcerptRange {
 9234                    context: Point::new(0, 0)..Point::new(3, 0),
 9235                    primary: None,
 9236                },
 9237                ExcerptRange {
 9238                    context: Point::new(5, 0)..Point::new(7, 0),
 9239                    primary: None,
 9240                },
 9241                ExcerptRange {
 9242                    context: Point::new(9, 0)..Point::new(10, 4),
 9243                    primary: None,
 9244                },
 9245            ],
 9246            cx,
 9247        );
 9248        multibuffer.push_excerpts(
 9249            buffer_2.clone(),
 9250            [
 9251                ExcerptRange {
 9252                    context: Point::new(0, 0)..Point::new(3, 0),
 9253                    primary: None,
 9254                },
 9255                ExcerptRange {
 9256                    context: Point::new(5, 0)..Point::new(7, 0),
 9257                    primary: None,
 9258                },
 9259                ExcerptRange {
 9260                    context: Point::new(9, 0)..Point::new(10, 4),
 9261                    primary: None,
 9262                },
 9263            ],
 9264            cx,
 9265        );
 9266        multibuffer.push_excerpts(
 9267            buffer_3.clone(),
 9268            [
 9269                ExcerptRange {
 9270                    context: Point::new(0, 0)..Point::new(3, 0),
 9271                    primary: None,
 9272                },
 9273                ExcerptRange {
 9274                    context: Point::new(5, 0)..Point::new(7, 0),
 9275                    primary: None,
 9276                },
 9277                ExcerptRange {
 9278                    context: Point::new(9, 0)..Point::new(10, 4),
 9279                    primary: None,
 9280                },
 9281            ],
 9282            cx,
 9283        );
 9284        multibuffer
 9285    });
 9286
 9287    let fs = FakeFs::new(cx.executor());
 9288    fs.insert_tree(
 9289        "/a",
 9290        json!({
 9291            "main.rs": sample_text_1,
 9292            "other.rs": sample_text_2,
 9293            "lib.rs": sample_text_3,
 9294        }),
 9295    )
 9296    .await;
 9297    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9298    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9299    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9300    let multi_buffer_editor =
 9301        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 9302    let multibuffer_item_id = workspace
 9303        .update(cx, |workspace, cx| {
 9304            assert!(
 9305                workspace.active_item(cx).is_none(),
 9306                "active item should be None before the first item is added"
 9307            );
 9308            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
 9309            let active_item = workspace
 9310                .active_item(cx)
 9311                .expect("should have an active item after adding the multi buffer");
 9312            assert!(
 9313                !active_item.is_singleton(cx),
 9314                "A multi buffer was expected to active after adding"
 9315            );
 9316            active_item.item_id()
 9317        })
 9318        .unwrap();
 9319    cx.executor().run_until_parked();
 9320
 9321    multi_buffer_editor.update(cx, |editor, cx| {
 9322        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 9323        editor.open_excerpts(&OpenExcerpts, cx);
 9324    });
 9325    cx.executor().run_until_parked();
 9326    let first_item_id = workspace
 9327        .update(cx, |workspace, cx| {
 9328            let active_item = workspace
 9329                .active_item(cx)
 9330                .expect("should have an active item after navigating into the 1st buffer");
 9331            let first_item_id = active_item.item_id();
 9332            assert_ne!(
 9333                first_item_id, multibuffer_item_id,
 9334                "Should navigate into the 1st buffer and activate it"
 9335            );
 9336            assert!(
 9337                active_item.is_singleton(cx),
 9338                "New active item should be a singleton buffer"
 9339            );
 9340            assert_eq!(
 9341                active_item
 9342                    .act_as::<Editor>(cx)
 9343                    .expect("should have navigated into an editor for the 1st buffer")
 9344                    .read(cx)
 9345                    .text(cx),
 9346                sample_text_1
 9347            );
 9348
 9349            workspace
 9350                .go_back(workspace.active_pane().downgrade(), cx)
 9351                .detach_and_log_err(cx);
 9352
 9353            first_item_id
 9354        })
 9355        .unwrap();
 9356    cx.executor().run_until_parked();
 9357    workspace
 9358        .update(cx, |workspace, cx| {
 9359            let active_item = workspace
 9360                .active_item(cx)
 9361                .expect("should have an active item after navigating back");
 9362            assert_eq!(
 9363                active_item.item_id(),
 9364                multibuffer_item_id,
 9365                "Should navigate back to the multi buffer"
 9366            );
 9367            assert!(!active_item.is_singleton(cx));
 9368        })
 9369        .unwrap();
 9370
 9371    multi_buffer_editor.update(cx, |editor, cx| {
 9372        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9373            s.select_ranges(Some(39..40))
 9374        });
 9375        editor.open_excerpts(&OpenExcerpts, cx);
 9376    });
 9377    cx.executor().run_until_parked();
 9378    let second_item_id = workspace
 9379        .update(cx, |workspace, cx| {
 9380            let active_item = workspace
 9381                .active_item(cx)
 9382                .expect("should have an active item after navigating into the 2nd buffer");
 9383            let second_item_id = active_item.item_id();
 9384            assert_ne!(
 9385                second_item_id, multibuffer_item_id,
 9386                "Should navigate away from the multibuffer"
 9387            );
 9388            assert_ne!(
 9389                second_item_id, first_item_id,
 9390                "Should navigate into the 2nd buffer and activate it"
 9391            );
 9392            assert!(
 9393                active_item.is_singleton(cx),
 9394                "New active item should be a singleton buffer"
 9395            );
 9396            assert_eq!(
 9397                active_item
 9398                    .act_as::<Editor>(cx)
 9399                    .expect("should have navigated into an editor")
 9400                    .read(cx)
 9401                    .text(cx),
 9402                sample_text_2
 9403            );
 9404
 9405            workspace
 9406                .go_back(workspace.active_pane().downgrade(), cx)
 9407                .detach_and_log_err(cx);
 9408
 9409            second_item_id
 9410        })
 9411        .unwrap();
 9412    cx.executor().run_until_parked();
 9413    workspace
 9414        .update(cx, |workspace, cx| {
 9415            let active_item = workspace
 9416                .active_item(cx)
 9417                .expect("should have an active item after navigating back from the 2nd buffer");
 9418            assert_eq!(
 9419                active_item.item_id(),
 9420                multibuffer_item_id,
 9421                "Should navigate back from the 2nd buffer to the multi buffer"
 9422            );
 9423            assert!(!active_item.is_singleton(cx));
 9424        })
 9425        .unwrap();
 9426
 9427    multi_buffer_editor.update(cx, |editor, cx| {
 9428        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9429            s.select_ranges(Some(60..70))
 9430        });
 9431        editor.open_excerpts(&OpenExcerpts, cx);
 9432    });
 9433    cx.executor().run_until_parked();
 9434    workspace
 9435        .update(cx, |workspace, cx| {
 9436            let active_item = workspace
 9437                .active_item(cx)
 9438                .expect("should have an active item after navigating into the 3rd buffer");
 9439            let third_item_id = active_item.item_id();
 9440            assert_ne!(
 9441                third_item_id, multibuffer_item_id,
 9442                "Should navigate into the 3rd buffer and activate it"
 9443            );
 9444            assert_ne!(third_item_id, first_item_id);
 9445            assert_ne!(third_item_id, second_item_id);
 9446            assert!(
 9447                active_item.is_singleton(cx),
 9448                "New active item should be a singleton buffer"
 9449            );
 9450            assert_eq!(
 9451                active_item
 9452                    .act_as::<Editor>(cx)
 9453                    .expect("should have navigated into an editor")
 9454                    .read(cx)
 9455                    .text(cx),
 9456                sample_text_3
 9457            );
 9458
 9459            workspace
 9460                .go_back(workspace.active_pane().downgrade(), cx)
 9461                .detach_and_log_err(cx);
 9462        })
 9463        .unwrap();
 9464    cx.executor().run_until_parked();
 9465    workspace
 9466        .update(cx, |workspace, cx| {
 9467            let active_item = workspace
 9468                .active_item(cx)
 9469                .expect("should have an active item after navigating back from the 3rd buffer");
 9470            assert_eq!(
 9471                active_item.item_id(),
 9472                multibuffer_item_id,
 9473                "Should navigate back from the 3rd buffer to the multi buffer"
 9474            );
 9475            assert!(!active_item.is_singleton(cx));
 9476        })
 9477        .unwrap();
 9478}
 9479
 9480#[gpui::test]
 9481async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9482    init_test(cx, |_| {});
 9483
 9484    let mut cx = EditorTestContext::new(cx).await;
 9485
 9486    let diff_base = r#"
 9487        use some::mod;
 9488
 9489        const A: u32 = 42;
 9490
 9491        fn main() {
 9492            println!("hello");
 9493
 9494            println!("world");
 9495        }
 9496        "#
 9497    .unindent();
 9498
 9499    cx.set_state(
 9500        &r#"
 9501        use some::modified;
 9502
 9503        ˇ
 9504        fn main() {
 9505            println!("hello there");
 9506
 9507            println!("around the");
 9508            println!("world");
 9509        }
 9510        "#
 9511        .unindent(),
 9512    );
 9513
 9514    cx.set_diff_base(Some(&diff_base));
 9515    executor.run_until_parked();
 9516    let unexpanded_hunks = vec![
 9517        (
 9518            "use some::mod;\n".to_string(),
 9519            DiffHunkStatus::Modified,
 9520            DisplayRow(0)..DisplayRow(1),
 9521        ),
 9522        (
 9523            "const A: u32 = 42;\n".to_string(),
 9524            DiffHunkStatus::Removed,
 9525            DisplayRow(2)..DisplayRow(2),
 9526        ),
 9527        (
 9528            "    println!(\"hello\");\n".to_string(),
 9529            DiffHunkStatus::Modified,
 9530            DisplayRow(4)..DisplayRow(5),
 9531        ),
 9532        (
 9533            "".to_string(),
 9534            DiffHunkStatus::Added,
 9535            DisplayRow(6)..DisplayRow(7),
 9536        ),
 9537    ];
 9538    cx.update_editor(|editor, cx| {
 9539        let snapshot = editor.snapshot(cx);
 9540        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9541        assert_eq!(all_hunks, unexpanded_hunks);
 9542    });
 9543
 9544    cx.update_editor(|editor, cx| {
 9545        for _ in 0..4 {
 9546            editor.go_to_hunk(&GoToHunk, cx);
 9547            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
 9548        }
 9549    });
 9550    executor.run_until_parked();
 9551    cx.assert_editor_state(
 9552        &r#"
 9553        use some::modified;
 9554
 9555        ˇ
 9556        fn main() {
 9557            println!("hello there");
 9558
 9559            println!("around the");
 9560            println!("world");
 9561        }
 9562        "#
 9563        .unindent(),
 9564    );
 9565    cx.update_editor(|editor, cx| {
 9566        let snapshot = editor.snapshot(cx);
 9567        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9568        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9569        assert_eq!(
 9570            expanded_hunks_background_highlights(editor, cx),
 9571            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
 9572            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9573        );
 9574        assert_eq!(
 9575            all_hunks,
 9576            vec![
 9577                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
 9578                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
 9579                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
 9580                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
 9581            ],
 9582            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9583            (from modified and removed hunks)"
 9584        );
 9585        assert_eq!(
 9586            all_hunks, all_expanded_hunks,
 9587            "Editor hunks should not change and all be expanded"
 9588        );
 9589    });
 9590
 9591    cx.update_editor(|editor, cx| {
 9592        editor.cancel(&Cancel, cx);
 9593
 9594        let snapshot = editor.snapshot(cx);
 9595        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9596        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9597        assert_eq!(
 9598            expanded_hunks_background_highlights(editor, cx),
 9599            Vec::new(),
 9600            "After cancelling in editor, no git highlights should be left"
 9601        );
 9602        assert_eq!(
 9603            all_expanded_hunks,
 9604            Vec::new(),
 9605            "After cancelling in editor, no hunks should be expanded"
 9606        );
 9607        assert_eq!(
 9608            all_hunks, unexpanded_hunks,
 9609            "After cancelling in editor, regular hunks' coordinates should get back to normal"
 9610        );
 9611    });
 9612}
 9613
 9614#[gpui::test]
 9615async fn test_toggled_diff_base_change(
 9616    executor: BackgroundExecutor,
 9617    cx: &mut gpui::TestAppContext,
 9618) {
 9619    init_test(cx, |_| {});
 9620
 9621    let mut cx = EditorTestContext::new(cx).await;
 9622
 9623    let diff_base = r#"
 9624        use some::mod1;
 9625        use some::mod2;
 9626
 9627        const A: u32 = 42;
 9628        const B: u32 = 42;
 9629        const C: u32 = 42;
 9630
 9631        fn main(ˇ) {
 9632            println!("hello");
 9633
 9634            println!("world");
 9635        }
 9636        "#
 9637    .unindent();
 9638
 9639    cx.set_state(
 9640        &r#"
 9641        use some::mod2;
 9642
 9643        const A: u32 = 42;
 9644        const C: u32 = 42;
 9645
 9646        fn main(ˇ) {
 9647            //println!("hello");
 9648
 9649            println!("world");
 9650            //
 9651            //
 9652        }
 9653        "#
 9654        .unindent(),
 9655    );
 9656
 9657    cx.set_diff_base(Some(&diff_base));
 9658    executor.run_until_parked();
 9659    cx.update_editor(|editor, cx| {
 9660        let snapshot = editor.snapshot(cx);
 9661        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9662        assert_eq!(
 9663            all_hunks,
 9664            vec![
 9665                (
 9666                    "use some::mod1;\n".to_string(),
 9667                    DiffHunkStatus::Removed,
 9668                    DisplayRow(0)..DisplayRow(0)
 9669                ),
 9670                (
 9671                    "const B: u32 = 42;\n".to_string(),
 9672                    DiffHunkStatus::Removed,
 9673                    DisplayRow(3)..DisplayRow(3)
 9674                ),
 9675                (
 9676                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9677                    DiffHunkStatus::Modified,
 9678                    DisplayRow(5)..DisplayRow(7)
 9679                ),
 9680                (
 9681                    "".to_string(),
 9682                    DiffHunkStatus::Added,
 9683                    DisplayRow(9)..DisplayRow(11)
 9684                ),
 9685            ]
 9686        );
 9687    });
 9688
 9689    cx.update_editor(|editor, cx| {
 9690        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9691    });
 9692    executor.run_until_parked();
 9693    cx.assert_editor_state(
 9694        &r#"
 9695        use some::mod2;
 9696
 9697        const A: u32 = 42;
 9698        const C: u32 = 42;
 9699
 9700        fn main(ˇ) {
 9701            //println!("hello");
 9702
 9703            println!("world");
 9704            //
 9705            //
 9706        }
 9707        "#
 9708        .unindent(),
 9709    );
 9710    cx.update_editor(|editor, cx| {
 9711        let snapshot = editor.snapshot(cx);
 9712        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9713        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9714        assert_eq!(
 9715            expanded_hunks_background_highlights(editor, cx),
 9716            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
 9717            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9718        );
 9719        assert_eq!(
 9720            all_hunks,
 9721            vec![
 9722                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
 9723                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
 9724                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
 9725                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
 9726            ],
 9727            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9728            (from modified and removed hunks)"
 9729        );
 9730        assert_eq!(
 9731            all_hunks, all_expanded_hunks,
 9732            "Editor hunks should not change and all be expanded"
 9733        );
 9734    });
 9735
 9736    cx.set_diff_base(Some("new diff base!"));
 9737    executor.run_until_parked();
 9738
 9739    cx.update_editor(|editor, cx| {
 9740        let snapshot = editor.snapshot(cx);
 9741        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9742        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9743        assert_eq!(
 9744            expanded_hunks_background_highlights(editor, cx),
 9745            Vec::new(),
 9746            "After diff base is changed, old git highlights should be removed"
 9747        );
 9748        assert_eq!(
 9749            all_expanded_hunks,
 9750            Vec::new(),
 9751            "After diff base is changed, old git hunk expansions should be removed"
 9752        );
 9753        assert_eq!(
 9754            all_hunks,
 9755            vec![(
 9756                "new diff base!".to_string(),
 9757                DiffHunkStatus::Modified,
 9758                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
 9759            )],
 9760            "After diff base is changed, hunks should update"
 9761        );
 9762    });
 9763}
 9764
 9765#[gpui::test]
 9766async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9767    init_test(cx, |_| {});
 9768
 9769    let mut cx = EditorTestContext::new(cx).await;
 9770
 9771    let diff_base = r#"
 9772        use some::mod1;
 9773        use some::mod2;
 9774
 9775        const A: u32 = 42;
 9776        const B: u32 = 42;
 9777        const C: u32 = 42;
 9778
 9779        fn main(ˇ) {
 9780            println!("hello");
 9781
 9782            println!("world");
 9783        }
 9784
 9785        fn another() {
 9786            println!("another");
 9787        }
 9788
 9789        fn another2() {
 9790            println!("another2");
 9791        }
 9792        "#
 9793    .unindent();
 9794
 9795    cx.set_state(
 9796        &r#"
 9797        «use some::mod2;
 9798
 9799        const A: u32 = 42;
 9800        const C: u32 = 42;
 9801
 9802        fn main() {
 9803            //println!("hello");
 9804
 9805            println!("world");
 9806            //
 9807            //ˇ»
 9808        }
 9809
 9810        fn another() {
 9811            println!("another");
 9812            println!("another");
 9813        }
 9814
 9815            println!("another2");
 9816        }
 9817        "#
 9818        .unindent(),
 9819    );
 9820
 9821    cx.set_diff_base(Some(&diff_base));
 9822    executor.run_until_parked();
 9823    cx.update_editor(|editor, cx| {
 9824        let snapshot = editor.snapshot(cx);
 9825        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9826        assert_eq!(
 9827            all_hunks,
 9828            vec![
 9829                (
 9830                    "use some::mod1;\n".to_string(),
 9831                    DiffHunkStatus::Removed,
 9832                    DisplayRow(0)..DisplayRow(0)
 9833                ),
 9834                (
 9835                    "const B: u32 = 42;\n".to_string(),
 9836                    DiffHunkStatus::Removed,
 9837                    DisplayRow(3)..DisplayRow(3)
 9838                ),
 9839                (
 9840                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9841                    DiffHunkStatus::Modified,
 9842                    DisplayRow(5)..DisplayRow(7)
 9843                ),
 9844                (
 9845                    "".to_string(),
 9846                    DiffHunkStatus::Added,
 9847                    DisplayRow(9)..DisplayRow(11)
 9848                ),
 9849                (
 9850                    "".to_string(),
 9851                    DiffHunkStatus::Added,
 9852                    DisplayRow(15)..DisplayRow(16)
 9853                ),
 9854                (
 9855                    "fn another2() {\n".to_string(),
 9856                    DiffHunkStatus::Removed,
 9857                    DisplayRow(18)..DisplayRow(18)
 9858                ),
 9859            ]
 9860        );
 9861    });
 9862
 9863    cx.update_editor(|editor, cx| {
 9864        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9865    });
 9866    executor.run_until_parked();
 9867    cx.assert_editor_state(
 9868        &r#"
 9869        «use some::mod2;
 9870
 9871        const A: u32 = 42;
 9872        const C: u32 = 42;
 9873
 9874        fn main() {
 9875            //println!("hello");
 9876
 9877            println!("world");
 9878            //
 9879            //ˇ»
 9880        }
 9881
 9882        fn another() {
 9883            println!("another");
 9884            println!("another");
 9885        }
 9886
 9887            println!("another2");
 9888        }
 9889        "#
 9890        .unindent(),
 9891    );
 9892    cx.update_editor(|editor, cx| {
 9893        let snapshot = editor.snapshot(cx);
 9894        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9895        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9896        assert_eq!(
 9897            expanded_hunks_background_highlights(editor, cx),
 9898            vec![
 9899                DisplayRow(9)..=DisplayRow(10),
 9900                DisplayRow(13)..=DisplayRow(14),
 9901                DisplayRow(19)..=DisplayRow(19)
 9902            ]
 9903        );
 9904        assert_eq!(
 9905            all_hunks,
 9906            vec![
 9907                (
 9908                    "use some::mod1;\n".to_string(),
 9909                    DiffHunkStatus::Removed,
 9910                    DisplayRow(1)..DisplayRow(1)
 9911                ),
 9912                (
 9913                    "const B: u32 = 42;\n".to_string(),
 9914                    DiffHunkStatus::Removed,
 9915                    DisplayRow(5)..DisplayRow(5)
 9916                ),
 9917                (
 9918                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9919                    DiffHunkStatus::Modified,
 9920                    DisplayRow(9)..DisplayRow(11)
 9921                ),
 9922                (
 9923                    "".to_string(),
 9924                    DiffHunkStatus::Added,
 9925                    DisplayRow(13)..DisplayRow(15)
 9926                ),
 9927                (
 9928                    "".to_string(),
 9929                    DiffHunkStatus::Added,
 9930                    DisplayRow(19)..DisplayRow(20)
 9931                ),
 9932                (
 9933                    "fn another2() {\n".to_string(),
 9934                    DiffHunkStatus::Removed,
 9935                    DisplayRow(23)..DisplayRow(23)
 9936                ),
 9937            ],
 9938        );
 9939        assert_eq!(all_hunks, all_expanded_hunks);
 9940    });
 9941
 9942    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
 9943    cx.executor().run_until_parked();
 9944    cx.assert_editor_state(
 9945        &r#"
 9946        «use some::mod2;
 9947
 9948        const A: u32 = 42;
 9949        const C: u32 = 42;
 9950
 9951        fn main() {
 9952            //println!("hello");
 9953
 9954            println!("world");
 9955            //
 9956            //ˇ»
 9957        }
 9958
 9959        fn another() {
 9960            println!("another");
 9961            println!("another");
 9962        }
 9963
 9964            println!("another2");
 9965        }
 9966        "#
 9967        .unindent(),
 9968    );
 9969    cx.update_editor(|editor, cx| {
 9970        let snapshot = editor.snapshot(cx);
 9971        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9972        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9973        assert_eq!(
 9974            expanded_hunks_background_highlights(editor, cx),
 9975            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
 9976            "Only one hunk is left not folded, its highlight should be visible"
 9977        );
 9978        assert_eq!(
 9979            all_hunks,
 9980            vec![
 9981                (
 9982                    "use some::mod1;\n".to_string(),
 9983                    DiffHunkStatus::Removed,
 9984                    DisplayRow(0)..DisplayRow(0)
 9985                ),
 9986                (
 9987                    "const B: u32 = 42;\n".to_string(),
 9988                    DiffHunkStatus::Removed,
 9989                    DisplayRow(0)..DisplayRow(0)
 9990                ),
 9991                (
 9992                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9993                    DiffHunkStatus::Modified,
 9994                    DisplayRow(0)..DisplayRow(0)
 9995                ),
 9996                (
 9997                    "".to_string(),
 9998                    DiffHunkStatus::Added,
 9999                    DisplayRow(0)..DisplayRow(1)
10000                ),
10001                (
10002                    "".to_string(),
10003                    DiffHunkStatus::Added,
10004                    DisplayRow(5)..DisplayRow(6)
10005                ),
10006                (
10007                    "fn another2() {\n".to_string(),
10008                    DiffHunkStatus::Removed,
10009                    DisplayRow(9)..DisplayRow(9)
10010                ),
10011            ],
10012            "Hunk list should still return shifted folded hunks"
10013        );
10014        assert_eq!(
10015            all_expanded_hunks,
10016            vec![
10017                (
10018                    "".to_string(),
10019                    DiffHunkStatus::Added,
10020                    DisplayRow(5)..DisplayRow(6)
10021                ),
10022                (
10023                    "fn another2() {\n".to_string(),
10024                    DiffHunkStatus::Removed,
10025                    DisplayRow(9)..DisplayRow(9)
10026                ),
10027            ],
10028            "Only non-folded hunks should be left expanded"
10029        );
10030    });
10031
10032    cx.update_editor(|editor, cx| {
10033        editor.select_all(&SelectAll, cx);
10034        editor.unfold_lines(&UnfoldLines, cx);
10035    });
10036    cx.executor().run_until_parked();
10037    cx.assert_editor_state(
10038        &r#"
10039        «use some::mod2;
10040
10041        const A: u32 = 42;
10042        const C: u32 = 42;
10043
10044        fn main() {
10045            //println!("hello");
10046
10047            println!("world");
10048            //
10049            //
10050        }
10051
10052        fn another() {
10053            println!("another");
10054            println!("another");
10055        }
10056
10057            println!("another2");
10058        }
10059        ˇ»"#
10060        .unindent(),
10061    );
10062    cx.update_editor(|editor, cx| {
10063        let snapshot = editor.snapshot(cx);
10064        let all_hunks = editor_hunks(editor, &snapshot, cx);
10065        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10066        assert_eq!(
10067            expanded_hunks_background_highlights(editor, cx),
10068            vec![
10069                DisplayRow(9)..=DisplayRow(10),
10070                DisplayRow(13)..=DisplayRow(14),
10071                DisplayRow(19)..=DisplayRow(19)
10072            ],
10073            "After unfolding, all hunk diffs should be visible again"
10074        );
10075        assert_eq!(
10076            all_hunks,
10077            vec![
10078                (
10079                    "use some::mod1;\n".to_string(),
10080                    DiffHunkStatus::Removed,
10081                    DisplayRow(1)..DisplayRow(1)
10082                ),
10083                (
10084                    "const B: u32 = 42;\n".to_string(),
10085                    DiffHunkStatus::Removed,
10086                    DisplayRow(5)..DisplayRow(5)
10087                ),
10088                (
10089                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10090                    DiffHunkStatus::Modified,
10091                    DisplayRow(9)..DisplayRow(11)
10092                ),
10093                (
10094                    "".to_string(),
10095                    DiffHunkStatus::Added,
10096                    DisplayRow(13)..DisplayRow(15)
10097                ),
10098                (
10099                    "".to_string(),
10100                    DiffHunkStatus::Added,
10101                    DisplayRow(19)..DisplayRow(20)
10102                ),
10103                (
10104                    "fn another2() {\n".to_string(),
10105                    DiffHunkStatus::Removed,
10106                    DisplayRow(23)..DisplayRow(23)
10107                ),
10108            ],
10109        );
10110        assert_eq!(all_hunks, all_expanded_hunks);
10111    });
10112}
10113
10114#[gpui::test]
10115async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
10116    init_test(cx, |_| {});
10117
10118    let cols = 4;
10119    let rows = 10;
10120    let sample_text_1 = sample_text(rows, cols, 'a');
10121    assert_eq!(
10122        sample_text_1,
10123        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10124    );
10125    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
10126    let sample_text_2 = sample_text(rows, cols, 'l');
10127    assert_eq!(
10128        sample_text_2,
10129        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10130    );
10131    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
10132    let sample_text_3 = sample_text(rows, cols, 'v');
10133    assert_eq!(
10134        sample_text_3,
10135        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10136    );
10137    let modified_sample_text_3 =
10138        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
10139    let buffer_1 = cx.new_model(|cx| {
10140        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
10141        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
10142        buffer
10143    });
10144    let buffer_2 = cx.new_model(|cx| {
10145        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
10146        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
10147        buffer
10148    });
10149    let buffer_3 = cx.new_model(|cx| {
10150        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
10151        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
10152        buffer
10153    });
10154
10155    let multi_buffer = cx.new_model(|cx| {
10156        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10157        multibuffer.push_excerpts(
10158            buffer_1.clone(),
10159            [
10160                ExcerptRange {
10161                    context: Point::new(0, 0)..Point::new(3, 0),
10162                    primary: None,
10163                },
10164                ExcerptRange {
10165                    context: Point::new(5, 0)..Point::new(7, 0),
10166                    primary: None,
10167                },
10168                ExcerptRange {
10169                    context: Point::new(9, 0)..Point::new(10, 4),
10170                    primary: None,
10171                },
10172            ],
10173            cx,
10174        );
10175        multibuffer.push_excerpts(
10176            buffer_2.clone(),
10177            [
10178                ExcerptRange {
10179                    context: Point::new(0, 0)..Point::new(3, 0),
10180                    primary: None,
10181                },
10182                ExcerptRange {
10183                    context: Point::new(5, 0)..Point::new(7, 0),
10184                    primary: None,
10185                },
10186                ExcerptRange {
10187                    context: Point::new(9, 0)..Point::new(10, 4),
10188                    primary: None,
10189                },
10190            ],
10191            cx,
10192        );
10193        multibuffer.push_excerpts(
10194            buffer_3.clone(),
10195            [
10196                ExcerptRange {
10197                    context: Point::new(0, 0)..Point::new(3, 0),
10198                    primary: None,
10199                },
10200                ExcerptRange {
10201                    context: Point::new(5, 0)..Point::new(7, 0),
10202                    primary: None,
10203                },
10204                ExcerptRange {
10205                    context: Point::new(9, 0)..Point::new(10, 4),
10206                    primary: None,
10207                },
10208            ],
10209            cx,
10210        );
10211        multibuffer
10212    });
10213
10214    let fs = FakeFs::new(cx.executor());
10215    fs.insert_tree(
10216        "/a",
10217        json!({
10218            "main.rs": modified_sample_text_1,
10219            "other.rs": modified_sample_text_2,
10220            "lib.rs": modified_sample_text_3,
10221        }),
10222    )
10223    .await;
10224
10225    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10226    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10227    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10228    let multi_buffer_editor =
10229        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
10230    cx.executor().run_until_parked();
10231
10232    let expected_all_hunks = vec![
10233        (
10234            "bbbb\n".to_string(),
10235            DiffHunkStatus::Removed,
10236            DisplayRow(3)..DisplayRow(3),
10237        ),
10238        (
10239            "nnnn\n".to_string(),
10240            DiffHunkStatus::Modified,
10241            DisplayRow(16)..DisplayRow(17),
10242        ),
10243        (
10244            "".to_string(),
10245            DiffHunkStatus::Added,
10246            DisplayRow(31)..DisplayRow(32),
10247        ),
10248    ];
10249    let expected_all_hunks_shifted = vec![
10250        (
10251            "bbbb\n".to_string(),
10252            DiffHunkStatus::Removed,
10253            DisplayRow(4)..DisplayRow(4),
10254        ),
10255        (
10256            "nnnn\n".to_string(),
10257            DiffHunkStatus::Modified,
10258            DisplayRow(18)..DisplayRow(19),
10259        ),
10260        (
10261            "".to_string(),
10262            DiffHunkStatus::Added,
10263            DisplayRow(33)..DisplayRow(34),
10264        ),
10265    ];
10266
10267    multi_buffer_editor.update(cx, |editor, cx| {
10268        let snapshot = editor.snapshot(cx);
10269        let all_hunks = editor_hunks(editor, &snapshot, cx);
10270        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10271        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10272        assert_eq!(all_hunks, expected_all_hunks);
10273        assert_eq!(all_expanded_hunks, Vec::new());
10274    });
10275
10276    multi_buffer_editor.update(cx, |editor, cx| {
10277        editor.select_all(&SelectAll, cx);
10278        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10279    });
10280    cx.executor().run_until_parked();
10281    multi_buffer_editor.update(cx, |editor, cx| {
10282        let snapshot = editor.snapshot(cx);
10283        let all_hunks = editor_hunks(editor, &snapshot, cx);
10284        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10285        assert_eq!(
10286            expanded_hunks_background_highlights(editor, cx),
10287            vec![
10288                DisplayRow(18)..=DisplayRow(18),
10289                DisplayRow(33)..=DisplayRow(33)
10290            ],
10291        );
10292        assert_eq!(all_hunks, expected_all_hunks_shifted);
10293        assert_eq!(all_hunks, all_expanded_hunks);
10294    });
10295
10296    multi_buffer_editor.update(cx, |editor, cx| {
10297        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10298    });
10299    cx.executor().run_until_parked();
10300    multi_buffer_editor.update(cx, |editor, cx| {
10301        let snapshot = editor.snapshot(cx);
10302        let all_hunks = editor_hunks(editor, &snapshot, cx);
10303        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10304        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10305        assert_eq!(all_hunks, expected_all_hunks);
10306        assert_eq!(all_expanded_hunks, Vec::new());
10307    });
10308
10309    multi_buffer_editor.update(cx, |editor, cx| {
10310        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10311    });
10312    cx.executor().run_until_parked();
10313    multi_buffer_editor.update(cx, |editor, cx| {
10314        let snapshot = editor.snapshot(cx);
10315        let all_hunks = editor_hunks(editor, &snapshot, cx);
10316        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10317        assert_eq!(
10318            expanded_hunks_background_highlights(editor, cx),
10319            vec![
10320                DisplayRow(18)..=DisplayRow(18),
10321                DisplayRow(33)..=DisplayRow(33)
10322            ],
10323        );
10324        assert_eq!(all_hunks, expected_all_hunks_shifted);
10325        assert_eq!(all_hunks, all_expanded_hunks);
10326    });
10327
10328    multi_buffer_editor.update(cx, |editor, cx| {
10329        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10330    });
10331    cx.executor().run_until_parked();
10332    multi_buffer_editor.update(cx, |editor, cx| {
10333        let snapshot = editor.snapshot(cx);
10334        let all_hunks = editor_hunks(editor, &snapshot, cx);
10335        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10336        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10337        assert_eq!(all_hunks, expected_all_hunks);
10338        assert_eq!(all_expanded_hunks, Vec::new());
10339    });
10340}
10341
10342#[gpui::test]
10343async fn test_edits_around_toggled_additions(
10344    executor: BackgroundExecutor,
10345    cx: &mut gpui::TestAppContext,
10346) {
10347    init_test(cx, |_| {});
10348
10349    let mut cx = EditorTestContext::new(cx).await;
10350
10351    let diff_base = r#"
10352        use some::mod1;
10353        use some::mod2;
10354
10355        const A: u32 = 42;
10356
10357        fn main() {
10358            println!("hello");
10359
10360            println!("world");
10361        }
10362        "#
10363    .unindent();
10364    executor.run_until_parked();
10365    cx.set_state(
10366        &r#"
10367        use some::mod1;
10368        use some::mod2;
10369
10370        const A: u32 = 42;
10371        const B: u32 = 42;
10372        const C: u32 = 42;
10373        ˇ
10374
10375        fn main() {
10376            println!("hello");
10377
10378            println!("world");
10379        }
10380        "#
10381        .unindent(),
10382    );
10383
10384    cx.set_diff_base(Some(&diff_base));
10385    executor.run_until_parked();
10386    cx.update_editor(|editor, cx| {
10387        let snapshot = editor.snapshot(cx);
10388        let all_hunks = editor_hunks(editor, &snapshot, cx);
10389        assert_eq!(
10390            all_hunks,
10391            vec![(
10392                "".to_string(),
10393                DiffHunkStatus::Added,
10394                DisplayRow(4)..DisplayRow(7)
10395            )]
10396        );
10397    });
10398    cx.update_editor(|editor, cx| {
10399        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10400    });
10401    executor.run_until_parked();
10402    cx.assert_editor_state(
10403        &r#"
10404        use some::mod1;
10405        use some::mod2;
10406
10407        const A: u32 = 42;
10408        const B: u32 = 42;
10409        const C: u32 = 42;
10410        ˇ
10411
10412        fn main() {
10413            println!("hello");
10414
10415            println!("world");
10416        }
10417        "#
10418        .unindent(),
10419    );
10420    cx.update_editor(|editor, cx| {
10421        let snapshot = editor.snapshot(cx);
10422        let all_hunks = editor_hunks(editor, &snapshot, cx);
10423        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10424        assert_eq!(
10425            all_hunks,
10426            vec![(
10427                "".to_string(),
10428                DiffHunkStatus::Added,
10429                DisplayRow(4)..DisplayRow(7)
10430            )]
10431        );
10432        assert_eq!(
10433            expanded_hunks_background_highlights(editor, cx),
10434            vec![DisplayRow(4)..=DisplayRow(6)]
10435        );
10436        assert_eq!(all_hunks, all_expanded_hunks);
10437    });
10438
10439    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
10440    executor.run_until_parked();
10441    cx.assert_editor_state(
10442        &r#"
10443        use some::mod1;
10444        use some::mod2;
10445
10446        const A: u32 = 42;
10447        const B: u32 = 42;
10448        const C: u32 = 42;
10449        const D: u32 = 42;
10450        ˇ
10451
10452        fn main() {
10453            println!("hello");
10454
10455            println!("world");
10456        }
10457        "#
10458        .unindent(),
10459    );
10460    cx.update_editor(|editor, cx| {
10461        let snapshot = editor.snapshot(cx);
10462        let all_hunks = editor_hunks(editor, &snapshot, cx);
10463        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10464        assert_eq!(
10465            all_hunks,
10466            vec![(
10467                "".to_string(),
10468                DiffHunkStatus::Added,
10469                DisplayRow(4)..DisplayRow(8)
10470            )]
10471        );
10472        assert_eq!(
10473            expanded_hunks_background_highlights(editor, cx),
10474            vec![DisplayRow(4)..=DisplayRow(6)],
10475            "Edited hunk should have one more line added"
10476        );
10477        assert_eq!(
10478            all_hunks, all_expanded_hunks,
10479            "Expanded hunk should also grow with the addition"
10480        );
10481    });
10482
10483    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
10484    executor.run_until_parked();
10485    cx.assert_editor_state(
10486        &r#"
10487        use some::mod1;
10488        use some::mod2;
10489
10490        const A: u32 = 42;
10491        const B: u32 = 42;
10492        const C: u32 = 42;
10493        const D: u32 = 42;
10494        const E: u32 = 42;
10495        ˇ
10496
10497        fn main() {
10498            println!("hello");
10499
10500            println!("world");
10501        }
10502        "#
10503        .unindent(),
10504    );
10505    cx.update_editor(|editor, cx| {
10506        let snapshot = editor.snapshot(cx);
10507        let all_hunks = editor_hunks(editor, &snapshot, cx);
10508        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10509        assert_eq!(
10510            all_hunks,
10511            vec![(
10512                "".to_string(),
10513                DiffHunkStatus::Added,
10514                DisplayRow(4)..DisplayRow(9)
10515            )]
10516        );
10517        assert_eq!(
10518            expanded_hunks_background_highlights(editor, cx),
10519            vec![DisplayRow(4)..=DisplayRow(6)],
10520            "Edited hunk should have one more line added"
10521        );
10522        assert_eq!(all_hunks, all_expanded_hunks);
10523    });
10524
10525    cx.update_editor(|editor, cx| {
10526        editor.move_up(&MoveUp, cx);
10527        editor.delete_line(&DeleteLine, cx);
10528    });
10529    executor.run_until_parked();
10530    cx.assert_editor_state(
10531        &r#"
10532        use some::mod1;
10533        use some::mod2;
10534
10535        const A: u32 = 42;
10536        const B: u32 = 42;
10537        const C: u32 = 42;
10538        const D: u32 = 42;
10539        ˇ
10540
10541        fn main() {
10542            println!("hello");
10543
10544            println!("world");
10545        }
10546        "#
10547        .unindent(),
10548    );
10549    cx.update_editor(|editor, cx| {
10550        let snapshot = editor.snapshot(cx);
10551        let all_hunks = editor_hunks(editor, &snapshot, cx);
10552        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10553        assert_eq!(
10554            all_hunks,
10555            vec![(
10556                "".to_string(),
10557                DiffHunkStatus::Added,
10558                DisplayRow(4)..DisplayRow(8)
10559            )]
10560        );
10561        assert_eq!(
10562            expanded_hunks_background_highlights(editor, cx),
10563            vec![DisplayRow(4)..=DisplayRow(6)],
10564            "Deleting a line should shrint the hunk"
10565        );
10566        assert_eq!(
10567            all_hunks, all_expanded_hunks,
10568            "Expanded hunk should also shrink with the addition"
10569        );
10570    });
10571
10572    cx.update_editor(|editor, cx| {
10573        editor.move_up(&MoveUp, cx);
10574        editor.delete_line(&DeleteLine, cx);
10575        editor.move_up(&MoveUp, cx);
10576        editor.delete_line(&DeleteLine, cx);
10577        editor.move_up(&MoveUp, cx);
10578        editor.delete_line(&DeleteLine, cx);
10579    });
10580    executor.run_until_parked();
10581    cx.assert_editor_state(
10582        &r#"
10583        use some::mod1;
10584        use some::mod2;
10585
10586        const A: u32 = 42;
10587        ˇ
10588
10589        fn main() {
10590            println!("hello");
10591
10592            println!("world");
10593        }
10594        "#
10595        .unindent(),
10596    );
10597    cx.update_editor(|editor, cx| {
10598        let snapshot = editor.snapshot(cx);
10599        let all_hunks = editor_hunks(editor, &snapshot, cx);
10600        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10601        assert_eq!(
10602            all_hunks,
10603            vec![(
10604                "".to_string(),
10605                DiffHunkStatus::Added,
10606                DisplayRow(5)..DisplayRow(6)
10607            )]
10608        );
10609        assert_eq!(
10610            expanded_hunks_background_highlights(editor, cx),
10611            vec![DisplayRow(5)..=DisplayRow(5)]
10612        );
10613        assert_eq!(all_hunks, all_expanded_hunks);
10614    });
10615
10616    cx.update_editor(|editor, cx| {
10617        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
10618        editor.delete_line(&DeleteLine, cx);
10619    });
10620    executor.run_until_parked();
10621    cx.assert_editor_state(
10622        &r#"
10623        ˇ
10624
10625        fn main() {
10626            println!("hello");
10627
10628            println!("world");
10629        }
10630        "#
10631        .unindent(),
10632    );
10633    cx.update_editor(|editor, cx| {
10634        let snapshot = editor.snapshot(cx);
10635        let all_hunks = editor_hunks(editor, &snapshot, cx);
10636        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10637        assert_eq!(
10638            all_hunks,
10639            vec![
10640                (
10641                    "use some::mod1;\nuse some::mod2;\n".to_string(),
10642                    DiffHunkStatus::Removed,
10643                    DisplayRow(0)..DisplayRow(0)
10644                ),
10645                (
10646                    "const A: u32 = 42;\n".to_string(),
10647                    DiffHunkStatus::Removed,
10648                    DisplayRow(2)..DisplayRow(2)
10649                )
10650            ]
10651        );
10652        assert_eq!(
10653            expanded_hunks_background_highlights(editor, cx),
10654            Vec::new(),
10655            "Should close all stale expanded addition hunks"
10656        );
10657        assert_eq!(
10658            all_expanded_hunks,
10659            vec![(
10660                "const A: u32 = 42;\n".to_string(),
10661                DiffHunkStatus::Removed,
10662                DisplayRow(2)..DisplayRow(2)
10663            )],
10664            "Should open hunks that were adjacent to the stale addition one"
10665        );
10666    });
10667}
10668
10669#[gpui::test]
10670async fn test_edits_around_toggled_deletions(
10671    executor: BackgroundExecutor,
10672    cx: &mut gpui::TestAppContext,
10673) {
10674    init_test(cx, |_| {});
10675
10676    let mut cx = EditorTestContext::new(cx).await;
10677
10678    let diff_base = r#"
10679        use some::mod1;
10680        use some::mod2;
10681
10682        const A: u32 = 42;
10683        const B: u32 = 42;
10684        const C: u32 = 42;
10685
10686
10687        fn main() {
10688            println!("hello");
10689
10690            println!("world");
10691        }
10692        "#
10693    .unindent();
10694    executor.run_until_parked();
10695    cx.set_state(
10696        &r#"
10697        use some::mod1;
10698        use some::mod2;
10699
10700        ˇconst B: u32 = 42;
10701        const C: u32 = 42;
10702
10703
10704        fn main() {
10705            println!("hello");
10706
10707            println!("world");
10708        }
10709        "#
10710        .unindent(),
10711    );
10712
10713    cx.set_diff_base(Some(&diff_base));
10714    executor.run_until_parked();
10715    cx.update_editor(|editor, cx| {
10716        let snapshot = editor.snapshot(cx);
10717        let all_hunks = editor_hunks(editor, &snapshot, cx);
10718        assert_eq!(
10719            all_hunks,
10720            vec![(
10721                "const A: u32 = 42;\n".to_string(),
10722                DiffHunkStatus::Removed,
10723                DisplayRow(3)..DisplayRow(3)
10724            )]
10725        );
10726    });
10727    cx.update_editor(|editor, cx| {
10728        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10729    });
10730    executor.run_until_parked();
10731    cx.assert_editor_state(
10732        &r#"
10733        use some::mod1;
10734        use some::mod2;
10735
10736        ˇconst B: u32 = 42;
10737        const C: u32 = 42;
10738
10739
10740        fn main() {
10741            println!("hello");
10742
10743            println!("world");
10744        }
10745        "#
10746        .unindent(),
10747    );
10748    cx.update_editor(|editor, cx| {
10749        let snapshot = editor.snapshot(cx);
10750        let all_hunks = editor_hunks(editor, &snapshot, cx);
10751        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10752        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10753        assert_eq!(
10754            all_hunks,
10755            vec![(
10756                "const A: u32 = 42;\n".to_string(),
10757                DiffHunkStatus::Removed,
10758                DisplayRow(4)..DisplayRow(4)
10759            )]
10760        );
10761        assert_eq!(all_hunks, all_expanded_hunks);
10762    });
10763
10764    cx.update_editor(|editor, cx| {
10765        editor.delete_line(&DeleteLine, cx);
10766    });
10767    executor.run_until_parked();
10768    cx.assert_editor_state(
10769        &r#"
10770        use some::mod1;
10771        use some::mod2;
10772
10773        ˇconst C: u32 = 42;
10774
10775
10776        fn main() {
10777            println!("hello");
10778
10779            println!("world");
10780        }
10781        "#
10782        .unindent(),
10783    );
10784    cx.update_editor(|editor, cx| {
10785        let snapshot = editor.snapshot(cx);
10786        let all_hunks = editor_hunks(editor, &snapshot, cx);
10787        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10788        assert_eq!(
10789            expanded_hunks_background_highlights(editor, cx),
10790            Vec::new(),
10791            "Deleted hunks do not highlight current editor's background"
10792        );
10793        assert_eq!(
10794            all_hunks,
10795            vec![(
10796                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
10797                DiffHunkStatus::Removed,
10798                DisplayRow(5)..DisplayRow(5)
10799            )]
10800        );
10801        assert_eq!(all_hunks, all_expanded_hunks);
10802    });
10803
10804    cx.update_editor(|editor, cx| {
10805        editor.delete_line(&DeleteLine, cx);
10806    });
10807    executor.run_until_parked();
10808    cx.assert_editor_state(
10809        &r#"
10810        use some::mod1;
10811        use some::mod2;
10812
10813        ˇ
10814
10815        fn main() {
10816            println!("hello");
10817
10818            println!("world");
10819        }
10820        "#
10821        .unindent(),
10822    );
10823    cx.update_editor(|editor, cx| {
10824        let snapshot = editor.snapshot(cx);
10825        let all_hunks = editor_hunks(editor, &snapshot, cx);
10826        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10827        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10828        assert_eq!(
10829            all_hunks,
10830            vec![(
10831                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
10832                DiffHunkStatus::Removed,
10833                DisplayRow(6)..DisplayRow(6)
10834            )]
10835        );
10836        assert_eq!(all_hunks, all_expanded_hunks);
10837    });
10838
10839    cx.update_editor(|editor, cx| {
10840        editor.handle_input("replacement", cx);
10841    });
10842    executor.run_until_parked();
10843    cx.assert_editor_state(
10844        &r#"
10845        use some::mod1;
10846        use some::mod2;
10847
10848        replacementˇ
10849
10850        fn main() {
10851            println!("hello");
10852
10853            println!("world");
10854        }
10855        "#
10856        .unindent(),
10857    );
10858    cx.update_editor(|editor, cx| {
10859        let snapshot = editor.snapshot(cx);
10860        let all_hunks = editor_hunks(editor, &snapshot, cx);
10861        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10862        assert_eq!(
10863            all_hunks,
10864            vec![(
10865                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
10866                DiffHunkStatus::Modified,
10867                DisplayRow(7)..DisplayRow(8)
10868            )]
10869        );
10870        assert_eq!(
10871            expanded_hunks_background_highlights(editor, cx),
10872            vec![DisplayRow(7)..=DisplayRow(7)],
10873            "Modified expanded hunks should display additions and highlight their background"
10874        );
10875        assert_eq!(all_hunks, all_expanded_hunks);
10876    });
10877}
10878
10879#[gpui::test]
10880async fn test_edits_around_toggled_modifications(
10881    executor: BackgroundExecutor,
10882    cx: &mut gpui::TestAppContext,
10883) {
10884    init_test(cx, |_| {});
10885
10886    let mut cx = EditorTestContext::new(cx).await;
10887
10888    let diff_base = r#"
10889        use some::mod1;
10890        use some::mod2;
10891
10892        const A: u32 = 42;
10893        const B: u32 = 42;
10894        const C: u32 = 42;
10895        const D: u32 = 42;
10896
10897
10898        fn main() {
10899            println!("hello");
10900
10901            println!("world");
10902        }"#
10903    .unindent();
10904    executor.run_until_parked();
10905    cx.set_state(
10906        &r#"
10907        use some::mod1;
10908        use some::mod2;
10909
10910        const A: u32 = 42;
10911        const B: u32 = 42;
10912        const C: u32 = 43ˇ
10913        const D: u32 = 42;
10914
10915
10916        fn main() {
10917            println!("hello");
10918
10919            println!("world");
10920        }"#
10921        .unindent(),
10922    );
10923
10924    cx.set_diff_base(Some(&diff_base));
10925    executor.run_until_parked();
10926    cx.update_editor(|editor, cx| {
10927        let snapshot = editor.snapshot(cx);
10928        let all_hunks = editor_hunks(editor, &snapshot, cx);
10929        assert_eq!(
10930            all_hunks,
10931            vec![(
10932                "const C: u32 = 42;\n".to_string(),
10933                DiffHunkStatus::Modified,
10934                DisplayRow(5)..DisplayRow(6)
10935            )]
10936        );
10937    });
10938    cx.update_editor(|editor, cx| {
10939        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10940    });
10941    executor.run_until_parked();
10942    cx.assert_editor_state(
10943        &r#"
10944        use some::mod1;
10945        use some::mod2;
10946
10947        const A: u32 = 42;
10948        const B: u32 = 42;
10949        const C: u32 = 43ˇ
10950        const D: u32 = 42;
10951
10952
10953        fn main() {
10954            println!("hello");
10955
10956            println!("world");
10957        }"#
10958        .unindent(),
10959    );
10960    cx.update_editor(|editor, cx| {
10961        let snapshot = editor.snapshot(cx);
10962        let all_hunks = editor_hunks(editor, &snapshot, cx);
10963        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10964        assert_eq!(
10965            expanded_hunks_background_highlights(editor, cx),
10966            vec![DisplayRow(6)..=DisplayRow(6)],
10967        );
10968        assert_eq!(
10969            all_hunks,
10970            vec![(
10971                "const C: u32 = 42;\n".to_string(),
10972                DiffHunkStatus::Modified,
10973                DisplayRow(6)..DisplayRow(7)
10974            )]
10975        );
10976        assert_eq!(all_hunks, all_expanded_hunks);
10977    });
10978
10979    cx.update_editor(|editor, cx| {
10980        editor.handle_input("\nnew_line\n", cx);
10981    });
10982    executor.run_until_parked();
10983    cx.assert_editor_state(
10984        &r#"
10985            use some::mod1;
10986            use some::mod2;
10987
10988            const A: u32 = 42;
10989            const B: u32 = 42;
10990            const C: u32 = 43
10991            new_line
10992            ˇ
10993            const D: u32 = 42;
10994
10995
10996            fn main() {
10997                println!("hello");
10998
10999                println!("world");
11000            }"#
11001        .unindent(),
11002    );
11003    cx.update_editor(|editor, cx| {
11004        let snapshot = editor.snapshot(cx);
11005        let all_hunks = editor_hunks(editor, &snapshot, cx);
11006        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11007        assert_eq!(
11008            expanded_hunks_background_highlights(editor, cx),
11009            vec![DisplayRow(6)..=DisplayRow(6)],
11010            "Modified hunk should grow highlighted lines on more text additions"
11011        );
11012        assert_eq!(
11013            all_hunks,
11014            vec![(
11015                "const C: u32 = 42;\n".to_string(),
11016                DiffHunkStatus::Modified,
11017                DisplayRow(6)..DisplayRow(9)
11018            )]
11019        );
11020        assert_eq!(all_hunks, all_expanded_hunks);
11021    });
11022
11023    cx.update_editor(|editor, cx| {
11024        editor.move_up(&MoveUp, cx);
11025        editor.move_up(&MoveUp, cx);
11026        editor.move_up(&MoveUp, cx);
11027        editor.delete_line(&DeleteLine, cx);
11028    });
11029    executor.run_until_parked();
11030    cx.assert_editor_state(
11031        &r#"
11032            use some::mod1;
11033            use some::mod2;
11034
11035            const A: u32 = 42;
11036            ˇconst C: u32 = 43
11037            new_line
11038
11039            const D: u32 = 42;
11040
11041
11042            fn main() {
11043                println!("hello");
11044
11045                println!("world");
11046            }"#
11047        .unindent(),
11048    );
11049    cx.update_editor(|editor, cx| {
11050        let snapshot = editor.snapshot(cx);
11051        let all_hunks = editor_hunks(editor, &snapshot, cx);
11052        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11053        assert_eq!(
11054            expanded_hunks_background_highlights(editor, cx),
11055            vec![DisplayRow(6)..=DisplayRow(8)],
11056        );
11057        assert_eq!(
11058            all_hunks,
11059            vec![(
11060                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11061                DiffHunkStatus::Modified,
11062                DisplayRow(6)..DisplayRow(9)
11063            )],
11064            "Modified hunk should grow deleted lines on text deletions above"
11065        );
11066        assert_eq!(all_hunks, all_expanded_hunks);
11067    });
11068
11069    cx.update_editor(|editor, cx| {
11070        editor.move_up(&MoveUp, cx);
11071        editor.handle_input("v", cx);
11072    });
11073    executor.run_until_parked();
11074    cx.assert_editor_state(
11075        &r#"
11076            use some::mod1;
11077            use some::mod2;
11078
11079            vˇconst A: u32 = 42;
11080            const C: u32 = 43
11081            new_line
11082
11083            const D: u32 = 42;
11084
11085
11086            fn main() {
11087                println!("hello");
11088
11089                println!("world");
11090            }"#
11091        .unindent(),
11092    );
11093    cx.update_editor(|editor, cx| {
11094        let snapshot = editor.snapshot(cx);
11095        let all_hunks = editor_hunks(editor, &snapshot, cx);
11096        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11097        assert_eq!(
11098            expanded_hunks_background_highlights(editor, cx),
11099            vec![DisplayRow(6)..=DisplayRow(9)],
11100            "Modified hunk should grow deleted lines on text modifications above"
11101        );
11102        assert_eq!(
11103            all_hunks,
11104            vec![(
11105                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11106                DiffHunkStatus::Modified,
11107                DisplayRow(6)..DisplayRow(10)
11108            )]
11109        );
11110        assert_eq!(all_hunks, all_expanded_hunks);
11111    });
11112
11113    cx.update_editor(|editor, cx| {
11114        editor.move_down(&MoveDown, cx);
11115        editor.move_down(&MoveDown, cx);
11116        editor.delete_line(&DeleteLine, cx)
11117    });
11118    executor.run_until_parked();
11119    cx.assert_editor_state(
11120        &r#"
11121            use some::mod1;
11122            use some::mod2;
11123
11124            vconst A: u32 = 42;
11125            const C: u32 = 43
11126            ˇ
11127            const D: u32 = 42;
11128
11129
11130            fn main() {
11131                println!("hello");
11132
11133                println!("world");
11134            }"#
11135        .unindent(),
11136    );
11137    cx.update_editor(|editor, cx| {
11138        let snapshot = editor.snapshot(cx);
11139        let all_hunks = editor_hunks(editor, &snapshot, cx);
11140        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11141        assert_eq!(
11142            expanded_hunks_background_highlights(editor, cx),
11143            vec![DisplayRow(6)..=DisplayRow(8)],
11144            "Modified hunk should grow shrink lines on modification lines removal"
11145        );
11146        assert_eq!(
11147            all_hunks,
11148            vec![(
11149                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11150                DiffHunkStatus::Modified,
11151                DisplayRow(6)..DisplayRow(9)
11152            )]
11153        );
11154        assert_eq!(all_hunks, all_expanded_hunks);
11155    });
11156
11157    cx.update_editor(|editor, cx| {
11158        editor.move_up(&MoveUp, cx);
11159        editor.move_up(&MoveUp, cx);
11160        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
11161        editor.delete_line(&DeleteLine, cx)
11162    });
11163    executor.run_until_parked();
11164    cx.assert_editor_state(
11165        &r#"
11166            use some::mod1;
11167            use some::mod2;
11168
11169            ˇ
11170
11171            fn main() {
11172                println!("hello");
11173
11174                println!("world");
11175            }"#
11176        .unindent(),
11177    );
11178    cx.update_editor(|editor, cx| {
11179        let snapshot = editor.snapshot(cx);
11180        let all_hunks = editor_hunks(editor, &snapshot, cx);
11181        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11182        assert_eq!(
11183            expanded_hunks_background_highlights(editor, cx),
11184            Vec::new(),
11185            "Modified hunk should turn into a removed one on all modified lines removal"
11186        );
11187        assert_eq!(
11188            all_hunks,
11189            vec![(
11190                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
11191                    .to_string(),
11192                DiffHunkStatus::Removed,
11193                DisplayRow(7)..DisplayRow(7)
11194            )]
11195        );
11196        assert_eq!(all_hunks, all_expanded_hunks);
11197    });
11198}
11199
11200#[gpui::test]
11201async fn test_multiple_expanded_hunks_merge(
11202    executor: BackgroundExecutor,
11203    cx: &mut gpui::TestAppContext,
11204) {
11205    init_test(cx, |_| {});
11206
11207    let mut cx = EditorTestContext::new(cx).await;
11208
11209    let diff_base = r#"
11210        use some::mod1;
11211        use some::mod2;
11212
11213        const A: u32 = 42;
11214        const B: u32 = 42;
11215        const C: u32 = 42;
11216        const D: u32 = 42;
11217
11218
11219        fn main() {
11220            println!("hello");
11221
11222            println!("world");
11223        }"#
11224    .unindent();
11225    executor.run_until_parked();
11226    cx.set_state(
11227        &r#"
11228        use some::mod1;
11229        use some::mod2;
11230
11231        const A: u32 = 42;
11232        const B: u32 = 42;
11233        const C: u32 = 43ˇ
11234        const D: u32 = 42;
11235
11236
11237        fn main() {
11238            println!("hello");
11239
11240            println!("world");
11241        }"#
11242        .unindent(),
11243    );
11244
11245    cx.set_diff_base(Some(&diff_base));
11246    executor.run_until_parked();
11247    cx.update_editor(|editor, cx| {
11248        let snapshot = editor.snapshot(cx);
11249        let all_hunks = editor_hunks(editor, &snapshot, cx);
11250        assert_eq!(
11251            all_hunks,
11252            vec![(
11253                "const C: u32 = 42;\n".to_string(),
11254                DiffHunkStatus::Modified,
11255                DisplayRow(5)..DisplayRow(6)
11256            )]
11257        );
11258    });
11259    cx.update_editor(|editor, cx| {
11260        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11261    });
11262    executor.run_until_parked();
11263    cx.assert_editor_state(
11264        &r#"
11265        use some::mod1;
11266        use some::mod2;
11267
11268        const A: u32 = 42;
11269        const B: u32 = 42;
11270        const C: u32 = 43ˇ
11271        const D: u32 = 42;
11272
11273
11274        fn main() {
11275            println!("hello");
11276
11277            println!("world");
11278        }"#
11279        .unindent(),
11280    );
11281    cx.update_editor(|editor, cx| {
11282        let snapshot = editor.snapshot(cx);
11283        let all_hunks = editor_hunks(editor, &snapshot, cx);
11284        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11285        assert_eq!(
11286            expanded_hunks_background_highlights(editor, cx),
11287            vec![DisplayRow(6)..=DisplayRow(6)],
11288        );
11289        assert_eq!(
11290            all_hunks,
11291            vec![(
11292                "const C: u32 = 42;\n".to_string(),
11293                DiffHunkStatus::Modified,
11294                DisplayRow(6)..DisplayRow(7)
11295            )]
11296        );
11297        assert_eq!(all_hunks, all_expanded_hunks);
11298    });
11299
11300    cx.update_editor(|editor, cx| {
11301        editor.handle_input("\nnew_line\n", cx);
11302    });
11303    executor.run_until_parked();
11304    cx.assert_editor_state(
11305        &r#"
11306            use some::mod1;
11307            use some::mod2;
11308
11309            const A: u32 = 42;
11310            const B: u32 = 42;
11311            const C: u32 = 43
11312            new_line
11313            ˇ
11314            const D: u32 = 42;
11315
11316
11317            fn main() {
11318                println!("hello");
11319
11320                println!("world");
11321            }"#
11322        .unindent(),
11323    );
11324}
11325
11326fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11327    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
11328    point..point
11329}
11330
11331fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11332    let (text, ranges) = marked_text_ranges(marked_text, true);
11333    assert_eq!(view.text(cx), text);
11334    assert_eq!(
11335        view.selections.ranges(cx),
11336        ranges,
11337        "Assert selections are {}",
11338        marked_text
11339    );
11340}
11341
11342/// Handle completion request passing a marked string specifying where the completion
11343/// should be triggered from using '|' character, what range should be replaced, and what completions
11344/// should be returned using '<' and '>' to delimit the range
11345pub fn handle_completion_request(
11346    cx: &mut EditorLspTestContext,
11347    marked_string: &str,
11348    completions: Vec<&'static str>,
11349) -> impl Future<Output = ()> {
11350    let complete_from_marker: TextRangeMarker = '|'.into();
11351    let replace_range_marker: TextRangeMarker = ('<', '>').into();
11352    let (_, mut marked_ranges) = marked_text_ranges_by(
11353        marked_string,
11354        vec![complete_from_marker.clone(), replace_range_marker.clone()],
11355    );
11356
11357    let complete_from_position =
11358        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
11359    let replace_range =
11360        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
11361
11362    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
11363        let completions = completions.clone();
11364        async move {
11365            assert_eq!(params.text_document_position.text_document.uri, url.clone());
11366            assert_eq!(
11367                params.text_document_position.position,
11368                complete_from_position
11369            );
11370            Ok(Some(lsp::CompletionResponse::Array(
11371                completions
11372                    .iter()
11373                    .map(|completion_text| lsp::CompletionItem {
11374                        label: completion_text.to_string(),
11375                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11376                            range: replace_range,
11377                            new_text: completion_text.to_string(),
11378                        })),
11379                        ..Default::default()
11380                    })
11381                    .collect(),
11382            )))
11383        }
11384    });
11385
11386    async move {
11387        request.next().await;
11388    }
11389}
11390
11391fn handle_resolve_completion_request(
11392    cx: &mut EditorLspTestContext,
11393    edits: Option<Vec<(&'static str, &'static str)>>,
11394) -> impl Future<Output = ()> {
11395    let edits = edits.map(|edits| {
11396        edits
11397            .iter()
11398            .map(|(marked_string, new_text)| {
11399                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
11400                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
11401                lsp::TextEdit::new(replace_range, new_text.to_string())
11402            })
11403            .collect::<Vec<_>>()
11404    });
11405
11406    let mut request =
11407        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11408            let edits = edits.clone();
11409            async move {
11410                Ok(lsp::CompletionItem {
11411                    additional_text_edits: edits,
11412                    ..Default::default()
11413                })
11414            }
11415        });
11416
11417    async move {
11418        request.next().await;
11419    }
11420}
11421
11422pub(crate) fn update_test_language_settings(
11423    cx: &mut TestAppContext,
11424    f: impl Fn(&mut AllLanguageSettingsContent),
11425) {
11426    _ = cx.update(|cx| {
11427        cx.update_global(|store: &mut SettingsStore, cx| {
11428            store.update_user_settings::<AllLanguageSettings>(cx, f);
11429        });
11430    });
11431}
11432
11433pub(crate) fn update_test_project_settings(
11434    cx: &mut TestAppContext,
11435    f: impl Fn(&mut ProjectSettings),
11436) {
11437    _ = cx.update(|cx| {
11438        cx.update_global(|store: &mut SettingsStore, cx| {
11439            store.update_user_settings::<ProjectSettings>(cx, f);
11440        });
11441    });
11442}
11443
11444pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
11445    _ = cx.update(|cx| {
11446        let store = SettingsStore::test(cx);
11447        cx.set_global(store);
11448        theme::init(theme::LoadThemes::JustBase, cx);
11449        release_channel::init("0.0.0", cx);
11450        client::init_settings(cx);
11451        language::init(cx);
11452        Project::init_settings(cx);
11453        workspace::init_settings(cx);
11454        crate::init(cx);
11455    });
11456
11457    update_test_language_settings(cx, f);
11458}
11459
11460pub(crate) fn rust_lang() -> Arc<Language> {
11461    Arc::new(Language::new(
11462        LanguageConfig {
11463            name: "Rust".into(),
11464            matcher: LanguageMatcher {
11465                path_suffixes: vec!["rs".to_string()],
11466                ..Default::default()
11467            },
11468            ..Default::default()
11469        },
11470        Some(tree_sitter_rust::language()),
11471    ))
11472}
11473
11474#[track_caller]
11475fn assert_hunk_revert(
11476    not_reverted_text_with_selections: &str,
11477    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
11478    expected_reverted_text_with_selections: &str,
11479    base_text: &str,
11480    cx: &mut EditorLspTestContext,
11481) {
11482    cx.set_state(not_reverted_text_with_selections);
11483    cx.update_editor(|editor, cx| {
11484        editor
11485            .buffer()
11486            .read(cx)
11487            .as_singleton()
11488            .unwrap()
11489            .update(cx, |buffer, cx| {
11490                buffer.set_diff_base(Some(base_text.into()), cx);
11491            });
11492    });
11493    cx.executor().run_until_parked();
11494
11495    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
11496        let snapshot = editor.buffer().read(cx).snapshot(cx);
11497        let reverted_hunk_statuses = snapshot
11498            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
11499            .map(|hunk| hunk_status(&hunk))
11500            .collect::<Vec<_>>();
11501
11502        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
11503        reverted_hunk_statuses
11504    });
11505    cx.executor().run_until_parked();
11506    cx.assert_editor_state(expected_reverted_text_with_selections);
11507    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
11508}