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