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