editor_tests.rs

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