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), FoldPlaceholder::test()),
  500                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  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), FoldPlaceholder::test()),
  909                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
  910                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
  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), FoldPlaceholder::test()),
 3413                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3414                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 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), FoldPlaceholder::test()),
 3897                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3898                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 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                (
 4554                    Point::new(0, 21)..Point::new(0, 24),
 4555                    FoldPlaceholder::test(),
 4556                ),
 4557                (
 4558                    Point::new(3, 20)..Point::new(3, 22),
 4559                    FoldPlaceholder::test(),
 4560                ),
 4561            ],
 4562            true,
 4563            cx,
 4564        );
 4565        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 4566    });
 4567    assert_eq!(
 4568        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
 4569        &[
 4570            DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 28),
 4571            DisplayPoint::new(DisplayRow(2), 35)..DisplayPoint::new(DisplayRow(2), 7),
 4572            DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(3), 23),
 4573        ]
 4574    );
 4575}
 4576
 4577#[gpui::test]
 4578async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 4579    init_test(cx, |_| {});
 4580
 4581    let language = Arc::new(
 4582        Language::new(
 4583            LanguageConfig {
 4584                brackets: BracketPairConfig {
 4585                    pairs: vec![
 4586                        BracketPair {
 4587                            start: "{".to_string(),
 4588                            end: "}".to_string(),
 4589                            close: false,
 4590                            newline: true,
 4591                        },
 4592                        BracketPair {
 4593                            start: "(".to_string(),
 4594                            end: ")".to_string(),
 4595                            close: false,
 4596                            newline: true,
 4597                        },
 4598                    ],
 4599                    ..Default::default()
 4600                },
 4601                ..Default::default()
 4602            },
 4603            Some(tree_sitter_rust::language()),
 4604        )
 4605        .with_indents_query(
 4606            r#"
 4607                (_ "(" ")" @end) @indent
 4608                (_ "{" "}" @end) @indent
 4609            "#,
 4610        )
 4611        .unwrap(),
 4612    );
 4613
 4614    let text = "fn a() {}";
 4615
 4616    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 4617    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 4618    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 4619    editor
 4620        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 4621        .await;
 4622
 4623    _ = editor.update(cx, |editor, cx| {
 4624        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 4625        editor.newline(&Newline, cx);
 4626        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 4627        assert_eq!(
 4628            editor.selections.ranges(cx),
 4629            &[
 4630                Point::new(1, 4)..Point::new(1, 4),
 4631                Point::new(3, 4)..Point::new(3, 4),
 4632                Point::new(5, 0)..Point::new(5, 0)
 4633            ]
 4634        );
 4635    });
 4636}
 4637
 4638#[gpui::test]
 4639async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
 4640    init_test(cx, |_| {});
 4641
 4642    let mut cx = EditorTestContext::new(cx).await;
 4643
 4644    let language = Arc::new(Language::new(
 4645        LanguageConfig {
 4646            brackets: BracketPairConfig {
 4647                pairs: vec![
 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: true,
 4664                        newline: true,
 4665                    },
 4666                    BracketPair {
 4667                        start: "[".to_string(),
 4668                        end: "]".to_string(),
 4669                        close: false,
 4670                        newline: true,
 4671                    },
 4672                    BracketPair {
 4673                        start: "\"".to_string(),
 4674                        end: "\"".to_string(),
 4675                        close: true,
 4676                        newline: false,
 4677                    },
 4678                ],
 4679                ..Default::default()
 4680            },
 4681            autoclose_before: "})]".to_string(),
 4682            ..Default::default()
 4683        },
 4684        Some(tree_sitter_rust::language()),
 4685    ));
 4686
 4687    cx.language_registry().add(language.clone());
 4688    cx.update_buffer(|buffer, cx| {
 4689        buffer.set_language(Some(language), cx);
 4690    });
 4691
 4692    cx.set_state(
 4693        &r#"
 4694            🏀ˇ
 4695            εˇ
 4696            ❤️ˇ
 4697        "#
 4698        .unindent(),
 4699    );
 4700
 4701    // autoclose multiple nested brackets at multiple cursors
 4702    cx.update_editor(|view, cx| {
 4703        view.handle_input("{", cx);
 4704        view.handle_input("{", cx);
 4705        view.handle_input("{", cx);
 4706    });
 4707    cx.assert_editor_state(
 4708        &"
 4709            🏀{{{ˇ}}}
 4710            ε{{{ˇ}}}
 4711            ❤️{{{ˇ}}}
 4712        "
 4713        .unindent(),
 4714    );
 4715
 4716    // insert a different closing bracket
 4717    cx.update_editor(|view, cx| {
 4718        view.handle_input(")", cx);
 4719    });
 4720    cx.assert_editor_state(
 4721        &"
 4722            🏀{{{)ˇ}}}
 4723            ε{{{)ˇ}}}
 4724            ❤️{{{)ˇ}}}
 4725        "
 4726        .unindent(),
 4727    );
 4728
 4729    // skip over the auto-closed brackets when typing a closing bracket
 4730    cx.update_editor(|view, cx| {
 4731        view.move_right(&MoveRight, cx);
 4732        view.handle_input("}", cx);
 4733        view.handle_input("}", cx);
 4734        view.handle_input("}", cx);
 4735    });
 4736    cx.assert_editor_state(
 4737        &"
 4738            🏀{{{)}}}}ˇ
 4739            ε{{{)}}}}ˇ
 4740            ❤️{{{)}}}}ˇ
 4741        "
 4742        .unindent(),
 4743    );
 4744
 4745    // autoclose multi-character pairs
 4746    cx.set_state(
 4747        &"
 4748            ˇ
 4749            ˇ
 4750        "
 4751        .unindent(),
 4752    );
 4753    cx.update_editor(|view, cx| {
 4754        view.handle_input("/", cx);
 4755        view.handle_input("*", cx);
 4756    });
 4757    cx.assert_editor_state(
 4758        &"
 4759            /*ˇ */
 4760            /*ˇ */
 4761        "
 4762        .unindent(),
 4763    );
 4764
 4765    // one cursor autocloses a multi-character pair, one cursor
 4766    // does not autoclose.
 4767    cx.set_state(
 4768        &"
 4769 4770            ˇ
 4771        "
 4772        .unindent(),
 4773    );
 4774    cx.update_editor(|view, cx| view.handle_input("*", cx));
 4775    cx.assert_editor_state(
 4776        &"
 4777            /*ˇ */
 4778 4779        "
 4780        .unindent(),
 4781    );
 4782
 4783    // Don't autoclose if the next character isn't whitespace and isn't
 4784    // listed in the language's "autoclose_before" section.
 4785    cx.set_state("ˇa b");
 4786    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4787    cx.assert_editor_state("{ˇa b");
 4788
 4789    // Don't autoclose if `close` is false for the bracket pair
 4790    cx.set_state("ˇ");
 4791    cx.update_editor(|view, cx| view.handle_input("[", cx));
 4792    cx.assert_editor_state("");
 4793
 4794    // Surround with brackets if text is selected
 4795    cx.set_state("«aˇ» b");
 4796    cx.update_editor(|view, cx| view.handle_input("{", cx));
 4797    cx.assert_editor_state("{«aˇ»} b");
 4798
 4799    // Autclose pair where the start and end characters are the same
 4800    cx.set_state("");
 4801    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4802    cx.assert_editor_state("a\"ˇ\"");
 4803    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 4804    cx.assert_editor_state("a\"\"ˇ");
 4805}
 4806
 4807#[gpui::test]
 4808async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 4809    init_test(cx, |settings| {
 4810        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 4811    });
 4812
 4813    let mut cx = EditorTestContext::new(cx).await;
 4814
 4815    let language = Arc::new(Language::new(
 4816        LanguageConfig {
 4817            brackets: BracketPairConfig {
 4818                pairs: vec![
 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: true,
 4829                        newline: true,
 4830                    },
 4831                    BracketPair {
 4832                        start: "[".to_string(),
 4833                        end: "]".to_string(),
 4834                        close: false,
 4835                        newline: true,
 4836                    },
 4837                ],
 4838                ..Default::default()
 4839            },
 4840            autoclose_before: "})]".to_string(),
 4841            ..Default::default()
 4842        },
 4843        Some(tree_sitter_rust::language()),
 4844    ));
 4845
 4846    cx.language_registry().add(language.clone());
 4847    cx.update_buffer(|buffer, cx| {
 4848        buffer.set_language(Some(language), cx);
 4849    });
 4850
 4851    cx.set_state(
 4852        &"
 4853            ˇ
 4854            ˇ
 4855            ˇ
 4856        "
 4857        .unindent(),
 4858    );
 4859
 4860    // ensure only matching closing brackets are skipped over
 4861    cx.update_editor(|view, cx| {
 4862        view.handle_input("}", cx);
 4863        view.move_left(&MoveLeft, cx);
 4864        view.handle_input(")", cx);
 4865        view.move_left(&MoveLeft, cx);
 4866    });
 4867    cx.assert_editor_state(
 4868        &"
 4869            ˇ)}
 4870            ˇ)}
 4871            ˇ)}
 4872        "
 4873        .unindent(),
 4874    );
 4875
 4876    // skip-over closing brackets at multiple cursors
 4877    cx.update_editor(|view, cx| {
 4878        view.handle_input(")", cx);
 4879        view.handle_input("}", cx);
 4880    });
 4881    cx.assert_editor_state(
 4882        &"
 4883            )}ˇ
 4884            )}ˇ
 4885            )}ˇ
 4886        "
 4887        .unindent(),
 4888    );
 4889
 4890    // ignore non-close brackets
 4891    cx.update_editor(|view, cx| {
 4892        view.handle_input("]", cx);
 4893        view.move_left(&MoveLeft, cx);
 4894        view.handle_input("]", cx);
 4895    });
 4896    cx.assert_editor_state(
 4897        &"
 4898            )}]ˇ]
 4899            )}]ˇ]
 4900            )}]ˇ]
 4901        "
 4902        .unindent(),
 4903    );
 4904}
 4905
 4906#[gpui::test]
 4907async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 4908    init_test(cx, |_| {});
 4909
 4910    let mut cx = EditorTestContext::new(cx).await;
 4911
 4912    let html_language = Arc::new(
 4913        Language::new(
 4914            LanguageConfig {
 4915                name: "HTML".into(),
 4916                brackets: BracketPairConfig {
 4917                    pairs: vec![
 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                        BracketPair {
 4931                            start: "(".into(),
 4932                            end: ")".into(),
 4933                            close: true,
 4934                            ..Default::default()
 4935                        },
 4936                    ],
 4937                    ..Default::default()
 4938                },
 4939                autoclose_before: "})]>".into(),
 4940                ..Default::default()
 4941            },
 4942            Some(tree_sitter_html::language()),
 4943        )
 4944        .with_injection_query(
 4945            r#"
 4946            (script_element
 4947                (raw_text) @content
 4948                (#set! "language" "javascript"))
 4949            "#,
 4950        )
 4951        .unwrap(),
 4952    );
 4953
 4954    let javascript_language = Arc::new(Language::new(
 4955        LanguageConfig {
 4956            name: "JavaScript".into(),
 4957            brackets: BracketPairConfig {
 4958                pairs: vec![
 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                    BracketPair {
 4972                        start: "(".into(),
 4973                        end: ")".into(),
 4974                        close: true,
 4975                        ..Default::default()
 4976                    },
 4977                ],
 4978                ..Default::default()
 4979            },
 4980            autoclose_before: "})]>".into(),
 4981            ..Default::default()
 4982        },
 4983        Some(tree_sitter_typescript::language_tsx()),
 4984    ));
 4985
 4986    cx.language_registry().add(html_language.clone());
 4987    cx.language_registry().add(javascript_language.clone());
 4988
 4989    cx.update_buffer(|buffer, cx| {
 4990        buffer.set_language(Some(html_language), cx);
 4991    });
 4992
 4993    cx.set_state(
 4994        &r#"
 4995            <body>ˇ
 4996                <script>
 4997                    var x = 1;ˇ
 4998                </script>
 4999            </body>ˇ
 5000        "#
 5001        .unindent(),
 5002    );
 5003
 5004    // Precondition: different languages are active at different locations.
 5005    cx.update_editor(|editor, cx| {
 5006        let snapshot = editor.snapshot(cx);
 5007        let cursors = editor.selections.ranges::<usize>(cx);
 5008        let languages = cursors
 5009            .iter()
 5010            .map(|c| snapshot.language_at(c.start).unwrap().name())
 5011            .collect::<Vec<_>>();
 5012        assert_eq!(
 5013            languages,
 5014            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 5015        );
 5016    });
 5017
 5018    // Angle brackets autoclose in HTML, but not JavaScript.
 5019    cx.update_editor(|editor, cx| {
 5020        editor.handle_input("<", cx);
 5021        editor.handle_input("a", cx);
 5022    });
 5023    cx.assert_editor_state(
 5024        &r#"
 5025            <body><aˇ>
 5026                <script>
 5027                    var x = 1;<aˇ
 5028                </script>
 5029            </body><aˇ>
 5030        "#
 5031        .unindent(),
 5032    );
 5033
 5034    // Curly braces and parens autoclose in both HTML and JavaScript.
 5035    cx.update_editor(|editor, cx| {
 5036        editor.handle_input(" b=", cx);
 5037        editor.handle_input("{", cx);
 5038        editor.handle_input("c", cx);
 5039        editor.handle_input("(", cx);
 5040    });
 5041    cx.assert_editor_state(
 5042        &r#"
 5043            <body><a b={c(ˇ)}>
 5044                <script>
 5045                    var x = 1;<a b={c(ˇ)}
 5046                </script>
 5047            </body><a b={c(ˇ)}>
 5048        "#
 5049        .unindent(),
 5050    );
 5051
 5052    // Brackets that were already autoclosed are skipped.
 5053    cx.update_editor(|editor, cx| {
 5054        editor.handle_input(")", cx);
 5055        editor.handle_input("d", cx);
 5056        editor.handle_input("}", cx);
 5057    });
 5058    cx.assert_editor_state(
 5059        &r#"
 5060            <body><a b={c()d}ˇ>
 5061                <script>
 5062                    var x = 1;<a b={c()d}ˇ
 5063                </script>
 5064            </body><a b={c()d}ˇ>
 5065        "#
 5066        .unindent(),
 5067    );
 5068    cx.update_editor(|editor, cx| {
 5069        editor.handle_input(">", cx);
 5070    });
 5071    cx.assert_editor_state(
 5072        &r#"
 5073            <body><a b={c()d}>ˇ
 5074                <script>
 5075                    var x = 1;<a b={c()d}>ˇ
 5076                </script>
 5077            </body><a b={c()d}>ˇ
 5078        "#
 5079        .unindent(),
 5080    );
 5081
 5082    // Reset
 5083    cx.set_state(
 5084        &r#"
 5085            <body>ˇ
 5086                <script>
 5087                    var x = 1;ˇ
 5088                </script>
 5089            </body>ˇ
 5090        "#
 5091        .unindent(),
 5092    );
 5093
 5094    cx.update_editor(|editor, cx| {
 5095        editor.handle_input("<", cx);
 5096    });
 5097    cx.assert_editor_state(
 5098        &r#"
 5099            <body><ˇ>
 5100                <script>
 5101                    var x = 1;<ˇ
 5102                </script>
 5103            </body><ˇ>
 5104        "#
 5105        .unindent(),
 5106    );
 5107
 5108    // When backspacing, the closing angle brackets are removed.
 5109    cx.update_editor(|editor, cx| {
 5110        editor.backspace(&Backspace, cx);
 5111    });
 5112    cx.assert_editor_state(
 5113        &r#"
 5114            <body>ˇ
 5115                <script>
 5116                    var x = 1;ˇ
 5117                </script>
 5118            </body>ˇ
 5119        "#
 5120        .unindent(),
 5121    );
 5122
 5123    // Block comments autoclose in JavaScript, but not HTML.
 5124    cx.update_editor(|editor, cx| {
 5125        editor.handle_input("/", cx);
 5126        editor.handle_input("*", cx);
 5127    });
 5128    cx.assert_editor_state(
 5129        &r#"
 5130            <body>/*ˇ
 5131                <script>
 5132                    var x = 1;/*ˇ */
 5133                </script>
 5134            </body>/*ˇ
 5135        "#
 5136        .unindent(),
 5137    );
 5138}
 5139
 5140#[gpui::test]
 5141async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 5142    init_test(cx, |_| {});
 5143
 5144    let mut cx = EditorTestContext::new(cx).await;
 5145
 5146    let rust_language = Arc::new(
 5147        Language::new(
 5148            LanguageConfig {
 5149                name: "Rust".into(),
 5150                brackets: serde_json::from_value(json!([
 5151                    { "start": "{", "end": "}", "close": true, "newline": true },
 5152                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 5153                ]))
 5154                .unwrap(),
 5155                autoclose_before: "})]>".into(),
 5156                ..Default::default()
 5157            },
 5158            Some(tree_sitter_rust::language()),
 5159        )
 5160        .with_override_query("(string_literal) @string")
 5161        .unwrap(),
 5162    );
 5163
 5164    cx.language_registry().add(rust_language.clone());
 5165    cx.update_buffer(|buffer, cx| {
 5166        buffer.set_language(Some(rust_language), cx);
 5167    });
 5168
 5169    cx.set_state(
 5170        &r#"
 5171            let x = ˇ
 5172        "#
 5173        .unindent(),
 5174    );
 5175
 5176    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 5177    cx.update_editor(|editor, cx| {
 5178        editor.handle_input("\"", cx);
 5179    });
 5180    cx.assert_editor_state(
 5181        &r#"
 5182            let x = "ˇ"
 5183        "#
 5184        .unindent(),
 5185    );
 5186
 5187    // Inserting another quotation mark. The cursor moves across the existing
 5188    // automatically-inserted quotation mark.
 5189    cx.update_editor(|editor, cx| {
 5190        editor.handle_input("\"", cx);
 5191    });
 5192    cx.assert_editor_state(
 5193        &r#"
 5194            let x = ""ˇ
 5195        "#
 5196        .unindent(),
 5197    );
 5198
 5199    // Reset
 5200    cx.set_state(
 5201        &r#"
 5202            let x = ˇ
 5203        "#
 5204        .unindent(),
 5205    );
 5206
 5207    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 5208    cx.update_editor(|editor, cx| {
 5209        editor.handle_input("\"", cx);
 5210        editor.handle_input(" ", cx);
 5211        editor.move_left(&Default::default(), cx);
 5212        editor.handle_input("\\", cx);
 5213        editor.handle_input("\"", cx);
 5214    });
 5215    cx.assert_editor_state(
 5216        &r#"
 5217            let x = "\"ˇ "
 5218        "#
 5219        .unindent(),
 5220    );
 5221
 5222    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 5223    // mark. Nothing is inserted.
 5224    cx.update_editor(|editor, cx| {
 5225        editor.move_right(&Default::default(), cx);
 5226        editor.handle_input("\"", cx);
 5227    });
 5228    cx.assert_editor_state(
 5229        &r#"
 5230            let x = "\" "ˇ
 5231        "#
 5232        .unindent(),
 5233    );
 5234}
 5235
 5236#[gpui::test]
 5237async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 5238    init_test(cx, |_| {});
 5239
 5240    let language = Arc::new(Language::new(
 5241        LanguageConfig {
 5242            brackets: BracketPairConfig {
 5243                pairs: vec![
 5244                    BracketPair {
 5245                        start: "{".to_string(),
 5246                        end: "}".to_string(),
 5247                        close: true,
 5248                        newline: true,
 5249                    },
 5250                    BracketPair {
 5251                        start: "/* ".to_string(),
 5252                        end: "*/".to_string(),
 5253                        close: true,
 5254                        ..Default::default()
 5255                    },
 5256                ],
 5257                ..Default::default()
 5258            },
 5259            ..Default::default()
 5260        },
 5261        Some(tree_sitter_rust::language()),
 5262    ));
 5263
 5264    let text = r#"
 5265        a
 5266        b
 5267        c
 5268    "#
 5269    .unindent();
 5270
 5271    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5272    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5273    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5274    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5275        .await;
 5276
 5277    _ = view.update(cx, |view, cx| {
 5278        view.change_selections(None, cx, |s| {
 5279            s.select_display_ranges([
 5280                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5281                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5282                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 5283            ])
 5284        });
 5285
 5286        view.handle_input("{", cx);
 5287        view.handle_input("{", cx);
 5288        view.handle_input("{", cx);
 5289        assert_eq!(
 5290            view.text(cx),
 5291            "
 5292                {{{a}}}
 5293                {{{b}}}
 5294                {{{c}}}
 5295            "
 5296            .unindent()
 5297        );
 5298        assert_eq!(
 5299            view.selections.display_ranges(cx),
 5300            [
 5301                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 5302                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 5303                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 5304            ]
 5305        );
 5306
 5307        view.undo(&Undo, cx);
 5308        view.undo(&Undo, cx);
 5309        view.undo(&Undo, cx);
 5310        assert_eq!(
 5311            view.text(cx),
 5312            "
 5313                a
 5314                b
 5315                c
 5316            "
 5317            .unindent()
 5318        );
 5319        assert_eq!(
 5320            view.selections.display_ranges(cx),
 5321            [
 5322                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5323                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5324                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5325            ]
 5326        );
 5327
 5328        // Ensure inserting the first character of a multi-byte bracket pair
 5329        // doesn't surround the selections with the bracket.
 5330        view.handle_input("/", cx);
 5331        assert_eq!(
 5332            view.text(cx),
 5333            "
 5334                /
 5335                /
 5336                /
 5337            "
 5338            .unindent()
 5339        );
 5340        assert_eq!(
 5341            view.selections.display_ranges(cx),
 5342            [
 5343                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5344                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5345                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5346            ]
 5347        );
 5348
 5349        view.undo(&Undo, cx);
 5350        assert_eq!(
 5351            view.text(cx),
 5352            "
 5353                a
 5354                b
 5355                c
 5356            "
 5357            .unindent()
 5358        );
 5359        assert_eq!(
 5360            view.selections.display_ranges(cx),
 5361            [
 5362                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5363                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 5364                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 5365            ]
 5366        );
 5367
 5368        // Ensure inserting the last character of a multi-byte bracket pair
 5369        // doesn't surround the selections with the bracket.
 5370        view.handle_input("*", cx);
 5371        assert_eq!(
 5372            view.text(cx),
 5373            "
 5374                *
 5375                *
 5376                *
 5377            "
 5378            .unindent()
 5379        );
 5380        assert_eq!(
 5381            view.selections.display_ranges(cx),
 5382            [
 5383                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 5384                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 5385                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 5386            ]
 5387        );
 5388    });
 5389}
 5390
 5391#[gpui::test]
 5392async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 5393    init_test(cx, |_| {});
 5394
 5395    let language = Arc::new(Language::new(
 5396        LanguageConfig {
 5397            brackets: BracketPairConfig {
 5398                pairs: vec![BracketPair {
 5399                    start: "{".to_string(),
 5400                    end: "}".to_string(),
 5401                    close: true,
 5402                    newline: true,
 5403                }],
 5404                ..Default::default()
 5405            },
 5406            autoclose_before: "}".to_string(),
 5407            ..Default::default()
 5408        },
 5409        Some(tree_sitter_rust::language()),
 5410    ));
 5411
 5412    let text = r#"
 5413        a
 5414        b
 5415        c
 5416    "#
 5417    .unindent();
 5418
 5419    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5420    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5421    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5422    editor
 5423        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5424        .await;
 5425
 5426    _ = editor.update(cx, |editor, cx| {
 5427        editor.change_selections(None, cx, |s| {
 5428            s.select_ranges([
 5429                Point::new(0, 1)..Point::new(0, 1),
 5430                Point::new(1, 1)..Point::new(1, 1),
 5431                Point::new(2, 1)..Point::new(2, 1),
 5432            ])
 5433        });
 5434
 5435        editor.handle_input("{", cx);
 5436        editor.handle_input("{", cx);
 5437        editor.handle_input("_", cx);
 5438        assert_eq!(
 5439            editor.text(cx),
 5440            "
 5441                a{{_}}
 5442                b{{_}}
 5443                c{{_}}
 5444            "
 5445            .unindent()
 5446        );
 5447        assert_eq!(
 5448            editor.selections.ranges::<Point>(cx),
 5449            [
 5450                Point::new(0, 4)..Point::new(0, 4),
 5451                Point::new(1, 4)..Point::new(1, 4),
 5452                Point::new(2, 4)..Point::new(2, 4)
 5453            ]
 5454        );
 5455
 5456        editor.backspace(&Default::default(), cx);
 5457        editor.backspace(&Default::default(), cx);
 5458        assert_eq!(
 5459            editor.text(cx),
 5460            "
 5461                a{}
 5462                b{}
 5463                c{}
 5464            "
 5465            .unindent()
 5466        );
 5467        assert_eq!(
 5468            editor.selections.ranges::<Point>(cx),
 5469            [
 5470                Point::new(0, 2)..Point::new(0, 2),
 5471                Point::new(1, 2)..Point::new(1, 2),
 5472                Point::new(2, 2)..Point::new(2, 2)
 5473            ]
 5474        );
 5475
 5476        editor.delete_to_previous_word_start(&Default::default(), cx);
 5477        assert_eq!(
 5478            editor.text(cx),
 5479            "
 5480                a
 5481                b
 5482                c
 5483            "
 5484            .unindent()
 5485        );
 5486        assert_eq!(
 5487            editor.selections.ranges::<Point>(cx),
 5488            [
 5489                Point::new(0, 1)..Point::new(0, 1),
 5490                Point::new(1, 1)..Point::new(1, 1),
 5491                Point::new(2, 1)..Point::new(2, 1)
 5492            ]
 5493        );
 5494    });
 5495}
 5496
 5497#[gpui::test]
 5498async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 5499    init_test(cx, |settings| {
 5500        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5501    });
 5502
 5503    let mut cx = EditorTestContext::new(cx).await;
 5504
 5505    let language = Arc::new(Language::new(
 5506        LanguageConfig {
 5507            brackets: BracketPairConfig {
 5508                pairs: vec![
 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: true,
 5519                        newline: true,
 5520                    },
 5521                    BracketPair {
 5522                        start: "[".to_string(),
 5523                        end: "]".to_string(),
 5524                        close: false,
 5525                        newline: true,
 5526                    },
 5527                ],
 5528                ..Default::default()
 5529            },
 5530            autoclose_before: "})]".to_string(),
 5531            ..Default::default()
 5532        },
 5533        Some(tree_sitter_rust::language()),
 5534    ));
 5535
 5536    cx.language_registry().add(language.clone());
 5537    cx.update_buffer(|buffer, cx| {
 5538        buffer.set_language(Some(language), cx);
 5539    });
 5540
 5541    cx.set_state(
 5542        &"
 5543            {(ˇ)}
 5544            [[ˇ]]
 5545            {(ˇ)}
 5546        "
 5547        .unindent(),
 5548    );
 5549
 5550    cx.update_editor(|view, cx| {
 5551        view.backspace(&Default::default(), cx);
 5552        view.backspace(&Default::default(), cx);
 5553    });
 5554
 5555    cx.assert_editor_state(
 5556        &"
 5557            ˇ
 5558            ˇ]]
 5559            ˇ
 5560        "
 5561        .unindent(),
 5562    );
 5563
 5564    cx.update_editor(|view, cx| {
 5565        view.handle_input("{", cx);
 5566        view.handle_input("{", cx);
 5567        view.move_right(&MoveRight, cx);
 5568        view.move_right(&MoveRight, cx);
 5569        view.move_left(&MoveLeft, cx);
 5570        view.move_left(&MoveLeft, cx);
 5571        view.backspace(&Default::default(), cx);
 5572    });
 5573
 5574    cx.assert_editor_state(
 5575        &"
 5576            {ˇ}
 5577            {ˇ}]]
 5578            {ˇ}
 5579        "
 5580        .unindent(),
 5581    );
 5582
 5583    cx.update_editor(|view, cx| {
 5584        view.backspace(&Default::default(), cx);
 5585    });
 5586
 5587    cx.assert_editor_state(
 5588        &"
 5589            ˇ
 5590            ˇ]]
 5591            ˇ
 5592        "
 5593        .unindent(),
 5594    );
 5595}
 5596
 5597#[gpui::test]
 5598async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 5599    init_test(cx, |_| {});
 5600
 5601    let language = Arc::new(Language::new(
 5602        LanguageConfig::default(),
 5603        Some(tree_sitter_rust::language()),
 5604    ));
 5605
 5606    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 5607    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5608    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5609    editor
 5610        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5611        .await;
 5612
 5613    _ = editor.update(cx, |editor, cx| {
 5614        editor.set_auto_replace_emoji_shortcode(true);
 5615
 5616        editor.handle_input("Hello ", cx);
 5617        editor.handle_input(":wave", cx);
 5618        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 5619
 5620        editor.handle_input(":", cx);
 5621        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 5622
 5623        editor.handle_input(" :smile", cx);
 5624        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 5625
 5626        editor.handle_input(":", cx);
 5627        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 5628
 5629        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 5630        editor.handle_input(":wave", cx);
 5631        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 5632
 5633        editor.handle_input(":", cx);
 5634        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 5635
 5636        editor.handle_input(":1", cx);
 5637        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 5638
 5639        editor.handle_input(":", cx);
 5640        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 5641
 5642        // Ensure shortcode does not get replaced when it is part of a word
 5643        editor.handle_input(" Test:wave", cx);
 5644        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 5645
 5646        editor.handle_input(":", cx);
 5647        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 5648
 5649        editor.set_auto_replace_emoji_shortcode(false);
 5650
 5651        // Ensure shortcode does not get replaced when auto replace is off
 5652        editor.handle_input(" :wave", cx);
 5653        assert_eq!(
 5654            editor.text(cx),
 5655            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 5656        );
 5657
 5658        editor.handle_input(":", cx);
 5659        assert_eq!(
 5660            editor.text(cx),
 5661            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 5662        );
 5663    });
 5664}
 5665
 5666#[gpui::test]
 5667async fn test_snippets(cx: &mut gpui::TestAppContext) {
 5668    init_test(cx, |_| {});
 5669
 5670    let (text, insertion_ranges) = marked_text_ranges(
 5671        indoc! {"
 5672            a.ˇ b
 5673            a.ˇ b
 5674            a.ˇ b
 5675        "},
 5676        false,
 5677    );
 5678
 5679    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 5680    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5681
 5682    _ = editor.update(cx, |editor, cx| {
 5683        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 5684
 5685        editor
 5686            .insert_snippet(&insertion_ranges, snippet, cx)
 5687            .unwrap();
 5688
 5689        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 5690            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 5691            assert_eq!(editor.text(cx), expected_text);
 5692            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 5693        }
 5694
 5695        assert(
 5696            editor,
 5697            cx,
 5698            indoc! {"
 5699                a.f(«one», two, «three») b
 5700                a.f(«one», two, «three») b
 5701                a.f(«one», two, «three») b
 5702            "},
 5703        );
 5704
 5705        // Can't move earlier than the first tab stop
 5706        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 5707        assert(
 5708            editor,
 5709            cx,
 5710            indoc! {"
 5711                a.f(«one», two, «three») b
 5712                a.f(«one», two, «three») b
 5713                a.f(«one», two, «three») b
 5714            "},
 5715        );
 5716
 5717        assert!(editor.move_to_next_snippet_tabstop(cx));
 5718        assert(
 5719            editor,
 5720            cx,
 5721            indoc! {"
 5722                a.f(one, «two», three) b
 5723                a.f(one, «two», three) b
 5724                a.f(one, «two», three) b
 5725            "},
 5726        );
 5727
 5728        editor.move_to_prev_snippet_tabstop(cx);
 5729        assert(
 5730            editor,
 5731            cx,
 5732            indoc! {"
 5733                a.f(«one», two, «three») b
 5734                a.f(«one», two, «three») b
 5735                a.f(«one», two, «three») b
 5736            "},
 5737        );
 5738
 5739        assert!(editor.move_to_next_snippet_tabstop(cx));
 5740        assert(
 5741            editor,
 5742            cx,
 5743            indoc! {"
 5744                a.f(one, «two», three) b
 5745                a.f(one, «two», three) b
 5746                a.f(one, «two», three) b
 5747            "},
 5748        );
 5749        assert!(editor.move_to_next_snippet_tabstop(cx));
 5750        assert(
 5751            editor,
 5752            cx,
 5753            indoc! {"
 5754                a.f(one, two, three)ˇ b
 5755                a.f(one, two, three)ˇ b
 5756                a.f(one, two, three)ˇ b
 5757            "},
 5758        );
 5759
 5760        // As soon as the last tab stop is reached, snippet state is gone
 5761        editor.move_to_prev_snippet_tabstop(cx);
 5762        assert(
 5763            editor,
 5764            cx,
 5765            indoc! {"
 5766                a.f(one, two, three)ˇ b
 5767                a.f(one, two, three)ˇ b
 5768                a.f(one, two, three)ˇ b
 5769            "},
 5770        );
 5771    });
 5772}
 5773
 5774#[gpui::test]
 5775async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 5776    init_test(cx, |_| {});
 5777
 5778    let fs = FakeFs::new(cx.executor());
 5779    fs.insert_file("/file.rs", Default::default()).await;
 5780
 5781    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 5782
 5783    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5784    language_registry.add(rust_lang());
 5785    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5786        "Rust",
 5787        FakeLspAdapter {
 5788            capabilities: lsp::ServerCapabilities {
 5789                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5790                ..Default::default()
 5791            },
 5792            ..Default::default()
 5793        },
 5794    );
 5795
 5796    let buffer = project
 5797        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 5798        .await
 5799        .unwrap();
 5800
 5801    cx.executor().start_waiting();
 5802    let fake_server = fake_servers.next().await.unwrap();
 5803
 5804    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5805    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5806    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5807    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5808
 5809    let save = editor
 5810        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5811        .unwrap();
 5812    fake_server
 5813        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5814            assert_eq!(
 5815                params.text_document.uri,
 5816                lsp::Url::from_file_path("/file.rs").unwrap()
 5817            );
 5818            assert_eq!(params.options.tab_size, 4);
 5819            Ok(Some(vec![lsp::TextEdit::new(
 5820                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 5821                ", ".to_string(),
 5822            )]))
 5823        })
 5824        .next()
 5825        .await;
 5826    cx.executor().start_waiting();
 5827    save.await;
 5828
 5829    assert_eq!(
 5830        editor.update(cx, |editor, cx| editor.text(cx)),
 5831        "one, two\nthree\n"
 5832    );
 5833    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5834
 5835    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 5836    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5837
 5838    // Ensure we can still save even if formatting hangs.
 5839    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5840        assert_eq!(
 5841            params.text_document.uri,
 5842            lsp::Url::from_file_path("/file.rs").unwrap()
 5843        );
 5844        futures::future::pending::<()>().await;
 5845        unreachable!()
 5846    });
 5847    let save = editor
 5848        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5849        .unwrap();
 5850    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 5851    cx.executor().start_waiting();
 5852    save.await;
 5853    assert_eq!(
 5854        editor.update(cx, |editor, cx| editor.text(cx)),
 5855        "one\ntwo\nthree\n"
 5856    );
 5857    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 5858
 5859    // For non-dirty buffer, no formatting request should be sent
 5860    let save = editor
 5861        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5862        .unwrap();
 5863    let _pending_format_request = fake_server
 5864        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 5865            panic!("Should not be invoked on non-dirty buffer");
 5866        })
 5867        .next();
 5868    cx.executor().start_waiting();
 5869    save.await;
 5870
 5871    // Set rust language override and assert overridden tabsize is sent to language server
 5872    update_test_language_settings(cx, |settings| {
 5873        settings.languages.insert(
 5874            "Rust".into(),
 5875            LanguageSettingsContent {
 5876                tab_size: NonZeroU32::new(8),
 5877                ..Default::default()
 5878            },
 5879        );
 5880    });
 5881
 5882    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 5883    assert!(cx.read(|cx| editor.is_dirty(cx)));
 5884    let save = editor
 5885        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 5886        .unwrap();
 5887    fake_server
 5888        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 5889            assert_eq!(
 5890                params.text_document.uri,
 5891                lsp::Url::from_file_path("/file.rs").unwrap()
 5892            );
 5893            assert_eq!(params.options.tab_size, 8);
 5894            Ok(Some(vec![]))
 5895        })
 5896        .next()
 5897        .await;
 5898    cx.executor().start_waiting();
 5899    save.await;
 5900}
 5901
 5902#[gpui::test]
 5903async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 5904    init_test(cx, |_| {});
 5905
 5906    let cols = 4;
 5907    let rows = 10;
 5908    let sample_text_1 = sample_text(rows, cols, 'a');
 5909    assert_eq!(
 5910        sample_text_1,
 5911        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 5912    );
 5913    let sample_text_2 = sample_text(rows, cols, 'l');
 5914    assert_eq!(
 5915        sample_text_2,
 5916        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 5917    );
 5918    let sample_text_3 = sample_text(rows, cols, 'v');
 5919    assert_eq!(
 5920        sample_text_3,
 5921        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 5922    );
 5923
 5924    let fs = FakeFs::new(cx.executor());
 5925    fs.insert_tree(
 5926        "/a",
 5927        json!({
 5928            "main.rs": sample_text_1,
 5929            "other.rs": sample_text_2,
 5930            "lib.rs": sample_text_3,
 5931        }),
 5932    )
 5933    .await;
 5934
 5935    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 5936    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 5937    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 5938
 5939    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 5940    language_registry.add(rust_lang());
 5941    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 5942        "Rust",
 5943        FakeLspAdapter {
 5944            capabilities: lsp::ServerCapabilities {
 5945                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5946                ..Default::default()
 5947            },
 5948            ..Default::default()
 5949        },
 5950    );
 5951
 5952    let worktree = project.update(cx, |project, _| {
 5953        let mut worktrees = project.worktrees().collect::<Vec<_>>();
 5954        assert_eq!(worktrees.len(), 1);
 5955        worktrees.pop().unwrap()
 5956    });
 5957    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 5958
 5959    let buffer_1 = project
 5960        .update(cx, |project, cx| {
 5961            project.open_buffer((worktree_id, "main.rs"), cx)
 5962        })
 5963        .await
 5964        .unwrap();
 5965    let buffer_2 = project
 5966        .update(cx, |project, cx| {
 5967            project.open_buffer((worktree_id, "other.rs"), cx)
 5968        })
 5969        .await
 5970        .unwrap();
 5971    let buffer_3 = project
 5972        .update(cx, |project, cx| {
 5973            project.open_buffer((worktree_id, "lib.rs"), cx)
 5974        })
 5975        .await
 5976        .unwrap();
 5977
 5978    let multi_buffer = cx.new_model(|cx| {
 5979        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
 5980        multi_buffer.push_excerpts(
 5981            buffer_1.clone(),
 5982            [
 5983                ExcerptRange {
 5984                    context: Point::new(0, 0)..Point::new(3, 0),
 5985                    primary: None,
 5986                },
 5987                ExcerptRange {
 5988                    context: Point::new(5, 0)..Point::new(7, 0),
 5989                    primary: None,
 5990                },
 5991                ExcerptRange {
 5992                    context: Point::new(9, 0)..Point::new(10, 4),
 5993                    primary: None,
 5994                },
 5995            ],
 5996            cx,
 5997        );
 5998        multi_buffer.push_excerpts(
 5999            buffer_2.clone(),
 6000            [
 6001                ExcerptRange {
 6002                    context: Point::new(0, 0)..Point::new(3, 0),
 6003                    primary: None,
 6004                },
 6005                ExcerptRange {
 6006                    context: Point::new(5, 0)..Point::new(7, 0),
 6007                    primary: None,
 6008                },
 6009                ExcerptRange {
 6010                    context: Point::new(9, 0)..Point::new(10, 4),
 6011                    primary: None,
 6012                },
 6013            ],
 6014            cx,
 6015        );
 6016        multi_buffer.push_excerpts(
 6017            buffer_3.clone(),
 6018            [
 6019                ExcerptRange {
 6020                    context: Point::new(0, 0)..Point::new(3, 0),
 6021                    primary: None,
 6022                },
 6023                ExcerptRange {
 6024                    context: Point::new(5, 0)..Point::new(7, 0),
 6025                    primary: None,
 6026                },
 6027                ExcerptRange {
 6028                    context: Point::new(9, 0)..Point::new(10, 4),
 6029                    primary: None,
 6030                },
 6031            ],
 6032            cx,
 6033        );
 6034        multi_buffer
 6035    });
 6036    let multi_buffer_editor =
 6037        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 6038
 6039    multi_buffer_editor.update(cx, |editor, cx| {
 6040        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 6041        editor.insert("|one|two|three|", cx);
 6042    });
 6043    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6044    multi_buffer_editor.update(cx, |editor, cx| {
 6045        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 6046            s.select_ranges(Some(60..70))
 6047        });
 6048        editor.insert("|four|five|six|", cx);
 6049    });
 6050    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 6051
 6052    // First two buffers should be edited, but not the third one.
 6053    assert_eq!(
 6054        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6055        "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}",
 6056    );
 6057    buffer_1.update(cx, |buffer, _| {
 6058        assert!(buffer.is_dirty());
 6059        assert_eq!(
 6060            buffer.text(),
 6061            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 6062        )
 6063    });
 6064    buffer_2.update(cx, |buffer, _| {
 6065        assert!(buffer.is_dirty());
 6066        assert_eq!(
 6067            buffer.text(),
 6068            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 6069        )
 6070    });
 6071    buffer_3.update(cx, |buffer, _| {
 6072        assert!(!buffer.is_dirty());
 6073        assert_eq!(buffer.text(), sample_text_3,)
 6074    });
 6075
 6076    cx.executor().start_waiting();
 6077    let save = multi_buffer_editor
 6078        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6079        .unwrap();
 6080
 6081    let fake_server = fake_servers.next().await.unwrap();
 6082    fake_server
 6083        .server
 6084        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6085            Ok(Some(vec![lsp::TextEdit::new(
 6086                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6087                format!("[{} formatted]", params.text_document.uri),
 6088            )]))
 6089        })
 6090        .detach();
 6091    save.await;
 6092
 6093    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 6094    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 6095    assert_eq!(
 6096        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 6097        "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}",
 6098    );
 6099    buffer_1.update(cx, |buffer, _| {
 6100        assert!(!buffer.is_dirty());
 6101        assert_eq!(
 6102            buffer.text(),
 6103            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 6104        )
 6105    });
 6106    buffer_2.update(cx, |buffer, _| {
 6107        assert!(!buffer.is_dirty());
 6108        assert_eq!(
 6109            buffer.text(),
 6110            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 6111        )
 6112    });
 6113    buffer_3.update(cx, |buffer, _| {
 6114        assert!(!buffer.is_dirty());
 6115        assert_eq!(buffer.text(), sample_text_3,)
 6116    });
 6117}
 6118
 6119#[gpui::test]
 6120async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 6121    init_test(cx, |_| {});
 6122
 6123    let fs = FakeFs::new(cx.executor());
 6124    fs.insert_file("/file.rs", Default::default()).await;
 6125
 6126    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6127
 6128    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6129    language_registry.add(rust_lang());
 6130    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6131        "Rust",
 6132        FakeLspAdapter {
 6133            capabilities: lsp::ServerCapabilities {
 6134                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 6135                ..Default::default()
 6136            },
 6137            ..Default::default()
 6138        },
 6139    );
 6140
 6141    let buffer = project
 6142        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6143        .await
 6144        .unwrap();
 6145
 6146    cx.executor().start_waiting();
 6147    let fake_server = fake_servers.next().await.unwrap();
 6148
 6149    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6150    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6151    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6152    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6153
 6154    let save = editor
 6155        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6156        .unwrap();
 6157    fake_server
 6158        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6159            assert_eq!(
 6160                params.text_document.uri,
 6161                lsp::Url::from_file_path("/file.rs").unwrap()
 6162            );
 6163            assert_eq!(params.options.tab_size, 4);
 6164            Ok(Some(vec![lsp::TextEdit::new(
 6165                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6166                ", ".to_string(),
 6167            )]))
 6168        })
 6169        .next()
 6170        .await;
 6171    cx.executor().start_waiting();
 6172    save.await;
 6173    assert_eq!(
 6174        editor.update(cx, |editor, cx| editor.text(cx)),
 6175        "one, two\nthree\n"
 6176    );
 6177    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6178
 6179    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6180    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6181
 6182    // Ensure we can still save even if formatting hangs.
 6183    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 6184        move |params, _| async move {
 6185            assert_eq!(
 6186                params.text_document.uri,
 6187                lsp::Url::from_file_path("/file.rs").unwrap()
 6188            );
 6189            futures::future::pending::<()>().await;
 6190            unreachable!()
 6191        },
 6192    );
 6193    let save = editor
 6194        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6195        .unwrap();
 6196    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6197    cx.executor().start_waiting();
 6198    save.await;
 6199    assert_eq!(
 6200        editor.update(cx, |editor, cx| editor.text(cx)),
 6201        "one\ntwo\nthree\n"
 6202    );
 6203    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6204
 6205    // For non-dirty buffer, no formatting request should be sent
 6206    let save = editor
 6207        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6208        .unwrap();
 6209    let _pending_format_request = fake_server
 6210        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6211            panic!("Should not be invoked on non-dirty buffer");
 6212        })
 6213        .next();
 6214    cx.executor().start_waiting();
 6215    save.await;
 6216
 6217    // Set Rust language override and assert overridden tabsize is sent to language server
 6218    update_test_language_settings(cx, |settings| {
 6219        settings.languages.insert(
 6220            "Rust".into(),
 6221            LanguageSettingsContent {
 6222                tab_size: NonZeroU32::new(8),
 6223                ..Default::default()
 6224            },
 6225        );
 6226    });
 6227
 6228    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6229    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6230    let save = editor
 6231        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6232        .unwrap();
 6233    fake_server
 6234        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 6235            assert_eq!(
 6236                params.text_document.uri,
 6237                lsp::Url::from_file_path("/file.rs").unwrap()
 6238            );
 6239            assert_eq!(params.options.tab_size, 8);
 6240            Ok(Some(vec![]))
 6241        })
 6242        .next()
 6243        .await;
 6244    cx.executor().start_waiting();
 6245    save.await;
 6246}
 6247
 6248#[gpui::test]
 6249async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 6250    init_test(cx, |settings| {
 6251        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
 6252    });
 6253
 6254    let fs = FakeFs::new(cx.executor());
 6255    fs.insert_file("/file.rs", Default::default()).await;
 6256
 6257    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6258
 6259    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6260    language_registry.add(Arc::new(Language::new(
 6261        LanguageConfig {
 6262            name: "Rust".into(),
 6263            matcher: LanguageMatcher {
 6264                path_suffixes: vec!["rs".to_string()],
 6265                ..Default::default()
 6266            },
 6267            ..LanguageConfig::default()
 6268        },
 6269        Some(tree_sitter_rust::language()),
 6270    )));
 6271    update_test_language_settings(cx, |settings| {
 6272        // Enable Prettier formatting for the same buffer, and ensure
 6273        // LSP is called instead of Prettier.
 6274        settings.defaults.prettier = Some(PrettierSettings {
 6275            allowed: true,
 6276            ..PrettierSettings::default()
 6277        });
 6278    });
 6279    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 6280        "Rust",
 6281        FakeLspAdapter {
 6282            capabilities: lsp::ServerCapabilities {
 6283                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6284                ..Default::default()
 6285            },
 6286            ..Default::default()
 6287        },
 6288    );
 6289
 6290    let buffer = project
 6291        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6292        .await
 6293        .unwrap();
 6294
 6295    cx.executor().start_waiting();
 6296    let fake_server = fake_servers.next().await.unwrap();
 6297
 6298    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6299    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6300    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6301
 6302    let format = editor
 6303        .update(cx, |editor, cx| {
 6304            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 6305        })
 6306        .unwrap();
 6307    fake_server
 6308        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6309            assert_eq!(
 6310                params.text_document.uri,
 6311                lsp::Url::from_file_path("/file.rs").unwrap()
 6312            );
 6313            assert_eq!(params.options.tab_size, 4);
 6314            Ok(Some(vec![lsp::TextEdit::new(
 6315                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6316                ", ".to_string(),
 6317            )]))
 6318        })
 6319        .next()
 6320        .await;
 6321    cx.executor().start_waiting();
 6322    format.await;
 6323    assert_eq!(
 6324        editor.update(cx, |editor, cx| editor.text(cx)),
 6325        "one, two\nthree\n"
 6326    );
 6327
 6328    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6329    // Ensure we don't lock if formatting hangs.
 6330    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6331        assert_eq!(
 6332            params.text_document.uri,
 6333            lsp::Url::from_file_path("/file.rs").unwrap()
 6334        );
 6335        futures::future::pending::<()>().await;
 6336        unreachable!()
 6337    });
 6338    let format = editor
 6339        .update(cx, |editor, cx| {
 6340            editor.perform_format(project, FormatTrigger::Manual, cx)
 6341        })
 6342        .unwrap();
 6343    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6344    cx.executor().start_waiting();
 6345    format.await;
 6346    assert_eq!(
 6347        editor.update(cx, |editor, cx| editor.text(cx)),
 6348        "one\ntwo\nthree\n"
 6349    );
 6350}
 6351
 6352#[gpui::test]
 6353async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 6354    init_test(cx, |_| {});
 6355
 6356    let mut cx = EditorLspTestContext::new_rust(
 6357        lsp::ServerCapabilities {
 6358            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6359            ..Default::default()
 6360        },
 6361        cx,
 6362    )
 6363    .await;
 6364
 6365    cx.set_state(indoc! {"
 6366        one.twoˇ
 6367    "});
 6368
 6369    // The format request takes a long time. When it completes, it inserts
 6370    // a newline and an indent before the `.`
 6371    cx.lsp
 6372        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 6373            let executor = cx.background_executor().clone();
 6374            async move {
 6375                executor.timer(Duration::from_millis(100)).await;
 6376                Ok(Some(vec![lsp::TextEdit {
 6377                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 6378                    new_text: "\n    ".into(),
 6379                }]))
 6380            }
 6381        });
 6382
 6383    // Submit a format request.
 6384    let format_1 = cx
 6385        .update_editor(|editor, cx| editor.format(&Format, cx))
 6386        .unwrap();
 6387    cx.executor().run_until_parked();
 6388
 6389    // Submit a second format request.
 6390    let format_2 = cx
 6391        .update_editor(|editor, cx| editor.format(&Format, cx))
 6392        .unwrap();
 6393    cx.executor().run_until_parked();
 6394
 6395    // Wait for both format requests to complete
 6396    cx.executor().advance_clock(Duration::from_millis(200));
 6397    cx.executor().start_waiting();
 6398    format_1.await.unwrap();
 6399    cx.executor().start_waiting();
 6400    format_2.await.unwrap();
 6401
 6402    // The formatting edits only happens once.
 6403    cx.assert_editor_state(indoc! {"
 6404        one
 6405            .twoˇ
 6406    "});
 6407}
 6408
 6409#[gpui::test]
 6410async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 6411    init_test(cx, |settings| {
 6412        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 6413    });
 6414
 6415    let mut cx = EditorLspTestContext::new_rust(
 6416        lsp::ServerCapabilities {
 6417            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6418            ..Default::default()
 6419        },
 6420        cx,
 6421    )
 6422    .await;
 6423
 6424    // Set up a buffer white some trailing whitespace and no trailing newline.
 6425    cx.set_state(
 6426        &[
 6427            "one ",   //
 6428            "twoˇ",   //
 6429            "three ", //
 6430            "four",   //
 6431        ]
 6432        .join("\n"),
 6433    );
 6434
 6435    // Submit a format request.
 6436    let format = cx
 6437        .update_editor(|editor, cx| editor.format(&Format, cx))
 6438        .unwrap();
 6439
 6440    // Record which buffer changes have been sent to the language server
 6441    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 6442    cx.lsp
 6443        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 6444            let buffer_changes = buffer_changes.clone();
 6445            move |params, _| {
 6446                buffer_changes.lock().extend(
 6447                    params
 6448                        .content_changes
 6449                        .into_iter()
 6450                        .map(|e| (e.range.unwrap(), e.text)),
 6451                );
 6452            }
 6453        });
 6454
 6455    // Handle formatting requests to the language server.
 6456    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 6457        let buffer_changes = buffer_changes.clone();
 6458        move |_, _| {
 6459            // When formatting is requested, trailing whitespace has already been stripped,
 6460            // and the trailing newline has already been added.
 6461            assert_eq!(
 6462                &buffer_changes.lock()[1..],
 6463                &[
 6464                    (
 6465                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 6466                        "".into()
 6467                    ),
 6468                    (
 6469                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 6470                        "".into()
 6471                    ),
 6472                    (
 6473                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 6474                        "\n".into()
 6475                    ),
 6476                ]
 6477            );
 6478
 6479            // Insert blank lines between each line of the buffer.
 6480            async move {
 6481                Ok(Some(vec![
 6482                    lsp::TextEdit {
 6483                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 6484                        new_text: "\n".into(),
 6485                    },
 6486                    lsp::TextEdit {
 6487                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 6488                        new_text: "\n".into(),
 6489                    },
 6490                ]))
 6491            }
 6492        }
 6493    });
 6494
 6495    // After formatting the buffer, the trailing whitespace is stripped,
 6496    // a newline is appended, and the edits provided by the language server
 6497    // have been applied.
 6498    format.await.unwrap();
 6499    cx.assert_editor_state(
 6500        &[
 6501            "one",   //
 6502            "",      //
 6503            "twoˇ",  //
 6504            "",      //
 6505            "three", //
 6506            "four",  //
 6507            "",      //
 6508        ]
 6509        .join("\n"),
 6510    );
 6511
 6512    // Undoing the formatting undoes the trailing whitespace removal, the
 6513    // trailing newline, and the LSP edits.
 6514    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 6515    cx.assert_editor_state(
 6516        &[
 6517            "one ",   //
 6518            "twoˇ",   //
 6519            "three ", //
 6520            "four",   //
 6521        ]
 6522        .join("\n"),
 6523    );
 6524}
 6525
 6526#[gpui::test]
 6527async fn test_completion(cx: &mut gpui::TestAppContext) {
 6528    init_test(cx, |_| {});
 6529
 6530    let mut cx = EditorLspTestContext::new_rust(
 6531        lsp::ServerCapabilities {
 6532            completion_provider: Some(lsp::CompletionOptions {
 6533                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 6534                resolve_provider: Some(true),
 6535                ..Default::default()
 6536            }),
 6537            ..Default::default()
 6538        },
 6539        cx,
 6540    )
 6541    .await;
 6542    let counter = Arc::new(AtomicUsize::new(0));
 6543
 6544    cx.set_state(indoc! {"
 6545        oneˇ
 6546        two
 6547        three
 6548    "});
 6549    cx.simulate_keystroke(".");
 6550    handle_completion_request(
 6551        &mut cx,
 6552        indoc! {"
 6553            one.|<>
 6554            two
 6555            three
 6556        "},
 6557        vec!["first_completion", "second_completion"],
 6558        counter.clone(),
 6559    )
 6560    .await;
 6561    cx.condition(|editor, _| editor.context_menu_visible())
 6562        .await;
 6563    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 6564
 6565    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6566        editor.context_menu_next(&Default::default(), cx);
 6567        editor
 6568            .confirm_completion(&ConfirmCompletion::default(), cx)
 6569            .unwrap()
 6570    });
 6571    cx.assert_editor_state(indoc! {"
 6572        one.second_completionˇ
 6573        two
 6574        three
 6575    "});
 6576
 6577    handle_resolve_completion_request(
 6578        &mut cx,
 6579        Some(vec![
 6580            (
 6581                //This overlaps with the primary completion edit which is
 6582                //misbehavior from the LSP spec, test that we filter it out
 6583                indoc! {"
 6584                    one.second_ˇcompletion
 6585                    two
 6586                    threeˇ
 6587                "},
 6588                "overlapping additional edit",
 6589            ),
 6590            (
 6591                indoc! {"
 6592                    one.second_completion
 6593                    two
 6594                    threeˇ
 6595                "},
 6596                "\nadditional edit",
 6597            ),
 6598        ]),
 6599    )
 6600    .await;
 6601    apply_additional_edits.await.unwrap();
 6602    cx.assert_editor_state(indoc! {"
 6603        one.second_completionˇ
 6604        two
 6605        three
 6606        additional edit
 6607    "});
 6608
 6609    cx.set_state(indoc! {"
 6610        one.second_completion
 6611        twoˇ
 6612        threeˇ
 6613        additional edit
 6614    "});
 6615    cx.simulate_keystroke(" ");
 6616    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6617    cx.simulate_keystroke("s");
 6618    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6619
 6620    cx.assert_editor_state(indoc! {"
 6621        one.second_completion
 6622        two sˇ
 6623        three sˇ
 6624        additional edit
 6625    "});
 6626    handle_completion_request(
 6627        &mut cx,
 6628        indoc! {"
 6629            one.second_completion
 6630            two s
 6631            three <s|>
 6632            additional edit
 6633        "},
 6634        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6635        counter.clone(),
 6636    )
 6637    .await;
 6638    cx.condition(|editor, _| editor.context_menu_visible())
 6639        .await;
 6640    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 6641
 6642    cx.simulate_keystroke("i");
 6643
 6644    handle_completion_request(
 6645        &mut cx,
 6646        indoc! {"
 6647            one.second_completion
 6648            two si
 6649            three <si|>
 6650            additional edit
 6651        "},
 6652        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 6653        counter.clone(),
 6654    )
 6655    .await;
 6656    cx.condition(|editor, _| editor.context_menu_visible())
 6657        .await;
 6658    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 6659
 6660    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6661        editor
 6662            .confirm_completion(&ConfirmCompletion::default(), cx)
 6663            .unwrap()
 6664    });
 6665    cx.assert_editor_state(indoc! {"
 6666        one.second_completion
 6667        two sixth_completionˇ
 6668        three sixth_completionˇ
 6669        additional edit
 6670    "});
 6671
 6672    handle_resolve_completion_request(&mut cx, None).await;
 6673    apply_additional_edits.await.unwrap();
 6674
 6675    _ = cx.update(|cx| {
 6676        cx.update_global::<SettingsStore, _>(|settings, cx| {
 6677            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 6678                settings.show_completions_on_input = Some(false);
 6679            });
 6680        })
 6681    });
 6682    cx.set_state("editorˇ");
 6683    cx.simulate_keystroke(".");
 6684    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6685    cx.simulate_keystroke("c");
 6686    cx.simulate_keystroke("l");
 6687    cx.simulate_keystroke("o");
 6688    cx.assert_editor_state("editor.cloˇ");
 6689    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
 6690    cx.update_editor(|editor, cx| {
 6691        editor.show_completions(&ShowCompletions, cx);
 6692    });
 6693    handle_completion_request(
 6694        &mut cx,
 6695        "editor.<clo|>",
 6696        vec!["close", "clobber"],
 6697        counter.clone(),
 6698    )
 6699    .await;
 6700    cx.condition(|editor, _| editor.context_menu_visible())
 6701        .await;
 6702    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 6703
 6704    let apply_additional_edits = cx.update_editor(|editor, cx| {
 6705        editor
 6706            .confirm_completion(&ConfirmCompletion::default(), cx)
 6707            .unwrap()
 6708    });
 6709    cx.assert_editor_state("editor.closeˇ");
 6710    handle_resolve_completion_request(&mut cx, None).await;
 6711    apply_additional_edits.await.unwrap();
 6712}
 6713
 6714#[gpui::test]
 6715async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 6716    init_test(cx, |_| {});
 6717
 6718    let mut cx = EditorLspTestContext::new_rust(
 6719        lsp::ServerCapabilities {
 6720            completion_provider: Some(lsp::CompletionOptions {
 6721                trigger_characters: Some(vec![".".to_string()]),
 6722                resolve_provider: Some(true),
 6723                ..Default::default()
 6724            }),
 6725            ..Default::default()
 6726        },
 6727        cx,
 6728    )
 6729    .await;
 6730
 6731    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 6732    cx.simulate_keystroke(".");
 6733    let completion_item = lsp::CompletionItem {
 6734        label: "Some".into(),
 6735        kind: Some(lsp::CompletionItemKind::SNIPPET),
 6736        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 6737        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 6738            kind: lsp::MarkupKind::Markdown,
 6739            value: "```rust\nSome(2)\n```".to_string(),
 6740        })),
 6741        deprecated: Some(false),
 6742        sort_text: Some("Some".to_string()),
 6743        filter_text: Some("Some".to_string()),
 6744        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 6745        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 6746            range: lsp::Range {
 6747                start: lsp::Position {
 6748                    line: 0,
 6749                    character: 22,
 6750                },
 6751                end: lsp::Position {
 6752                    line: 0,
 6753                    character: 22,
 6754                },
 6755            },
 6756            new_text: "Some(2)".to_string(),
 6757        })),
 6758        additional_text_edits: Some(vec![lsp::TextEdit {
 6759            range: lsp::Range {
 6760                start: lsp::Position {
 6761                    line: 0,
 6762                    character: 20,
 6763                },
 6764                end: lsp::Position {
 6765                    line: 0,
 6766                    character: 22,
 6767                },
 6768            },
 6769            new_text: "".to_string(),
 6770        }]),
 6771        ..Default::default()
 6772    };
 6773
 6774    let closure_completion_item = completion_item.clone();
 6775    let counter = Arc::new(AtomicUsize::new(0));
 6776    let counter_clone = counter.clone();
 6777    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 6778        let task_completion_item = closure_completion_item.clone();
 6779        counter_clone.fetch_add(1, atomic::Ordering::Release);
 6780        async move {
 6781            Ok(Some(lsp::CompletionResponse::Array(vec![
 6782                task_completion_item,
 6783            ])))
 6784        }
 6785    });
 6786
 6787    cx.condition(|editor, _| editor.context_menu_visible())
 6788        .await;
 6789    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 6790    assert!(request.next().await.is_some());
 6791    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 6792
 6793    cx.simulate_keystroke("S");
 6794    cx.simulate_keystroke("o");
 6795    cx.simulate_keystroke("m");
 6796    cx.condition(|editor, _| editor.context_menu_visible())
 6797        .await;
 6798    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 6799    assert!(request.next().await.is_some());
 6800    assert!(request.next().await.is_some());
 6801    assert!(request.next().await.is_some());
 6802    request.close();
 6803    assert!(request.next().await.is_none());
 6804    assert_eq!(
 6805        counter.load(atomic::Ordering::Acquire),
 6806        4,
 6807        "With the completions menu open, only one LSP request should happen per input"
 6808    );
 6809}
 6810
 6811#[gpui::test]
 6812async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 6813    init_test(cx, |_| {});
 6814    let mut cx = EditorTestContext::new(cx).await;
 6815    let language = Arc::new(Language::new(
 6816        LanguageConfig {
 6817            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 6818            ..Default::default()
 6819        },
 6820        Some(tree_sitter_rust::language()),
 6821    ));
 6822    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 6823
 6824    // If multiple selections intersect a line, the line is only toggled once.
 6825    cx.set_state(indoc! {"
 6826        fn a() {
 6827            «//b();
 6828            ˇ»// «c();
 6829            //ˇ»  d();
 6830        }
 6831    "});
 6832
 6833    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6834
 6835    cx.assert_editor_state(indoc! {"
 6836        fn a() {
 6837            «b();
 6838            c();
 6839            ˇ» d();
 6840        }
 6841    "});
 6842
 6843    // The comment prefix is inserted at the same column for every line in a
 6844    // selection.
 6845    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6846
 6847    cx.assert_editor_state(indoc! {"
 6848        fn a() {
 6849            // «b();
 6850            // c();
 6851            ˇ»//  d();
 6852        }
 6853    "});
 6854
 6855    // If a selection ends at the beginning of a line, that line is not toggled.
 6856    cx.set_selections_state(indoc! {"
 6857        fn a() {
 6858            // b();
 6859            «// c();
 6860        ˇ»    //  d();
 6861        }
 6862    "});
 6863
 6864    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6865
 6866    cx.assert_editor_state(indoc! {"
 6867        fn a() {
 6868            // b();
 6869            «c();
 6870        ˇ»    //  d();
 6871        }
 6872    "});
 6873
 6874    // If a selection span a single line and is empty, the line is toggled.
 6875    cx.set_state(indoc! {"
 6876        fn a() {
 6877            a();
 6878            b();
 6879        ˇ
 6880        }
 6881    "});
 6882
 6883    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6884
 6885    cx.assert_editor_state(indoc! {"
 6886        fn a() {
 6887            a();
 6888            b();
 6889        //•ˇ
 6890        }
 6891    "});
 6892
 6893    // If a selection span multiple lines, empty lines are not toggled.
 6894    cx.set_state(indoc! {"
 6895        fn a() {
 6896            «a();
 6897
 6898            c();ˇ»
 6899        }
 6900    "});
 6901
 6902    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6903
 6904    cx.assert_editor_state(indoc! {"
 6905        fn a() {
 6906            // «a();
 6907
 6908            // c();ˇ»
 6909        }
 6910    "});
 6911
 6912    // If a selection includes multiple comment prefixes, all lines are uncommented.
 6913    cx.set_state(indoc! {"
 6914        fn a() {
 6915            «// a();
 6916            /// b();
 6917            //! c();ˇ»
 6918        }
 6919    "});
 6920
 6921    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 6922
 6923    cx.assert_editor_state(indoc! {"
 6924        fn a() {
 6925            «a();
 6926            b();
 6927            c();ˇ»
 6928        }
 6929    "});
 6930}
 6931
 6932#[gpui::test]
 6933async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 6934    init_test(cx, |_| {});
 6935
 6936    let language = Arc::new(Language::new(
 6937        LanguageConfig {
 6938            line_comments: vec!["// ".into()],
 6939            ..Default::default()
 6940        },
 6941        Some(tree_sitter_rust::language()),
 6942    ));
 6943
 6944    let mut cx = EditorTestContext::new(cx).await;
 6945
 6946    cx.language_registry().add(language.clone());
 6947    cx.update_buffer(|buffer, cx| {
 6948        buffer.set_language(Some(language), cx);
 6949    });
 6950
 6951    let toggle_comments = &ToggleComments {
 6952        advance_downwards: true,
 6953    };
 6954
 6955    // Single cursor on one line -> advance
 6956    // Cursor moves horizontally 3 characters as well on non-blank line
 6957    cx.set_state(indoc!(
 6958        "fn a() {
 6959             ˇdog();
 6960             cat();
 6961        }"
 6962    ));
 6963    cx.update_editor(|editor, cx| {
 6964        editor.toggle_comments(toggle_comments, cx);
 6965    });
 6966    cx.assert_editor_state(indoc!(
 6967        "fn a() {
 6968             // dog();
 6969             catˇ();
 6970        }"
 6971    ));
 6972
 6973    // Single selection on one line -> don't advance
 6974    cx.set_state(indoc!(
 6975        "fn a() {
 6976             «dog()ˇ»;
 6977             cat();
 6978        }"
 6979    ));
 6980    cx.update_editor(|editor, cx| {
 6981        editor.toggle_comments(toggle_comments, cx);
 6982    });
 6983    cx.assert_editor_state(indoc!(
 6984        "fn a() {
 6985             // «dog()ˇ»;
 6986             cat();
 6987        }"
 6988    ));
 6989
 6990    // Multiple cursors on one line -> advance
 6991    cx.set_state(indoc!(
 6992        "fn a() {
 6993             ˇdˇog();
 6994             cat();
 6995        }"
 6996    ));
 6997    cx.update_editor(|editor, cx| {
 6998        editor.toggle_comments(toggle_comments, cx);
 6999    });
 7000    cx.assert_editor_state(indoc!(
 7001        "fn a() {
 7002             // dog();
 7003             catˇ(ˇ);
 7004        }"
 7005    ));
 7006
 7007    // Multiple cursors on one line, with selection -> don't advance
 7008    cx.set_state(indoc!(
 7009        "fn a() {
 7010             ˇdˇog«()ˇ»;
 7011             cat();
 7012        }"
 7013    ));
 7014    cx.update_editor(|editor, cx| {
 7015        editor.toggle_comments(toggle_comments, cx);
 7016    });
 7017    cx.assert_editor_state(indoc!(
 7018        "fn a() {
 7019             // ˇdˇog«()ˇ»;
 7020             cat();
 7021        }"
 7022    ));
 7023
 7024    // Single cursor on one line -> advance
 7025    // Cursor moves to column 0 on blank line
 7026    cx.set_state(indoc!(
 7027        "fn a() {
 7028             ˇdog();
 7029
 7030             cat();
 7031        }"
 7032    ));
 7033    cx.update_editor(|editor, cx| {
 7034        editor.toggle_comments(toggle_comments, cx);
 7035    });
 7036    cx.assert_editor_state(indoc!(
 7037        "fn a() {
 7038             // dog();
 7039        ˇ
 7040             cat();
 7041        }"
 7042    ));
 7043
 7044    // Single cursor on one line -> advance
 7045    // Cursor starts and ends at column 0
 7046    cx.set_state(indoc!(
 7047        "fn a() {
 7048         ˇ    dog();
 7049             cat();
 7050        }"
 7051    ));
 7052    cx.update_editor(|editor, cx| {
 7053        editor.toggle_comments(toggle_comments, cx);
 7054    });
 7055    cx.assert_editor_state(indoc!(
 7056        "fn a() {
 7057             // dog();
 7058         ˇ    cat();
 7059        }"
 7060    ));
 7061}
 7062
 7063#[gpui::test]
 7064async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 7065    init_test(cx, |_| {});
 7066
 7067    let mut cx = EditorTestContext::new(cx).await;
 7068
 7069    let html_language = Arc::new(
 7070        Language::new(
 7071            LanguageConfig {
 7072                name: "HTML".into(),
 7073                block_comment: Some(("<!-- ".into(), " -->".into())),
 7074                ..Default::default()
 7075            },
 7076            Some(tree_sitter_html::language()),
 7077        )
 7078        .with_injection_query(
 7079            r#"
 7080            (script_element
 7081                (raw_text) @content
 7082                (#set! "language" "javascript"))
 7083            "#,
 7084        )
 7085        .unwrap(),
 7086    );
 7087
 7088    let javascript_language = Arc::new(Language::new(
 7089        LanguageConfig {
 7090            name: "JavaScript".into(),
 7091            line_comments: vec!["// ".into()],
 7092            ..Default::default()
 7093        },
 7094        Some(tree_sitter_typescript::language_tsx()),
 7095    ));
 7096
 7097    cx.language_registry().add(html_language.clone());
 7098    cx.language_registry().add(javascript_language.clone());
 7099    cx.update_buffer(|buffer, cx| {
 7100        buffer.set_language(Some(html_language), cx);
 7101    });
 7102
 7103    // Toggle comments for empty selections
 7104    cx.set_state(
 7105        &r#"
 7106            <p>A</p>ˇ
 7107            <p>B</p>ˇ
 7108            <p>C</p>ˇ
 7109        "#
 7110        .unindent(),
 7111    );
 7112    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7113    cx.assert_editor_state(
 7114        &r#"
 7115            <!-- <p>A</p>ˇ -->
 7116            <!-- <p>B</p>ˇ -->
 7117            <!-- <p>C</p>ˇ -->
 7118        "#
 7119        .unindent(),
 7120    );
 7121    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7122    cx.assert_editor_state(
 7123        &r#"
 7124            <p>A</p>ˇ
 7125            <p>B</p>ˇ
 7126            <p>C</p>ˇ
 7127        "#
 7128        .unindent(),
 7129    );
 7130
 7131    // Toggle comments for mixture of empty and non-empty selections, where
 7132    // multiple selections occupy a given line.
 7133    cx.set_state(
 7134        &r#"
 7135            <p>A«</p>
 7136            <p>ˇ»B</p>ˇ
 7137            <p>C«</p>
 7138            <p>ˇ»D</p>ˇ
 7139        "#
 7140        .unindent(),
 7141    );
 7142
 7143    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7144    cx.assert_editor_state(
 7145        &r#"
 7146            <!-- <p>A«</p>
 7147            <p>ˇ»B</p>ˇ -->
 7148            <!-- <p>C«</p>
 7149            <p>ˇ»D</p>ˇ -->
 7150        "#
 7151        .unindent(),
 7152    );
 7153    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7154    cx.assert_editor_state(
 7155        &r#"
 7156            <p>A«</p>
 7157            <p>ˇ»B</p>ˇ
 7158            <p>C«</p>
 7159            <p>ˇ»D</p>ˇ
 7160        "#
 7161        .unindent(),
 7162    );
 7163
 7164    // Toggle comments when different languages are active for different
 7165    // selections.
 7166    cx.set_state(
 7167        &r#"
 7168            ˇ<script>
 7169                ˇvar x = new Y();
 7170            ˇ</script>
 7171        "#
 7172        .unindent(),
 7173    );
 7174    cx.executor().run_until_parked();
 7175    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 7176    cx.assert_editor_state(
 7177        &r#"
 7178            <!-- ˇ<script> -->
 7179                // ˇvar x = new Y();
 7180            <!-- ˇ</script> -->
 7181        "#
 7182        .unindent(),
 7183    );
 7184}
 7185
 7186#[gpui::test]
 7187fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 7188    init_test(cx, |_| {});
 7189
 7190    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7191    let multibuffer = cx.new_model(|cx| {
 7192        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7193        multibuffer.push_excerpts(
 7194            buffer.clone(),
 7195            [
 7196                ExcerptRange {
 7197                    context: Point::new(0, 0)..Point::new(0, 4),
 7198                    primary: None,
 7199                },
 7200                ExcerptRange {
 7201                    context: Point::new(1, 0)..Point::new(1, 4),
 7202                    primary: None,
 7203                },
 7204            ],
 7205            cx,
 7206        );
 7207        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 7208        multibuffer
 7209    });
 7210
 7211    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7212    _ = view.update(cx, |view, cx| {
 7213        assert_eq!(view.text(cx), "aaaa\nbbbb");
 7214        view.change_selections(None, cx, |s| {
 7215            s.select_ranges([
 7216                Point::new(0, 0)..Point::new(0, 0),
 7217                Point::new(1, 0)..Point::new(1, 0),
 7218            ])
 7219        });
 7220
 7221        view.handle_input("X", cx);
 7222        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 7223        assert_eq!(
 7224            view.selections.ranges(cx),
 7225            [
 7226                Point::new(0, 1)..Point::new(0, 1),
 7227                Point::new(1, 1)..Point::new(1, 1),
 7228            ]
 7229        );
 7230
 7231        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 7232        view.change_selections(None, cx, |s| {
 7233            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 7234        });
 7235        view.backspace(&Default::default(), cx);
 7236        assert_eq!(view.text(cx), "Xa\nbbb");
 7237        assert_eq!(
 7238            view.selections.ranges(cx),
 7239            [Point::new(1, 0)..Point::new(1, 0)]
 7240        );
 7241
 7242        view.change_selections(None, cx, |s| {
 7243            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 7244        });
 7245        view.backspace(&Default::default(), cx);
 7246        assert_eq!(view.text(cx), "X\nbb");
 7247        assert_eq!(
 7248            view.selections.ranges(cx),
 7249            [Point::new(0, 1)..Point::new(0, 1)]
 7250        );
 7251    });
 7252}
 7253
 7254#[gpui::test]
 7255fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 7256    init_test(cx, |_| {});
 7257
 7258    let markers = vec![('[', ']').into(), ('(', ')').into()];
 7259    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 7260        indoc! {"
 7261            [aaaa
 7262            (bbbb]
 7263            cccc)",
 7264        },
 7265        markers.clone(),
 7266    );
 7267    let excerpt_ranges = markers.into_iter().map(|marker| {
 7268        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 7269        ExcerptRange {
 7270            context,
 7271            primary: None,
 7272        }
 7273    });
 7274    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 7275    let multibuffer = cx.new_model(|cx| {
 7276        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7277        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 7278        multibuffer
 7279    });
 7280
 7281    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 7282    _ = view.update(cx, |view, cx| {
 7283        let (expected_text, selection_ranges) = marked_text_ranges(
 7284            indoc! {"
 7285                aaaa
 7286                bˇbbb
 7287                bˇbbˇb
 7288                cccc"
 7289            },
 7290            true,
 7291        );
 7292        assert_eq!(view.text(cx), expected_text);
 7293        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 7294
 7295        view.handle_input("X", cx);
 7296
 7297        let (expected_text, expected_selections) = marked_text_ranges(
 7298            indoc! {"
 7299                aaaa
 7300                bXˇbbXb
 7301                bXˇbbXˇb
 7302                cccc"
 7303            },
 7304            false,
 7305        );
 7306        assert_eq!(view.text(cx), expected_text);
 7307        assert_eq!(view.selections.ranges(cx), expected_selections);
 7308
 7309        view.newline(&Newline, cx);
 7310        let (expected_text, expected_selections) = marked_text_ranges(
 7311            indoc! {"
 7312                aaaa
 7313                bX
 7314                ˇbbX
 7315                b
 7316                bX
 7317                ˇbbX
 7318                ˇb
 7319                cccc"
 7320            },
 7321            false,
 7322        );
 7323        assert_eq!(view.text(cx), expected_text);
 7324        assert_eq!(view.selections.ranges(cx), expected_selections);
 7325    });
 7326}
 7327
 7328#[gpui::test]
 7329fn test_refresh_selections(cx: &mut TestAppContext) {
 7330    init_test(cx, |_| {});
 7331
 7332    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7333    let mut excerpt1_id = None;
 7334    let multibuffer = cx.new_model(|cx| {
 7335        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7336        excerpt1_id = multibuffer
 7337            .push_excerpts(
 7338                buffer.clone(),
 7339                [
 7340                    ExcerptRange {
 7341                        context: Point::new(0, 0)..Point::new(1, 4),
 7342                        primary: None,
 7343                    },
 7344                    ExcerptRange {
 7345                        context: Point::new(1, 0)..Point::new(2, 4),
 7346                        primary: None,
 7347                    },
 7348                ],
 7349                cx,
 7350            )
 7351            .into_iter()
 7352            .next();
 7353        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7354        multibuffer
 7355    });
 7356
 7357    let editor = cx.add_window(|cx| {
 7358        let mut editor = build_editor(multibuffer.clone(), cx);
 7359        let snapshot = editor.snapshot(cx);
 7360        editor.change_selections(None, cx, |s| {
 7361            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 7362        });
 7363        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 7364        assert_eq!(
 7365            editor.selections.ranges(cx),
 7366            [
 7367                Point::new(1, 3)..Point::new(1, 3),
 7368                Point::new(2, 1)..Point::new(2, 1),
 7369            ]
 7370        );
 7371        editor
 7372    });
 7373
 7374    // Refreshing selections is a no-op when excerpts haven't changed.
 7375    _ = editor.update(cx, |editor, cx| {
 7376        editor.change_selections(None, cx, |s| s.refresh());
 7377        assert_eq!(
 7378            editor.selections.ranges(cx),
 7379            [
 7380                Point::new(1, 3)..Point::new(1, 3),
 7381                Point::new(2, 1)..Point::new(2, 1),
 7382            ]
 7383        );
 7384    });
 7385
 7386    _ = multibuffer.update(cx, |multibuffer, cx| {
 7387        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7388    });
 7389    _ = editor.update(cx, |editor, cx| {
 7390        // Removing an excerpt causes the first selection to become degenerate.
 7391        assert_eq!(
 7392            editor.selections.ranges(cx),
 7393            [
 7394                Point::new(0, 0)..Point::new(0, 0),
 7395                Point::new(0, 1)..Point::new(0, 1)
 7396            ]
 7397        );
 7398
 7399        // Refreshing selections will relocate the first selection to the original buffer
 7400        // location.
 7401        editor.change_selections(None, cx, |s| s.refresh());
 7402        assert_eq!(
 7403            editor.selections.ranges(cx),
 7404            [
 7405                Point::new(0, 1)..Point::new(0, 1),
 7406                Point::new(0, 3)..Point::new(0, 3)
 7407            ]
 7408        );
 7409        assert!(editor.selections.pending_anchor().is_some());
 7410    });
 7411}
 7412
 7413#[gpui::test]
 7414fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 7415    init_test(cx, |_| {});
 7416
 7417    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 7418    let mut excerpt1_id = None;
 7419    let multibuffer = cx.new_model(|cx| {
 7420        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 7421        excerpt1_id = multibuffer
 7422            .push_excerpts(
 7423                buffer.clone(),
 7424                [
 7425                    ExcerptRange {
 7426                        context: Point::new(0, 0)..Point::new(1, 4),
 7427                        primary: None,
 7428                    },
 7429                    ExcerptRange {
 7430                        context: Point::new(1, 0)..Point::new(2, 4),
 7431                        primary: None,
 7432                    },
 7433                ],
 7434                cx,
 7435            )
 7436            .into_iter()
 7437            .next();
 7438        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 7439        multibuffer
 7440    });
 7441
 7442    let editor = cx.add_window(|cx| {
 7443        let mut editor = build_editor(multibuffer.clone(), cx);
 7444        let snapshot = editor.snapshot(cx);
 7445        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 7446        assert_eq!(
 7447            editor.selections.ranges(cx),
 7448            [Point::new(1, 3)..Point::new(1, 3)]
 7449        );
 7450        editor
 7451    });
 7452
 7453    _ = multibuffer.update(cx, |multibuffer, cx| {
 7454        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 7455    });
 7456    _ = editor.update(cx, |editor, cx| {
 7457        assert_eq!(
 7458            editor.selections.ranges(cx),
 7459            [Point::new(0, 0)..Point::new(0, 0)]
 7460        );
 7461
 7462        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 7463        editor.change_selections(None, cx, |s| s.refresh());
 7464        assert_eq!(
 7465            editor.selections.ranges(cx),
 7466            [Point::new(0, 3)..Point::new(0, 3)]
 7467        );
 7468        assert!(editor.selections.pending_anchor().is_some());
 7469    });
 7470}
 7471
 7472#[gpui::test]
 7473async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 7474    init_test(cx, |_| {});
 7475
 7476    let language = Arc::new(
 7477        Language::new(
 7478            LanguageConfig {
 7479                brackets: BracketPairConfig {
 7480                    pairs: vec![
 7481                        BracketPair {
 7482                            start: "{".to_string(),
 7483                            end: "}".to_string(),
 7484                            close: true,
 7485                            newline: true,
 7486                        },
 7487                        BracketPair {
 7488                            start: "/* ".to_string(),
 7489                            end: " */".to_string(),
 7490                            close: true,
 7491                            newline: true,
 7492                        },
 7493                    ],
 7494                    ..Default::default()
 7495                },
 7496                ..Default::default()
 7497            },
 7498            Some(tree_sitter_rust::language()),
 7499        )
 7500        .with_indents_query("")
 7501        .unwrap(),
 7502    );
 7503
 7504    let text = concat!(
 7505        "{   }\n",     //
 7506        "  x\n",       //
 7507        "  /*   */\n", //
 7508        "x\n",         //
 7509        "{{} }\n",     //
 7510    );
 7511
 7512    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 7513    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7514    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 7515    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 7516        .await;
 7517
 7518    _ = view.update(cx, |view, cx| {
 7519        view.change_selections(None, cx, |s| {
 7520            s.select_display_ranges([
 7521                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 7522                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 7523                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 7524            ])
 7525        });
 7526        view.newline(&Newline, cx);
 7527
 7528        assert_eq!(
 7529            view.buffer().read(cx).read(cx).text(),
 7530            concat!(
 7531                "{ \n",    // Suppress rustfmt
 7532                "\n",      //
 7533                "}\n",     //
 7534                "  x\n",   //
 7535                "  /* \n", //
 7536                "  \n",    //
 7537                "  */\n",  //
 7538                "x\n",     //
 7539                "{{} \n",  //
 7540                "}\n",     //
 7541            )
 7542        );
 7543    });
 7544}
 7545
 7546#[gpui::test]
 7547fn test_highlighted_ranges(cx: &mut TestAppContext) {
 7548    init_test(cx, |_| {});
 7549
 7550    let editor = cx.add_window(|cx| {
 7551        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 7552        build_editor(buffer.clone(), cx)
 7553    });
 7554
 7555    _ = editor.update(cx, |editor, cx| {
 7556        struct Type1;
 7557        struct Type2;
 7558
 7559        let buffer = editor.buffer.read(cx).snapshot(cx);
 7560
 7561        let anchor_range =
 7562            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 7563
 7564        editor.highlight_background::<Type1>(
 7565            &[
 7566                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 7567                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 7568                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 7569                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 7570            ],
 7571            |_| Hsla::red(),
 7572            cx,
 7573        );
 7574        editor.highlight_background::<Type2>(
 7575            &[
 7576                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 7577                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 7578                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 7579                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 7580            ],
 7581            |_| Hsla::green(),
 7582            cx,
 7583        );
 7584
 7585        let snapshot = editor.snapshot(cx);
 7586        let mut highlighted_ranges = editor.background_highlights_in_range(
 7587            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 7588            &snapshot,
 7589            cx.theme().colors(),
 7590        );
 7591        // Enforce a consistent ordering based on color without relying on the ordering of the
 7592        // highlight's `TypeId` which is non-executor.
 7593        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 7594        assert_eq!(
 7595            highlighted_ranges,
 7596            &[
 7597                (
 7598                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 7599                    Hsla::red(),
 7600                ),
 7601                (
 7602                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7603                    Hsla::red(),
 7604                ),
 7605                (
 7606                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 7607                    Hsla::green(),
 7608                ),
 7609                (
 7610                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 7611                    Hsla::green(),
 7612                ),
 7613            ]
 7614        );
 7615        assert_eq!(
 7616            editor.background_highlights_in_range(
 7617                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 7618                &snapshot,
 7619                cx.theme().colors(),
 7620            ),
 7621            &[(
 7622                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 7623                Hsla::red(),
 7624            )]
 7625        );
 7626    });
 7627}
 7628
 7629#[gpui::test]
 7630async fn test_following(cx: &mut gpui::TestAppContext) {
 7631    init_test(cx, |_| {});
 7632
 7633    let fs = FakeFs::new(cx.executor());
 7634    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7635
 7636    let buffer = project.update(cx, |project, cx| {
 7637        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 7638        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 7639    });
 7640    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 7641    let follower = cx.update(|cx| {
 7642        cx.open_window(
 7643            WindowOptions {
 7644                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 7645                    gpui::Point::new(0.into(), 0.into()),
 7646                    gpui::Point::new(10.into(), 80.into()),
 7647                ))),
 7648                ..Default::default()
 7649            },
 7650            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 7651        )
 7652    });
 7653
 7654    let is_still_following = Rc::new(RefCell::new(true));
 7655    let follower_edit_event_count = Rc::new(RefCell::new(0));
 7656    let pending_update = Rc::new(RefCell::new(None));
 7657    _ = follower.update(cx, {
 7658        let update = pending_update.clone();
 7659        let is_still_following = is_still_following.clone();
 7660        let follower_edit_event_count = follower_edit_event_count.clone();
 7661        |_, cx| {
 7662            cx.subscribe(
 7663                &leader.root_view(cx).unwrap(),
 7664                move |_, leader, event, cx| {
 7665                    leader
 7666                        .read(cx)
 7667                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7668                },
 7669            )
 7670            .detach();
 7671
 7672            cx.subscribe(
 7673                &follower.root_view(cx).unwrap(),
 7674                move |_, _, event: &EditorEvent, _cx| {
 7675                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 7676                        *is_still_following.borrow_mut() = false;
 7677                    }
 7678
 7679                    if let EditorEvent::BufferEdited = event {
 7680                        *follower_edit_event_count.borrow_mut() += 1;
 7681                    }
 7682                },
 7683            )
 7684            .detach();
 7685        }
 7686    });
 7687
 7688    // Update the selections only
 7689    _ = leader.update(cx, |leader, cx| {
 7690        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7691    });
 7692    follower
 7693        .update(cx, |follower, cx| {
 7694            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7695        })
 7696        .unwrap()
 7697        .await
 7698        .unwrap();
 7699    _ = follower.update(cx, |follower, cx| {
 7700        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 7701    });
 7702    assert_eq!(*is_still_following.borrow(), true);
 7703    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7704
 7705    // Update the scroll position only
 7706    _ = leader.update(cx, |leader, cx| {
 7707        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7708    });
 7709    follower
 7710        .update(cx, |follower, cx| {
 7711            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7712        })
 7713        .unwrap()
 7714        .await
 7715        .unwrap();
 7716    assert_eq!(
 7717        follower
 7718            .update(cx, |follower, cx| follower.scroll_position(cx))
 7719            .unwrap(),
 7720        gpui::Point::new(1.5, 3.5)
 7721    );
 7722    assert_eq!(*is_still_following.borrow(), true);
 7723    assert_eq!(*follower_edit_event_count.borrow(), 0);
 7724
 7725    // Update the selections and scroll position. The follower's scroll position is updated
 7726    // via autoscroll, not via the leader's exact scroll position.
 7727    _ = leader.update(cx, |leader, cx| {
 7728        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 7729        leader.request_autoscroll(Autoscroll::newest(), cx);
 7730        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 7731    });
 7732    follower
 7733        .update(cx, |follower, cx| {
 7734            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7735        })
 7736        .unwrap()
 7737        .await
 7738        .unwrap();
 7739    _ = follower.update(cx, |follower, cx| {
 7740        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 7741        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 7742    });
 7743    assert_eq!(*is_still_following.borrow(), true);
 7744
 7745    // Creating a pending selection that precedes another selection
 7746    _ = leader.update(cx, |leader, cx| {
 7747        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 7748        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 7749    });
 7750    follower
 7751        .update(cx, |follower, cx| {
 7752            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7753        })
 7754        .unwrap()
 7755        .await
 7756        .unwrap();
 7757    _ = follower.update(cx, |follower, cx| {
 7758        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 7759    });
 7760    assert_eq!(*is_still_following.borrow(), true);
 7761
 7762    // Extend the pending selection so that it surrounds another selection
 7763    _ = leader.update(cx, |leader, cx| {
 7764        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 7765    });
 7766    follower
 7767        .update(cx, |follower, cx| {
 7768            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 7769        })
 7770        .unwrap()
 7771        .await
 7772        .unwrap();
 7773    _ = follower.update(cx, |follower, cx| {
 7774        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
 7775    });
 7776
 7777    // Scrolling locally breaks the follow
 7778    _ = follower.update(cx, |follower, cx| {
 7779        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
 7780        follower.set_scroll_anchor(
 7781            ScrollAnchor {
 7782                anchor: top_anchor,
 7783                offset: gpui::Point::new(0.0, 0.5),
 7784            },
 7785            cx,
 7786        );
 7787    });
 7788    assert_eq!(*is_still_following.borrow(), false);
 7789}
 7790
 7791#[gpui::test]
 7792async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
 7793    init_test(cx, |_| {});
 7794
 7795    let fs = FakeFs::new(cx.executor());
 7796    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7797    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7798    let pane = workspace
 7799        .update(cx, |workspace, _| workspace.active_pane().clone())
 7800        .unwrap();
 7801
 7802    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7803
 7804    let leader = pane.update(cx, |_, cx| {
 7805        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
 7806        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
 7807    });
 7808
 7809    // Start following the editor when it has no excerpts.
 7810    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7811    let follower_1 = cx
 7812        .update_window(*workspace.deref(), |_, cx| {
 7813            Editor::from_state_proto(
 7814                pane.clone(),
 7815                workspace.root_view(cx).unwrap(),
 7816                ViewId {
 7817                    creator: Default::default(),
 7818                    id: 0,
 7819                },
 7820                &mut state_message,
 7821                cx,
 7822            )
 7823        })
 7824        .unwrap()
 7825        .unwrap()
 7826        .await
 7827        .unwrap();
 7828
 7829    let update_message = Rc::new(RefCell::new(None));
 7830    follower_1.update(cx, {
 7831        let update = update_message.clone();
 7832        |_, cx| {
 7833            cx.subscribe(&leader, move |_, leader, event, cx| {
 7834                leader
 7835                    .read(cx)
 7836                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 7837            })
 7838            .detach();
 7839        }
 7840    });
 7841
 7842    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
 7843        (
 7844            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
 7845            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
 7846        )
 7847    });
 7848
 7849    // Insert some excerpts.
 7850    _ = leader.update(cx, |leader, cx| {
 7851        leader.buffer.update(cx, |multibuffer, cx| {
 7852            let excerpt_ids = multibuffer.push_excerpts(
 7853                buffer_1.clone(),
 7854                [
 7855                    ExcerptRange {
 7856                        context: 1..6,
 7857                        primary: None,
 7858                    },
 7859                    ExcerptRange {
 7860                        context: 12..15,
 7861                        primary: None,
 7862                    },
 7863                    ExcerptRange {
 7864                        context: 0..3,
 7865                        primary: None,
 7866                    },
 7867                ],
 7868                cx,
 7869            );
 7870            multibuffer.insert_excerpts_after(
 7871                excerpt_ids[0],
 7872                buffer_2.clone(),
 7873                [
 7874                    ExcerptRange {
 7875                        context: 8..12,
 7876                        primary: None,
 7877                    },
 7878                    ExcerptRange {
 7879                        context: 0..6,
 7880                        primary: None,
 7881                    },
 7882                ],
 7883                cx,
 7884            );
 7885        });
 7886    });
 7887
 7888    // Apply the update of adding the excerpts.
 7889    follower_1
 7890        .update(cx, |follower, cx| {
 7891            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7892        })
 7893        .await
 7894        .unwrap();
 7895    assert_eq!(
 7896        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7897        leader.update(cx, |editor, cx| editor.text(cx))
 7898    );
 7899    update_message.borrow_mut().take();
 7900
 7901    // Start following separately after it already has excerpts.
 7902    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
 7903    let follower_2 = cx
 7904        .update_window(*workspace.deref(), |_, cx| {
 7905            Editor::from_state_proto(
 7906                pane.clone(),
 7907                workspace.root_view(cx).unwrap().clone(),
 7908                ViewId {
 7909                    creator: Default::default(),
 7910                    id: 0,
 7911                },
 7912                &mut state_message,
 7913                cx,
 7914            )
 7915        })
 7916        .unwrap()
 7917        .unwrap()
 7918        .await
 7919        .unwrap();
 7920    assert_eq!(
 7921        follower_2.update(cx, |editor, cx| editor.text(cx)),
 7922        leader.update(cx, |editor, cx| editor.text(cx))
 7923    );
 7924
 7925    // Remove some excerpts.
 7926    _ = leader.update(cx, |leader, cx| {
 7927        leader.buffer.update(cx, |multibuffer, cx| {
 7928            let excerpt_ids = multibuffer.excerpt_ids();
 7929            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
 7930            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
 7931        });
 7932    });
 7933
 7934    // Apply the update of removing the excerpts.
 7935    follower_1
 7936        .update(cx, |follower, cx| {
 7937            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7938        })
 7939        .await
 7940        .unwrap();
 7941    follower_2
 7942        .update(cx, |follower, cx| {
 7943            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
 7944        })
 7945        .await
 7946        .unwrap();
 7947    update_message.borrow_mut().take();
 7948    assert_eq!(
 7949        follower_1.update(cx, |editor, cx| editor.text(cx)),
 7950        leader.update(cx, |editor, cx| editor.text(cx))
 7951    );
 7952}
 7953
 7954#[gpui::test]
 7955async fn go_to_prev_overlapping_diagnostic(
 7956    executor: BackgroundExecutor,
 7957    cx: &mut gpui::TestAppContext,
 7958) {
 7959    init_test(cx, |_| {});
 7960
 7961    let mut cx = EditorTestContext::new(cx).await;
 7962    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
 7963
 7964    cx.set_state(indoc! {"
 7965        ˇfn func(abc def: i32) -> u32 {
 7966        }
 7967    "});
 7968
 7969    _ = cx.update(|cx| {
 7970        _ = project.update(cx, |project, cx| {
 7971            project
 7972                .update_diagnostics(
 7973                    LanguageServerId(0),
 7974                    lsp::PublishDiagnosticsParams {
 7975                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
 7976                        version: None,
 7977                        diagnostics: vec![
 7978                            lsp::Diagnostic {
 7979                                range: lsp::Range::new(
 7980                                    lsp::Position::new(0, 11),
 7981                                    lsp::Position::new(0, 12),
 7982                                ),
 7983                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7984                                ..Default::default()
 7985                            },
 7986                            lsp::Diagnostic {
 7987                                range: lsp::Range::new(
 7988                                    lsp::Position::new(0, 12),
 7989                                    lsp::Position::new(0, 15),
 7990                                ),
 7991                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 7992                                ..Default::default()
 7993                            },
 7994                            lsp::Diagnostic {
 7995                                range: lsp::Range::new(
 7996                                    lsp::Position::new(0, 25),
 7997                                    lsp::Position::new(0, 28),
 7998                                ),
 7999                                severity: Some(lsp::DiagnosticSeverity::ERROR),
 8000                                ..Default::default()
 8001                            },
 8002                        ],
 8003                    },
 8004                    &[],
 8005                    cx,
 8006                )
 8007                .unwrap()
 8008        });
 8009    });
 8010
 8011    executor.run_until_parked();
 8012
 8013    cx.update_editor(|editor, cx| {
 8014        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8015    });
 8016
 8017    cx.assert_editor_state(indoc! {"
 8018        fn func(abc def: i32) -> ˇu32 {
 8019        }
 8020    "});
 8021
 8022    cx.update_editor(|editor, cx| {
 8023        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8024    });
 8025
 8026    cx.assert_editor_state(indoc! {"
 8027        fn func(abc ˇdef: i32) -> u32 {
 8028        }
 8029    "});
 8030
 8031    cx.update_editor(|editor, cx| {
 8032        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8033    });
 8034
 8035    cx.assert_editor_state(indoc! {"
 8036        fn func(abcˇ def: i32) -> u32 {
 8037        }
 8038    "});
 8039
 8040    cx.update_editor(|editor, cx| {
 8041        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
 8042    });
 8043
 8044    cx.assert_editor_state(indoc! {"
 8045        fn func(abc def: i32) -> ˇu32 {
 8046        }
 8047    "});
 8048}
 8049
 8050#[gpui::test]
 8051async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 8052    init_test(cx, |_| {});
 8053
 8054    let mut cx = EditorTestContext::new(cx).await;
 8055
 8056    let diff_base = r#"
 8057        use some::mod;
 8058
 8059        const A: u32 = 42;
 8060
 8061        fn main() {
 8062            println!("hello");
 8063
 8064            println!("world");
 8065        }
 8066        "#
 8067    .unindent();
 8068
 8069    // Edits are modified, removed, modified, added
 8070    cx.set_state(
 8071        &r#"
 8072        use some::modified;
 8073
 8074        ˇ
 8075        fn main() {
 8076            println!("hello there");
 8077
 8078            println!("around the");
 8079            println!("world");
 8080        }
 8081        "#
 8082        .unindent(),
 8083    );
 8084
 8085    cx.set_diff_base(Some(&diff_base));
 8086    executor.run_until_parked();
 8087
 8088    cx.update_editor(|editor, cx| {
 8089        //Wrap around the bottom of the buffer
 8090        for _ in 0..3 {
 8091            editor.go_to_hunk(&GoToHunk, cx);
 8092        }
 8093    });
 8094
 8095    cx.assert_editor_state(
 8096        &r#"
 8097        ˇuse some::modified;
 8098
 8099
 8100        fn main() {
 8101            println!("hello there");
 8102
 8103            println!("around the");
 8104            println!("world");
 8105        }
 8106        "#
 8107        .unindent(),
 8108    );
 8109
 8110    cx.update_editor(|editor, cx| {
 8111        //Wrap around the top of the buffer
 8112        for _ in 0..2 {
 8113            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8114        }
 8115    });
 8116
 8117    cx.assert_editor_state(
 8118        &r#"
 8119        use some::modified;
 8120
 8121
 8122        fn main() {
 8123        ˇ    println!("hello there");
 8124
 8125            println!("around the");
 8126            println!("world");
 8127        }
 8128        "#
 8129        .unindent(),
 8130    );
 8131
 8132    cx.update_editor(|editor, cx| {
 8133        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8134    });
 8135
 8136    cx.assert_editor_state(
 8137        &r#"
 8138        use some::modified;
 8139
 8140        ˇ
 8141        fn main() {
 8142            println!("hello there");
 8143
 8144            println!("around the");
 8145            println!("world");
 8146        }
 8147        "#
 8148        .unindent(),
 8149    );
 8150
 8151    cx.update_editor(|editor, cx| {
 8152        for _ in 0..3 {
 8153            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
 8154        }
 8155    });
 8156
 8157    cx.assert_editor_state(
 8158        &r#"
 8159        use some::modified;
 8160
 8161
 8162        fn main() {
 8163        ˇ    println!("hello there");
 8164
 8165            println!("around the");
 8166            println!("world");
 8167        }
 8168        "#
 8169        .unindent(),
 8170    );
 8171
 8172    cx.update_editor(|editor, cx| {
 8173        editor.fold(&Fold, cx);
 8174
 8175        //Make sure that the fold only gets one hunk
 8176        for _ in 0..4 {
 8177            editor.go_to_hunk(&GoToHunk, cx);
 8178        }
 8179    });
 8180
 8181    cx.assert_editor_state(
 8182        &r#"
 8183        ˇuse some::modified;
 8184
 8185
 8186        fn main() {
 8187            println!("hello there");
 8188
 8189            println!("around the");
 8190            println!("world");
 8191        }
 8192        "#
 8193        .unindent(),
 8194    );
 8195}
 8196
 8197#[test]
 8198fn test_split_words() {
 8199    fn split(text: &str) -> Vec<&str> {
 8200        split_words(text).collect()
 8201    }
 8202
 8203    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
 8204    assert_eq!(split("hello_world"), &["hello_", "world"]);
 8205    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
 8206    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
 8207    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
 8208    assert_eq!(split("helloworld"), &["helloworld"]);
 8209
 8210    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
 8211}
 8212
 8213#[gpui::test]
 8214async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 8215    init_test(cx, |_| {});
 8216
 8217    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
 8218    let mut assert = |before, after| {
 8219        let _state_context = cx.set_state(before);
 8220        cx.update_editor(|editor, cx| {
 8221            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
 8222        });
 8223        cx.assert_editor_state(after);
 8224    };
 8225
 8226    // Outside bracket jumps to outside of matching bracket
 8227    assert("console.logˇ(var);", "console.log(var)ˇ;");
 8228    assert("console.log(var)ˇ;", "console.logˇ(var);");
 8229
 8230    // Inside bracket jumps to inside of matching bracket
 8231    assert("console.log(ˇvar);", "console.log(varˇ);");
 8232    assert("console.log(varˇ);", "console.log(ˇvar);");
 8233
 8234    // When outside a bracket and inside, favor jumping to the inside bracket
 8235    assert(
 8236        "console.log('foo', [1, 2, 3]ˇ);",
 8237        "console.log(ˇ'foo', [1, 2, 3]);",
 8238    );
 8239    assert(
 8240        "console.log(ˇ'foo', [1, 2, 3]);",
 8241        "console.log('foo', [1, 2, 3]ˇ);",
 8242    );
 8243
 8244    // Bias forward if two options are equally likely
 8245    assert(
 8246        "let result = curried_fun()ˇ();",
 8247        "let result = curried_fun()()ˇ;",
 8248    );
 8249
 8250    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
 8251    assert(
 8252        indoc! {"
 8253            function test() {
 8254                console.log('test')ˇ
 8255            }"},
 8256        indoc! {"
 8257            function test() {
 8258                console.logˇ('test')
 8259            }"},
 8260    );
 8261}
 8262
 8263#[gpui::test]
 8264async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
 8265    init_test(cx, |_| {});
 8266
 8267    let fs = FakeFs::new(cx.executor());
 8268    fs.insert_tree(
 8269        "/a",
 8270        json!({
 8271            "main.rs": "fn main() { let a = 5; }",
 8272            "other.rs": "// Test file",
 8273        }),
 8274    )
 8275    .await;
 8276    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8277
 8278    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8279    language_registry.add(Arc::new(Language::new(
 8280        LanguageConfig {
 8281            name: "Rust".into(),
 8282            matcher: LanguageMatcher {
 8283                path_suffixes: vec!["rs".to_string()],
 8284                ..Default::default()
 8285            },
 8286            brackets: BracketPairConfig {
 8287                pairs: vec![BracketPair {
 8288                    start: "{".to_string(),
 8289                    end: "}".to_string(),
 8290                    close: true,
 8291                    newline: true,
 8292                }],
 8293                disabled_scopes_by_bracket_ix: Vec::new(),
 8294            },
 8295            ..Default::default()
 8296        },
 8297        Some(tree_sitter_rust::language()),
 8298    )));
 8299    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8300        "Rust",
 8301        FakeLspAdapter {
 8302            capabilities: lsp::ServerCapabilities {
 8303                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
 8304                    first_trigger_character: "{".to_string(),
 8305                    more_trigger_character: None,
 8306                }),
 8307                ..Default::default()
 8308            },
 8309            ..Default::default()
 8310        },
 8311    );
 8312
 8313    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8314
 8315    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8316
 8317    let worktree_id = workspace
 8318        .update(cx, |workspace, cx| {
 8319            workspace.project().update(cx, |project, cx| {
 8320                project.worktrees().next().unwrap().read(cx).id()
 8321            })
 8322        })
 8323        .unwrap();
 8324
 8325    let buffer = project
 8326        .update(cx, |project, cx| {
 8327            project.open_local_buffer("/a/main.rs", cx)
 8328        })
 8329        .await
 8330        .unwrap();
 8331    cx.executor().run_until_parked();
 8332    cx.executor().start_waiting();
 8333    let fake_server = fake_servers.next().await.unwrap();
 8334    let editor_handle = workspace
 8335        .update(cx, |workspace, cx| {
 8336            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
 8337        })
 8338        .unwrap()
 8339        .await
 8340        .unwrap()
 8341        .downcast::<Editor>()
 8342        .unwrap();
 8343
 8344    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
 8345        assert_eq!(
 8346            params.text_document_position.text_document.uri,
 8347            lsp::Url::from_file_path("/a/main.rs").unwrap(),
 8348        );
 8349        assert_eq!(
 8350            params.text_document_position.position,
 8351            lsp::Position::new(0, 21),
 8352        );
 8353
 8354        Ok(Some(vec![lsp::TextEdit {
 8355            new_text: "]".to_string(),
 8356            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
 8357        }]))
 8358    });
 8359
 8360    editor_handle.update(cx, |editor, cx| {
 8361        editor.focus(cx);
 8362        editor.change_selections(None, cx, |s| {
 8363            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
 8364        });
 8365        editor.handle_input("{", cx);
 8366    });
 8367
 8368    cx.executor().run_until_parked();
 8369
 8370    _ = buffer.update(cx, |buffer, _| {
 8371        assert_eq!(
 8372            buffer.text(),
 8373            "fn main() { let a = {5}; }",
 8374            "No extra braces from on type formatting should appear in the buffer"
 8375        )
 8376    });
 8377}
 8378
 8379#[gpui::test]
 8380async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
 8381    init_test(cx, |_| {});
 8382
 8383    let fs = FakeFs::new(cx.executor());
 8384    fs.insert_tree(
 8385        "/a",
 8386        json!({
 8387            "main.rs": "fn main() { let a = 5; }",
 8388            "other.rs": "// Test file",
 8389        }),
 8390    )
 8391    .await;
 8392
 8393    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8394
 8395    let server_restarts = Arc::new(AtomicUsize::new(0));
 8396    let closure_restarts = Arc::clone(&server_restarts);
 8397    let language_server_name = "test language server";
 8398    let language_name: Arc<str> = "Rust".into();
 8399
 8400    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8401    language_registry.add(Arc::new(Language::new(
 8402        LanguageConfig {
 8403            name: Arc::clone(&language_name),
 8404            matcher: LanguageMatcher {
 8405                path_suffixes: vec!["rs".to_string()],
 8406                ..Default::default()
 8407            },
 8408            ..Default::default()
 8409        },
 8410        Some(tree_sitter_rust::language()),
 8411    )));
 8412    let mut fake_servers = language_registry.register_fake_lsp_adapter(
 8413        "Rust",
 8414        FakeLspAdapter {
 8415            name: language_server_name,
 8416            initialization_options: Some(json!({
 8417                "testOptionValue": true
 8418            })),
 8419            initializer: Some(Box::new(move |fake_server| {
 8420                let task_restarts = Arc::clone(&closure_restarts);
 8421                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
 8422                    task_restarts.fetch_add(1, atomic::Ordering::Release);
 8423                    futures::future::ready(Ok(()))
 8424                });
 8425            })),
 8426            ..Default::default()
 8427        },
 8428    );
 8429
 8430    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8431    let _buffer = project
 8432        .update(cx, |project, cx| {
 8433            project.open_local_buffer("/a/main.rs", cx)
 8434        })
 8435        .await
 8436        .unwrap();
 8437    let _fake_server = fake_servers.next().await.unwrap();
 8438    update_test_language_settings(cx, |language_settings| {
 8439        language_settings.languages.insert(
 8440            Arc::clone(&language_name),
 8441            LanguageSettingsContent {
 8442                tab_size: NonZeroU32::new(8),
 8443                ..Default::default()
 8444            },
 8445        );
 8446    });
 8447    cx.executor().run_until_parked();
 8448    assert_eq!(
 8449        server_restarts.load(atomic::Ordering::Acquire),
 8450        0,
 8451        "Should not restart LSP server on an unrelated change"
 8452    );
 8453
 8454    update_test_project_settings(cx, |project_settings| {
 8455        project_settings.lsp.insert(
 8456            "Some other server name".into(),
 8457            LspSettings {
 8458                binary: None,
 8459                settings: None,
 8460                initialization_options: Some(json!({
 8461                    "some other init value": false
 8462                })),
 8463            },
 8464        );
 8465    });
 8466    cx.executor().run_until_parked();
 8467    assert_eq!(
 8468        server_restarts.load(atomic::Ordering::Acquire),
 8469        0,
 8470        "Should not restart LSP server on an unrelated LSP settings change"
 8471    );
 8472
 8473    update_test_project_settings(cx, |project_settings| {
 8474        project_settings.lsp.insert(
 8475            language_server_name.into(),
 8476            LspSettings {
 8477                binary: None,
 8478                settings: None,
 8479                initialization_options: Some(json!({
 8480                    "anotherInitValue": false
 8481                })),
 8482            },
 8483        );
 8484    });
 8485    cx.executor().run_until_parked();
 8486    assert_eq!(
 8487        server_restarts.load(atomic::Ordering::Acquire),
 8488        1,
 8489        "Should restart LSP server on a related LSP settings change"
 8490    );
 8491
 8492    update_test_project_settings(cx, |project_settings| {
 8493        project_settings.lsp.insert(
 8494            language_server_name.into(),
 8495            LspSettings {
 8496                binary: None,
 8497                settings: None,
 8498                initialization_options: Some(json!({
 8499                    "anotherInitValue": false
 8500                })),
 8501            },
 8502        );
 8503    });
 8504    cx.executor().run_until_parked();
 8505    assert_eq!(
 8506        server_restarts.load(atomic::Ordering::Acquire),
 8507        1,
 8508        "Should not restart LSP server on a related LSP settings change that is the same"
 8509    );
 8510
 8511    update_test_project_settings(cx, |project_settings| {
 8512        project_settings.lsp.insert(
 8513            language_server_name.into(),
 8514            LspSettings {
 8515                binary: None,
 8516                settings: None,
 8517                initialization_options: None,
 8518            },
 8519        );
 8520    });
 8521    cx.executor().run_until_parked();
 8522    assert_eq!(
 8523        server_restarts.load(atomic::Ordering::Acquire),
 8524        2,
 8525        "Should restart LSP server on another related LSP settings change"
 8526    );
 8527}
 8528
 8529#[gpui::test]
 8530async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
 8531    init_test(cx, |_| {});
 8532
 8533    let mut cx = EditorLspTestContext::new_rust(
 8534        lsp::ServerCapabilities {
 8535            completion_provider: Some(lsp::CompletionOptions {
 8536                trigger_characters: Some(vec![".".to_string()]),
 8537                resolve_provider: Some(true),
 8538                ..Default::default()
 8539            }),
 8540            ..Default::default()
 8541        },
 8542        cx,
 8543    )
 8544    .await;
 8545
 8546    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8547    cx.simulate_keystroke(".");
 8548    let completion_item = lsp::CompletionItem {
 8549        label: "some".into(),
 8550        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8551        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8552        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8553            kind: lsp::MarkupKind::Markdown,
 8554            value: "```rust\nSome(2)\n```".to_string(),
 8555        })),
 8556        deprecated: Some(false),
 8557        sort_text: Some("fffffff2".to_string()),
 8558        filter_text: Some("some".to_string()),
 8559        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8560        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8561            range: lsp::Range {
 8562                start: lsp::Position {
 8563                    line: 0,
 8564                    character: 22,
 8565                },
 8566                end: lsp::Position {
 8567                    line: 0,
 8568                    character: 22,
 8569                },
 8570            },
 8571            new_text: "Some(2)".to_string(),
 8572        })),
 8573        additional_text_edits: Some(vec![lsp::TextEdit {
 8574            range: lsp::Range {
 8575                start: lsp::Position {
 8576                    line: 0,
 8577                    character: 20,
 8578                },
 8579                end: lsp::Position {
 8580                    line: 0,
 8581                    character: 22,
 8582                },
 8583            },
 8584            new_text: "".to_string(),
 8585        }]),
 8586        ..Default::default()
 8587    };
 8588
 8589    let closure_completion_item = completion_item.clone();
 8590    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8591        let task_completion_item = closure_completion_item.clone();
 8592        async move {
 8593            Ok(Some(lsp::CompletionResponse::Array(vec![
 8594                task_completion_item,
 8595            ])))
 8596        }
 8597    });
 8598
 8599    request.next().await;
 8600
 8601    cx.condition(|editor, _| editor.context_menu_visible())
 8602        .await;
 8603    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8604        editor
 8605            .confirm_completion(&ConfirmCompletion::default(), cx)
 8606            .unwrap()
 8607    });
 8608    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
 8609
 8610    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
 8611        let task_completion_item = completion_item.clone();
 8612        async move { Ok(task_completion_item) }
 8613    })
 8614    .next()
 8615    .await
 8616    .unwrap();
 8617    apply_additional_edits.await.unwrap();
 8618    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
 8619}
 8620
 8621#[gpui::test]
 8622async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
 8623    init_test(cx, |_| {});
 8624
 8625    let mut cx = EditorLspTestContext::new(
 8626        Language::new(
 8627            LanguageConfig {
 8628                matcher: LanguageMatcher {
 8629                    path_suffixes: vec!["jsx".into()],
 8630                    ..Default::default()
 8631                },
 8632                overrides: [(
 8633                    "element".into(),
 8634                    LanguageConfigOverride {
 8635                        word_characters: Override::Set(['-'].into_iter().collect()),
 8636                        ..Default::default()
 8637                    },
 8638                )]
 8639                .into_iter()
 8640                .collect(),
 8641                ..Default::default()
 8642            },
 8643            Some(tree_sitter_typescript::language_tsx()),
 8644        )
 8645        .with_override_query("(jsx_self_closing_element) @element")
 8646        .unwrap(),
 8647        lsp::ServerCapabilities {
 8648            completion_provider: Some(lsp::CompletionOptions {
 8649                trigger_characters: Some(vec![":".to_string()]),
 8650                ..Default::default()
 8651            }),
 8652            ..Default::default()
 8653        },
 8654        cx,
 8655    )
 8656    .await;
 8657
 8658    cx.lsp
 8659        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8660            Ok(Some(lsp::CompletionResponse::Array(vec![
 8661                lsp::CompletionItem {
 8662                    label: "bg-blue".into(),
 8663                    ..Default::default()
 8664                },
 8665                lsp::CompletionItem {
 8666                    label: "bg-red".into(),
 8667                    ..Default::default()
 8668                },
 8669                lsp::CompletionItem {
 8670                    label: "bg-yellow".into(),
 8671                    ..Default::default()
 8672                },
 8673            ])))
 8674        });
 8675
 8676    cx.set_state(r#"<p class="bgˇ" />"#);
 8677
 8678    // Trigger completion when typing a dash, because the dash is an extra
 8679    // word character in the 'element' scope, which contains the cursor.
 8680    cx.simulate_keystroke("-");
 8681    cx.executor().run_until_parked();
 8682    cx.update_editor(|editor, _| {
 8683        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8684            assert_eq!(
 8685                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8686                &["bg-red", "bg-blue", "bg-yellow"]
 8687            );
 8688        } else {
 8689            panic!("expected completion menu to be open");
 8690        }
 8691    });
 8692
 8693    cx.simulate_keystroke("l");
 8694    cx.executor().run_until_parked();
 8695    cx.update_editor(|editor, _| {
 8696        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8697            assert_eq!(
 8698                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8699                &["bg-blue", "bg-yellow"]
 8700            );
 8701        } else {
 8702            panic!("expected completion menu to be open");
 8703        }
 8704    });
 8705
 8706    // When filtering completions, consider the character after the '-' to
 8707    // be the start of a subword.
 8708    cx.set_state(r#"<p class="yelˇ" />"#);
 8709    cx.simulate_keystroke("l");
 8710    cx.executor().run_until_parked();
 8711    cx.update_editor(|editor, _| {
 8712        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
 8713            assert_eq!(
 8714                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
 8715                &["bg-yellow"]
 8716            );
 8717        } else {
 8718            panic!("expected completion menu to be open");
 8719        }
 8720    });
 8721}
 8722
 8723#[gpui::test]
 8724async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 8725    init_test(cx, |settings| {
 8726        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
 8727    });
 8728
 8729    let fs = FakeFs::new(cx.executor());
 8730    fs.insert_file("/file.ts", Default::default()).await;
 8731
 8732    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
 8733    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8734
 8735    language_registry.add(Arc::new(Language::new(
 8736        LanguageConfig {
 8737            name: "TypeScript".into(),
 8738            matcher: LanguageMatcher {
 8739                path_suffixes: vec!["ts".to_string()],
 8740                ..Default::default()
 8741            },
 8742            ..Default::default()
 8743        },
 8744        Some(tree_sitter_rust::language()),
 8745    )));
 8746    update_test_language_settings(cx, |settings| {
 8747        settings.defaults.prettier = Some(PrettierSettings {
 8748            allowed: true,
 8749            ..PrettierSettings::default()
 8750        });
 8751    });
 8752
 8753    let test_plugin = "test_plugin";
 8754    let _ = language_registry.register_fake_lsp_adapter(
 8755        "TypeScript",
 8756        FakeLspAdapter {
 8757            prettier_plugins: vec![test_plugin],
 8758            ..Default::default()
 8759        },
 8760    );
 8761
 8762    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
 8763    let buffer = project
 8764        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
 8765        .await
 8766        .unwrap();
 8767
 8768    let buffer_text = "one\ntwo\nthree\n";
 8769    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 8770    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 8771    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 8772
 8773    editor
 8774        .update(cx, |editor, cx| {
 8775            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8776        })
 8777        .unwrap()
 8778        .await;
 8779    assert_eq!(
 8780        editor.update(cx, |editor, cx| editor.text(cx)),
 8781        buffer_text.to_string() + prettier_format_suffix,
 8782        "Test prettier formatting was not applied to the original buffer text",
 8783    );
 8784
 8785    update_test_language_settings(cx, |settings| {
 8786        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
 8787    });
 8788    let format = editor.update(cx, |editor, cx| {
 8789        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
 8790    });
 8791    format.await.unwrap();
 8792    assert_eq!(
 8793        editor.update(cx, |editor, cx| editor.text(cx)),
 8794        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
 8795        "Autoformatting (via test prettier) was not applied to the original buffer text",
 8796    );
 8797}
 8798
 8799#[gpui::test]
 8800async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
 8801    init_test(cx, |_| {});
 8802    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8803    let base_text = indoc! {r#"struct Row;
 8804struct Row1;
 8805struct Row2;
 8806
 8807struct Row4;
 8808struct Row5;
 8809struct Row6;
 8810
 8811struct Row8;
 8812struct Row9;
 8813struct Row10;"#};
 8814
 8815    // When addition hunks are not adjacent to carets, no hunk revert is performed
 8816    assert_hunk_revert(
 8817        indoc! {r#"struct Row;
 8818                   struct Row1;
 8819                   struct Row1.1;
 8820                   struct Row1.2;
 8821                   struct Row2;ˇ
 8822
 8823                   struct Row4;
 8824                   struct Row5;
 8825                   struct Row6;
 8826
 8827                   struct Row8;
 8828                   ˇstruct Row9;
 8829                   struct Row9.1;
 8830                   struct Row9.2;
 8831                   struct Row9.3;
 8832                   struct Row10;"#},
 8833        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8834        indoc! {r#"struct Row;
 8835                   struct Row1;
 8836                   struct Row1.1;
 8837                   struct Row1.2;
 8838                   struct Row2;ˇ
 8839
 8840                   struct Row4;
 8841                   struct Row5;
 8842                   struct Row6;
 8843
 8844                   struct Row8;
 8845                   ˇstruct Row9;
 8846                   struct Row9.1;
 8847                   struct Row9.2;
 8848                   struct Row9.3;
 8849                   struct Row10;"#},
 8850        base_text,
 8851        &mut cx,
 8852    );
 8853    // Same for selections
 8854    assert_hunk_revert(
 8855        indoc! {r#"struct Row;
 8856                   struct Row1;
 8857                   struct Row2;
 8858                   struct Row2.1;
 8859                   struct Row2.2;
 8860                   «ˇ
 8861                   struct Row4;
 8862                   struct» Row5;
 8863                   «struct Row6;
 8864                   ˇ»
 8865                   struct Row9.1;
 8866                   struct Row9.2;
 8867                   struct Row9.3;
 8868                   struct Row8;
 8869                   struct Row9;
 8870                   struct Row10;"#},
 8871        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
 8872        indoc! {r#"struct Row;
 8873                   struct Row1;
 8874                   struct Row2;
 8875                   struct Row2.1;
 8876                   struct Row2.2;
 8877                   «ˇ
 8878                   struct Row4;
 8879                   struct» Row5;
 8880                   «struct Row6;
 8881                   ˇ»
 8882                   struct Row9.1;
 8883                   struct Row9.2;
 8884                   struct Row9.3;
 8885                   struct Row8;
 8886                   struct Row9;
 8887                   struct Row10;"#},
 8888        base_text,
 8889        &mut cx,
 8890    );
 8891
 8892    // When carets and selections intersect the addition hunks, those are reverted.
 8893    // Adjacent carets got merged.
 8894    assert_hunk_revert(
 8895        indoc! {r#"struct Row;
 8896                   ˇ// something on the top
 8897                   struct Row1;
 8898                   struct Row2;
 8899                   struct Roˇw3.1;
 8900                   struct Row2.2;
 8901                   struct Row2.3;ˇ
 8902
 8903                   struct Row4;
 8904                   struct ˇRow5.1;
 8905                   struct Row5.2;
 8906                   struct «Rowˇ»5.3;
 8907                   struct Row5;
 8908                   struct Row6;
 8909                   ˇ
 8910                   struct Row9.1;
 8911                   struct «Rowˇ»9.2;
 8912                   struct «ˇRow»9.3;
 8913                   struct Row8;
 8914                   struct Row9;
 8915                   «ˇ// something on bottom»
 8916                   struct Row10;"#},
 8917        vec![
 8918            DiffHunkStatus::Added,
 8919            DiffHunkStatus::Added,
 8920            DiffHunkStatus::Added,
 8921            DiffHunkStatus::Added,
 8922            DiffHunkStatus::Added,
 8923        ],
 8924        indoc! {r#"struct Row;
 8925                   ˇstruct Row1;
 8926                   struct Row2;
 8927                   ˇ
 8928                   struct Row4;
 8929                   ˇstruct Row5;
 8930                   struct Row6;
 8931                   ˇ
 8932                   ˇstruct Row8;
 8933                   struct Row9;
 8934                   ˇstruct Row10;"#},
 8935        base_text,
 8936        &mut cx,
 8937    );
 8938}
 8939
 8940#[gpui::test]
 8941async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
 8942    init_test(cx, |_| {});
 8943    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 8944    let base_text = indoc! {r#"struct Row;
 8945struct Row1;
 8946struct Row2;
 8947
 8948struct Row4;
 8949struct Row5;
 8950struct Row6;
 8951
 8952struct Row8;
 8953struct Row9;
 8954struct Row10;"#};
 8955
 8956    // Modification hunks behave the same as the addition ones.
 8957    assert_hunk_revert(
 8958        indoc! {r#"struct Row;
 8959                   struct Row1;
 8960                   struct Row33;
 8961                   ˇ
 8962                   struct Row4;
 8963                   struct Row5;
 8964                   struct Row6;
 8965                   ˇ
 8966                   struct Row99;
 8967                   struct Row9;
 8968                   struct Row10;"#},
 8969        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8970        indoc! {r#"struct Row;
 8971                   struct Row1;
 8972                   struct Row33;
 8973                   ˇ
 8974                   struct Row4;
 8975                   struct Row5;
 8976                   struct Row6;
 8977                   ˇ
 8978                   struct Row99;
 8979                   struct Row9;
 8980                   struct Row10;"#},
 8981        base_text,
 8982        &mut cx,
 8983    );
 8984    assert_hunk_revert(
 8985        indoc! {r#"struct Row;
 8986                   struct Row1;
 8987                   struct Row33;
 8988                   «ˇ
 8989                   struct Row4;
 8990                   struct» Row5;
 8991                   «struct Row6;
 8992                   ˇ»
 8993                   struct Row99;
 8994                   struct Row9;
 8995                   struct Row10;"#},
 8996        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
 8997        indoc! {r#"struct Row;
 8998                   struct Row1;
 8999                   struct Row33;
 9000                   «ˇ
 9001                   struct Row4;
 9002                   struct» Row5;
 9003                   «struct Row6;
 9004                   ˇ»
 9005                   struct Row99;
 9006                   struct Row9;
 9007                   struct Row10;"#},
 9008        base_text,
 9009        &mut cx,
 9010    );
 9011
 9012    assert_hunk_revert(
 9013        indoc! {r#"ˇstruct Row1.1;
 9014                   struct Row1;
 9015                   «ˇstr»uct Row22;
 9016
 9017                   struct ˇRow44;
 9018                   struct Row5;
 9019                   struct «Rˇ»ow66;ˇ
 9020
 9021                   «struˇ»ct Row88;
 9022                   struct Row9;
 9023                   struct Row1011;ˇ"#},
 9024        vec![
 9025            DiffHunkStatus::Modified,
 9026            DiffHunkStatus::Modified,
 9027            DiffHunkStatus::Modified,
 9028            DiffHunkStatus::Modified,
 9029            DiffHunkStatus::Modified,
 9030            DiffHunkStatus::Modified,
 9031        ],
 9032        indoc! {r#"struct Row;
 9033                   ˇstruct Row1;
 9034                   struct Row2;
 9035                   ˇ
 9036                   struct Row4;
 9037                   ˇstruct Row5;
 9038                   struct Row6;
 9039                   ˇ
 9040                   struct Row8;
 9041                   ˇstruct Row9;
 9042                   struct Row10;ˇ"#},
 9043        base_text,
 9044        &mut cx,
 9045    );
 9046}
 9047
 9048#[gpui::test]
 9049async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
 9050    init_test(cx, |_| {});
 9051    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9052    let base_text = indoc! {r#"struct Row;
 9053struct Row1;
 9054struct Row2;
 9055
 9056struct Row4;
 9057struct Row5;
 9058struct Row6;
 9059
 9060struct Row8;
 9061struct Row9;
 9062struct Row10;"#};
 9063
 9064    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
 9065    assert_hunk_revert(
 9066        indoc! {r#"struct Row;
 9067                   struct Row2;
 9068
 9069                   ˇstruct Row4;
 9070                   struct Row5;
 9071                   struct Row6;
 9072                   ˇ
 9073                   struct Row8;
 9074                   struct Row10;"#},
 9075        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9076        indoc! {r#"struct Row;
 9077                   struct Row2;
 9078
 9079                   ˇstruct Row4;
 9080                   struct Row5;
 9081                   struct Row6;
 9082                   ˇ
 9083                   struct Row8;
 9084                   struct Row10;"#},
 9085        base_text,
 9086        &mut cx,
 9087    );
 9088    assert_hunk_revert(
 9089        indoc! {r#"struct Row;
 9090                   struct Row2;
 9091
 9092                   «ˇstruct Row4;
 9093                   struct» Row5;
 9094                   «struct Row6;
 9095                   ˇ»
 9096                   struct Row8;
 9097                   struct Row10;"#},
 9098        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9099        indoc! {r#"struct Row;
 9100                   struct Row2;
 9101
 9102                   «ˇstruct Row4;
 9103                   struct» Row5;
 9104                   «struct Row6;
 9105                   ˇ»
 9106                   struct Row8;
 9107                   struct Row10;"#},
 9108        base_text,
 9109        &mut cx,
 9110    );
 9111
 9112    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
 9113    assert_hunk_revert(
 9114        indoc! {r#"struct Row;
 9115                   ˇstruct Row2;
 9116
 9117                   struct Row4;
 9118                   struct Row5;
 9119                   struct Row6;
 9120
 9121                   struct Row8;ˇ
 9122                   struct Row10;"#},
 9123        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
 9124        indoc! {r#"struct Row;
 9125                   struct Row1;
 9126                   ˇstruct Row2;
 9127
 9128                   struct Row4;
 9129                   struct Row5;
 9130                   struct Row6;
 9131
 9132                   struct Row8;ˇ
 9133                   struct Row9;
 9134                   struct Row10;"#},
 9135        base_text,
 9136        &mut cx,
 9137    );
 9138    assert_hunk_revert(
 9139        indoc! {r#"struct Row;
 9140                   struct Row2«ˇ;
 9141                   struct Row4;
 9142                   struct» Row5;
 9143                   «struct Row6;
 9144
 9145                   struct Row8;ˇ»
 9146                   struct Row10;"#},
 9147        vec![
 9148            DiffHunkStatus::Removed,
 9149            DiffHunkStatus::Removed,
 9150            DiffHunkStatus::Removed,
 9151        ],
 9152        indoc! {r#"struct Row;
 9153                   struct Row1;
 9154                   struct Row2«ˇ;
 9155
 9156                   struct Row4;
 9157                   struct» Row5;
 9158                   «struct Row6;
 9159
 9160                   struct Row8;ˇ»
 9161                   struct Row9;
 9162                   struct Row10;"#},
 9163        base_text,
 9164        &mut cx,
 9165    );
 9166}
 9167
 9168#[gpui::test]
 9169async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
 9170    init_test(cx, |_| {});
 9171
 9172    let cols = 4;
 9173    let rows = 10;
 9174    let sample_text_1 = sample_text(rows, cols, 'a');
 9175    assert_eq!(
 9176        sample_text_1,
 9177        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9178    );
 9179    let sample_text_2 = sample_text(rows, cols, 'l');
 9180    assert_eq!(
 9181        sample_text_2,
 9182        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9183    );
 9184    let sample_text_3 = sample_text(rows, cols, 'v');
 9185    assert_eq!(
 9186        sample_text_3,
 9187        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9188    );
 9189
 9190    fn diff_every_buffer_row(
 9191        buffer: &Model<Buffer>,
 9192        sample_text: String,
 9193        cols: usize,
 9194        cx: &mut gpui::TestAppContext,
 9195    ) {
 9196        // revert first character in each row, creating one large diff hunk per buffer
 9197        let is_first_char = |offset: usize| offset % cols == 0;
 9198        buffer.update(cx, |buffer, cx| {
 9199            buffer.set_text(
 9200                sample_text
 9201                    .chars()
 9202                    .enumerate()
 9203                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
 9204                    .collect::<String>(),
 9205                cx,
 9206            );
 9207            buffer.set_diff_base(Some(sample_text), cx);
 9208        });
 9209        cx.executor().run_until_parked();
 9210    }
 9211
 9212    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9213    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9214
 9215    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9216    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9217
 9218    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9219    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9220
 9221    let multibuffer = cx.new_model(|cx| {
 9222        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9223        multibuffer.push_excerpts(
 9224            buffer_1.clone(),
 9225            [
 9226                ExcerptRange {
 9227                    context: Point::new(0, 0)..Point::new(3, 0),
 9228                    primary: None,
 9229                },
 9230                ExcerptRange {
 9231                    context: Point::new(5, 0)..Point::new(7, 0),
 9232                    primary: None,
 9233                },
 9234                ExcerptRange {
 9235                    context: Point::new(9, 0)..Point::new(10, 4),
 9236                    primary: None,
 9237                },
 9238            ],
 9239            cx,
 9240        );
 9241        multibuffer.push_excerpts(
 9242            buffer_2.clone(),
 9243            [
 9244                ExcerptRange {
 9245                    context: Point::new(0, 0)..Point::new(3, 0),
 9246                    primary: None,
 9247                },
 9248                ExcerptRange {
 9249                    context: Point::new(5, 0)..Point::new(7, 0),
 9250                    primary: None,
 9251                },
 9252                ExcerptRange {
 9253                    context: Point::new(9, 0)..Point::new(10, 4),
 9254                    primary: None,
 9255                },
 9256            ],
 9257            cx,
 9258        );
 9259        multibuffer.push_excerpts(
 9260            buffer_3.clone(),
 9261            [
 9262                ExcerptRange {
 9263                    context: Point::new(0, 0)..Point::new(3, 0),
 9264                    primary: None,
 9265                },
 9266                ExcerptRange {
 9267                    context: Point::new(5, 0)..Point::new(7, 0),
 9268                    primary: None,
 9269                },
 9270                ExcerptRange {
 9271                    context: Point::new(9, 0)..Point::new(10, 4),
 9272                    primary: None,
 9273                },
 9274            ],
 9275            cx,
 9276        );
 9277        multibuffer
 9278    });
 9279
 9280    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9281    editor.update(cx, |editor, cx| {
 9282        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");
 9283        editor.select_all(&SelectAll, cx);
 9284        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9285    });
 9286    cx.executor().run_until_parked();
 9287    // When all ranges are selected, all buffer hunks are reverted.
 9288    editor.update(cx, |editor, cx| {
 9289        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");
 9290    });
 9291    buffer_1.update(cx, |buffer, _| {
 9292        assert_eq!(buffer.text(), sample_text_1);
 9293    });
 9294    buffer_2.update(cx, |buffer, _| {
 9295        assert_eq!(buffer.text(), sample_text_2);
 9296    });
 9297    buffer_3.update(cx, |buffer, _| {
 9298        assert_eq!(buffer.text(), sample_text_3);
 9299    });
 9300
 9301    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
 9302    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
 9303    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
 9304    editor.update(cx, |editor, cx| {
 9305        editor.change_selections(None, cx, |s| {
 9306            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
 9307        });
 9308        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
 9309    });
 9310    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
 9311    // but not affect buffer_2 and its related excerpts.
 9312    editor.update(cx, |editor, cx| {
 9313        assert_eq!(
 9314            editor.text(cx),
 9315            "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"
 9316        );
 9317    });
 9318    buffer_1.update(cx, |buffer, _| {
 9319        assert_eq!(buffer.text(), sample_text_1);
 9320    });
 9321    buffer_2.update(cx, |buffer, _| {
 9322        assert_eq!(
 9323            buffer.text(),
 9324            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
 9325        );
 9326    });
 9327    buffer_3.update(cx, |buffer, _| {
 9328        assert_eq!(
 9329            buffer.text(),
 9330            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
 9331        );
 9332    });
 9333}
 9334
 9335#[gpui::test]
 9336async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 9337    init_test(cx, |_| {});
 9338
 9339    let cols = 4;
 9340    let rows = 10;
 9341    let sample_text_1 = sample_text(rows, cols, 'a');
 9342    assert_eq!(
 9343        sample_text_1,
 9344        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 9345    );
 9346    let sample_text_2 = sample_text(rows, cols, 'l');
 9347    assert_eq!(
 9348        sample_text_2,
 9349        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 9350    );
 9351    let sample_text_3 = sample_text(rows, cols, 'v');
 9352    assert_eq!(
 9353        sample_text_3,
 9354        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 9355    );
 9356
 9357    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
 9358    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
 9359    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
 9360
 9361    let multi_buffer = cx.new_model(|cx| {
 9362        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
 9363        multibuffer.push_excerpts(
 9364            buffer_1.clone(),
 9365            [
 9366                ExcerptRange {
 9367                    context: Point::new(0, 0)..Point::new(3, 0),
 9368                    primary: None,
 9369                },
 9370                ExcerptRange {
 9371                    context: Point::new(5, 0)..Point::new(7, 0),
 9372                    primary: None,
 9373                },
 9374                ExcerptRange {
 9375                    context: Point::new(9, 0)..Point::new(10, 4),
 9376                    primary: None,
 9377                },
 9378            ],
 9379            cx,
 9380        );
 9381        multibuffer.push_excerpts(
 9382            buffer_2.clone(),
 9383            [
 9384                ExcerptRange {
 9385                    context: Point::new(0, 0)..Point::new(3, 0),
 9386                    primary: None,
 9387                },
 9388                ExcerptRange {
 9389                    context: Point::new(5, 0)..Point::new(7, 0),
 9390                    primary: None,
 9391                },
 9392                ExcerptRange {
 9393                    context: Point::new(9, 0)..Point::new(10, 4),
 9394                    primary: None,
 9395                },
 9396            ],
 9397            cx,
 9398        );
 9399        multibuffer.push_excerpts(
 9400            buffer_3.clone(),
 9401            [
 9402                ExcerptRange {
 9403                    context: Point::new(0, 0)..Point::new(3, 0),
 9404                    primary: None,
 9405                },
 9406                ExcerptRange {
 9407                    context: Point::new(5, 0)..Point::new(7, 0),
 9408                    primary: None,
 9409                },
 9410                ExcerptRange {
 9411                    context: Point::new(9, 0)..Point::new(10, 4),
 9412                    primary: None,
 9413                },
 9414            ],
 9415            cx,
 9416        );
 9417        multibuffer
 9418    });
 9419
 9420    let fs = FakeFs::new(cx.executor());
 9421    fs.insert_tree(
 9422        "/a",
 9423        json!({
 9424            "main.rs": sample_text_1,
 9425            "other.rs": sample_text_2,
 9426            "lib.rs": sample_text_3,
 9427        }),
 9428    )
 9429    .await;
 9430    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 9431    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 9432    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 9433    let multi_buffer_editor =
 9434        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
 9435    let multibuffer_item_id = workspace
 9436        .update(cx, |workspace, cx| {
 9437            assert!(
 9438                workspace.active_item(cx).is_none(),
 9439                "active item should be None before the first item is added"
 9440            );
 9441            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), None, cx);
 9442            let active_item = workspace
 9443                .active_item(cx)
 9444                .expect("should have an active item after adding the multi buffer");
 9445            assert!(
 9446                !active_item.is_singleton(cx),
 9447                "A multi buffer was expected to active after adding"
 9448            );
 9449            active_item.item_id()
 9450        })
 9451        .unwrap();
 9452    cx.executor().run_until_parked();
 9453
 9454    multi_buffer_editor.update(cx, |editor, cx| {
 9455        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 9456        editor.open_excerpts(&OpenExcerpts, cx);
 9457    });
 9458    cx.executor().run_until_parked();
 9459    let first_item_id = workspace
 9460        .update(cx, |workspace, cx| {
 9461            let active_item = workspace
 9462                .active_item(cx)
 9463                .expect("should have an active item after navigating into the 1st buffer");
 9464            let first_item_id = active_item.item_id();
 9465            assert_ne!(
 9466                first_item_id, multibuffer_item_id,
 9467                "Should navigate into the 1st buffer and activate it"
 9468            );
 9469            assert!(
 9470                active_item.is_singleton(cx),
 9471                "New active item should be a singleton buffer"
 9472            );
 9473            assert_eq!(
 9474                active_item
 9475                    .act_as::<Editor>(cx)
 9476                    .expect("should have navigated into an editor for the 1st buffer")
 9477                    .read(cx)
 9478                    .text(cx),
 9479                sample_text_1
 9480            );
 9481
 9482            workspace
 9483                .go_back(workspace.active_pane().downgrade(), cx)
 9484                .detach_and_log_err(cx);
 9485
 9486            first_item_id
 9487        })
 9488        .unwrap();
 9489    cx.executor().run_until_parked();
 9490    workspace
 9491        .update(cx, |workspace, cx| {
 9492            let active_item = workspace
 9493                .active_item(cx)
 9494                .expect("should have an active item after navigating back");
 9495            assert_eq!(
 9496                active_item.item_id(),
 9497                multibuffer_item_id,
 9498                "Should navigate back to the multi buffer"
 9499            );
 9500            assert!(!active_item.is_singleton(cx));
 9501        })
 9502        .unwrap();
 9503
 9504    multi_buffer_editor.update(cx, |editor, cx| {
 9505        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9506            s.select_ranges(Some(39..40))
 9507        });
 9508        editor.open_excerpts(&OpenExcerpts, cx);
 9509    });
 9510    cx.executor().run_until_parked();
 9511    let second_item_id = workspace
 9512        .update(cx, |workspace, cx| {
 9513            let active_item = workspace
 9514                .active_item(cx)
 9515                .expect("should have an active item after navigating into the 2nd buffer");
 9516            let second_item_id = active_item.item_id();
 9517            assert_ne!(
 9518                second_item_id, multibuffer_item_id,
 9519                "Should navigate away from the multibuffer"
 9520            );
 9521            assert_ne!(
 9522                second_item_id, first_item_id,
 9523                "Should navigate into the 2nd buffer and activate it"
 9524            );
 9525            assert!(
 9526                active_item.is_singleton(cx),
 9527                "New active item should be a singleton buffer"
 9528            );
 9529            assert_eq!(
 9530                active_item
 9531                    .act_as::<Editor>(cx)
 9532                    .expect("should have navigated into an editor")
 9533                    .read(cx)
 9534                    .text(cx),
 9535                sample_text_2
 9536            );
 9537
 9538            workspace
 9539                .go_back(workspace.active_pane().downgrade(), cx)
 9540                .detach_and_log_err(cx);
 9541
 9542            second_item_id
 9543        })
 9544        .unwrap();
 9545    cx.executor().run_until_parked();
 9546    workspace
 9547        .update(cx, |workspace, cx| {
 9548            let active_item = workspace
 9549                .active_item(cx)
 9550                .expect("should have an active item after navigating back from the 2nd buffer");
 9551            assert_eq!(
 9552                active_item.item_id(),
 9553                multibuffer_item_id,
 9554                "Should navigate back from the 2nd buffer to the multi buffer"
 9555            );
 9556            assert!(!active_item.is_singleton(cx));
 9557        })
 9558        .unwrap();
 9559
 9560    multi_buffer_editor.update(cx, |editor, cx| {
 9561        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 9562            s.select_ranges(Some(60..70))
 9563        });
 9564        editor.open_excerpts(&OpenExcerpts, cx);
 9565    });
 9566    cx.executor().run_until_parked();
 9567    workspace
 9568        .update(cx, |workspace, cx| {
 9569            let active_item = workspace
 9570                .active_item(cx)
 9571                .expect("should have an active item after navigating into the 3rd buffer");
 9572            let third_item_id = active_item.item_id();
 9573            assert_ne!(
 9574                third_item_id, multibuffer_item_id,
 9575                "Should navigate into the 3rd buffer and activate it"
 9576            );
 9577            assert_ne!(third_item_id, first_item_id);
 9578            assert_ne!(third_item_id, second_item_id);
 9579            assert!(
 9580                active_item.is_singleton(cx),
 9581                "New active item should be a singleton buffer"
 9582            );
 9583            assert_eq!(
 9584                active_item
 9585                    .act_as::<Editor>(cx)
 9586                    .expect("should have navigated into an editor")
 9587                    .read(cx)
 9588                    .text(cx),
 9589                sample_text_3
 9590            );
 9591
 9592            workspace
 9593                .go_back(workspace.active_pane().downgrade(), cx)
 9594                .detach_and_log_err(cx);
 9595        })
 9596        .unwrap();
 9597    cx.executor().run_until_parked();
 9598    workspace
 9599        .update(cx, |workspace, cx| {
 9600            let active_item = workspace
 9601                .active_item(cx)
 9602                .expect("should have an active item after navigating back from the 3rd buffer");
 9603            assert_eq!(
 9604                active_item.item_id(),
 9605                multibuffer_item_id,
 9606                "Should navigate back from the 3rd buffer to the multi buffer"
 9607            );
 9608            assert!(!active_item.is_singleton(cx));
 9609        })
 9610        .unwrap();
 9611}
 9612
 9613#[gpui::test]
 9614async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9615    init_test(cx, |_| {});
 9616
 9617    let mut cx = EditorTestContext::new(cx).await;
 9618
 9619    let diff_base = r#"
 9620        use some::mod;
 9621
 9622        const A: u32 = 42;
 9623
 9624        fn main() {
 9625            println!("hello");
 9626
 9627            println!("world");
 9628        }
 9629        "#
 9630    .unindent();
 9631
 9632    cx.set_state(
 9633        &r#"
 9634        use some::modified;
 9635
 9636        ˇ
 9637        fn main() {
 9638            println!("hello there");
 9639
 9640            println!("around the");
 9641            println!("world");
 9642        }
 9643        "#
 9644        .unindent(),
 9645    );
 9646
 9647    cx.set_diff_base(Some(&diff_base));
 9648    executor.run_until_parked();
 9649    let unexpanded_hunks = vec![
 9650        (
 9651            "use some::mod;\n".to_string(),
 9652            DiffHunkStatus::Modified,
 9653            DisplayRow(0)..DisplayRow(1),
 9654        ),
 9655        (
 9656            "const A: u32 = 42;\n".to_string(),
 9657            DiffHunkStatus::Removed,
 9658            DisplayRow(2)..DisplayRow(2),
 9659        ),
 9660        (
 9661            "    println!(\"hello\");\n".to_string(),
 9662            DiffHunkStatus::Modified,
 9663            DisplayRow(4)..DisplayRow(5),
 9664        ),
 9665        (
 9666            "".to_string(),
 9667            DiffHunkStatus::Added,
 9668            DisplayRow(6)..DisplayRow(7),
 9669        ),
 9670    ];
 9671    cx.update_editor(|editor, cx| {
 9672        let snapshot = editor.snapshot(cx);
 9673        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9674        assert_eq!(all_hunks, unexpanded_hunks);
 9675    });
 9676
 9677    cx.update_editor(|editor, cx| {
 9678        for _ in 0..4 {
 9679            editor.go_to_hunk(&GoToHunk, cx);
 9680            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
 9681        }
 9682    });
 9683    executor.run_until_parked();
 9684    cx.assert_editor_state(
 9685        &r#"
 9686        use some::modified;
 9687
 9688        ˇ
 9689        fn main() {
 9690            println!("hello there");
 9691
 9692            println!("around the");
 9693            println!("world");
 9694        }
 9695        "#
 9696        .unindent(),
 9697    );
 9698    cx.update_editor(|editor, cx| {
 9699        let snapshot = editor.snapshot(cx);
 9700        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9701        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9702        assert_eq!(
 9703            expanded_hunks_background_highlights(editor, cx),
 9704            vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
 9705            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9706        );
 9707        assert_eq!(
 9708            all_hunks,
 9709            vec![
 9710                ("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
 9711                ("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
 9712                ("    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
 9713                ("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
 9714            ],
 9715            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9716            (from modified and removed hunks)"
 9717        );
 9718        assert_eq!(
 9719            all_hunks, all_expanded_hunks,
 9720            "Editor hunks should not change and all be expanded"
 9721        );
 9722    });
 9723
 9724    cx.update_editor(|editor, cx| {
 9725        editor.cancel(&Cancel, cx);
 9726
 9727        let snapshot = editor.snapshot(cx);
 9728        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9729        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9730        assert_eq!(
 9731            expanded_hunks_background_highlights(editor, cx),
 9732            Vec::new(),
 9733            "After cancelling in editor, no git highlights should be left"
 9734        );
 9735        assert_eq!(
 9736            all_expanded_hunks,
 9737            Vec::new(),
 9738            "After cancelling in editor, no hunks should be expanded"
 9739        );
 9740        assert_eq!(
 9741            all_hunks, unexpanded_hunks,
 9742            "After cancelling in editor, regular hunks' coordinates should get back to normal"
 9743        );
 9744    });
 9745}
 9746
 9747#[gpui::test]
 9748async fn test_toggled_diff_base_change(
 9749    executor: BackgroundExecutor,
 9750    cx: &mut gpui::TestAppContext,
 9751) {
 9752    init_test(cx, |_| {});
 9753
 9754    let mut cx = EditorTestContext::new(cx).await;
 9755
 9756    let diff_base = r#"
 9757        use some::mod1;
 9758        use some::mod2;
 9759
 9760        const A: u32 = 42;
 9761        const B: u32 = 42;
 9762        const C: u32 = 42;
 9763
 9764        fn main(ˇ) {
 9765            println!("hello");
 9766
 9767            println!("world");
 9768        }
 9769        "#
 9770    .unindent();
 9771
 9772    cx.set_state(
 9773        &r#"
 9774        use some::mod2;
 9775
 9776        const A: u32 = 42;
 9777        const C: u32 = 42;
 9778
 9779        fn main(ˇ) {
 9780            //println!("hello");
 9781
 9782            println!("world");
 9783            //
 9784            //
 9785        }
 9786        "#
 9787        .unindent(),
 9788    );
 9789
 9790    cx.set_diff_base(Some(&diff_base));
 9791    executor.run_until_parked();
 9792    cx.update_editor(|editor, cx| {
 9793        let snapshot = editor.snapshot(cx);
 9794        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9795        assert_eq!(
 9796            all_hunks,
 9797            vec![
 9798                (
 9799                    "use some::mod1;\n".to_string(),
 9800                    DiffHunkStatus::Removed,
 9801                    DisplayRow(0)..DisplayRow(0)
 9802                ),
 9803                (
 9804                    "const B: u32 = 42;\n".to_string(),
 9805                    DiffHunkStatus::Removed,
 9806                    DisplayRow(3)..DisplayRow(3)
 9807                ),
 9808                (
 9809                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9810                    DiffHunkStatus::Modified,
 9811                    DisplayRow(5)..DisplayRow(7)
 9812                ),
 9813                (
 9814                    "".to_string(),
 9815                    DiffHunkStatus::Added,
 9816                    DisplayRow(9)..DisplayRow(11)
 9817                ),
 9818            ]
 9819        );
 9820    });
 9821
 9822    cx.update_editor(|editor, cx| {
 9823        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9824    });
 9825    executor.run_until_parked();
 9826    cx.assert_editor_state(
 9827        &r#"
 9828        use some::mod2;
 9829
 9830        const A: u32 = 42;
 9831        const C: u32 = 42;
 9832
 9833        fn main(ˇ) {
 9834            //println!("hello");
 9835
 9836            println!("world");
 9837            //
 9838            //
 9839        }
 9840        "#
 9841        .unindent(),
 9842    );
 9843    cx.update_editor(|editor, cx| {
 9844        let snapshot = editor.snapshot(cx);
 9845        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9846        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
 9847        assert_eq!(
 9848            expanded_hunks_background_highlights(editor, cx),
 9849            vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
 9850            "After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
 9851        );
 9852        assert_eq!(
 9853            all_hunks,
 9854            vec![
 9855                ("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
 9856                ("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
 9857                ("fn main(ˇ) {\n    println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
 9858                ("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
 9859            ],
 9860            "After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
 9861            (from modified and removed hunks)"
 9862        );
 9863        assert_eq!(
 9864            all_hunks, all_expanded_hunks,
 9865            "Editor hunks should not change and all be expanded"
 9866        );
 9867    });
 9868
 9869    cx.set_diff_base(Some("new diff base!"));
 9870    executor.run_until_parked();
 9871
 9872    cx.update_editor(|editor, cx| {
 9873        let snapshot = editor.snapshot(cx);
 9874        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9875        let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
 9876        assert_eq!(
 9877            expanded_hunks_background_highlights(editor, cx),
 9878            Vec::new(),
 9879            "After diff base is changed, old git highlights should be removed"
 9880        );
 9881        assert_eq!(
 9882            all_expanded_hunks,
 9883            Vec::new(),
 9884            "After diff base is changed, old git hunk expansions should be removed"
 9885        );
 9886        assert_eq!(
 9887            all_hunks,
 9888            vec![(
 9889                "new diff base!".to_string(),
 9890                DiffHunkStatus::Modified,
 9891                DisplayRow(0)..snapshot.display_snapshot.max_point().row()
 9892            )],
 9893            "After diff base is changed, hunks should update"
 9894        );
 9895    });
 9896}
 9897
 9898#[gpui::test]
 9899async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
 9900    init_test(cx, |_| {});
 9901
 9902    let mut cx = EditorTestContext::new(cx).await;
 9903
 9904    let diff_base = r#"
 9905        use some::mod1;
 9906        use some::mod2;
 9907
 9908        const A: u32 = 42;
 9909        const B: u32 = 42;
 9910        const C: u32 = 42;
 9911
 9912        fn main(ˇ) {
 9913            println!("hello");
 9914
 9915            println!("world");
 9916        }
 9917
 9918        fn another() {
 9919            println!("another");
 9920        }
 9921
 9922        fn another2() {
 9923            println!("another2");
 9924        }
 9925        "#
 9926    .unindent();
 9927
 9928    cx.set_state(
 9929        &r#"
 9930        «use some::mod2;
 9931
 9932        const A: u32 = 42;
 9933        const C: u32 = 42;
 9934
 9935        fn main() {
 9936            //println!("hello");
 9937
 9938            println!("world");
 9939            //
 9940            //ˇ»
 9941        }
 9942
 9943        fn another() {
 9944            println!("another");
 9945            println!("another");
 9946        }
 9947
 9948            println!("another2");
 9949        }
 9950        "#
 9951        .unindent(),
 9952    );
 9953
 9954    cx.set_diff_base(Some(&diff_base));
 9955    executor.run_until_parked();
 9956    cx.update_editor(|editor, cx| {
 9957        let snapshot = editor.snapshot(cx);
 9958        let all_hunks = editor_hunks(editor, &snapshot, cx);
 9959        assert_eq!(
 9960            all_hunks,
 9961            vec![
 9962                (
 9963                    "use some::mod1;\n".to_string(),
 9964                    DiffHunkStatus::Removed,
 9965                    DisplayRow(0)..DisplayRow(0)
 9966                ),
 9967                (
 9968                    "const B: u32 = 42;\n".to_string(),
 9969                    DiffHunkStatus::Removed,
 9970                    DisplayRow(3)..DisplayRow(3)
 9971                ),
 9972                (
 9973                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
 9974                    DiffHunkStatus::Modified,
 9975                    DisplayRow(5)..DisplayRow(7)
 9976                ),
 9977                (
 9978                    "".to_string(),
 9979                    DiffHunkStatus::Added,
 9980                    DisplayRow(9)..DisplayRow(11)
 9981                ),
 9982                (
 9983                    "".to_string(),
 9984                    DiffHunkStatus::Added,
 9985                    DisplayRow(15)..DisplayRow(16)
 9986                ),
 9987                (
 9988                    "fn another2() {\n".to_string(),
 9989                    DiffHunkStatus::Removed,
 9990                    DisplayRow(18)..DisplayRow(18)
 9991                ),
 9992            ]
 9993        );
 9994    });
 9995
 9996    cx.update_editor(|editor, cx| {
 9997        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
 9998    });
 9999    executor.run_until_parked();
10000    cx.assert_editor_state(
10001        &r#"
10002        «use some::mod2;
10003
10004        const A: u32 = 42;
10005        const C: u32 = 42;
10006
10007        fn main() {
10008            //println!("hello");
10009
10010            println!("world");
10011            //
10012            //ˇ»
10013        }
10014
10015        fn another() {
10016            println!("another");
10017            println!("another");
10018        }
10019
10020            println!("another2");
10021        }
10022        "#
10023        .unindent(),
10024    );
10025    cx.update_editor(|editor, cx| {
10026        let snapshot = editor.snapshot(cx);
10027        let all_hunks = editor_hunks(editor, &snapshot, cx);
10028        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10029        assert_eq!(
10030            expanded_hunks_background_highlights(editor, cx),
10031            vec![
10032                DisplayRow(9)..=DisplayRow(10),
10033                DisplayRow(13)..=DisplayRow(14),
10034                DisplayRow(19)..=DisplayRow(19)
10035            ]
10036        );
10037        assert_eq!(
10038            all_hunks,
10039            vec![
10040                (
10041                    "use some::mod1;\n".to_string(),
10042                    DiffHunkStatus::Removed,
10043                    DisplayRow(1)..DisplayRow(1)
10044                ),
10045                (
10046                    "const B: u32 = 42;\n".to_string(),
10047                    DiffHunkStatus::Removed,
10048                    DisplayRow(5)..DisplayRow(5)
10049                ),
10050                (
10051                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10052                    DiffHunkStatus::Modified,
10053                    DisplayRow(9)..DisplayRow(11)
10054                ),
10055                (
10056                    "".to_string(),
10057                    DiffHunkStatus::Added,
10058                    DisplayRow(13)..DisplayRow(15)
10059                ),
10060                (
10061                    "".to_string(),
10062                    DiffHunkStatus::Added,
10063                    DisplayRow(19)..DisplayRow(20)
10064                ),
10065                (
10066                    "fn another2() {\n".to_string(),
10067                    DiffHunkStatus::Removed,
10068                    DisplayRow(23)..DisplayRow(23)
10069                ),
10070            ],
10071        );
10072        assert_eq!(all_hunks, all_expanded_hunks);
10073    });
10074
10075    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
10076    cx.executor().run_until_parked();
10077    cx.assert_editor_state(
10078        &r#"
10079        «use some::mod2;
10080
10081        const A: u32 = 42;
10082        const C: u32 = 42;
10083
10084        fn main() {
10085            //println!("hello");
10086
10087            println!("world");
10088            //
10089            //ˇ»
10090        }
10091
10092        fn another() {
10093            println!("another");
10094            println!("another");
10095        }
10096
10097            println!("another2");
10098        }
10099        "#
10100        .unindent(),
10101    );
10102    cx.update_editor(|editor, cx| {
10103        let snapshot = editor.snapshot(cx);
10104        let all_hunks = editor_hunks(editor, &snapshot, cx);
10105        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10106        assert_eq!(
10107            expanded_hunks_background_highlights(editor, cx),
10108            vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
10109            "Only one hunk is left not folded, its highlight should be visible"
10110        );
10111        assert_eq!(
10112            all_hunks,
10113            vec![
10114                (
10115                    "use some::mod1;\n".to_string(),
10116                    DiffHunkStatus::Removed,
10117                    DisplayRow(0)..DisplayRow(0)
10118                ),
10119                (
10120                    "const B: u32 = 42;\n".to_string(),
10121                    DiffHunkStatus::Removed,
10122                    DisplayRow(0)..DisplayRow(0)
10123                ),
10124                (
10125                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10126                    DiffHunkStatus::Modified,
10127                    DisplayRow(0)..DisplayRow(0)
10128                ),
10129                (
10130                    "".to_string(),
10131                    DiffHunkStatus::Added,
10132                    DisplayRow(0)..DisplayRow(1)
10133                ),
10134                (
10135                    "".to_string(),
10136                    DiffHunkStatus::Added,
10137                    DisplayRow(5)..DisplayRow(6)
10138                ),
10139                (
10140                    "fn another2() {\n".to_string(),
10141                    DiffHunkStatus::Removed,
10142                    DisplayRow(9)..DisplayRow(9)
10143                ),
10144            ],
10145            "Hunk list should still return shifted folded hunks"
10146        );
10147        assert_eq!(
10148            all_expanded_hunks,
10149            vec![
10150                (
10151                    "".to_string(),
10152                    DiffHunkStatus::Added,
10153                    DisplayRow(5)..DisplayRow(6)
10154                ),
10155                (
10156                    "fn another2() {\n".to_string(),
10157                    DiffHunkStatus::Removed,
10158                    DisplayRow(9)..DisplayRow(9)
10159                ),
10160            ],
10161            "Only non-folded hunks should be left expanded"
10162        );
10163    });
10164
10165    cx.update_editor(|editor, cx| {
10166        editor.select_all(&SelectAll, cx);
10167        editor.unfold_lines(&UnfoldLines, cx);
10168    });
10169    cx.executor().run_until_parked();
10170    cx.assert_editor_state(
10171        &r#"
10172        «use some::mod2;
10173
10174        const A: u32 = 42;
10175        const C: u32 = 42;
10176
10177        fn main() {
10178            //println!("hello");
10179
10180            println!("world");
10181            //
10182            //
10183        }
10184
10185        fn another() {
10186            println!("another");
10187            println!("another");
10188        }
10189
10190            println!("another2");
10191        }
10192        ˇ»"#
10193        .unindent(),
10194    );
10195    cx.update_editor(|editor, cx| {
10196        let snapshot = editor.snapshot(cx);
10197        let all_hunks = editor_hunks(editor, &snapshot, cx);
10198        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10199        assert_eq!(
10200            expanded_hunks_background_highlights(editor, cx),
10201            vec![
10202                DisplayRow(9)..=DisplayRow(10),
10203                DisplayRow(13)..=DisplayRow(14),
10204                DisplayRow(19)..=DisplayRow(19)
10205            ],
10206            "After unfolding, all hunk diffs should be visible again"
10207        );
10208        assert_eq!(
10209            all_hunks,
10210            vec![
10211                (
10212                    "use some::mod1;\n".to_string(),
10213                    DiffHunkStatus::Removed,
10214                    DisplayRow(1)..DisplayRow(1)
10215                ),
10216                (
10217                    "const B: u32 = 42;\n".to_string(),
10218                    DiffHunkStatus::Removed,
10219                    DisplayRow(5)..DisplayRow(5)
10220                ),
10221                (
10222                    "fn main(ˇ) {\n    println!(\"hello\");\n".to_string(),
10223                    DiffHunkStatus::Modified,
10224                    DisplayRow(9)..DisplayRow(11)
10225                ),
10226                (
10227                    "".to_string(),
10228                    DiffHunkStatus::Added,
10229                    DisplayRow(13)..DisplayRow(15)
10230                ),
10231                (
10232                    "".to_string(),
10233                    DiffHunkStatus::Added,
10234                    DisplayRow(19)..DisplayRow(20)
10235                ),
10236                (
10237                    "fn another2() {\n".to_string(),
10238                    DiffHunkStatus::Removed,
10239                    DisplayRow(23)..DisplayRow(23)
10240                ),
10241            ],
10242        );
10243        assert_eq!(all_hunks, all_expanded_hunks);
10244    });
10245}
10246
10247#[gpui::test]
10248async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
10249    init_test(cx, |_| {});
10250
10251    let cols = 4;
10252    let rows = 10;
10253    let sample_text_1 = sample_text(rows, cols, 'a');
10254    assert_eq!(
10255        sample_text_1,
10256        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
10257    );
10258    let modified_sample_text_1 = "aaaa\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
10259    let sample_text_2 = sample_text(rows, cols, 'l');
10260    assert_eq!(
10261        sample_text_2,
10262        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
10263    );
10264    let modified_sample_text_2 = "llll\nmmmm\n1n1n1n1n1\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
10265    let sample_text_3 = sample_text(rows, cols, 'v');
10266    assert_eq!(
10267        sample_text_3,
10268        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
10269    );
10270    let modified_sample_text_3 =
10271        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n@@@@\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
10272    let buffer_1 = cx.new_model(|cx| {
10273        let mut buffer = Buffer::local(modified_sample_text_1.to_string(), cx);
10274        buffer.set_diff_base(Some(sample_text_1.clone()), cx);
10275        buffer
10276    });
10277    let buffer_2 = cx.new_model(|cx| {
10278        let mut buffer = Buffer::local(modified_sample_text_2.to_string(), cx);
10279        buffer.set_diff_base(Some(sample_text_2.clone()), cx);
10280        buffer
10281    });
10282    let buffer_3 = cx.new_model(|cx| {
10283        let mut buffer = Buffer::local(modified_sample_text_3.to_string(), cx);
10284        buffer.set_diff_base(Some(sample_text_3.clone()), cx);
10285        buffer
10286    });
10287
10288    let multi_buffer = cx.new_model(|cx| {
10289        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
10290        multibuffer.push_excerpts(
10291            buffer_1.clone(),
10292            [
10293                ExcerptRange {
10294                    context: Point::new(0, 0)..Point::new(3, 0),
10295                    primary: None,
10296                },
10297                ExcerptRange {
10298                    context: Point::new(5, 0)..Point::new(7, 0),
10299                    primary: None,
10300                },
10301                ExcerptRange {
10302                    context: Point::new(9, 0)..Point::new(10, 4),
10303                    primary: None,
10304                },
10305            ],
10306            cx,
10307        );
10308        multibuffer.push_excerpts(
10309            buffer_2.clone(),
10310            [
10311                ExcerptRange {
10312                    context: Point::new(0, 0)..Point::new(3, 0),
10313                    primary: None,
10314                },
10315                ExcerptRange {
10316                    context: Point::new(5, 0)..Point::new(7, 0),
10317                    primary: None,
10318                },
10319                ExcerptRange {
10320                    context: Point::new(9, 0)..Point::new(10, 4),
10321                    primary: None,
10322                },
10323            ],
10324            cx,
10325        );
10326        multibuffer.push_excerpts(
10327            buffer_3.clone(),
10328            [
10329                ExcerptRange {
10330                    context: Point::new(0, 0)..Point::new(3, 0),
10331                    primary: None,
10332                },
10333                ExcerptRange {
10334                    context: Point::new(5, 0)..Point::new(7, 0),
10335                    primary: None,
10336                },
10337                ExcerptRange {
10338                    context: Point::new(9, 0)..Point::new(10, 4),
10339                    primary: None,
10340                },
10341            ],
10342            cx,
10343        );
10344        multibuffer
10345    });
10346
10347    let fs = FakeFs::new(cx.executor());
10348    fs.insert_tree(
10349        "/a",
10350        json!({
10351            "main.rs": modified_sample_text_1,
10352            "other.rs": modified_sample_text_2,
10353            "lib.rs": modified_sample_text_3,
10354        }),
10355    )
10356    .await;
10357
10358    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10359    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10360    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10361    let multi_buffer_editor =
10362        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
10363    cx.executor().run_until_parked();
10364
10365    let expected_all_hunks = vec![
10366        (
10367            "bbbb\n".to_string(),
10368            DiffHunkStatus::Removed,
10369            DisplayRow(3)..DisplayRow(3),
10370        ),
10371        (
10372            "nnnn\n".to_string(),
10373            DiffHunkStatus::Modified,
10374            DisplayRow(16)..DisplayRow(17),
10375        ),
10376        (
10377            "".to_string(),
10378            DiffHunkStatus::Added,
10379            DisplayRow(31)..DisplayRow(32),
10380        ),
10381    ];
10382    let expected_all_hunks_shifted = vec![
10383        (
10384            "bbbb\n".to_string(),
10385            DiffHunkStatus::Removed,
10386            DisplayRow(4)..DisplayRow(4),
10387        ),
10388        (
10389            "nnnn\n".to_string(),
10390            DiffHunkStatus::Modified,
10391            DisplayRow(18)..DisplayRow(19),
10392        ),
10393        (
10394            "".to_string(),
10395            DiffHunkStatus::Added,
10396            DisplayRow(33)..DisplayRow(34),
10397        ),
10398    ];
10399
10400    multi_buffer_editor.update(cx, |editor, cx| {
10401        let snapshot = editor.snapshot(cx);
10402        let all_hunks = editor_hunks(editor, &snapshot, cx);
10403        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10404        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10405        assert_eq!(all_hunks, expected_all_hunks);
10406        assert_eq!(all_expanded_hunks, Vec::new());
10407    });
10408
10409    multi_buffer_editor.update(cx, |editor, cx| {
10410        editor.select_all(&SelectAll, cx);
10411        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10412    });
10413    cx.executor().run_until_parked();
10414    multi_buffer_editor.update(cx, |editor, cx| {
10415        let snapshot = editor.snapshot(cx);
10416        let all_hunks = editor_hunks(editor, &snapshot, cx);
10417        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10418        assert_eq!(
10419            expanded_hunks_background_highlights(editor, cx),
10420            vec![
10421                DisplayRow(18)..=DisplayRow(18),
10422                DisplayRow(33)..=DisplayRow(33)
10423            ],
10424        );
10425        assert_eq!(all_hunks, expected_all_hunks_shifted);
10426        assert_eq!(all_hunks, all_expanded_hunks);
10427    });
10428
10429    multi_buffer_editor.update(cx, |editor, cx| {
10430        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10431    });
10432    cx.executor().run_until_parked();
10433    multi_buffer_editor.update(cx, |editor, cx| {
10434        let snapshot = editor.snapshot(cx);
10435        let all_hunks = editor_hunks(editor, &snapshot, cx);
10436        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10437        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10438        assert_eq!(all_hunks, expected_all_hunks);
10439        assert_eq!(all_expanded_hunks, Vec::new());
10440    });
10441
10442    multi_buffer_editor.update(cx, |editor, cx| {
10443        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10444    });
10445    cx.executor().run_until_parked();
10446    multi_buffer_editor.update(cx, |editor, cx| {
10447        let snapshot = editor.snapshot(cx);
10448        let all_hunks = editor_hunks(editor, &snapshot, cx);
10449        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10450        assert_eq!(
10451            expanded_hunks_background_highlights(editor, cx),
10452            vec![
10453                DisplayRow(18)..=DisplayRow(18),
10454                DisplayRow(33)..=DisplayRow(33)
10455            ],
10456        );
10457        assert_eq!(all_hunks, expected_all_hunks_shifted);
10458        assert_eq!(all_hunks, all_expanded_hunks);
10459    });
10460
10461    multi_buffer_editor.update(cx, |editor, cx| {
10462        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
10463    });
10464    cx.executor().run_until_parked();
10465    multi_buffer_editor.update(cx, |editor, cx| {
10466        let snapshot = editor.snapshot(cx);
10467        let all_hunks = editor_hunks(editor, &snapshot, cx);
10468        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10469        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10470        assert_eq!(all_hunks, expected_all_hunks);
10471        assert_eq!(all_expanded_hunks, Vec::new());
10472    });
10473}
10474
10475#[gpui::test]
10476async fn test_edits_around_toggled_additions(
10477    executor: BackgroundExecutor,
10478    cx: &mut gpui::TestAppContext,
10479) {
10480    init_test(cx, |_| {});
10481
10482    let mut cx = EditorTestContext::new(cx).await;
10483
10484    let diff_base = r#"
10485        use some::mod1;
10486        use some::mod2;
10487
10488        const A: u32 = 42;
10489
10490        fn main() {
10491            println!("hello");
10492
10493            println!("world");
10494        }
10495        "#
10496    .unindent();
10497    executor.run_until_parked();
10498    cx.set_state(
10499        &r#"
10500        use some::mod1;
10501        use some::mod2;
10502
10503        const A: u32 = 42;
10504        const B: u32 = 42;
10505        const C: u32 = 42;
10506        ˇ
10507
10508        fn main() {
10509            println!("hello");
10510
10511            println!("world");
10512        }
10513        "#
10514        .unindent(),
10515    );
10516
10517    cx.set_diff_base(Some(&diff_base));
10518    executor.run_until_parked();
10519    cx.update_editor(|editor, cx| {
10520        let snapshot = editor.snapshot(cx);
10521        let all_hunks = editor_hunks(editor, &snapshot, cx);
10522        assert_eq!(
10523            all_hunks,
10524            vec![(
10525                "".to_string(),
10526                DiffHunkStatus::Added,
10527                DisplayRow(4)..DisplayRow(7)
10528            )]
10529        );
10530    });
10531    cx.update_editor(|editor, cx| {
10532        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10533    });
10534    executor.run_until_parked();
10535    cx.assert_editor_state(
10536        &r#"
10537        use some::mod1;
10538        use some::mod2;
10539
10540        const A: u32 = 42;
10541        const B: u32 = 42;
10542        const C: u32 = 42;
10543        ˇ
10544
10545        fn main() {
10546            println!("hello");
10547
10548            println!("world");
10549        }
10550        "#
10551        .unindent(),
10552    );
10553    cx.update_editor(|editor, cx| {
10554        let snapshot = editor.snapshot(cx);
10555        let all_hunks = editor_hunks(editor, &snapshot, cx);
10556        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10557        assert_eq!(
10558            all_hunks,
10559            vec![(
10560                "".to_string(),
10561                DiffHunkStatus::Added,
10562                DisplayRow(4)..DisplayRow(7)
10563            )]
10564        );
10565        assert_eq!(
10566            expanded_hunks_background_highlights(editor, cx),
10567            vec![DisplayRow(4)..=DisplayRow(6)]
10568        );
10569        assert_eq!(all_hunks, all_expanded_hunks);
10570    });
10571
10572    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
10573    executor.run_until_parked();
10574    cx.assert_editor_state(
10575        &r#"
10576        use some::mod1;
10577        use some::mod2;
10578
10579        const A: u32 = 42;
10580        const B: u32 = 42;
10581        const C: u32 = 42;
10582        const D: u32 = 42;
10583        ˇ
10584
10585        fn main() {
10586            println!("hello");
10587
10588            println!("world");
10589        }
10590        "#
10591        .unindent(),
10592    );
10593    cx.update_editor(|editor, cx| {
10594        let snapshot = editor.snapshot(cx);
10595        let all_hunks = editor_hunks(editor, &snapshot, cx);
10596        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10597        assert_eq!(
10598            all_hunks,
10599            vec![(
10600                "".to_string(),
10601                DiffHunkStatus::Added,
10602                DisplayRow(4)..DisplayRow(8)
10603            )]
10604        );
10605        assert_eq!(
10606            expanded_hunks_background_highlights(editor, cx),
10607            vec![DisplayRow(4)..=DisplayRow(6)],
10608            "Edited hunk should have one more line added"
10609        );
10610        assert_eq!(
10611            all_hunks, all_expanded_hunks,
10612            "Expanded hunk should also grow with the addition"
10613        );
10614    });
10615
10616    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
10617    executor.run_until_parked();
10618    cx.assert_editor_state(
10619        &r#"
10620        use some::mod1;
10621        use some::mod2;
10622
10623        const A: u32 = 42;
10624        const B: u32 = 42;
10625        const C: u32 = 42;
10626        const D: u32 = 42;
10627        const E: u32 = 42;
10628        ˇ
10629
10630        fn main() {
10631            println!("hello");
10632
10633            println!("world");
10634        }
10635        "#
10636        .unindent(),
10637    );
10638    cx.update_editor(|editor, cx| {
10639        let snapshot = editor.snapshot(cx);
10640        let all_hunks = editor_hunks(editor, &snapshot, cx);
10641        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10642        assert_eq!(
10643            all_hunks,
10644            vec![(
10645                "".to_string(),
10646                DiffHunkStatus::Added,
10647                DisplayRow(4)..DisplayRow(9)
10648            )]
10649        );
10650        assert_eq!(
10651            expanded_hunks_background_highlights(editor, cx),
10652            vec![DisplayRow(4)..=DisplayRow(6)],
10653            "Edited hunk should have one more line added"
10654        );
10655        assert_eq!(all_hunks, all_expanded_hunks);
10656    });
10657
10658    cx.update_editor(|editor, cx| {
10659        editor.move_up(&MoveUp, cx);
10660        editor.delete_line(&DeleteLine, cx);
10661    });
10662    executor.run_until_parked();
10663    cx.assert_editor_state(
10664        &r#"
10665        use some::mod1;
10666        use some::mod2;
10667
10668        const A: u32 = 42;
10669        const B: u32 = 42;
10670        const C: u32 = 42;
10671        const D: u32 = 42;
10672        ˇ
10673
10674        fn main() {
10675            println!("hello");
10676
10677            println!("world");
10678        }
10679        "#
10680        .unindent(),
10681    );
10682    cx.update_editor(|editor, cx| {
10683        let snapshot = editor.snapshot(cx);
10684        let all_hunks = editor_hunks(editor, &snapshot, cx);
10685        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10686        assert_eq!(
10687            all_hunks,
10688            vec![(
10689                "".to_string(),
10690                DiffHunkStatus::Added,
10691                DisplayRow(4)..DisplayRow(8)
10692            )]
10693        );
10694        assert_eq!(
10695            expanded_hunks_background_highlights(editor, cx),
10696            vec![DisplayRow(4)..=DisplayRow(6)],
10697            "Deleting a line should shrint the hunk"
10698        );
10699        assert_eq!(
10700            all_hunks, all_expanded_hunks,
10701            "Expanded hunk should also shrink with the addition"
10702        );
10703    });
10704
10705    cx.update_editor(|editor, cx| {
10706        editor.move_up(&MoveUp, cx);
10707        editor.delete_line(&DeleteLine, cx);
10708        editor.move_up(&MoveUp, cx);
10709        editor.delete_line(&DeleteLine, cx);
10710        editor.move_up(&MoveUp, cx);
10711        editor.delete_line(&DeleteLine, cx);
10712    });
10713    executor.run_until_parked();
10714    cx.assert_editor_state(
10715        &r#"
10716        use some::mod1;
10717        use some::mod2;
10718
10719        const A: u32 = 42;
10720        ˇ
10721
10722        fn main() {
10723            println!("hello");
10724
10725            println!("world");
10726        }
10727        "#
10728        .unindent(),
10729    );
10730    cx.update_editor(|editor, cx| {
10731        let snapshot = editor.snapshot(cx);
10732        let all_hunks = editor_hunks(editor, &snapshot, cx);
10733        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10734        assert_eq!(
10735            all_hunks,
10736            vec![(
10737                "".to_string(),
10738                DiffHunkStatus::Added,
10739                DisplayRow(5)..DisplayRow(6)
10740            )]
10741        );
10742        assert_eq!(
10743            expanded_hunks_background_highlights(editor, cx),
10744            vec![DisplayRow(5)..=DisplayRow(5)]
10745        );
10746        assert_eq!(all_hunks, all_expanded_hunks);
10747    });
10748
10749    cx.update_editor(|editor, cx| {
10750        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
10751        editor.delete_line(&DeleteLine, cx);
10752    });
10753    executor.run_until_parked();
10754    cx.assert_editor_state(
10755        &r#"
10756        ˇ
10757
10758        fn main() {
10759            println!("hello");
10760
10761            println!("world");
10762        }
10763        "#
10764        .unindent(),
10765    );
10766    cx.update_editor(|editor, cx| {
10767        let snapshot = editor.snapshot(cx);
10768        let all_hunks = editor_hunks(editor, &snapshot, cx);
10769        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10770        assert_eq!(
10771            all_hunks,
10772            vec![
10773                (
10774                    "use some::mod1;\nuse some::mod2;\n".to_string(),
10775                    DiffHunkStatus::Removed,
10776                    DisplayRow(0)..DisplayRow(0)
10777                ),
10778                (
10779                    "const A: u32 = 42;\n".to_string(),
10780                    DiffHunkStatus::Removed,
10781                    DisplayRow(2)..DisplayRow(2)
10782                )
10783            ]
10784        );
10785        assert_eq!(
10786            expanded_hunks_background_highlights(editor, cx),
10787            Vec::new(),
10788            "Should close all stale expanded addition hunks"
10789        );
10790        assert_eq!(
10791            all_expanded_hunks,
10792            vec![(
10793                "const A: u32 = 42;\n".to_string(),
10794                DiffHunkStatus::Removed,
10795                DisplayRow(2)..DisplayRow(2)
10796            )],
10797            "Should open hunks that were adjacent to the stale addition one"
10798        );
10799    });
10800}
10801
10802#[gpui::test]
10803async fn test_edits_around_toggled_deletions(
10804    executor: BackgroundExecutor,
10805    cx: &mut gpui::TestAppContext,
10806) {
10807    init_test(cx, |_| {});
10808
10809    let mut cx = EditorTestContext::new(cx).await;
10810
10811    let diff_base = r#"
10812        use some::mod1;
10813        use some::mod2;
10814
10815        const A: u32 = 42;
10816        const B: u32 = 42;
10817        const C: u32 = 42;
10818
10819
10820        fn main() {
10821            println!("hello");
10822
10823            println!("world");
10824        }
10825        "#
10826    .unindent();
10827    executor.run_until_parked();
10828    cx.set_state(
10829        &r#"
10830        use some::mod1;
10831        use some::mod2;
10832
10833        ˇconst B: u32 = 42;
10834        const C: u32 = 42;
10835
10836
10837        fn main() {
10838            println!("hello");
10839
10840            println!("world");
10841        }
10842        "#
10843        .unindent(),
10844    );
10845
10846    cx.set_diff_base(Some(&diff_base));
10847    executor.run_until_parked();
10848    cx.update_editor(|editor, cx| {
10849        let snapshot = editor.snapshot(cx);
10850        let all_hunks = editor_hunks(editor, &snapshot, cx);
10851        assert_eq!(
10852            all_hunks,
10853            vec![(
10854                "const A: u32 = 42;\n".to_string(),
10855                DiffHunkStatus::Removed,
10856                DisplayRow(3)..DisplayRow(3)
10857            )]
10858        );
10859    });
10860    cx.update_editor(|editor, cx| {
10861        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
10862    });
10863    executor.run_until_parked();
10864    cx.assert_editor_state(
10865        &r#"
10866        use some::mod1;
10867        use some::mod2;
10868
10869        ˇconst B: u32 = 42;
10870        const C: u32 = 42;
10871
10872
10873        fn main() {
10874            println!("hello");
10875
10876            println!("world");
10877        }
10878        "#
10879        .unindent(),
10880    );
10881    cx.update_editor(|editor, cx| {
10882        let snapshot = editor.snapshot(cx);
10883        let all_hunks = editor_hunks(editor, &snapshot, cx);
10884        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10885        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10886        assert_eq!(
10887            all_hunks,
10888            vec![(
10889                "const A: u32 = 42;\n".to_string(),
10890                DiffHunkStatus::Removed,
10891                DisplayRow(4)..DisplayRow(4)
10892            )]
10893        );
10894        assert_eq!(all_hunks, all_expanded_hunks);
10895    });
10896
10897    cx.update_editor(|editor, cx| {
10898        editor.delete_line(&DeleteLine, cx);
10899    });
10900    executor.run_until_parked();
10901    cx.assert_editor_state(
10902        &r#"
10903        use some::mod1;
10904        use some::mod2;
10905
10906        ˇconst C: u32 = 42;
10907
10908
10909        fn main() {
10910            println!("hello");
10911
10912            println!("world");
10913        }
10914        "#
10915        .unindent(),
10916    );
10917    cx.update_editor(|editor, cx| {
10918        let snapshot = editor.snapshot(cx);
10919        let all_hunks = editor_hunks(editor, &snapshot, cx);
10920        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10921        assert_eq!(
10922            expanded_hunks_background_highlights(editor, cx),
10923            Vec::new(),
10924            "Deleted hunks do not highlight current editor's background"
10925        );
10926        assert_eq!(
10927            all_hunks,
10928            vec![(
10929                "const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
10930                DiffHunkStatus::Removed,
10931                DisplayRow(5)..DisplayRow(5)
10932            )]
10933        );
10934        assert_eq!(all_hunks, all_expanded_hunks);
10935    });
10936
10937    cx.update_editor(|editor, cx| {
10938        editor.delete_line(&DeleteLine, cx);
10939    });
10940    executor.run_until_parked();
10941    cx.assert_editor_state(
10942        &r#"
10943        use some::mod1;
10944        use some::mod2;
10945
10946        ˇ
10947
10948        fn main() {
10949            println!("hello");
10950
10951            println!("world");
10952        }
10953        "#
10954        .unindent(),
10955    );
10956    cx.update_editor(|editor, cx| {
10957        let snapshot = editor.snapshot(cx);
10958        let all_hunks = editor_hunks(editor, &snapshot, cx);
10959        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10960        assert_eq!(expanded_hunks_background_highlights(editor, cx), Vec::new());
10961        assert_eq!(
10962            all_hunks,
10963            vec![(
10964                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
10965                DiffHunkStatus::Removed,
10966                DisplayRow(6)..DisplayRow(6)
10967            )]
10968        );
10969        assert_eq!(all_hunks, all_expanded_hunks);
10970    });
10971
10972    cx.update_editor(|editor, cx| {
10973        editor.handle_input("replacement", cx);
10974    });
10975    executor.run_until_parked();
10976    cx.assert_editor_state(
10977        &r#"
10978        use some::mod1;
10979        use some::mod2;
10980
10981        replacementˇ
10982
10983        fn main() {
10984            println!("hello");
10985
10986            println!("world");
10987        }
10988        "#
10989        .unindent(),
10990    );
10991    cx.update_editor(|editor, cx| {
10992        let snapshot = editor.snapshot(cx);
10993        let all_hunks = editor_hunks(editor, &snapshot, cx);
10994        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
10995        assert_eq!(
10996            all_hunks,
10997            vec![(
10998                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
10999                DiffHunkStatus::Modified,
11000                DisplayRow(7)..DisplayRow(8)
11001            )]
11002        );
11003        assert_eq!(
11004            expanded_hunks_background_highlights(editor, cx),
11005            vec![DisplayRow(7)..=DisplayRow(7)],
11006            "Modified expanded hunks should display additions and highlight their background"
11007        );
11008        assert_eq!(all_hunks, all_expanded_hunks);
11009    });
11010}
11011
11012#[gpui::test]
11013async fn test_edits_around_toggled_modifications(
11014    executor: BackgroundExecutor,
11015    cx: &mut gpui::TestAppContext,
11016) {
11017    init_test(cx, |_| {});
11018
11019    let mut cx = EditorTestContext::new(cx).await;
11020
11021    let diff_base = r#"
11022        use some::mod1;
11023        use some::mod2;
11024
11025        const A: u32 = 42;
11026        const B: u32 = 42;
11027        const C: u32 = 42;
11028        const D: u32 = 42;
11029
11030
11031        fn main() {
11032            println!("hello");
11033
11034            println!("world");
11035        }"#
11036    .unindent();
11037    executor.run_until_parked();
11038    cx.set_state(
11039        &r#"
11040        use some::mod1;
11041        use some::mod2;
11042
11043        const A: u32 = 42;
11044        const B: u32 = 42;
11045        const C: u32 = 43ˇ
11046        const D: u32 = 42;
11047
11048
11049        fn main() {
11050            println!("hello");
11051
11052            println!("world");
11053        }"#
11054        .unindent(),
11055    );
11056
11057    cx.set_diff_base(Some(&diff_base));
11058    executor.run_until_parked();
11059    cx.update_editor(|editor, cx| {
11060        let snapshot = editor.snapshot(cx);
11061        let all_hunks = editor_hunks(editor, &snapshot, cx);
11062        assert_eq!(
11063            all_hunks,
11064            vec![(
11065                "const C: u32 = 42;\n".to_string(),
11066                DiffHunkStatus::Modified,
11067                DisplayRow(5)..DisplayRow(6)
11068            )]
11069        );
11070    });
11071    cx.update_editor(|editor, cx| {
11072        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11073    });
11074    executor.run_until_parked();
11075    cx.assert_editor_state(
11076        &r#"
11077        use some::mod1;
11078        use some::mod2;
11079
11080        const A: u32 = 42;
11081        const B: u32 = 42;
11082        const C: u32 = 43ˇ
11083        const D: u32 = 42;
11084
11085
11086        fn main() {
11087            println!("hello");
11088
11089            println!("world");
11090        }"#
11091        .unindent(),
11092    );
11093    cx.update_editor(|editor, cx| {
11094        let snapshot = editor.snapshot(cx);
11095        let all_hunks = editor_hunks(editor, &snapshot, cx);
11096        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11097        assert_eq!(
11098            expanded_hunks_background_highlights(editor, cx),
11099            vec![DisplayRow(6)..=DisplayRow(6)],
11100        );
11101        assert_eq!(
11102            all_hunks,
11103            vec![(
11104                "const C: u32 = 42;\n".to_string(),
11105                DiffHunkStatus::Modified,
11106                DisplayRow(6)..DisplayRow(7)
11107            )]
11108        );
11109        assert_eq!(all_hunks, all_expanded_hunks);
11110    });
11111
11112    cx.update_editor(|editor, cx| {
11113        editor.handle_input("\nnew_line\n", cx);
11114    });
11115    executor.run_until_parked();
11116    cx.assert_editor_state(
11117        &r#"
11118            use some::mod1;
11119            use some::mod2;
11120
11121            const A: u32 = 42;
11122            const B: u32 = 42;
11123            const C: u32 = 43
11124            new_line
11125            ˇ
11126            const D: u32 = 42;
11127
11128
11129            fn main() {
11130                println!("hello");
11131
11132                println!("world");
11133            }"#
11134        .unindent(),
11135    );
11136    cx.update_editor(|editor, cx| {
11137        let snapshot = editor.snapshot(cx);
11138        let all_hunks = editor_hunks(editor, &snapshot, cx);
11139        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11140        assert_eq!(
11141            expanded_hunks_background_highlights(editor, cx),
11142            vec![DisplayRow(6)..=DisplayRow(6)],
11143            "Modified hunk should grow highlighted lines on more text additions"
11144        );
11145        assert_eq!(
11146            all_hunks,
11147            vec![(
11148                "const C: u32 = 42;\n".to_string(),
11149                DiffHunkStatus::Modified,
11150                DisplayRow(6)..DisplayRow(9)
11151            )]
11152        );
11153        assert_eq!(all_hunks, all_expanded_hunks);
11154    });
11155
11156    cx.update_editor(|editor, cx| {
11157        editor.move_up(&MoveUp, cx);
11158        editor.move_up(&MoveUp, cx);
11159        editor.move_up(&MoveUp, cx);
11160        editor.delete_line(&DeleteLine, cx);
11161    });
11162    executor.run_until_parked();
11163    cx.assert_editor_state(
11164        &r#"
11165            use some::mod1;
11166            use some::mod2;
11167
11168            const A: u32 = 42;
11169            ˇconst C: u32 = 43
11170            new_line
11171
11172            const D: u32 = 42;
11173
11174
11175            fn main() {
11176                println!("hello");
11177
11178                println!("world");
11179            }"#
11180        .unindent(),
11181    );
11182    cx.update_editor(|editor, cx| {
11183        let snapshot = editor.snapshot(cx);
11184        let all_hunks = editor_hunks(editor, &snapshot, cx);
11185        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11186        assert_eq!(
11187            expanded_hunks_background_highlights(editor, cx),
11188            vec![DisplayRow(6)..=DisplayRow(8)],
11189        );
11190        assert_eq!(
11191            all_hunks,
11192            vec![(
11193                "const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11194                DiffHunkStatus::Modified,
11195                DisplayRow(6)..DisplayRow(9)
11196            )],
11197            "Modified hunk should grow deleted lines on text deletions above"
11198        );
11199        assert_eq!(all_hunks, all_expanded_hunks);
11200    });
11201
11202    cx.update_editor(|editor, cx| {
11203        editor.move_up(&MoveUp, cx);
11204        editor.handle_input("v", cx);
11205    });
11206    executor.run_until_parked();
11207    cx.assert_editor_state(
11208        &r#"
11209            use some::mod1;
11210            use some::mod2;
11211
11212            vˇconst A: u32 = 42;
11213            const C: u32 = 43
11214            new_line
11215
11216            const D: u32 = 42;
11217
11218
11219            fn main() {
11220                println!("hello");
11221
11222                println!("world");
11223            }"#
11224        .unindent(),
11225    );
11226    cx.update_editor(|editor, cx| {
11227        let snapshot = editor.snapshot(cx);
11228        let all_hunks = editor_hunks(editor, &snapshot, cx);
11229        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11230        assert_eq!(
11231            expanded_hunks_background_highlights(editor, cx),
11232            vec![DisplayRow(6)..=DisplayRow(9)],
11233            "Modified hunk should grow deleted lines on text modifications above"
11234        );
11235        assert_eq!(
11236            all_hunks,
11237            vec![(
11238                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11239                DiffHunkStatus::Modified,
11240                DisplayRow(6)..DisplayRow(10)
11241            )]
11242        );
11243        assert_eq!(all_hunks, all_expanded_hunks);
11244    });
11245
11246    cx.update_editor(|editor, cx| {
11247        editor.move_down(&MoveDown, cx);
11248        editor.move_down(&MoveDown, cx);
11249        editor.delete_line(&DeleteLine, cx)
11250    });
11251    executor.run_until_parked();
11252    cx.assert_editor_state(
11253        &r#"
11254            use some::mod1;
11255            use some::mod2;
11256
11257            vconst A: u32 = 42;
11258            const C: u32 = 43
11259            ˇ
11260            const D: u32 = 42;
11261
11262
11263            fn main() {
11264                println!("hello");
11265
11266                println!("world");
11267            }"#
11268        .unindent(),
11269    );
11270    cx.update_editor(|editor, cx| {
11271        let snapshot = editor.snapshot(cx);
11272        let all_hunks = editor_hunks(editor, &snapshot, cx);
11273        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11274        assert_eq!(
11275            expanded_hunks_background_highlights(editor, cx),
11276            vec![DisplayRow(6)..=DisplayRow(8)],
11277            "Modified hunk should grow shrink lines on modification lines removal"
11278        );
11279        assert_eq!(
11280            all_hunks,
11281            vec![(
11282                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
11283                DiffHunkStatus::Modified,
11284                DisplayRow(6)..DisplayRow(9)
11285            )]
11286        );
11287        assert_eq!(all_hunks, all_expanded_hunks);
11288    });
11289
11290    cx.update_editor(|editor, cx| {
11291        editor.move_up(&MoveUp, cx);
11292        editor.move_up(&MoveUp, cx);
11293        editor.select_down_by_lines(&SelectDownByLines { lines: 4 }, cx);
11294        editor.delete_line(&DeleteLine, cx)
11295    });
11296    executor.run_until_parked();
11297    cx.assert_editor_state(
11298        &r#"
11299            use some::mod1;
11300            use some::mod2;
11301
11302            ˇ
11303
11304            fn main() {
11305                println!("hello");
11306
11307                println!("world");
11308            }"#
11309        .unindent(),
11310    );
11311    cx.update_editor(|editor, cx| {
11312        let snapshot = editor.snapshot(cx);
11313        let all_hunks = editor_hunks(editor, &snapshot, cx);
11314        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11315        assert_eq!(
11316            expanded_hunks_background_highlights(editor, cx),
11317            Vec::new(),
11318            "Modified hunk should turn into a removed one on all modified lines removal"
11319        );
11320        assert_eq!(
11321            all_hunks,
11322            vec![(
11323                "const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
11324                    .to_string(),
11325                DiffHunkStatus::Removed,
11326                DisplayRow(7)..DisplayRow(7)
11327            )]
11328        );
11329        assert_eq!(all_hunks, all_expanded_hunks);
11330    });
11331}
11332
11333#[gpui::test]
11334async fn test_multiple_expanded_hunks_merge(
11335    executor: BackgroundExecutor,
11336    cx: &mut gpui::TestAppContext,
11337) {
11338    init_test(cx, |_| {});
11339
11340    let mut cx = EditorTestContext::new(cx).await;
11341
11342    let diff_base = r#"
11343        use some::mod1;
11344        use some::mod2;
11345
11346        const A: u32 = 42;
11347        const B: u32 = 42;
11348        const C: u32 = 42;
11349        const D: u32 = 42;
11350
11351
11352        fn main() {
11353            println!("hello");
11354
11355            println!("world");
11356        }"#
11357    .unindent();
11358    executor.run_until_parked();
11359    cx.set_state(
11360        &r#"
11361        use some::mod1;
11362        use some::mod2;
11363
11364        const A: u32 = 42;
11365        const B: u32 = 42;
11366        const C: u32 = 43ˇ
11367        const D: u32 = 42;
11368
11369
11370        fn main() {
11371            println!("hello");
11372
11373            println!("world");
11374        }"#
11375        .unindent(),
11376    );
11377
11378    cx.set_diff_base(Some(&diff_base));
11379    executor.run_until_parked();
11380    cx.update_editor(|editor, cx| {
11381        let snapshot = editor.snapshot(cx);
11382        let all_hunks = editor_hunks(editor, &snapshot, cx);
11383        assert_eq!(
11384            all_hunks,
11385            vec![(
11386                "const C: u32 = 42;\n".to_string(),
11387                DiffHunkStatus::Modified,
11388                DisplayRow(5)..DisplayRow(6)
11389            )]
11390        );
11391    });
11392    cx.update_editor(|editor, cx| {
11393        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
11394    });
11395    executor.run_until_parked();
11396    cx.assert_editor_state(
11397        &r#"
11398        use some::mod1;
11399        use some::mod2;
11400
11401        const A: u32 = 42;
11402        const B: u32 = 42;
11403        const C: u32 = 43ˇ
11404        const D: u32 = 42;
11405
11406
11407        fn main() {
11408            println!("hello");
11409
11410            println!("world");
11411        }"#
11412        .unindent(),
11413    );
11414    cx.update_editor(|editor, cx| {
11415        let snapshot = editor.snapshot(cx);
11416        let all_hunks = editor_hunks(editor, &snapshot, cx);
11417        let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
11418        assert_eq!(
11419            expanded_hunks_background_highlights(editor, cx),
11420            vec![DisplayRow(6)..=DisplayRow(6)],
11421        );
11422        assert_eq!(
11423            all_hunks,
11424            vec![(
11425                "const C: u32 = 42;\n".to_string(),
11426                DiffHunkStatus::Modified,
11427                DisplayRow(6)..DisplayRow(7)
11428            )]
11429        );
11430        assert_eq!(all_hunks, all_expanded_hunks);
11431    });
11432
11433    cx.update_editor(|editor, cx| {
11434        editor.handle_input("\nnew_line\n", cx);
11435    });
11436    executor.run_until_parked();
11437    cx.assert_editor_state(
11438        &r#"
11439            use some::mod1;
11440            use some::mod2;
11441
11442            const A: u32 = 42;
11443            const B: u32 = 42;
11444            const C: u32 = 43
11445            new_line
11446            ˇ
11447            const D: u32 = 42;
11448
11449
11450            fn main() {
11451                println!("hello");
11452
11453                println!("world");
11454            }"#
11455        .unindent(),
11456    );
11457}
11458
11459async fn setup_indent_guides_editor(
11460    text: &str,
11461    cx: &mut gpui::TestAppContext,
11462) -> (BufferId, EditorTestContext) {
11463    init_test(cx, |_| {});
11464
11465    let mut cx = EditorTestContext::new(cx).await;
11466
11467    let buffer_id = cx.update_editor(|editor, cx| {
11468        editor.set_text(text, cx);
11469        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
11470        let buffer_id = buffer_ids[0];
11471        buffer_id
11472    });
11473
11474    (buffer_id, cx)
11475}
11476
11477fn assert_indent_guides(
11478    range: Range<u32>,
11479    expected: Vec<IndentGuide>,
11480    active_indices: Option<Vec<usize>>,
11481    cx: &mut EditorTestContext,
11482) {
11483    let indent_guides = cx.update_editor(|editor, cx| {
11484        let snapshot = editor.snapshot(cx).display_snapshot;
11485        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
11486            MultiBufferRow(range.start)..MultiBufferRow(range.end),
11487            &snapshot,
11488            cx,
11489        );
11490
11491        indent_guides.sort_by(|a, b| {
11492            a.depth.cmp(&b.depth).then(
11493                a.start_row
11494                    .cmp(&b.start_row)
11495                    .then(a.end_row.cmp(&b.end_row)),
11496            )
11497        });
11498        indent_guides
11499    });
11500
11501    if let Some(expected) = active_indices {
11502        let active_indices = cx.update_editor(|editor, cx| {
11503            let snapshot = editor.snapshot(cx).display_snapshot;
11504            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
11505        });
11506
11507        assert_eq!(
11508            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
11509            expected,
11510            "Active indent guide indices do not match"
11511        );
11512    }
11513
11514    let expected: Vec<_> = expected
11515        .into_iter()
11516        .map(|guide| MultiBufferIndentGuide {
11517            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
11518            buffer: guide,
11519        })
11520        .collect();
11521
11522    assert_eq!(indent_guides, expected, "Indent guides do not match");
11523}
11524
11525#[gpui::test]
11526async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
11527    let (buffer_id, mut cx) = setup_indent_guides_editor(
11528        &"
11529    fn main() {
11530        let a = 1;
11531    }"
11532        .unindent(),
11533        cx,
11534    )
11535    .await;
11536
11537    assert_indent_guides(
11538        0..3,
11539        vec![IndentGuide::new(buffer_id, 1, 1, 0, 4)],
11540        None,
11541        &mut cx,
11542    );
11543}
11544
11545#[gpui::test]
11546async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
11547    let (buffer_id, mut cx) = setup_indent_guides_editor(
11548        &"
11549    fn main() {
11550        let a = 1;
11551        let b = 2;
11552    }"
11553        .unindent(),
11554        cx,
11555    )
11556    .await;
11557
11558    assert_indent_guides(
11559        0..4,
11560        vec![IndentGuide::new(buffer_id, 1, 2, 0, 4)],
11561        None,
11562        &mut cx,
11563    );
11564}
11565
11566#[gpui::test]
11567async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
11568    let (buffer_id, mut cx) = setup_indent_guides_editor(
11569        &"
11570    fn main() {
11571        let a = 1;
11572        if a == 3 {
11573            let b = 2;
11574        } else {
11575            let c = 3;
11576        }
11577    }"
11578        .unindent(),
11579        cx,
11580    )
11581    .await;
11582
11583    assert_indent_guides(
11584        0..8,
11585        vec![
11586            IndentGuide::new(buffer_id, 1, 6, 0, 4),
11587            IndentGuide::new(buffer_id, 3, 3, 1, 4),
11588            IndentGuide::new(buffer_id, 5, 5, 1, 4),
11589        ],
11590        None,
11591        &mut cx,
11592    );
11593}
11594
11595#[gpui::test]
11596async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
11597    let (buffer_id, mut cx) = setup_indent_guides_editor(
11598        &"
11599    fn main() {
11600        let a = 1;
11601            let b = 2;
11602        let c = 3;
11603    }"
11604        .unindent(),
11605        cx,
11606    )
11607    .await;
11608
11609    assert_indent_guides(
11610        0..5,
11611        vec![
11612            IndentGuide::new(buffer_id, 1, 3, 0, 4),
11613            IndentGuide::new(buffer_id, 2, 2, 1, 4),
11614        ],
11615        None,
11616        &mut cx,
11617    );
11618}
11619
11620#[gpui::test]
11621async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
11622    let (buffer_id, mut cx) = setup_indent_guides_editor(
11623        &"
11624        fn main() {
11625            let a = 1;
11626
11627            let c = 3;
11628        }"
11629        .unindent(),
11630        cx,
11631    )
11632    .await;
11633
11634    assert_indent_guides(
11635        0..5,
11636        vec![IndentGuide::new(buffer_id, 1, 3, 0, 4)],
11637        None,
11638        &mut cx,
11639    );
11640}
11641
11642#[gpui::test]
11643async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
11644    let (buffer_id, mut cx) = setup_indent_guides_editor(
11645        &"
11646        fn main() {
11647            let a = 1;
11648
11649            let c = 3;
11650
11651            if a == 3 {
11652                let b = 2;
11653            } else {
11654                let c = 3;
11655            }
11656        }"
11657        .unindent(),
11658        cx,
11659    )
11660    .await;
11661
11662    assert_indent_guides(
11663        0..11,
11664        vec![
11665            IndentGuide::new(buffer_id, 1, 9, 0, 4),
11666            IndentGuide::new(buffer_id, 6, 6, 1, 4),
11667            IndentGuide::new(buffer_id, 8, 8, 1, 4),
11668        ],
11669        None,
11670        &mut cx,
11671    );
11672}
11673
11674#[gpui::test]
11675async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
11676    let (buffer_id, mut cx) = setup_indent_guides_editor(
11677        &"
11678        fn main() {
11679            let a = 1;
11680
11681            let c = 3;
11682
11683            if a == 3 {
11684                let b = 2;
11685            } else {
11686                let c = 3;
11687            }
11688        }"
11689        .unindent(),
11690        cx,
11691    )
11692    .await;
11693
11694    assert_indent_guides(
11695        1..11,
11696        vec![
11697            IndentGuide::new(buffer_id, 1, 9, 0, 4),
11698            IndentGuide::new(buffer_id, 6, 6, 1, 4),
11699            IndentGuide::new(buffer_id, 8, 8, 1, 4),
11700        ],
11701        None,
11702        &mut cx,
11703    );
11704}
11705
11706#[gpui::test]
11707async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
11708    let (buffer_id, mut cx) = setup_indent_guides_editor(
11709        &"
11710        fn main() {
11711            let a = 1;
11712
11713            let c = 3;
11714
11715            if a == 3 {
11716                let b = 2;
11717            } else {
11718                let c = 3;
11719            }
11720        }"
11721        .unindent(),
11722        cx,
11723    )
11724    .await;
11725
11726    assert_indent_guides(
11727        1..10,
11728        vec![
11729            IndentGuide::new(buffer_id, 1, 9, 0, 4),
11730            IndentGuide::new(buffer_id, 6, 6, 1, 4),
11731            IndentGuide::new(buffer_id, 8, 8, 1, 4),
11732        ],
11733        None,
11734        &mut cx,
11735    );
11736}
11737
11738#[gpui::test]
11739async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
11740    let (buffer_id, mut cx) = setup_indent_guides_editor(
11741        &"
11742        block1
11743            block2
11744                block3
11745                    block4
11746            block2
11747        block1
11748        block1"
11749            .unindent(),
11750        cx,
11751    )
11752    .await;
11753
11754    assert_indent_guides(
11755        1..10,
11756        vec![
11757            IndentGuide::new(buffer_id, 1, 4, 0, 4),
11758            IndentGuide::new(buffer_id, 2, 3, 1, 4),
11759            IndentGuide::new(buffer_id, 3, 3, 2, 4),
11760        ],
11761        None,
11762        &mut cx,
11763    );
11764}
11765
11766#[gpui::test]
11767async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
11768    let (buffer_id, mut cx) = setup_indent_guides_editor(
11769        &"
11770        block1
11771            block2
11772                block3
11773
11774        block1
11775        block1"
11776            .unindent(),
11777        cx,
11778    )
11779    .await;
11780
11781    assert_indent_guides(
11782        0..6,
11783        vec![
11784            IndentGuide::new(buffer_id, 1, 2, 0, 4),
11785            IndentGuide::new(buffer_id, 2, 2, 1, 4),
11786        ],
11787        None,
11788        &mut cx,
11789    );
11790}
11791
11792#[gpui::test]
11793async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
11794    let (buffer_id, mut cx) = setup_indent_guides_editor(
11795        &"
11796        block1
11797
11798
11799
11800            block2
11801        "
11802        .unindent(),
11803        cx,
11804    )
11805    .await;
11806
11807    assert_indent_guides(
11808        0..1,
11809        vec![IndentGuide::new(buffer_id, 1, 1, 0, 4)],
11810        None,
11811        &mut cx,
11812    );
11813}
11814
11815#[gpui::test]
11816async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
11817    let (buffer_id, mut cx) = setup_indent_guides_editor(
11818        &"
11819        def a:
11820        \tb = 3
11821        \tif True:
11822        \t\tc = 4
11823        \t\td = 5
11824        \tprint(b)
11825        "
11826        .unindent(),
11827        cx,
11828    )
11829    .await;
11830
11831    assert_indent_guides(
11832        0..6,
11833        vec![
11834            IndentGuide::new(buffer_id, 1, 6, 0, 4),
11835            IndentGuide::new(buffer_id, 3, 4, 1, 4),
11836        ],
11837        None,
11838        &mut cx,
11839    );
11840}
11841
11842#[gpui::test]
11843async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
11844    let (buffer_id, mut cx) = setup_indent_guides_editor(
11845        &"
11846    fn main() {
11847        let a = 1;
11848    }"
11849        .unindent(),
11850        cx,
11851    )
11852    .await;
11853
11854    cx.update_editor(|editor, cx| {
11855        editor.change_selections(None, cx, |s| {
11856            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
11857        });
11858    });
11859
11860    assert_indent_guides(
11861        0..3,
11862        vec![IndentGuide::new(buffer_id, 1, 1, 0, 4)],
11863        Some(vec![0]),
11864        &mut cx,
11865    );
11866}
11867
11868#[gpui::test]
11869async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
11870    let (buffer_id, mut cx) = setup_indent_guides_editor(
11871        &"
11872    fn main() {
11873        if 1 == 2 {
11874            let a = 1;
11875        }
11876    }"
11877        .unindent(),
11878        cx,
11879    )
11880    .await;
11881
11882    cx.update_editor(|editor, cx| {
11883        editor.change_selections(None, cx, |s| {
11884            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
11885        });
11886    });
11887
11888    assert_indent_guides(
11889        0..4,
11890        vec![
11891            IndentGuide::new(buffer_id, 1, 3, 0, 4),
11892            IndentGuide::new(buffer_id, 2, 2, 1, 4),
11893        ],
11894        Some(vec![1]),
11895        &mut cx,
11896    );
11897
11898    cx.update_editor(|editor, cx| {
11899        editor.change_selections(None, cx, |s| {
11900            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
11901        });
11902    });
11903
11904    assert_indent_guides(
11905        0..4,
11906        vec![
11907            IndentGuide::new(buffer_id, 1, 3, 0, 4),
11908            IndentGuide::new(buffer_id, 2, 2, 1, 4),
11909        ],
11910        Some(vec![1]),
11911        &mut cx,
11912    );
11913
11914    cx.update_editor(|editor, cx| {
11915        editor.change_selections(None, cx, |s| {
11916            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
11917        });
11918    });
11919
11920    assert_indent_guides(
11921        0..4,
11922        vec![
11923            IndentGuide::new(buffer_id, 1, 3, 0, 4),
11924            IndentGuide::new(buffer_id, 2, 2, 1, 4),
11925        ],
11926        Some(vec![0]),
11927        &mut cx,
11928    );
11929}
11930
11931#[gpui::test]
11932async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
11933    let (buffer_id, mut cx) = setup_indent_guides_editor(
11934        &"
11935    fn main() {
11936        let a = 1;
11937
11938        let b = 2;
11939    }"
11940        .unindent(),
11941        cx,
11942    )
11943    .await;
11944
11945    cx.update_editor(|editor, cx| {
11946        editor.change_selections(None, cx, |s| {
11947            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
11948        });
11949    });
11950
11951    assert_indent_guides(
11952        0..5,
11953        vec![IndentGuide::new(buffer_id, 1, 3, 0, 4)],
11954        Some(vec![0]),
11955        &mut cx,
11956    );
11957}
11958
11959#[gpui::test]
11960async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
11961    let (buffer_id, mut cx) = setup_indent_guides_editor(
11962        &"
11963    def m:
11964        a = 1
11965        pass"
11966            .unindent(),
11967        cx,
11968    )
11969    .await;
11970
11971    cx.update_editor(|editor, cx| {
11972        editor.change_selections(None, cx, |s| {
11973            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
11974        });
11975    });
11976
11977    assert_indent_guides(
11978        0..3,
11979        vec![IndentGuide::new(buffer_id, 1, 2, 0, 4)],
11980        Some(vec![0]),
11981        &mut cx,
11982    );
11983}
11984
11985#[gpui::test]
11986fn test_flap_insertion_and_rendering(cx: &mut TestAppContext) {
11987    init_test(cx, |_| {});
11988
11989    let editor = cx.add_window(|cx| {
11990        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
11991        build_editor(buffer, cx)
11992    });
11993
11994    let render_args = Arc::new(Mutex::new(None));
11995    let snapshot = editor
11996        .update(cx, |editor, cx| {
11997            let snapshot = editor.buffer().read(cx).snapshot(cx);
11998            let range =
11999                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
12000
12001            struct RenderArgs {
12002                row: MultiBufferRow,
12003                folded: bool,
12004                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
12005            }
12006
12007            let flap = Flap::new(
12008                range,
12009                FoldPlaceholder::test(),
12010                {
12011                    let toggle_callback = render_args.clone();
12012                    move |row, folded, callback, _cx| {
12013                        *toggle_callback.lock() = Some(RenderArgs {
12014                            row,
12015                            folded,
12016                            callback,
12017                        });
12018                        div()
12019                    }
12020                },
12021                |_row, _folded, _cx| div(),
12022            );
12023
12024            editor.insert_flaps(Some(flap), cx);
12025            let snapshot = editor.snapshot(cx);
12026            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
12027            snapshot
12028        })
12029        .unwrap();
12030
12031    let render_args = render_args.lock().take().unwrap();
12032    assert_eq!(render_args.row, MultiBufferRow(1));
12033    assert_eq!(render_args.folded, false);
12034    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12035
12036    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
12037        .unwrap();
12038    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12039    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
12040
12041    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
12042        .unwrap();
12043    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
12044    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
12045}
12046
12047fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
12048    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
12049    point..point
12050}
12051
12052fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
12053    let (text, ranges) = marked_text_ranges(marked_text, true);
12054    assert_eq!(view.text(cx), text);
12055    assert_eq!(
12056        view.selections.ranges(cx),
12057        ranges,
12058        "Assert selections are {}",
12059        marked_text
12060    );
12061}
12062
12063/// Handle completion request passing a marked string specifying where the completion
12064/// should be triggered from using '|' character, what range should be replaced, and what completions
12065/// should be returned using '<' and '>' to delimit the range
12066pub fn handle_completion_request(
12067    cx: &mut EditorLspTestContext,
12068    marked_string: &str,
12069    completions: Vec<&'static str>,
12070    counter: Arc<AtomicUsize>,
12071) -> impl Future<Output = ()> {
12072    let complete_from_marker: TextRangeMarker = '|'.into();
12073    let replace_range_marker: TextRangeMarker = ('<', '>').into();
12074    let (_, mut marked_ranges) = marked_text_ranges_by(
12075        marked_string,
12076        vec![complete_from_marker.clone(), replace_range_marker.clone()],
12077    );
12078
12079    let complete_from_position =
12080        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
12081    let replace_range =
12082        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
12083
12084    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
12085        let completions = completions.clone();
12086        counter.fetch_add(1, atomic::Ordering::Release);
12087        async move {
12088            assert_eq!(params.text_document_position.text_document.uri, url.clone());
12089            assert_eq!(
12090                params.text_document_position.position,
12091                complete_from_position
12092            );
12093            Ok(Some(lsp::CompletionResponse::Array(
12094                completions
12095                    .iter()
12096                    .map(|completion_text| lsp::CompletionItem {
12097                        label: completion_text.to_string(),
12098                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12099                            range: replace_range,
12100                            new_text: completion_text.to_string(),
12101                        })),
12102                        ..Default::default()
12103                    })
12104                    .collect(),
12105            )))
12106        }
12107    });
12108
12109    async move {
12110        request.next().await;
12111    }
12112}
12113
12114fn handle_resolve_completion_request(
12115    cx: &mut EditorLspTestContext,
12116    edits: Option<Vec<(&'static str, &'static str)>>,
12117) -> impl Future<Output = ()> {
12118    let edits = edits.map(|edits| {
12119        edits
12120            .iter()
12121            .map(|(marked_string, new_text)| {
12122                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
12123                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
12124                lsp::TextEdit::new(replace_range, new_text.to_string())
12125            })
12126            .collect::<Vec<_>>()
12127    });
12128
12129    let mut request =
12130        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12131            let edits = edits.clone();
12132            async move {
12133                Ok(lsp::CompletionItem {
12134                    additional_text_edits: edits,
12135                    ..Default::default()
12136                })
12137            }
12138        });
12139
12140    async move {
12141        request.next().await;
12142    }
12143}
12144
12145pub(crate) fn update_test_language_settings(
12146    cx: &mut TestAppContext,
12147    f: impl Fn(&mut AllLanguageSettingsContent),
12148) {
12149    _ = cx.update(|cx| {
12150        SettingsStore::update_global(cx, |store, cx| {
12151            store.update_user_settings::<AllLanguageSettings>(cx, f);
12152        });
12153    });
12154}
12155
12156pub(crate) fn update_test_project_settings(
12157    cx: &mut TestAppContext,
12158    f: impl Fn(&mut ProjectSettings),
12159) {
12160    _ = cx.update(|cx| {
12161        SettingsStore::update_global(cx, |store, cx| {
12162            store.update_user_settings::<ProjectSettings>(cx, f);
12163        });
12164    });
12165}
12166
12167pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
12168    _ = cx.update(|cx| {
12169        let store = SettingsStore::test(cx);
12170        cx.set_global(store);
12171        theme::init(theme::LoadThemes::JustBase, cx);
12172        release_channel::init("0.0.0", cx);
12173        client::init_settings(cx);
12174        language::init(cx);
12175        Project::init_settings(cx);
12176        workspace::init_settings(cx);
12177        crate::init(cx);
12178    });
12179
12180    update_test_language_settings(cx, f);
12181}
12182
12183pub(crate) fn rust_lang() -> Arc<Language> {
12184    Arc::new(Language::new(
12185        LanguageConfig {
12186            name: "Rust".into(),
12187            matcher: LanguageMatcher {
12188                path_suffixes: vec!["rs".to_string()],
12189                ..Default::default()
12190            },
12191            ..Default::default()
12192        },
12193        Some(tree_sitter_rust::language()),
12194    ))
12195}
12196
12197#[track_caller]
12198fn assert_hunk_revert(
12199    not_reverted_text_with_selections: &str,
12200    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
12201    expected_reverted_text_with_selections: &str,
12202    base_text: &str,
12203    cx: &mut EditorLspTestContext,
12204) {
12205    cx.set_state(not_reverted_text_with_selections);
12206    cx.update_editor(|editor, cx| {
12207        editor
12208            .buffer()
12209            .read(cx)
12210            .as_singleton()
12211            .unwrap()
12212            .update(cx, |buffer, cx| {
12213                buffer.set_diff_base(Some(base_text.into()), cx);
12214            });
12215    });
12216    cx.executor().run_until_parked();
12217
12218    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
12219        let snapshot = editor.buffer().read(cx).snapshot(cx);
12220        let reverted_hunk_statuses = snapshot
12221            .git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
12222            .map(|hunk| hunk_status(&hunk))
12223            .collect::<Vec<_>>();
12224
12225        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12226        reverted_hunk_statuses
12227    });
12228    cx.executor().run_until_parked();
12229    cx.assert_editor_state(expected_reverted_text_with_selections);
12230    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
12231}