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