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