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