editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor,
    6        editor_lsp_test_context::{git_commit_lang, EditorLspTestContext},
    7        editor_test_context::EditorTestContext,
    8        select_ranges,
    9    },
   10    JoinLines,
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions,
   17};
   18use indoc::indoc;
   19use language::{
   20    language_settings::{
   21        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   22        LanguageSettingsContent, PrettierSettings,
   23    },
   24    BracketPairConfig,
   25    Capability::ReadWrite,
   26    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   27    Override, Point,
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use multi_buffer::{IndentGuide, PathKey};
   31use parking_lot::Mutex;
   32use pretty_assertions::{assert_eq, assert_ne};
   33use project::{
   34    debugger::breakpoint_store::{BreakpointKind, SerializedBreakpoint},
   35    project_settings::{LspSettings, ProjectSettings},
   36    FakeFs,
   37};
   38use serde_json::{self, json};
   39use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   40use std::{
   41    iter,
   42    sync::atomic::{self, AtomicUsize},
   43};
   44use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   45use text::ToPoint as _;
   46use unindent::Unindent;
   47use util::{
   48    assert_set_eq, path,
   49    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   50    uri,
   51};
   52use workspace::{
   53    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   54    NavigationEntry, ViewId,
   55};
   56
   57#[gpui::test]
   58fn test_edit_events(cx: &mut TestAppContext) {
   59    init_test(cx, |_| {});
   60
   61    let buffer = cx.new(|cx| {
   62        let mut buffer = language::Buffer::local("123456", cx);
   63        buffer.set_group_interval(Duration::from_secs(1));
   64        buffer
   65    });
   66
   67    let events = Rc::new(RefCell::new(Vec::new()));
   68    let editor1 = cx.add_window({
   69        let events = events.clone();
   70        |window, cx| {
   71            let entity = cx.entity().clone();
   72            cx.subscribe_in(
   73                &entity,
   74                window,
   75                move |_, _, event: &EditorEvent, _, _| match event {
   76                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   77                    EditorEvent::BufferEdited => {
   78                        events.borrow_mut().push(("editor1", "buffer edited"))
   79                    }
   80                    _ => {}
   81                },
   82            )
   83            .detach();
   84            Editor::for_buffer(buffer.clone(), None, window, cx)
   85        }
   86    });
   87
   88    let editor2 = cx.add_window({
   89        let events = events.clone();
   90        |window, cx| {
   91            cx.subscribe_in(
   92                &cx.entity().clone(),
   93                window,
   94                move |_, _, event: &EditorEvent, _, _| match event {
   95                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   96                    EditorEvent::BufferEdited => {
   97                        events.borrow_mut().push(("editor2", "buffer edited"))
   98                    }
   99                    _ => {}
  100                },
  101            )
  102            .detach();
  103            Editor::for_buffer(buffer.clone(), None, window, cx)
  104        }
  105    });
  106
  107    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  108
  109    // Mutating editor 1 will emit an `Edited` event only for that editor.
  110    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  111    assert_eq!(
  112        mem::take(&mut *events.borrow_mut()),
  113        [
  114            ("editor1", "edited"),
  115            ("editor1", "buffer edited"),
  116            ("editor2", "buffer edited"),
  117        ]
  118    );
  119
  120    // Mutating editor 2 will emit an `Edited` event only for that editor.
  121    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  122    assert_eq!(
  123        mem::take(&mut *events.borrow_mut()),
  124        [
  125            ("editor2", "edited"),
  126            ("editor1", "buffer edited"),
  127            ("editor2", "buffer edited"),
  128        ]
  129    );
  130
  131    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  132    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  133    assert_eq!(
  134        mem::take(&mut *events.borrow_mut()),
  135        [
  136            ("editor1", "edited"),
  137            ("editor1", "buffer edited"),
  138            ("editor2", "buffer edited"),
  139        ]
  140    );
  141
  142    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  143    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  144    assert_eq!(
  145        mem::take(&mut *events.borrow_mut()),
  146        [
  147            ("editor1", "edited"),
  148            ("editor1", "buffer edited"),
  149            ("editor2", "buffer edited"),
  150        ]
  151    );
  152
  153    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  154    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  155    assert_eq!(
  156        mem::take(&mut *events.borrow_mut()),
  157        [
  158            ("editor2", "edited"),
  159            ("editor1", "buffer edited"),
  160            ("editor2", "buffer edited"),
  161        ]
  162    );
  163
  164    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  165    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  166    assert_eq!(
  167        mem::take(&mut *events.borrow_mut()),
  168        [
  169            ("editor2", "edited"),
  170            ("editor1", "buffer edited"),
  171            ("editor2", "buffer edited"),
  172        ]
  173    );
  174
  175    // No event is emitted when the mutation is a no-op.
  176    _ = editor2.update(cx, |editor, window, cx| {
  177        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  178
  179        editor.backspace(&Backspace, window, cx);
  180    });
  181    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  182}
  183
  184#[gpui::test]
  185fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  186    init_test(cx, |_| {});
  187
  188    let mut now = Instant::now();
  189    let group_interval = Duration::from_millis(1);
  190    let buffer = cx.new(|cx| {
  191        let mut buf = language::Buffer::local("123456", cx);
  192        buf.set_group_interval(group_interval);
  193        buf
  194    });
  195    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  196    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  197
  198    _ = editor.update(cx, |editor, window, cx| {
  199        editor.start_transaction_at(now, window, cx);
  200        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  201
  202        editor.insert("cd", window, cx);
  203        editor.end_transaction_at(now, cx);
  204        assert_eq!(editor.text(cx), "12cd56");
  205        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  206
  207        editor.start_transaction_at(now, window, cx);
  208        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  209        editor.insert("e", window, cx);
  210        editor.end_transaction_at(now, cx);
  211        assert_eq!(editor.text(cx), "12cde6");
  212        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  213
  214        now += group_interval + Duration::from_millis(1);
  215        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  216
  217        // Simulate an edit in another editor
  218        buffer.update(cx, |buffer, cx| {
  219            buffer.start_transaction_at(now, cx);
  220            buffer.edit([(0..1, "a")], None, cx);
  221            buffer.edit([(1..1, "b")], None, cx);
  222            buffer.end_transaction_at(now, cx);
  223        });
  224
  225        assert_eq!(editor.text(cx), "ab2cde6");
  226        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  227
  228        // Last transaction happened past the group interval in a different editor.
  229        // Undo it individually and don't restore selections.
  230        editor.undo(&Undo, window, cx);
  231        assert_eq!(editor.text(cx), "12cde6");
  232        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  233
  234        // First two transactions happened within the group interval in this editor.
  235        // Undo them together and restore selections.
  236        editor.undo(&Undo, window, cx);
  237        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  238        assert_eq!(editor.text(cx), "123456");
  239        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  240
  241        // Redo the first two transactions together.
  242        editor.redo(&Redo, window, cx);
  243        assert_eq!(editor.text(cx), "12cde6");
  244        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  245
  246        // Redo the last transaction on its own.
  247        editor.redo(&Redo, window, cx);
  248        assert_eq!(editor.text(cx), "ab2cde6");
  249        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  250
  251        // Test empty transactions.
  252        editor.start_transaction_at(now, window, cx);
  253        editor.end_transaction_at(now, cx);
  254        editor.undo(&Undo, window, cx);
  255        assert_eq!(editor.text(cx), "12cde6");
  256    });
  257}
  258
  259#[gpui::test]
  260fn test_ime_composition(cx: &mut TestAppContext) {
  261    init_test(cx, |_| {});
  262
  263    let buffer = cx.new(|cx| {
  264        let mut buffer = language::Buffer::local("abcde", cx);
  265        // Ensure automatic grouping doesn't occur.
  266        buffer.set_group_interval(Duration::ZERO);
  267        buffer
  268    });
  269
  270    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  271    cx.add_window(|window, cx| {
  272        let mut editor = build_editor(buffer.clone(), window, cx);
  273
  274        // Start a new IME composition.
  275        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  276        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  278        assert_eq!(editor.text(cx), "äbcde");
  279        assert_eq!(
  280            editor.marked_text_ranges(cx),
  281            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  282        );
  283
  284        // Finalize IME composition.
  285        editor.replace_text_in_range(None, "ā", window, cx);
  286        assert_eq!(editor.text(cx), "ābcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288
  289        // IME composition edits are grouped and are undone/redone at once.
  290        editor.undo(&Default::default(), window, cx);
  291        assert_eq!(editor.text(cx), "abcde");
  292        assert_eq!(editor.marked_text_ranges(cx), None);
  293        editor.redo(&Default::default(), window, cx);
  294        assert_eq!(editor.text(cx), "ābcde");
  295        assert_eq!(editor.marked_text_ranges(cx), None);
  296
  297        // Start a new IME composition.
  298        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  299        assert_eq!(
  300            editor.marked_text_ranges(cx),
  301            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  302        );
  303
  304        // Undoing during an IME composition cancels it.
  305        editor.undo(&Default::default(), window, cx);
  306        assert_eq!(editor.text(cx), "ābcde");
  307        assert_eq!(editor.marked_text_ranges(cx), None);
  308
  309        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  310        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  311        assert_eq!(editor.text(cx), "ābcdè");
  312        assert_eq!(
  313            editor.marked_text_ranges(cx),
  314            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  315        );
  316
  317        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  318        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  319        assert_eq!(editor.text(cx), "ābcdę");
  320        assert_eq!(editor.marked_text_ranges(cx), None);
  321
  322        // Start a new IME composition with multiple cursors.
  323        editor.change_selections(None, window, cx, |s| {
  324            s.select_ranges([
  325                OffsetUtf16(1)..OffsetUtf16(1),
  326                OffsetUtf16(3)..OffsetUtf16(3),
  327                OffsetUtf16(5)..OffsetUtf16(5),
  328            ])
  329        });
  330        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  331        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  332        assert_eq!(
  333            editor.marked_text_ranges(cx),
  334            Some(vec![
  335                OffsetUtf16(0)..OffsetUtf16(3),
  336                OffsetUtf16(4)..OffsetUtf16(7),
  337                OffsetUtf16(8)..OffsetUtf16(11)
  338            ])
  339        );
  340
  341        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  342        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  343        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  344        assert_eq!(
  345            editor.marked_text_ranges(cx),
  346            Some(vec![
  347                OffsetUtf16(1)..OffsetUtf16(2),
  348                OffsetUtf16(5)..OffsetUtf16(6),
  349                OffsetUtf16(9)..OffsetUtf16(10)
  350            ])
  351        );
  352
  353        // Finalize IME composition with multiple cursors.
  354        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  355        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  356        assert_eq!(editor.marked_text_ranges(cx), None);
  357
  358        editor
  359    });
  360}
  361
  362#[gpui::test]
  363fn test_selection_with_mouse(cx: &mut TestAppContext) {
  364    init_test(cx, |_| {});
  365
  366    let editor = cx.add_window(|window, cx| {
  367        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  368        build_editor(buffer, window, cx)
  369    });
  370
  371    _ = editor.update(cx, |editor, window, cx| {
  372        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  373    });
  374    assert_eq!(
  375        editor
  376            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  377            .unwrap(),
  378        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  379    );
  380
  381    _ = editor.update(cx, |editor, window, cx| {
  382        editor.update_selection(
  383            DisplayPoint::new(DisplayRow(3), 3),
  384            0,
  385            gpui::Point::<f32>::default(),
  386            window,
  387            cx,
  388        );
  389    });
  390
  391    assert_eq!(
  392        editor
  393            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  394            .unwrap(),
  395        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  396    );
  397
  398    _ = editor.update(cx, |editor, window, cx| {
  399        editor.update_selection(
  400            DisplayPoint::new(DisplayRow(1), 1),
  401            0,
  402            gpui::Point::<f32>::default(),
  403            window,
  404            cx,
  405        );
  406    });
  407
  408    assert_eq!(
  409        editor
  410            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  411            .unwrap(),
  412        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  413    );
  414
  415    _ = editor.update(cx, |editor, window, cx| {
  416        editor.end_selection(window, cx);
  417        editor.update_selection(
  418            DisplayPoint::new(DisplayRow(3), 3),
  419            0,
  420            gpui::Point::<f32>::default(),
  421            window,
  422            cx,
  423        );
  424    });
  425
  426    assert_eq!(
  427        editor
  428            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  429            .unwrap(),
  430        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  431    );
  432
  433    _ = editor.update(cx, |editor, window, cx| {
  434        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  435        editor.update_selection(
  436            DisplayPoint::new(DisplayRow(0), 0),
  437            0,
  438            gpui::Point::<f32>::default(),
  439            window,
  440            cx,
  441        );
  442    });
  443
  444    assert_eq!(
  445        editor
  446            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  447            .unwrap(),
  448        [
  449            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  450            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  451        ]
  452    );
  453
  454    _ = editor.update(cx, |editor, window, cx| {
  455        editor.end_selection(window, cx);
  456    });
  457
  458    assert_eq!(
  459        editor
  460            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  461            .unwrap(),
  462        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  463    );
  464}
  465
  466#[gpui::test]
  467fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  468    init_test(cx, |_| {});
  469
  470    let editor = cx.add_window(|window, cx| {
  471        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  472        build_editor(buffer, window, cx)
  473    });
  474
  475    _ = editor.update(cx, |editor, window, cx| {
  476        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  477    });
  478
  479    _ = editor.update(cx, |editor, window, cx| {
  480        editor.end_selection(window, cx);
  481    });
  482
  483    _ = editor.update(cx, |editor, window, cx| {
  484        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  485    });
  486
  487    _ = editor.update(cx, |editor, window, cx| {
  488        editor.end_selection(window, cx);
  489    });
  490
  491    assert_eq!(
  492        editor
  493            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  494            .unwrap(),
  495        [
  496            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  497            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  498        ]
  499    );
  500
  501    _ = editor.update(cx, |editor, window, cx| {
  502        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  503    });
  504
  505    _ = editor.update(cx, |editor, window, cx| {
  506        editor.end_selection(window, cx);
  507    });
  508
  509    assert_eq!(
  510        editor
  511            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  512            .unwrap(),
  513        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  514    );
  515}
  516
  517#[gpui::test]
  518fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  519    init_test(cx, |_| {});
  520
  521    let editor = cx.add_window(|window, cx| {
  522        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  523        build_editor(buffer, window, cx)
  524    });
  525
  526    _ = editor.update(cx, |editor, window, cx| {
  527        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  528        assert_eq!(
  529            editor.selections.display_ranges(cx),
  530            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  531        );
  532    });
  533
  534    _ = editor.update(cx, |editor, window, cx| {
  535        editor.update_selection(
  536            DisplayPoint::new(DisplayRow(3), 3),
  537            0,
  538            gpui::Point::<f32>::default(),
  539            window,
  540            cx,
  541        );
  542        assert_eq!(
  543            editor.selections.display_ranges(cx),
  544            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  545        );
  546    });
  547
  548    _ = editor.update(cx, |editor, window, cx| {
  549        editor.cancel(&Cancel, window, cx);
  550        editor.update_selection(
  551            DisplayPoint::new(DisplayRow(1), 1),
  552            0,
  553            gpui::Point::<f32>::default(),
  554            window,
  555            cx,
  556        );
  557        assert_eq!(
  558            editor.selections.display_ranges(cx),
  559            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  560        );
  561    });
  562}
  563
  564#[gpui::test]
  565fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  566    init_test(cx, |_| {});
  567
  568    let editor = cx.add_window(|window, cx| {
  569        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  570        build_editor(buffer, window, cx)
  571    });
  572
  573    _ = editor.update(cx, |editor, window, cx| {
  574        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  575        assert_eq!(
  576            editor.selections.display_ranges(cx),
  577            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  578        );
  579
  580        editor.move_down(&Default::default(), window, cx);
  581        assert_eq!(
  582            editor.selections.display_ranges(cx),
  583            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  584        );
  585
  586        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  587        assert_eq!(
  588            editor.selections.display_ranges(cx),
  589            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  590        );
  591
  592        editor.move_up(&Default::default(), window, cx);
  593        assert_eq!(
  594            editor.selections.display_ranges(cx),
  595            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  596        );
  597    });
  598}
  599
  600#[gpui::test]
  601fn test_clone(cx: &mut TestAppContext) {
  602    init_test(cx, |_| {});
  603
  604    let (text, selection_ranges) = marked_text_ranges(
  605        indoc! {"
  606            one
  607            two
  608            threeˇ
  609            four
  610            fiveˇ
  611        "},
  612        true,
  613    );
  614
  615    let editor = cx.add_window(|window, cx| {
  616        let buffer = MultiBuffer::build_simple(&text, cx);
  617        build_editor(buffer, window, cx)
  618    });
  619
  620    _ = editor.update(cx, |editor, window, cx| {
  621        editor.change_selections(None, window, cx, |s| {
  622            s.select_ranges(selection_ranges.clone())
  623        });
  624        editor.fold_creases(
  625            vec![
  626                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  627                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  628            ],
  629            true,
  630            window,
  631            cx,
  632        );
  633    });
  634
  635    let cloned_editor = editor
  636        .update(cx, |editor, _, cx| {
  637            cx.open_window(Default::default(), |window, cx| {
  638                cx.new(|cx| editor.clone(window, cx))
  639            })
  640        })
  641        .unwrap()
  642        .unwrap();
  643
  644    let snapshot = editor
  645        .update(cx, |e, window, cx| e.snapshot(window, cx))
  646        .unwrap();
  647    let cloned_snapshot = cloned_editor
  648        .update(cx, |e, window, cx| e.snapshot(window, cx))
  649        .unwrap();
  650
  651    assert_eq!(
  652        cloned_editor
  653            .update(cx, |e, _, cx| e.display_text(cx))
  654            .unwrap(),
  655        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  656    );
  657    assert_eq!(
  658        cloned_snapshot
  659            .folds_in_range(0..text.len())
  660            .collect::<Vec<_>>(),
  661        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  662    );
  663    assert_set_eq!(
  664        cloned_editor
  665            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  666            .unwrap(),
  667        editor
  668            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  669            .unwrap()
  670    );
  671    assert_set_eq!(
  672        cloned_editor
  673            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  674            .unwrap(),
  675        editor
  676            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  677            .unwrap()
  678    );
  679}
  680
  681#[gpui::test]
  682async fn test_navigation_history(cx: &mut TestAppContext) {
  683    init_test(cx, |_| {});
  684
  685    use workspace::item::Item;
  686
  687    let fs = FakeFs::new(cx.executor());
  688    let project = Project::test(fs, [], cx).await;
  689    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  690    let pane = workspace
  691        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  692        .unwrap();
  693
  694    _ = workspace.update(cx, |_v, window, cx| {
  695        cx.new(|cx| {
  696            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  697            let mut editor = build_editor(buffer.clone(), window, cx);
  698            let handle = cx.entity();
  699            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  700
  701            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  702                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  703            }
  704
  705            // Move the cursor a small distance.
  706            // Nothing is added to the navigation history.
  707            editor.change_selections(None, window, cx, |s| {
  708                s.select_display_ranges([
  709                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  710                ])
  711            });
  712            editor.change_selections(None, window, cx, |s| {
  713                s.select_display_ranges([
  714                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  715                ])
  716            });
  717            assert!(pop_history(&mut editor, cx).is_none());
  718
  719            // Move the cursor a large distance.
  720            // The history can jump back to the previous position.
  721            editor.change_selections(None, window, cx, |s| {
  722                s.select_display_ranges([
  723                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  724                ])
  725            });
  726            let nav_entry = pop_history(&mut editor, cx).unwrap();
  727            editor.navigate(nav_entry.data.unwrap(), window, cx);
  728            assert_eq!(nav_entry.item.id(), cx.entity_id());
  729            assert_eq!(
  730                editor.selections.display_ranges(cx),
  731                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  732            );
  733            assert!(pop_history(&mut editor, cx).is_none());
  734
  735            // Move the cursor a small distance via the mouse.
  736            // Nothing is added to the navigation history.
  737            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  738            editor.end_selection(window, cx);
  739            assert_eq!(
  740                editor.selections.display_ranges(cx),
  741                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  742            );
  743            assert!(pop_history(&mut editor, cx).is_none());
  744
  745            // Move the cursor a large distance via the mouse.
  746            // The history can jump back to the previous position.
  747            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  748            editor.end_selection(window, cx);
  749            assert_eq!(
  750                editor.selections.display_ranges(cx),
  751                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  752            );
  753            let nav_entry = pop_history(&mut editor, cx).unwrap();
  754            editor.navigate(nav_entry.data.unwrap(), window, cx);
  755            assert_eq!(nav_entry.item.id(), cx.entity_id());
  756            assert_eq!(
  757                editor.selections.display_ranges(cx),
  758                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  759            );
  760            assert!(pop_history(&mut editor, cx).is_none());
  761
  762            // Set scroll position to check later
  763            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  764            let original_scroll_position = editor.scroll_manager.anchor();
  765
  766            // Jump to the end of the document and adjust scroll
  767            editor.move_to_end(&MoveToEnd, window, cx);
  768            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  769            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  770
  771            let nav_entry = pop_history(&mut editor, cx).unwrap();
  772            editor.navigate(nav_entry.data.unwrap(), window, cx);
  773            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  774
  775            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  776            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  777            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  778            let invalid_point = Point::new(9999, 0);
  779            editor.navigate(
  780                Box::new(NavigationData {
  781                    cursor_anchor: invalid_anchor,
  782                    cursor_position: invalid_point,
  783                    scroll_anchor: ScrollAnchor {
  784                        anchor: invalid_anchor,
  785                        offset: Default::default(),
  786                    },
  787                    scroll_top_row: invalid_point.row,
  788                }),
  789                window,
  790                cx,
  791            );
  792            assert_eq!(
  793                editor.selections.display_ranges(cx),
  794                &[editor.max_point(cx)..editor.max_point(cx)]
  795            );
  796            assert_eq!(
  797                editor.scroll_position(cx),
  798                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  799            );
  800
  801            editor
  802        })
  803    });
  804}
  805
  806#[gpui::test]
  807fn test_cancel(cx: &mut TestAppContext) {
  808    init_test(cx, |_| {});
  809
  810    let editor = cx.add_window(|window, cx| {
  811        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  812        build_editor(buffer, window, cx)
  813    });
  814
  815    _ = editor.update(cx, |editor, window, cx| {
  816        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  817        editor.update_selection(
  818            DisplayPoint::new(DisplayRow(1), 1),
  819            0,
  820            gpui::Point::<f32>::default(),
  821            window,
  822            cx,
  823        );
  824        editor.end_selection(window, cx);
  825
  826        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  827        editor.update_selection(
  828            DisplayPoint::new(DisplayRow(0), 3),
  829            0,
  830            gpui::Point::<f32>::default(),
  831            window,
  832            cx,
  833        );
  834        editor.end_selection(window, cx);
  835        assert_eq!(
  836            editor.selections.display_ranges(cx),
  837            [
  838                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  839                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  840            ]
  841        );
  842    });
  843
  844    _ = editor.update(cx, |editor, window, cx| {
  845        editor.cancel(&Cancel, window, cx);
  846        assert_eq!(
  847            editor.selections.display_ranges(cx),
  848            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  849        );
  850    });
  851
  852    _ = editor.update(cx, |editor, window, cx| {
  853        editor.cancel(&Cancel, window, cx);
  854        assert_eq!(
  855            editor.selections.display_ranges(cx),
  856            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  857        );
  858    });
  859}
  860
  861#[gpui::test]
  862fn test_fold_action(cx: &mut TestAppContext) {
  863    init_test(cx, |_| {});
  864
  865    let editor = cx.add_window(|window, cx| {
  866        let buffer = MultiBuffer::build_simple(
  867            &"
  868                impl Foo {
  869                    // Hello!
  870
  871                    fn a() {
  872                        1
  873                    }
  874
  875                    fn b() {
  876                        2
  877                    }
  878
  879                    fn c() {
  880                        3
  881                    }
  882                }
  883            "
  884            .unindent(),
  885            cx,
  886        );
  887        build_editor(buffer.clone(), window, cx)
  888    });
  889
  890    _ = editor.update(cx, |editor, window, cx| {
  891        editor.change_selections(None, window, cx, |s| {
  892            s.select_display_ranges([
  893                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  894            ]);
  895        });
  896        editor.fold(&Fold, window, cx);
  897        assert_eq!(
  898            editor.display_text(cx),
  899            "
  900                impl Foo {
  901                    // Hello!
  902
  903                    fn a() {
  904                        1
  905                    }
  906
  907                    fn b() {⋯
  908                    }
  909
  910                    fn c() {⋯
  911                    }
  912                }
  913            "
  914            .unindent(),
  915        );
  916
  917        editor.fold(&Fold, window, cx);
  918        assert_eq!(
  919            editor.display_text(cx),
  920            "
  921                impl Foo {⋯
  922                }
  923            "
  924            .unindent(),
  925        );
  926
  927        editor.unfold_lines(&UnfoldLines, window, cx);
  928        assert_eq!(
  929            editor.display_text(cx),
  930            "
  931                impl Foo {
  932                    // Hello!
  933
  934                    fn a() {
  935                        1
  936                    }
  937
  938                    fn b() {⋯
  939                    }
  940
  941                    fn c() {⋯
  942                    }
  943                }
  944            "
  945            .unindent(),
  946        );
  947
  948        editor.unfold_lines(&UnfoldLines, window, cx);
  949        assert_eq!(
  950            editor.display_text(cx),
  951            editor.buffer.read(cx).read(cx).text()
  952        );
  953    });
  954}
  955
  956#[gpui::test]
  957fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  958    init_test(cx, |_| {});
  959
  960    let editor = cx.add_window(|window, cx| {
  961        let buffer = MultiBuffer::build_simple(
  962            &"
  963                class Foo:
  964                    # Hello!
  965
  966                    def a():
  967                        print(1)
  968
  969                    def b():
  970                        print(2)
  971
  972                    def c():
  973                        print(3)
  974            "
  975            .unindent(),
  976            cx,
  977        );
  978        build_editor(buffer.clone(), window, cx)
  979    });
  980
  981    _ = editor.update(cx, |editor, window, cx| {
  982        editor.change_selections(None, window, cx, |s| {
  983            s.select_display_ranges([
  984                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  985            ]);
  986        });
  987        editor.fold(&Fold, window, cx);
  988        assert_eq!(
  989            editor.display_text(cx),
  990            "
  991                class Foo:
  992                    # Hello!
  993
  994                    def a():
  995                        print(1)
  996
  997                    def b():⋯
  998
  999                    def c():⋯
 1000            "
 1001            .unindent(),
 1002        );
 1003
 1004        editor.fold(&Fold, window, cx);
 1005        assert_eq!(
 1006            editor.display_text(cx),
 1007            "
 1008                class Foo:⋯
 1009            "
 1010            .unindent(),
 1011        );
 1012
 1013        editor.unfold_lines(&UnfoldLines, window, cx);
 1014        assert_eq!(
 1015            editor.display_text(cx),
 1016            "
 1017                class Foo:
 1018                    # Hello!
 1019
 1020                    def a():
 1021                        print(1)
 1022
 1023                    def b():⋯
 1024
 1025                    def c():⋯
 1026            "
 1027            .unindent(),
 1028        );
 1029
 1030        editor.unfold_lines(&UnfoldLines, window, cx);
 1031        assert_eq!(
 1032            editor.display_text(cx),
 1033            editor.buffer.read(cx).read(cx).text()
 1034        );
 1035    });
 1036}
 1037
 1038#[gpui::test]
 1039fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1040    init_test(cx, |_| {});
 1041
 1042    let editor = cx.add_window(|window, cx| {
 1043        let buffer = MultiBuffer::build_simple(
 1044            &"
 1045                class Foo:
 1046                    # Hello!
 1047
 1048                    def a():
 1049                        print(1)
 1050
 1051                    def b():
 1052                        print(2)
 1053
 1054
 1055                    def c():
 1056                        print(3)
 1057
 1058
 1059            "
 1060            .unindent(),
 1061            cx,
 1062        );
 1063        build_editor(buffer.clone(), window, cx)
 1064    });
 1065
 1066    _ = editor.update(cx, |editor, window, cx| {
 1067        editor.change_selections(None, window, cx, |s| {
 1068            s.select_display_ranges([
 1069                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1070            ]);
 1071        });
 1072        editor.fold(&Fold, window, cx);
 1073        assert_eq!(
 1074            editor.display_text(cx),
 1075            "
 1076                class Foo:
 1077                    # Hello!
 1078
 1079                    def a():
 1080                        print(1)
 1081
 1082                    def b():⋯
 1083
 1084
 1085                    def c():⋯
 1086
 1087
 1088            "
 1089            .unindent(),
 1090        );
 1091
 1092        editor.fold(&Fold, window, cx);
 1093        assert_eq!(
 1094            editor.display_text(cx),
 1095            "
 1096                class Foo:⋯
 1097
 1098
 1099            "
 1100            .unindent(),
 1101        );
 1102
 1103        editor.unfold_lines(&UnfoldLines, window, cx);
 1104        assert_eq!(
 1105            editor.display_text(cx),
 1106            "
 1107                class Foo:
 1108                    # Hello!
 1109
 1110                    def a():
 1111                        print(1)
 1112
 1113                    def b():⋯
 1114
 1115
 1116                    def c():⋯
 1117
 1118
 1119            "
 1120            .unindent(),
 1121        );
 1122
 1123        editor.unfold_lines(&UnfoldLines, window, cx);
 1124        assert_eq!(
 1125            editor.display_text(cx),
 1126            editor.buffer.read(cx).read(cx).text()
 1127        );
 1128    });
 1129}
 1130
 1131#[gpui::test]
 1132fn test_fold_at_level(cx: &mut TestAppContext) {
 1133    init_test(cx, |_| {});
 1134
 1135    let editor = cx.add_window(|window, cx| {
 1136        let buffer = MultiBuffer::build_simple(
 1137            &"
 1138                class Foo:
 1139                    # Hello!
 1140
 1141                    def a():
 1142                        print(1)
 1143
 1144                    def b():
 1145                        print(2)
 1146
 1147
 1148                class Bar:
 1149                    # World!
 1150
 1151                    def a():
 1152                        print(1)
 1153
 1154                    def b():
 1155                        print(2)
 1156
 1157
 1158            "
 1159            .unindent(),
 1160            cx,
 1161        );
 1162        build_editor(buffer.clone(), window, cx)
 1163    });
 1164
 1165    _ = editor.update(cx, |editor, window, cx| {
 1166        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1167        assert_eq!(
 1168            editor.display_text(cx),
 1169            "
 1170                class Foo:
 1171                    # Hello!
 1172
 1173                    def a():⋯
 1174
 1175                    def b():⋯
 1176
 1177
 1178                class Bar:
 1179                    # World!
 1180
 1181                    def a():⋯
 1182
 1183                    def b():⋯
 1184
 1185
 1186            "
 1187            .unindent(),
 1188        );
 1189
 1190        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1191        assert_eq!(
 1192            editor.display_text(cx),
 1193            "
 1194                class Foo:⋯
 1195
 1196
 1197                class Bar:⋯
 1198
 1199
 1200            "
 1201            .unindent(),
 1202        );
 1203
 1204        editor.unfold_all(&UnfoldAll, window, cx);
 1205        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1206        assert_eq!(
 1207            editor.display_text(cx),
 1208            "
 1209                class Foo:
 1210                    # Hello!
 1211
 1212                    def a():
 1213                        print(1)
 1214
 1215                    def b():
 1216                        print(2)
 1217
 1218
 1219                class Bar:
 1220                    # World!
 1221
 1222                    def a():
 1223                        print(1)
 1224
 1225                    def b():
 1226                        print(2)
 1227
 1228
 1229            "
 1230            .unindent(),
 1231        );
 1232
 1233        assert_eq!(
 1234            editor.display_text(cx),
 1235            editor.buffer.read(cx).read(cx).text()
 1236        );
 1237    });
 1238}
 1239
 1240#[gpui::test]
 1241fn test_move_cursor(cx: &mut TestAppContext) {
 1242    init_test(cx, |_| {});
 1243
 1244    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1245    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1246
 1247    buffer.update(cx, |buffer, cx| {
 1248        buffer.edit(
 1249            vec![
 1250                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1251                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1252            ],
 1253            None,
 1254            cx,
 1255        );
 1256    });
 1257    _ = editor.update(cx, |editor, window, cx| {
 1258        assert_eq!(
 1259            editor.selections.display_ranges(cx),
 1260            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1261        );
 1262
 1263        editor.move_down(&MoveDown, window, cx);
 1264        assert_eq!(
 1265            editor.selections.display_ranges(cx),
 1266            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1267        );
 1268
 1269        editor.move_right(&MoveRight, window, cx);
 1270        assert_eq!(
 1271            editor.selections.display_ranges(cx),
 1272            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1273        );
 1274
 1275        editor.move_left(&MoveLeft, window, cx);
 1276        assert_eq!(
 1277            editor.selections.display_ranges(cx),
 1278            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1279        );
 1280
 1281        editor.move_up(&MoveUp, window, cx);
 1282        assert_eq!(
 1283            editor.selections.display_ranges(cx),
 1284            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1285        );
 1286
 1287        editor.move_to_end(&MoveToEnd, window, cx);
 1288        assert_eq!(
 1289            editor.selections.display_ranges(cx),
 1290            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1291        );
 1292
 1293        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1294        assert_eq!(
 1295            editor.selections.display_ranges(cx),
 1296            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1297        );
 1298
 1299        editor.change_selections(None, window, cx, |s| {
 1300            s.select_display_ranges([
 1301                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1302            ]);
 1303        });
 1304        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1305        assert_eq!(
 1306            editor.selections.display_ranges(cx),
 1307            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1308        );
 1309
 1310        editor.select_to_end(&SelectToEnd, window, cx);
 1311        assert_eq!(
 1312            editor.selections.display_ranges(cx),
 1313            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1314        );
 1315    });
 1316}
 1317
 1318// TODO: Re-enable this test
 1319#[cfg(target_os = "macos")]
 1320#[gpui::test]
 1321fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1322    init_test(cx, |_| {});
 1323
 1324    let editor = cx.add_window(|window, cx| {
 1325        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1326        build_editor(buffer.clone(), window, cx)
 1327    });
 1328
 1329    assert_eq!('🟥'.len_utf8(), 4);
 1330    assert_eq!('α'.len_utf8(), 2);
 1331
 1332    _ = editor.update(cx, |editor, window, cx| {
 1333        editor.fold_creases(
 1334            vec![
 1335                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1338            ],
 1339            true,
 1340            window,
 1341            cx,
 1342        );
 1343        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1344
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧".len())]
 1354        );
 1355        editor.move_right(&MoveRight, window, cx);
 1356        assert_eq!(
 1357            editor.selections.display_ranges(cx),
 1358            &[empty_range(0, "🟥🟧⋯".len())]
 1359        );
 1360
 1361        editor.move_down(&MoveDown, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "ab".len())]
 1375        );
 1376        editor.move_left(&MoveLeft, window, cx);
 1377        assert_eq!(
 1378            editor.selections.display_ranges(cx),
 1379            &[empty_range(1, "a".len())]
 1380        );
 1381
 1382        editor.move_down(&MoveDown, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "α".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯".len())]
 1396        );
 1397        editor.move_right(&MoveRight, window, cx);
 1398        assert_eq!(
 1399            editor.selections.display_ranges(cx),
 1400            &[empty_range(2, "αβ⋯ε".len())]
 1401        );
 1402
 1403        editor.move_up(&MoveUp, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(1, "ab⋯e".len())]
 1407        );
 1408        editor.move_down(&MoveDown, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(2, "αβ⋯ε".len())]
 1412        );
 1413        editor.move_up(&MoveUp, window, cx);
 1414        assert_eq!(
 1415            editor.selections.display_ranges(cx),
 1416            &[empty_range(1, "ab⋯e".len())]
 1417        );
 1418
 1419        editor.move_up(&MoveUp, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥🟧".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "🟥".len())]
 1428        );
 1429        editor.move_left(&MoveLeft, window, cx);
 1430        assert_eq!(
 1431            editor.selections.display_ranges(cx),
 1432            &[empty_range(0, "".len())]
 1433        );
 1434    });
 1435}
 1436
 1437#[gpui::test]
 1438fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1439    init_test(cx, |_| {});
 1440
 1441    let editor = cx.add_window(|window, cx| {
 1442        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1443        build_editor(buffer.clone(), window, cx)
 1444    });
 1445    _ = editor.update(cx, |editor, window, cx| {
 1446        editor.change_selections(None, window, cx, |s| {
 1447            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1448        });
 1449
 1450        // moving above start of document should move selection to start of document,
 1451        // but the next move down should still be at the original goal_x
 1452        editor.move_up(&MoveUp, window, cx);
 1453        assert_eq!(
 1454            editor.selections.display_ranges(cx),
 1455            &[empty_range(0, "".len())]
 1456        );
 1457
 1458        editor.move_down(&MoveDown, window, cx);
 1459        assert_eq!(
 1460            editor.selections.display_ranges(cx),
 1461            &[empty_range(1, "abcd".len())]
 1462        );
 1463
 1464        editor.move_down(&MoveDown, window, cx);
 1465        assert_eq!(
 1466            editor.selections.display_ranges(cx),
 1467            &[empty_range(2, "αβγ".len())]
 1468        );
 1469
 1470        editor.move_down(&MoveDown, window, cx);
 1471        assert_eq!(
 1472            editor.selections.display_ranges(cx),
 1473            &[empty_range(3, "abcd".len())]
 1474        );
 1475
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1480        );
 1481
 1482        // moving past end of document should not change goal_x
 1483        editor.move_down(&MoveDown, window, cx);
 1484        assert_eq!(
 1485            editor.selections.display_ranges(cx),
 1486            &[empty_range(5, "".len())]
 1487        );
 1488
 1489        editor.move_down(&MoveDown, window, cx);
 1490        assert_eq!(
 1491            editor.selections.display_ranges(cx),
 1492            &[empty_range(5, "".len())]
 1493        );
 1494
 1495        editor.move_up(&MoveUp, window, cx);
 1496        assert_eq!(
 1497            editor.selections.display_ranges(cx),
 1498            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1499        );
 1500
 1501        editor.move_up(&MoveUp, window, cx);
 1502        assert_eq!(
 1503            editor.selections.display_ranges(cx),
 1504            &[empty_range(3, "abcd".len())]
 1505        );
 1506
 1507        editor.move_up(&MoveUp, window, cx);
 1508        assert_eq!(
 1509            editor.selections.display_ranges(cx),
 1510            &[empty_range(2, "αβγ".len())]
 1511        );
 1512    });
 1513}
 1514
 1515#[gpui::test]
 1516fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1517    init_test(cx, |_| {});
 1518    let move_to_beg = MoveToBeginningOfLine {
 1519        stop_at_soft_wraps: true,
 1520        stop_at_indent: true,
 1521    };
 1522
 1523    let delete_to_beg = DeleteToBeginningOfLine {
 1524        stop_at_indent: false,
 1525    };
 1526
 1527    let move_to_end = MoveToEndOfLine {
 1528        stop_at_soft_wraps: true,
 1529    };
 1530
 1531    let editor = cx.add_window(|window, cx| {
 1532        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1533        build_editor(buffer, window, cx)
 1534    });
 1535    _ = editor.update(cx, |editor, window, cx| {
 1536        editor.change_selections(None, window, cx, |s| {
 1537            s.select_display_ranges([
 1538                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1539                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1540            ]);
 1541        });
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1572                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1573            ]
 1574        );
 1575    });
 1576
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    // Moving to the end of line again is a no-op.
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_to_end_of_line(&move_to_end, window, cx);
 1591        assert_eq!(
 1592            editor.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1595                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = editor.update(cx, |editor, window, cx| {
 1601        editor.move_left(&MoveLeft, window, cx);
 1602        editor.select_to_beginning_of_line(
 1603            &SelectToBeginningOfLine {
 1604                stop_at_soft_wraps: true,
 1605                stop_at_indent: true,
 1606            },
 1607            window,
 1608            cx,
 1609        );
 1610        assert_eq!(
 1611            editor.selections.display_ranges(cx),
 1612            &[
 1613                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1614                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1615            ]
 1616        );
 1617    });
 1618
 1619    _ = editor.update(cx, |editor, window, cx| {
 1620        editor.select_to_beginning_of_line(
 1621            &SelectToBeginningOfLine {
 1622                stop_at_soft_wraps: true,
 1623                stop_at_indent: true,
 1624            },
 1625            window,
 1626            cx,
 1627        );
 1628        assert_eq!(
 1629            editor.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636
 1637    _ = editor.update(cx, |editor, window, cx| {
 1638        editor.select_to_beginning_of_line(
 1639            &SelectToBeginningOfLine {
 1640                stop_at_soft_wraps: true,
 1641                stop_at_indent: true,
 1642            },
 1643            window,
 1644            cx,
 1645        );
 1646        assert_eq!(
 1647            editor.selections.display_ranges(cx),
 1648            &[
 1649                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1650                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1651            ]
 1652        );
 1653    });
 1654
 1655    _ = editor.update(cx, |editor, window, cx| {
 1656        editor.select_to_end_of_line(
 1657            &SelectToEndOfLine {
 1658                stop_at_soft_wraps: true,
 1659            },
 1660            window,
 1661            cx,
 1662        );
 1663        assert_eq!(
 1664            editor.selections.display_ranges(cx),
 1665            &[
 1666                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1667                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1668            ]
 1669        );
 1670    });
 1671
 1672    _ = editor.update(cx, |editor, window, cx| {
 1673        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1674        assert_eq!(editor.display_text(cx), "ab\n  de");
 1675        assert_eq!(
 1676            editor.selections.display_ranges(cx),
 1677            &[
 1678                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1679                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1680            ]
 1681        );
 1682    });
 1683
 1684    _ = editor.update(cx, |editor, window, cx| {
 1685        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1686        assert_eq!(editor.display_text(cx), "\n");
 1687        assert_eq!(
 1688            editor.selections.display_ranges(cx),
 1689            &[
 1690                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1691                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1692            ]
 1693        );
 1694    });
 1695}
 1696
 1697#[gpui::test]
 1698fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1699    init_test(cx, |_| {});
 1700    let move_to_beg = MoveToBeginningOfLine {
 1701        stop_at_soft_wraps: false,
 1702        stop_at_indent: false,
 1703    };
 1704
 1705    let move_to_end = MoveToEndOfLine {
 1706        stop_at_soft_wraps: false,
 1707    };
 1708
 1709    let editor = cx.add_window(|window, cx| {
 1710        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1711        build_editor(buffer, window, cx)
 1712    });
 1713
 1714    _ = editor.update(cx, |editor, window, cx| {
 1715        editor.set_wrap_width(Some(140.0.into()), cx);
 1716
 1717        // We expect the following lines after wrapping
 1718        // ```
 1719        // thequickbrownfox
 1720        // jumpedoverthelazydo
 1721        // gs
 1722        // ```
 1723        // The final `gs` was soft-wrapped onto a new line.
 1724        assert_eq!(
 1725            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1726            editor.display_text(cx),
 1727        );
 1728
 1729        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1730        // Start the cursor at the `k` on the first line
 1731        editor.change_selections(None, window, cx, |s| {
 1732            s.select_display_ranges([
 1733                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1734            ]);
 1735        });
 1736
 1737        // Moving to the beginning of the line should put us at the beginning of the line.
 1738        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1739        assert_eq!(
 1740            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1741            editor.selections.display_ranges(cx)
 1742        );
 1743
 1744        // Moving to the end of the line should put us at the end of the line.
 1745        editor.move_to_end_of_line(&move_to_end, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1752        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1753        editor.change_selections(None, window, cx, |s| {
 1754            s.select_display_ranges([
 1755                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1756            ]);
 1757        });
 1758
 1759        // Moving to the beginning of the line should put us at the start of the second line of
 1760        // display text, i.e., the `j`.
 1761        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the beginning of the line again should be a no-op.
 1768        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773
 1774        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1775        // next display line.
 1776        editor.move_to_end_of_line(&move_to_end, window, cx);
 1777        assert_eq!(
 1778            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1779            editor.selections.display_ranges(cx)
 1780        );
 1781
 1782        // Moving to the end of the line again should be a no-op.
 1783        editor.move_to_end_of_line(&move_to_end, window, cx);
 1784        assert_eq!(
 1785            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1786            editor.selections.display_ranges(cx)
 1787        );
 1788    });
 1789}
 1790
 1791#[gpui::test]
 1792fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1793    init_test(cx, |_| {});
 1794
 1795    let move_to_beg = MoveToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let select_to_beg = SelectToBeginningOfLine {
 1801        stop_at_soft_wraps: true,
 1802        stop_at_indent: true,
 1803    };
 1804
 1805    let delete_to_beg = DeleteToBeginningOfLine {
 1806        stop_at_indent: true,
 1807    };
 1808
 1809    let move_to_end = MoveToEndOfLine {
 1810        stop_at_soft_wraps: false,
 1811    };
 1812
 1813    let editor = cx.add_window(|window, cx| {
 1814        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1815        build_editor(buffer, window, cx)
 1816    });
 1817
 1818    _ = editor.update(cx, |editor, window, cx| {
 1819        editor.change_selections(None, window, cx, |s| {
 1820            s.select_display_ranges([
 1821                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1822                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1823            ]);
 1824        });
 1825
 1826        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1827        // and the second cursor at the first non-whitespace character in the line.
 1828        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1829        assert_eq!(
 1830            editor.selections.display_ranges(cx),
 1831            &[
 1832                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1834            ]
 1835        );
 1836
 1837        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1838        // and should move the second cursor to the beginning of the line.
 1839        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1840        assert_eq!(
 1841            editor.selections.display_ranges(cx),
 1842            &[
 1843                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1844                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1845            ]
 1846        );
 1847
 1848        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1849        // and should move the second cursor back to the first non-whitespace character in the line.
 1850        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1851        assert_eq!(
 1852            editor.selections.display_ranges(cx),
 1853            &[
 1854                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1855                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1856            ]
 1857        );
 1858
 1859        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1860        // and to the first non-whitespace character in the line for the second cursor.
 1861        editor.move_to_end_of_line(&move_to_end, window, cx);
 1862        editor.move_left(&MoveLeft, window, cx);
 1863        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1864        assert_eq!(
 1865            editor.selections.display_ranges(cx),
 1866            &[
 1867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1868                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1869            ]
 1870        );
 1871
 1872        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1873        // and should select to the beginning of the line for the second cursor.
 1874        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[
 1878                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1879                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1880            ]
 1881        );
 1882
 1883        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1884        // and should delete to the first non-whitespace character in the line for the second cursor.
 1885        editor.move_to_end_of_line(&move_to_end, window, cx);
 1886        editor.move_left(&MoveLeft, window, cx);
 1887        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1888        assert_eq!(editor.text(cx), "c\n  f");
 1889    });
 1890}
 1891
 1892#[gpui::test]
 1893fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1894    init_test(cx, |_| {});
 1895
 1896    let editor = cx.add_window(|window, cx| {
 1897        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1898        build_editor(buffer, window, cx)
 1899    });
 1900    _ = editor.update(cx, |editor, window, cx| {
 1901        editor.change_selections(None, window, cx, |s| {
 1902            s.select_display_ranges([
 1903                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1904                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1905            ])
 1906        });
 1907
 1908        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1909        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1918        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1921        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1924        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1925
 1926        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1927        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1928
 1929        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1930        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1931
 1932        editor.move_right(&MoveRight, window, cx);
 1933        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1934        assert_selection_ranges(
 1935            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1936            editor,
 1937            cx,
 1938        );
 1939
 1940        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1941        assert_selection_ranges(
 1942            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1943            editor,
 1944            cx,
 1945        );
 1946
 1947        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1948        assert_selection_ranges(
 1949            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1950            editor,
 1951            cx,
 1952        );
 1953    });
 1954}
 1955
 1956#[gpui::test]
 1957fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1958    init_test(cx, |_| {});
 1959
 1960    let editor = cx.add_window(|window, cx| {
 1961        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1962        build_editor(buffer, window, cx)
 1963    });
 1964
 1965    _ = editor.update(cx, |editor, window, cx| {
 1966        editor.set_wrap_width(Some(140.0.into()), cx);
 1967        assert_eq!(
 1968            editor.display_text(cx),
 1969            "use one::{\n    two::three::\n    four::five\n};"
 1970        );
 1971
 1972        editor.change_selections(None, window, cx, |s| {
 1973            s.select_display_ranges([
 1974                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1975            ]);
 1976        });
 1977
 1978        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1979        assert_eq!(
 1980            editor.selections.display_ranges(cx),
 1981            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1982        );
 1983
 1984        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1985        assert_eq!(
 1986            editor.selections.display_ranges(cx),
 1987            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1988        );
 1989
 1990        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1991        assert_eq!(
 1992            editor.selections.display_ranges(cx),
 1993            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1994        );
 1995
 1996        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1997        assert_eq!(
 1998            editor.selections.display_ranges(cx),
 1999            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2000        );
 2001
 2002        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2003        assert_eq!(
 2004            editor.selections.display_ranges(cx),
 2005            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2006        );
 2007
 2008        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2009        assert_eq!(
 2010            editor.selections.display_ranges(cx),
 2011            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2012        );
 2013    });
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2029
 2030    cx.set_state(
 2031        &r#"ˇone
 2032        two
 2033
 2034        three
 2035        fourˇ
 2036        five
 2037
 2038        six"#
 2039            .unindent(),
 2040    );
 2041
 2042    cx.update_editor(|editor, window, cx| {
 2043        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2044    });
 2045    cx.assert_editor_state(
 2046        &r#"one
 2047        two
 2048        ˇ
 2049        three
 2050        four
 2051        five
 2052        ˇ
 2053        six"#
 2054            .unindent(),
 2055    );
 2056
 2057    cx.update_editor(|editor, window, cx| {
 2058        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2059    });
 2060    cx.assert_editor_state(
 2061        &r#"one
 2062        two
 2063
 2064        three
 2065        four
 2066        five
 2067        ˇ
 2068        sixˇ"#
 2069            .unindent(),
 2070    );
 2071
 2072    cx.update_editor(|editor, window, cx| {
 2073        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2074    });
 2075    cx.assert_editor_state(
 2076        &r#"one
 2077        two
 2078
 2079        three
 2080        four
 2081        five
 2082
 2083        sixˇ"#
 2084            .unindent(),
 2085    );
 2086
 2087    cx.update_editor(|editor, window, cx| {
 2088        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2089    });
 2090    cx.assert_editor_state(
 2091        &r#"one
 2092        two
 2093
 2094        three
 2095        four
 2096        five
 2097        ˇ
 2098        six"#
 2099            .unindent(),
 2100    );
 2101
 2102    cx.update_editor(|editor, window, cx| {
 2103        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2104    });
 2105    cx.assert_editor_state(
 2106        &r#"one
 2107        two
 2108        ˇ
 2109        three
 2110        four
 2111        five
 2112
 2113        six"#
 2114            .unindent(),
 2115    );
 2116
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2119    });
 2120    cx.assert_editor_state(
 2121        &r#"ˇone
 2122        two
 2123
 2124        three
 2125        four
 2126        five
 2127
 2128        six"#
 2129            .unindent(),
 2130    );
 2131}
 2132
 2133#[gpui::test]
 2134async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136    let mut cx = EditorTestContext::new(cx).await;
 2137    let line_height = cx.editor(|editor, window, _| {
 2138        editor
 2139            .style()
 2140            .unwrap()
 2141            .text
 2142            .line_height_in_pixels(window.rem_size())
 2143    });
 2144    let window = cx.window;
 2145    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2146
 2147    cx.set_state(
 2148        r#"ˇone
 2149        two
 2150        three
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#,
 2159    );
 2160
 2161    cx.update_editor(|editor, window, cx| {
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 0.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 3.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 6.)
 2175        );
 2176        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2177        assert_eq!(
 2178            editor.snapshot(window, cx).scroll_position(),
 2179            gpui::Point::new(0., 3.)
 2180        );
 2181
 2182        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 1.)
 2186        );
 2187        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2188        assert_eq!(
 2189            editor.snapshot(window, cx).scroll_position(),
 2190            gpui::Point::new(0., 3.)
 2191        );
 2192    });
 2193}
 2194
 2195#[gpui::test]
 2196async fn test_autoscroll(cx: &mut TestAppContext) {
 2197    init_test(cx, |_| {});
 2198    let mut cx = EditorTestContext::new(cx).await;
 2199
 2200    let line_height = cx.update_editor(|editor, window, cx| {
 2201        editor.set_vertical_scroll_margin(2, cx);
 2202        editor
 2203            .style()
 2204            .unwrap()
 2205            .text
 2206            .line_height_in_pixels(window.rem_size())
 2207    });
 2208    let window = cx.window;
 2209    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2210
 2211    cx.set_state(
 2212        r#"ˇone
 2213            two
 2214            three
 2215            four
 2216            five
 2217            six
 2218            seven
 2219            eight
 2220            nine
 2221            ten
 2222        "#,
 2223    );
 2224    cx.update_editor(|editor, window, cx| {
 2225        assert_eq!(
 2226            editor.snapshot(window, cx).scroll_position(),
 2227            gpui::Point::new(0., 0.0)
 2228        );
 2229    });
 2230
 2231    // Add a cursor below the visible area. Since both cursors cannot fit
 2232    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2233    // allows the vertical scroll margin below that cursor.
 2234    cx.update_editor(|editor, window, cx| {
 2235        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2236            selections.select_ranges([
 2237                Point::new(0, 0)..Point::new(0, 0),
 2238                Point::new(6, 0)..Point::new(6, 0),
 2239            ]);
 2240        })
 2241    });
 2242    cx.update_editor(|editor, window, cx| {
 2243        assert_eq!(
 2244            editor.snapshot(window, cx).scroll_position(),
 2245            gpui::Point::new(0., 3.0)
 2246        );
 2247    });
 2248
 2249    // Move down. The editor cursor scrolls down to track the newest cursor.
 2250    cx.update_editor(|editor, window, cx| {
 2251        editor.move_down(&Default::default(), window, cx);
 2252    });
 2253    cx.update_editor(|editor, window, cx| {
 2254        assert_eq!(
 2255            editor.snapshot(window, cx).scroll_position(),
 2256            gpui::Point::new(0., 4.0)
 2257        );
 2258    });
 2259
 2260    // Add a cursor above the visible area. Since both cursors fit on screen,
 2261    // the editor scrolls to show both.
 2262    cx.update_editor(|editor, window, cx| {
 2263        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2264            selections.select_ranges([
 2265                Point::new(1, 0)..Point::new(1, 0),
 2266                Point::new(6, 0)..Point::new(6, 0),
 2267            ]);
 2268        })
 2269    });
 2270    cx.update_editor(|editor, window, cx| {
 2271        assert_eq!(
 2272            editor.snapshot(window, cx).scroll_position(),
 2273            gpui::Point::new(0., 1.0)
 2274        );
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281    let mut cx = EditorTestContext::new(cx).await;
 2282
 2283    let line_height = cx.editor(|editor, window, _cx| {
 2284        editor
 2285            .style()
 2286            .unwrap()
 2287            .text
 2288            .line_height_in_pixels(window.rem_size())
 2289    });
 2290    let window = cx.window;
 2291    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2292    cx.set_state(
 2293        &r#"
 2294        ˇone
 2295        two
 2296        threeˇ
 2297        four
 2298        five
 2299        six
 2300        seven
 2301        eight
 2302        nine
 2303        ten
 2304        "#
 2305        .unindent(),
 2306    );
 2307
 2308    cx.update_editor(|editor, window, cx| {
 2309        editor.move_page_down(&MovePageDown::default(), window, cx)
 2310    });
 2311    cx.assert_editor_state(
 2312        &r#"
 2313        one
 2314        two
 2315        three
 2316        ˇfour
 2317        five
 2318        sixˇ
 2319        seven
 2320        eight
 2321        nine
 2322        ten
 2323        "#
 2324        .unindent(),
 2325    );
 2326
 2327    cx.update_editor(|editor, window, cx| {
 2328        editor.move_page_down(&MovePageDown::default(), window, cx)
 2329    });
 2330    cx.assert_editor_state(
 2331        &r#"
 2332        one
 2333        two
 2334        three
 2335        four
 2336        five
 2337        six
 2338        ˇseven
 2339        eight
 2340        nineˇ
 2341        ten
 2342        "#
 2343        .unindent(),
 2344    );
 2345
 2346    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2347    cx.assert_editor_state(
 2348        &r#"
 2349        one
 2350        two
 2351        three
 2352        ˇfour
 2353        five
 2354        sixˇ
 2355        seven
 2356        eight
 2357        nine
 2358        ten
 2359        "#
 2360        .unindent(),
 2361    );
 2362
 2363    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2364    cx.assert_editor_state(
 2365        &r#"
 2366        ˇone
 2367        two
 2368        threeˇ
 2369        four
 2370        five
 2371        six
 2372        seven
 2373        eight
 2374        nine
 2375        ten
 2376        "#
 2377        .unindent(),
 2378    );
 2379
 2380    // Test select collapsing
 2381    cx.update_editor(|editor, window, cx| {
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385    });
 2386    cx.assert_editor_state(
 2387        &r#"
 2388        one
 2389        two
 2390        three
 2391        four
 2392        five
 2393        six
 2394        seven
 2395        eight
 2396        nine
 2397        ˇten
 2398        ˇ"#
 2399        .unindent(),
 2400    );
 2401}
 2402
 2403#[gpui::test]
 2404async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2405    init_test(cx, |_| {});
 2406    let mut cx = EditorTestContext::new(cx).await;
 2407    cx.set_state("one «two threeˇ» four");
 2408    cx.update_editor(|editor, window, cx| {
 2409        editor.delete_to_beginning_of_line(
 2410            &DeleteToBeginningOfLine {
 2411                stop_at_indent: false,
 2412            },
 2413            window,
 2414            cx,
 2415        );
 2416        assert_eq!(editor.text(cx), " four");
 2417    });
 2418}
 2419
 2420#[gpui::test]
 2421fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2422    init_test(cx, |_| {});
 2423
 2424    let editor = cx.add_window(|window, cx| {
 2425        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2426        build_editor(buffer.clone(), window, cx)
 2427    });
 2428
 2429    _ = editor.update(cx, |editor, window, cx| {
 2430        editor.change_selections(None, window, cx, |s| {
 2431            s.select_display_ranges([
 2432                // an empty selection - the preceding word fragment is deleted
 2433                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2434                // characters selected - they are deleted
 2435                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2436            ])
 2437        });
 2438        editor.delete_to_previous_word_start(
 2439            &DeleteToPreviousWordStart {
 2440                ignore_newlines: false,
 2441            },
 2442            window,
 2443            cx,
 2444        );
 2445        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2446    });
 2447
 2448    _ = editor.update(cx, |editor, window, cx| {
 2449        editor.change_selections(None, window, cx, |s| {
 2450            s.select_display_ranges([
 2451                // an empty selection - the following word fragment is deleted
 2452                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2453                // characters selected - they are deleted
 2454                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2455            ])
 2456        });
 2457        editor.delete_to_next_word_end(
 2458            &DeleteToNextWordEnd {
 2459                ignore_newlines: false,
 2460            },
 2461            window,
 2462            cx,
 2463        );
 2464        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2465    });
 2466}
 2467
 2468#[gpui::test]
 2469fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2470    init_test(cx, |_| {});
 2471
 2472    let editor = cx.add_window(|window, cx| {
 2473        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2474        build_editor(buffer.clone(), window, cx)
 2475    });
 2476    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2477        ignore_newlines: false,
 2478    };
 2479    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2480        ignore_newlines: true,
 2481    };
 2482
 2483    _ = editor.update(cx, |editor, window, cx| {
 2484        editor.change_selections(None, window, cx, |s| {
 2485            s.select_display_ranges([
 2486                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2487            ])
 2488        });
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2501    });
 2502}
 2503
 2504#[gpui::test]
 2505fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2506    init_test(cx, |_| {});
 2507
 2508    let editor = cx.add_window(|window, cx| {
 2509        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2510        build_editor(buffer.clone(), window, cx)
 2511    });
 2512    let del_to_next_word_end = DeleteToNextWordEnd {
 2513        ignore_newlines: false,
 2514    };
 2515    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2516        ignore_newlines: true,
 2517    };
 2518
 2519    _ = editor.update(cx, |editor, window, cx| {
 2520        editor.change_selections(None, window, cx, |s| {
 2521            s.select_display_ranges([
 2522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2523            ])
 2524        });
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "one\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "\n   two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(
 2537            editor.buffer.read(cx).read(cx).text(),
 2538            "two\nthree\n   four"
 2539        );
 2540        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2541        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2542        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2546    });
 2547}
 2548
 2549#[gpui::test]
 2550fn test_newline(cx: &mut TestAppContext) {
 2551    init_test(cx, |_| {});
 2552
 2553    let editor = cx.add_window(|window, cx| {
 2554        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2555        build_editor(buffer.clone(), window, cx)
 2556    });
 2557
 2558    _ = editor.update(cx, |editor, window, cx| {
 2559        editor.change_selections(None, window, cx, |s| {
 2560            s.select_display_ranges([
 2561                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2563                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2564            ])
 2565        });
 2566
 2567        editor.newline(&Newline, window, cx);
 2568        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2569    });
 2570}
 2571
 2572#[gpui::test]
 2573fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2574    init_test(cx, |_| {});
 2575
 2576    let editor = cx.add_window(|window, cx| {
 2577        let buffer = MultiBuffer::build_simple(
 2578            "
 2579                a
 2580                b(
 2581                    X
 2582                )
 2583                c(
 2584                    X
 2585                )
 2586            "
 2587            .unindent()
 2588            .as_str(),
 2589            cx,
 2590        );
 2591        let mut editor = build_editor(buffer.clone(), window, cx);
 2592        editor.change_selections(None, window, cx, |s| {
 2593            s.select_ranges([
 2594                Point::new(2, 4)..Point::new(2, 5),
 2595                Point::new(5, 4)..Point::new(5, 5),
 2596            ])
 2597        });
 2598        editor
 2599    });
 2600
 2601    _ = editor.update(cx, |editor, window, cx| {
 2602        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2603        editor.buffer.update(cx, |buffer, cx| {
 2604            buffer.edit(
 2605                [
 2606                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2607                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2608                ],
 2609                None,
 2610                cx,
 2611            );
 2612            assert_eq!(
 2613                buffer.read(cx).text(),
 2614                "
 2615                    a
 2616                    b()
 2617                    c()
 2618                "
 2619                .unindent()
 2620            );
 2621        });
 2622        assert_eq!(
 2623            editor.selections.ranges(cx),
 2624            &[
 2625                Point::new(1, 2)..Point::new(1, 2),
 2626                Point::new(2, 2)..Point::new(2, 2),
 2627            ],
 2628        );
 2629
 2630        editor.newline(&Newline, window, cx);
 2631        assert_eq!(
 2632            editor.text(cx),
 2633            "
 2634                a
 2635                b(
 2636                )
 2637                c(
 2638                )
 2639            "
 2640            .unindent()
 2641        );
 2642
 2643        // The selections are moved after the inserted newlines
 2644        assert_eq!(
 2645            editor.selections.ranges(cx),
 2646            &[
 2647                Point::new(2, 0)..Point::new(2, 0),
 2648                Point::new(4, 0)..Point::new(4, 0),
 2649            ],
 2650        );
 2651    });
 2652}
 2653
 2654#[gpui::test]
 2655async fn test_newline_above(cx: &mut TestAppContext) {
 2656    init_test(cx, |settings| {
 2657        settings.defaults.tab_size = NonZeroU32::new(4)
 2658    });
 2659
 2660    let language = Arc::new(
 2661        Language::new(
 2662            LanguageConfig::default(),
 2663            Some(tree_sitter_rust::LANGUAGE.into()),
 2664        )
 2665        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2666        .unwrap(),
 2667    );
 2668
 2669    let mut cx = EditorTestContext::new(cx).await;
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671    cx.set_state(indoc! {"
 2672        const a: ˇA = (
 2673 2674                «const_functionˇ»(ˇ),
 2675                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2676 2677        ˇ);ˇ
 2678    "});
 2679
 2680    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2681    cx.assert_editor_state(indoc! {"
 2682        ˇ
 2683        const a: A = (
 2684            ˇ
 2685            (
 2686                ˇ
 2687                ˇ
 2688                const_function(),
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                something_else,
 2694                ˇ
 2695            )
 2696            ˇ
 2697            ˇ
 2698        );
 2699    "});
 2700}
 2701
 2702#[gpui::test]
 2703async fn test_newline_below(cx: &mut TestAppContext) {
 2704    init_test(cx, |settings| {
 2705        settings.defaults.tab_size = NonZeroU32::new(4)
 2706    });
 2707
 2708    let language = Arc::new(
 2709        Language::new(
 2710            LanguageConfig::default(),
 2711            Some(tree_sitter_rust::LANGUAGE.into()),
 2712        )
 2713        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2714        .unwrap(),
 2715    );
 2716
 2717    let mut cx = EditorTestContext::new(cx).await;
 2718    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2719    cx.set_state(indoc! {"
 2720        const a: ˇA = (
 2721 2722                «const_functionˇ»(ˇ),
 2723                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2724 2725        ˇ);ˇ
 2726    "});
 2727
 2728    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2729    cx.assert_editor_state(indoc! {"
 2730        const a: A = (
 2731            ˇ
 2732            (
 2733                ˇ
 2734                const_function(),
 2735                ˇ
 2736                ˇ
 2737                something_else,
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742            )
 2743            ˇ
 2744        );
 2745        ˇ
 2746        ˇ
 2747    "});
 2748}
 2749
 2750#[gpui::test]
 2751async fn test_newline_comments(cx: &mut TestAppContext) {
 2752    init_test(cx, |settings| {
 2753        settings.defaults.tab_size = NonZeroU32::new(4)
 2754    });
 2755
 2756    let language = Arc::new(Language::new(
 2757        LanguageConfig {
 2758            line_comments: vec!["//".into()],
 2759            ..LanguageConfig::default()
 2760        },
 2761        None,
 2762    ));
 2763    {
 2764        let mut cx = EditorTestContext::new(cx).await;
 2765        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2766        cx.set_state(indoc! {"
 2767        // Fooˇ
 2768    "});
 2769
 2770        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2771        cx.assert_editor_state(indoc! {"
 2772        // Foo
 2773        //ˇ
 2774    "});
 2775        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2776        cx.set_state(indoc! {"
 2777        ˇ// Foo
 2778    "});
 2779        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2780        cx.assert_editor_state(indoc! {"
 2781
 2782        ˇ// Foo
 2783    "});
 2784    }
 2785    // Ensure that comment continuations can be disabled.
 2786    update_test_language_settings(cx, |settings| {
 2787        settings.defaults.extend_comment_on_newline = Some(false);
 2788    });
 2789    let mut cx = EditorTestContext::new(cx).await;
 2790    cx.set_state(indoc! {"
 2791        // Fooˇ
 2792    "});
 2793    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794    cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        ˇ
 2797    "});
 2798}
 2799
 2800#[gpui::test]
 2801fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2802    init_test(cx, |_| {});
 2803
 2804    let editor = cx.add_window(|window, cx| {
 2805        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2806        let mut editor = build_editor(buffer.clone(), window, cx);
 2807        editor.change_selections(None, window, cx, |s| {
 2808            s.select_ranges([3..4, 11..12, 19..20])
 2809        });
 2810        editor
 2811    });
 2812
 2813    _ = editor.update(cx, |editor, window, cx| {
 2814        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2815        editor.buffer.update(cx, |buffer, cx| {
 2816            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2817            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2818        });
 2819        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2820
 2821        editor.insert("Z", window, cx);
 2822        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2823
 2824        // The selections are moved after the inserted characters
 2825        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2826    });
 2827}
 2828
 2829#[gpui::test]
 2830async fn test_tab(cx: &mut TestAppContext) {
 2831    init_test(cx, |settings| {
 2832        settings.defaults.tab_size = NonZeroU32::new(3)
 2833    });
 2834
 2835    let mut cx = EditorTestContext::new(cx).await;
 2836    cx.set_state(indoc! {"
 2837        ˇabˇc
 2838        ˇ🏀ˇ🏀ˇefg
 2839 2840    "});
 2841    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843           ˇab ˇc
 2844           ˇ🏀  ˇ🏀  ˇefg
 2845        d  ˇ
 2846    "});
 2847
 2848    cx.set_state(indoc! {"
 2849        a
 2850        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        a
 2855           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2856    "});
 2857}
 2858
 2859#[gpui::test]
 2860async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2861    init_test(cx, |_| {});
 2862
 2863    let mut cx = EditorTestContext::new(cx).await;
 2864    let language = Arc::new(
 2865        Language::new(
 2866            LanguageConfig::default(),
 2867            Some(tree_sitter_rust::LANGUAGE.into()),
 2868        )
 2869        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2870        .unwrap(),
 2871    );
 2872    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2873
 2874    // cursors that are already at the suggested indent level insert
 2875    // a soft tab. cursors that are to the left of the suggested indent
 2876    // auto-indent their line.
 2877    cx.set_state(indoc! {"
 2878        ˇ
 2879        const a: B = (
 2880            c(
 2881                d(
 2882        ˇ
 2883                )
 2884        ˇ
 2885        ˇ    )
 2886        );
 2887    "});
 2888    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2889    cx.assert_editor_state(indoc! {"
 2890            ˇ
 2891        const a: B = (
 2892            c(
 2893                d(
 2894                    ˇ
 2895                )
 2896                ˇ
 2897            ˇ)
 2898        );
 2899    "});
 2900
 2901    // handle auto-indent when there are multiple cursors on the same line
 2902    cx.set_state(indoc! {"
 2903        const a: B = (
 2904            c(
 2905        ˇ    ˇ
 2906        ˇ    )
 2907        );
 2908    "});
 2909    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2910    cx.assert_editor_state(indoc! {"
 2911        const a: B = (
 2912            c(
 2913                ˇ
 2914            ˇ)
 2915        );
 2916    "});
 2917}
 2918
 2919#[gpui::test]
 2920async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2921    init_test(cx, |settings| {
 2922        settings.defaults.tab_size = NonZeroU32::new(4)
 2923    });
 2924
 2925    let language = Arc::new(
 2926        Language::new(
 2927            LanguageConfig::default(),
 2928            Some(tree_sitter_rust::LANGUAGE.into()),
 2929        )
 2930        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2931        .unwrap(),
 2932    );
 2933
 2934    let mut cx = EditorTestContext::new(cx).await;
 2935    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2936    cx.set_state(indoc! {"
 2937        fn a() {
 2938            if b {
 2939        \t ˇc
 2940            }
 2941        }
 2942    "});
 2943
 2944    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2945    cx.assert_editor_state(indoc! {"
 2946        fn a() {
 2947            if b {
 2948                ˇc
 2949            }
 2950        }
 2951    "});
 2952}
 2953
 2954#[gpui::test]
 2955async fn test_indent_outdent(cx: &mut TestAppContext) {
 2956    init_test(cx, |settings| {
 2957        settings.defaults.tab_size = NonZeroU32::new(4);
 2958    });
 2959
 2960    let mut cx = EditorTestContext::new(cx).await;
 2961
 2962    cx.set_state(indoc! {"
 2963          «oneˇ» «twoˇ»
 2964        three
 2965         four
 2966    "});
 2967    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2968    cx.assert_editor_state(indoc! {"
 2969            «oneˇ» «twoˇ»
 2970        three
 2971         four
 2972    "});
 2973
 2974    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2975    cx.assert_editor_state(indoc! {"
 2976        «oneˇ» «twoˇ»
 2977        three
 2978         four
 2979    "});
 2980
 2981    // select across line ending
 2982    cx.set_state(indoc! {"
 2983        one two
 2984        t«hree
 2985        ˇ» four
 2986    "});
 2987    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2988    cx.assert_editor_state(indoc! {"
 2989        one two
 2990            t«hree
 2991        ˇ» four
 2992    "});
 2993
 2994    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2995    cx.assert_editor_state(indoc! {"
 2996        one two
 2997        t«hree
 2998        ˇ» four
 2999    "});
 3000
 3001    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3002    cx.set_state(indoc! {"
 3003        one two
 3004        ˇthree
 3005            four
 3006    "});
 3007    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3008    cx.assert_editor_state(indoc! {"
 3009        one two
 3010            ˇthree
 3011            four
 3012    "});
 3013
 3014    cx.set_state(indoc! {"
 3015        one two
 3016        ˇ    three
 3017            four
 3018    "});
 3019    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3020    cx.assert_editor_state(indoc! {"
 3021        one two
 3022        ˇthree
 3023            four
 3024    "});
 3025}
 3026
 3027#[gpui::test]
 3028async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3029    init_test(cx, |settings| {
 3030        settings.defaults.hard_tabs = Some(true);
 3031    });
 3032
 3033    let mut cx = EditorTestContext::new(cx).await;
 3034
 3035    // select two ranges on one line
 3036    cx.set_state(indoc! {"
 3037        «oneˇ» «twoˇ»
 3038        three
 3039        four
 3040    "});
 3041    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3042    cx.assert_editor_state(indoc! {"
 3043        \t«oneˇ» «twoˇ»
 3044        three
 3045        four
 3046    "});
 3047    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3048    cx.assert_editor_state(indoc! {"
 3049        \t\t«oneˇ» «twoˇ»
 3050        three
 3051        four
 3052    "});
 3053    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3054    cx.assert_editor_state(indoc! {"
 3055        \t«oneˇ» «twoˇ»
 3056        three
 3057        four
 3058    "});
 3059    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3060    cx.assert_editor_state(indoc! {"
 3061        «oneˇ» «twoˇ»
 3062        three
 3063        four
 3064    "});
 3065
 3066    // select across a line ending
 3067    cx.set_state(indoc! {"
 3068        one two
 3069        t«hree
 3070        ˇ»four
 3071    "});
 3072    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3073    cx.assert_editor_state(indoc! {"
 3074        one two
 3075        \tt«hree
 3076        ˇ»four
 3077    "});
 3078    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3079    cx.assert_editor_state(indoc! {"
 3080        one two
 3081        \t\tt«hree
 3082        ˇ»four
 3083    "});
 3084    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3085    cx.assert_editor_state(indoc! {"
 3086        one two
 3087        \tt«hree
 3088        ˇ»four
 3089    "});
 3090    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3091    cx.assert_editor_state(indoc! {"
 3092        one two
 3093        t«hree
 3094        ˇ»four
 3095    "});
 3096
 3097    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3098    cx.set_state(indoc! {"
 3099        one two
 3100        ˇthree
 3101        four
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        one two
 3106        ˇthree
 3107        four
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        one two
 3112        \tˇthree
 3113        four
 3114    "});
 3115    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3116    cx.assert_editor_state(indoc! {"
 3117        one two
 3118        ˇthree
 3119        four
 3120    "});
 3121}
 3122
 3123#[gpui::test]
 3124fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3125    init_test(cx, |settings| {
 3126        settings.languages.extend([
 3127            (
 3128                "TOML".into(),
 3129                LanguageSettingsContent {
 3130                    tab_size: NonZeroU32::new(2),
 3131                    ..Default::default()
 3132                },
 3133            ),
 3134            (
 3135                "Rust".into(),
 3136                LanguageSettingsContent {
 3137                    tab_size: NonZeroU32::new(4),
 3138                    ..Default::default()
 3139                },
 3140            ),
 3141        ]);
 3142    });
 3143
 3144    let toml_language = Arc::new(Language::new(
 3145        LanguageConfig {
 3146            name: "TOML".into(),
 3147            ..Default::default()
 3148        },
 3149        None,
 3150    ));
 3151    let rust_language = Arc::new(Language::new(
 3152        LanguageConfig {
 3153            name: "Rust".into(),
 3154            ..Default::default()
 3155        },
 3156        None,
 3157    ));
 3158
 3159    let toml_buffer =
 3160        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3161    let rust_buffer =
 3162        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3163    let multibuffer = cx.new(|cx| {
 3164        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3165        multibuffer.push_excerpts(
 3166            toml_buffer.clone(),
 3167            [ExcerptRange {
 3168                context: Point::new(0, 0)..Point::new(2, 0),
 3169                primary: None,
 3170            }],
 3171            cx,
 3172        );
 3173        multibuffer.push_excerpts(
 3174            rust_buffer.clone(),
 3175            [ExcerptRange {
 3176                context: Point::new(0, 0)..Point::new(1, 0),
 3177                primary: None,
 3178            }],
 3179            cx,
 3180        );
 3181        multibuffer
 3182    });
 3183
 3184    cx.add_window(|window, cx| {
 3185        let mut editor = build_editor(multibuffer, window, cx);
 3186
 3187        assert_eq!(
 3188            editor.text(cx),
 3189            indoc! {"
 3190                a = 1
 3191                b = 2
 3192
 3193                const c: usize = 3;
 3194            "}
 3195        );
 3196
 3197        select_ranges(
 3198            &mut editor,
 3199            indoc! {"
 3200                «aˇ» = 1
 3201                b = 2
 3202
 3203                «const c:ˇ» usize = 3;
 3204            "},
 3205            window,
 3206            cx,
 3207        );
 3208
 3209        editor.tab(&Tab, window, cx);
 3210        assert_text_with_selections(
 3211            &mut editor,
 3212            indoc! {"
 3213                  «aˇ» = 1
 3214                b = 2
 3215
 3216                    «const c:ˇ» usize = 3;
 3217            "},
 3218            cx,
 3219        );
 3220        editor.backtab(&Backtab, window, cx);
 3221        assert_text_with_selections(
 3222            &mut editor,
 3223            indoc! {"
 3224                «aˇ» = 1
 3225                b = 2
 3226
 3227                «const c:ˇ» usize = 3;
 3228            "},
 3229            cx,
 3230        );
 3231
 3232        editor
 3233    });
 3234}
 3235
 3236#[gpui::test]
 3237async fn test_backspace(cx: &mut TestAppContext) {
 3238    init_test(cx, |_| {});
 3239
 3240    let mut cx = EditorTestContext::new(cx).await;
 3241
 3242    // Basic backspace
 3243    cx.set_state(indoc! {"
 3244        onˇe two three
 3245        fou«rˇ» five six
 3246        seven «ˇeight nine
 3247        »ten
 3248    "});
 3249    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3250    cx.assert_editor_state(indoc! {"
 3251        oˇe two three
 3252        fouˇ five six
 3253        seven ˇten
 3254    "});
 3255
 3256    // Test backspace inside and around indents
 3257    cx.set_state(indoc! {"
 3258        zero
 3259            ˇone
 3260                ˇtwo
 3261            ˇ ˇ ˇ  three
 3262        ˇ  ˇ  four
 3263    "});
 3264    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3265    cx.assert_editor_state(indoc! {"
 3266        zero
 3267        ˇone
 3268            ˇtwo
 3269        ˇ  threeˇ  four
 3270    "});
 3271
 3272    // Test backspace with line_mode set to true
 3273    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3274    cx.set_state(indoc! {"
 3275        The ˇquick ˇbrown
 3276        fox jumps over
 3277        the lazy dog
 3278        ˇThe qu«ick bˇ»rown"});
 3279    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3280    cx.assert_editor_state(indoc! {"
 3281        ˇfox jumps over
 3282        the lazy dogˇ"});
 3283}
 3284
 3285#[gpui::test]
 3286async fn test_delete(cx: &mut TestAppContext) {
 3287    init_test(cx, |_| {});
 3288
 3289    let mut cx = EditorTestContext::new(cx).await;
 3290    cx.set_state(indoc! {"
 3291        onˇe two three
 3292        fou«rˇ» five six
 3293        seven «ˇeight nine
 3294        »ten
 3295    "});
 3296    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3297    cx.assert_editor_state(indoc! {"
 3298        onˇ two three
 3299        fouˇ five six
 3300        seven ˇten
 3301    "});
 3302
 3303    // Test backspace with line_mode set to true
 3304    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3305    cx.set_state(indoc! {"
 3306        The ˇquick ˇbrown
 3307        fox «ˇjum»ps over
 3308        the lazy dog
 3309        ˇThe qu«ick bˇ»rown"});
 3310    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3311    cx.assert_editor_state("ˇthe lazy dogˇ");
 3312}
 3313
 3314#[gpui::test]
 3315fn test_delete_line(cx: &mut TestAppContext) {
 3316    init_test(cx, |_| {});
 3317
 3318    let editor = cx.add_window(|window, cx| {
 3319        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3320        build_editor(buffer, window, cx)
 3321    });
 3322    _ = editor.update(cx, |editor, window, cx| {
 3323        editor.change_selections(None, window, cx, |s| {
 3324            s.select_display_ranges([
 3325                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3326                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3327                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3328            ])
 3329        });
 3330        editor.delete_line(&DeleteLine, window, cx);
 3331        assert_eq!(editor.display_text(cx), "ghi");
 3332        assert_eq!(
 3333            editor.selections.display_ranges(cx),
 3334            vec![
 3335                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3336                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3337            ]
 3338        );
 3339    });
 3340
 3341    let editor = cx.add_window(|window, cx| {
 3342        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3343        build_editor(buffer, window, cx)
 3344    });
 3345    _ = editor.update(cx, |editor, window, cx| {
 3346        editor.change_selections(None, window, cx, |s| {
 3347            s.select_display_ranges([
 3348                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3349            ])
 3350        });
 3351        editor.delete_line(&DeleteLine, window, cx);
 3352        assert_eq!(editor.display_text(cx), "ghi\n");
 3353        assert_eq!(
 3354            editor.selections.display_ranges(cx),
 3355            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3356        );
 3357    });
 3358}
 3359
 3360#[gpui::test]
 3361fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3362    init_test(cx, |_| {});
 3363
 3364    cx.add_window(|window, cx| {
 3365        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3366        let mut editor = build_editor(buffer.clone(), window, cx);
 3367        let buffer = buffer.read(cx).as_singleton().unwrap();
 3368
 3369        assert_eq!(
 3370            editor.selections.ranges::<Point>(cx),
 3371            &[Point::new(0, 0)..Point::new(0, 0)]
 3372        );
 3373
 3374        // When on single line, replace newline at end by space
 3375        editor.join_lines(&JoinLines, window, cx);
 3376        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3377        assert_eq!(
 3378            editor.selections.ranges::<Point>(cx),
 3379            &[Point::new(0, 3)..Point::new(0, 3)]
 3380        );
 3381
 3382        // When multiple lines are selected, remove newlines that are spanned by the selection
 3383        editor.change_selections(None, window, cx, |s| {
 3384            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3385        });
 3386        editor.join_lines(&JoinLines, window, cx);
 3387        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3388        assert_eq!(
 3389            editor.selections.ranges::<Point>(cx),
 3390            &[Point::new(0, 11)..Point::new(0, 11)]
 3391        );
 3392
 3393        // Undo should be transactional
 3394        editor.undo(&Undo, window, cx);
 3395        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3396        assert_eq!(
 3397            editor.selections.ranges::<Point>(cx),
 3398            &[Point::new(0, 5)..Point::new(2, 2)]
 3399        );
 3400
 3401        // When joining an empty line don't insert a space
 3402        editor.change_selections(None, window, cx, |s| {
 3403            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3404        });
 3405        editor.join_lines(&JoinLines, window, cx);
 3406        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3407        assert_eq!(
 3408            editor.selections.ranges::<Point>(cx),
 3409            [Point::new(2, 3)..Point::new(2, 3)]
 3410        );
 3411
 3412        // We can remove trailing newlines
 3413        editor.join_lines(&JoinLines, window, cx);
 3414        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3415        assert_eq!(
 3416            editor.selections.ranges::<Point>(cx),
 3417            [Point::new(2, 3)..Point::new(2, 3)]
 3418        );
 3419
 3420        // We don't blow up on the last line
 3421        editor.join_lines(&JoinLines, window, cx);
 3422        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3423        assert_eq!(
 3424            editor.selections.ranges::<Point>(cx),
 3425            [Point::new(2, 3)..Point::new(2, 3)]
 3426        );
 3427
 3428        // reset to test indentation
 3429        editor.buffer.update(cx, |buffer, cx| {
 3430            buffer.edit(
 3431                [
 3432                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3433                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3434                ],
 3435                None,
 3436                cx,
 3437            )
 3438        });
 3439
 3440        // We remove any leading spaces
 3441        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3442        editor.change_selections(None, window, cx, |s| {
 3443            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3444        });
 3445        editor.join_lines(&JoinLines, window, cx);
 3446        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3447
 3448        // We don't insert a space for a line containing only spaces
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3451
 3452        // We ignore any leading tabs
 3453        editor.join_lines(&JoinLines, window, cx);
 3454        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3455
 3456        editor
 3457    });
 3458}
 3459
 3460#[gpui::test]
 3461fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3462    init_test(cx, |_| {});
 3463
 3464    cx.add_window(|window, cx| {
 3465        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3466        let mut editor = build_editor(buffer.clone(), window, cx);
 3467        let buffer = buffer.read(cx).as_singleton().unwrap();
 3468
 3469        editor.change_selections(None, window, cx, |s| {
 3470            s.select_ranges([
 3471                Point::new(0, 2)..Point::new(1, 1),
 3472                Point::new(1, 2)..Point::new(1, 2),
 3473                Point::new(3, 1)..Point::new(3, 2),
 3474            ])
 3475        });
 3476
 3477        editor.join_lines(&JoinLines, window, cx);
 3478        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3479
 3480        assert_eq!(
 3481            editor.selections.ranges::<Point>(cx),
 3482            [
 3483                Point::new(0, 7)..Point::new(0, 7),
 3484                Point::new(1, 3)..Point::new(1, 3)
 3485            ]
 3486        );
 3487        editor
 3488    });
 3489}
 3490
 3491#[gpui::test]
 3492async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3493    init_test(cx, |_| {});
 3494
 3495    let mut cx = EditorTestContext::new(cx).await;
 3496
 3497    let diff_base = r#"
 3498        Line 0
 3499        Line 1
 3500        Line 2
 3501        Line 3
 3502        "#
 3503    .unindent();
 3504
 3505    cx.set_state(
 3506        &r#"
 3507        ˇLine 0
 3508        Line 1
 3509        Line 2
 3510        Line 3
 3511        "#
 3512        .unindent(),
 3513    );
 3514
 3515    cx.set_head_text(&diff_base);
 3516    executor.run_until_parked();
 3517
 3518    // Join lines
 3519    cx.update_editor(|editor, window, cx| {
 3520        editor.join_lines(&JoinLines, window, cx);
 3521    });
 3522    executor.run_until_parked();
 3523
 3524    cx.assert_editor_state(
 3525        &r#"
 3526        Line 0ˇ Line 1
 3527        Line 2
 3528        Line 3
 3529        "#
 3530        .unindent(),
 3531    );
 3532    // Join again
 3533    cx.update_editor(|editor, window, cx| {
 3534        editor.join_lines(&JoinLines, window, cx);
 3535    });
 3536    executor.run_until_parked();
 3537
 3538    cx.assert_editor_state(
 3539        &r#"
 3540        Line 0 Line 1ˇ Line 2
 3541        Line 3
 3542        "#
 3543        .unindent(),
 3544    );
 3545}
 3546
 3547#[gpui::test]
 3548async fn test_custom_newlines_cause_no_false_positive_diffs(
 3549    executor: BackgroundExecutor,
 3550    cx: &mut TestAppContext,
 3551) {
 3552    init_test(cx, |_| {});
 3553    let mut cx = EditorTestContext::new(cx).await;
 3554    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3555    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3556    executor.run_until_parked();
 3557
 3558    cx.update_editor(|editor, window, cx| {
 3559        let snapshot = editor.snapshot(window, cx);
 3560        assert_eq!(
 3561            snapshot
 3562                .buffer_snapshot
 3563                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3564                .collect::<Vec<_>>(),
 3565            Vec::new(),
 3566            "Should not have any diffs for files with custom newlines"
 3567        );
 3568    });
 3569}
 3570
 3571#[gpui::test]
 3572async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3573    init_test(cx, |_| {});
 3574
 3575    let mut cx = EditorTestContext::new(cx).await;
 3576
 3577    // Test sort_lines_case_insensitive()
 3578    cx.set_state(indoc! {"
 3579        «z
 3580        y
 3581        x
 3582        Z
 3583        Y
 3584        Xˇ»
 3585    "});
 3586    cx.update_editor(|e, window, cx| {
 3587        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3588    });
 3589    cx.assert_editor_state(indoc! {"
 3590        «x
 3591        X
 3592        y
 3593        Y
 3594        z
 3595        Zˇ»
 3596    "});
 3597
 3598    // Test reverse_lines()
 3599    cx.set_state(indoc! {"
 3600        «5
 3601        4
 3602        3
 3603        2
 3604        1ˇ»
 3605    "});
 3606    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3607    cx.assert_editor_state(indoc! {"
 3608        «1
 3609        2
 3610        3
 3611        4
 3612        5ˇ»
 3613    "});
 3614
 3615    // Skip testing shuffle_line()
 3616
 3617    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3618    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3619
 3620    // Don't manipulate when cursor is on single line, but expand the selection
 3621    cx.set_state(indoc! {"
 3622        ddˇdd
 3623        ccc
 3624        bb
 3625        a
 3626    "});
 3627    cx.update_editor(|e, window, cx| {
 3628        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3629    });
 3630    cx.assert_editor_state(indoc! {"
 3631        «ddddˇ»
 3632        ccc
 3633        bb
 3634        a
 3635    "});
 3636
 3637    // Basic manipulate case
 3638    // Start selection moves to column 0
 3639    // End of selection shrinks to fit shorter line
 3640    cx.set_state(indoc! {"
 3641        dd«d
 3642        ccc
 3643        bb
 3644        aaaaaˇ»
 3645    "});
 3646    cx.update_editor(|e, window, cx| {
 3647        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3648    });
 3649    cx.assert_editor_state(indoc! {"
 3650        «aaaaa
 3651        bb
 3652        ccc
 3653        dddˇ»
 3654    "});
 3655
 3656    // Manipulate case with newlines
 3657    cx.set_state(indoc! {"
 3658        dd«d
 3659        ccc
 3660
 3661        bb
 3662        aaaaa
 3663
 3664        ˇ»
 3665    "});
 3666    cx.update_editor(|e, window, cx| {
 3667        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3668    });
 3669    cx.assert_editor_state(indoc! {"
 3670        «
 3671
 3672        aaaaa
 3673        bb
 3674        ccc
 3675        dddˇ»
 3676
 3677    "});
 3678
 3679    // Adding new line
 3680    cx.set_state(indoc! {"
 3681        aa«a
 3682        bbˇ»b
 3683    "});
 3684    cx.update_editor(|e, window, cx| {
 3685        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3686    });
 3687    cx.assert_editor_state(indoc! {"
 3688        «aaa
 3689        bbb
 3690        added_lineˇ»
 3691    "});
 3692
 3693    // Removing line
 3694    cx.set_state(indoc! {"
 3695        aa«a
 3696        bbbˇ»
 3697    "});
 3698    cx.update_editor(|e, window, cx| {
 3699        e.manipulate_lines(window, cx, |lines| {
 3700            lines.pop();
 3701        })
 3702    });
 3703    cx.assert_editor_state(indoc! {"
 3704        «aaaˇ»
 3705    "});
 3706
 3707    // Removing all lines
 3708    cx.set_state(indoc! {"
 3709        aa«a
 3710        bbbˇ»
 3711    "});
 3712    cx.update_editor(|e, window, cx| {
 3713        e.manipulate_lines(window, cx, |lines| {
 3714            lines.drain(..);
 3715        })
 3716    });
 3717    cx.assert_editor_state(indoc! {"
 3718        ˇ
 3719    "});
 3720}
 3721
 3722#[gpui::test]
 3723async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3724    init_test(cx, |_| {});
 3725
 3726    let mut cx = EditorTestContext::new(cx).await;
 3727
 3728    // Consider continuous selection as single selection
 3729    cx.set_state(indoc! {"
 3730        Aaa«aa
 3731        cˇ»c«c
 3732        bb
 3733        aaaˇ»aa
 3734    "});
 3735    cx.update_editor(|e, window, cx| {
 3736        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3737    });
 3738    cx.assert_editor_state(indoc! {"
 3739        «Aaaaa
 3740        ccc
 3741        bb
 3742        aaaaaˇ»
 3743    "});
 3744
 3745    cx.set_state(indoc! {"
 3746        Aaa«aa
 3747        cˇ»c«c
 3748        bb
 3749        aaaˇ»aa
 3750    "});
 3751    cx.update_editor(|e, window, cx| {
 3752        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3753    });
 3754    cx.assert_editor_state(indoc! {"
 3755        «Aaaaa
 3756        ccc
 3757        bbˇ»
 3758    "});
 3759
 3760    // Consider non continuous selection as distinct dedup operations
 3761    cx.set_state(indoc! {"
 3762        «aaaaa
 3763        bb
 3764        aaaaa
 3765        aaaaaˇ»
 3766
 3767        aaa«aaˇ»
 3768    "});
 3769    cx.update_editor(|e, window, cx| {
 3770        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3771    });
 3772    cx.assert_editor_state(indoc! {"
 3773        «aaaaa
 3774        bbˇ»
 3775
 3776        «aaaaaˇ»
 3777    "});
 3778}
 3779
 3780#[gpui::test]
 3781async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3782    init_test(cx, |_| {});
 3783
 3784    let mut cx = EditorTestContext::new(cx).await;
 3785
 3786    cx.set_state(indoc! {"
 3787        «Aaa
 3788        aAa
 3789        Aaaˇ»
 3790    "});
 3791    cx.update_editor(|e, window, cx| {
 3792        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3793    });
 3794    cx.assert_editor_state(indoc! {"
 3795        «Aaa
 3796        aAaˇ»
 3797    "});
 3798
 3799    cx.set_state(indoc! {"
 3800        «Aaa
 3801        aAa
 3802        aaAˇ»
 3803    "});
 3804    cx.update_editor(|e, window, cx| {
 3805        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3806    });
 3807    cx.assert_editor_state(indoc! {"
 3808        «Aaaˇ»
 3809    "});
 3810}
 3811
 3812#[gpui::test]
 3813async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3814    init_test(cx, |_| {});
 3815
 3816    let mut cx = EditorTestContext::new(cx).await;
 3817
 3818    // Manipulate with multiple selections on a single line
 3819    cx.set_state(indoc! {"
 3820        dd«dd
 3821        cˇ»c«c
 3822        bb
 3823        aaaˇ»aa
 3824    "});
 3825    cx.update_editor(|e, window, cx| {
 3826        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3827    });
 3828    cx.assert_editor_state(indoc! {"
 3829        «aaaaa
 3830        bb
 3831        ccc
 3832        ddddˇ»
 3833    "});
 3834
 3835    // Manipulate with multiple disjoin selections
 3836    cx.set_state(indoc! {"
 3837 3838        4
 3839        3
 3840        2
 3841        1ˇ»
 3842
 3843        dd«dd
 3844        ccc
 3845        bb
 3846        aaaˇ»aa
 3847    "});
 3848    cx.update_editor(|e, window, cx| {
 3849        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3850    });
 3851    cx.assert_editor_state(indoc! {"
 3852        «1
 3853        2
 3854        3
 3855        4
 3856        5ˇ»
 3857
 3858        «aaaaa
 3859        bb
 3860        ccc
 3861        ddddˇ»
 3862    "});
 3863
 3864    // Adding lines on each selection
 3865    cx.set_state(indoc! {"
 3866 3867        1ˇ»
 3868
 3869        bb«bb
 3870        aaaˇ»aa
 3871    "});
 3872    cx.update_editor(|e, window, cx| {
 3873        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3874    });
 3875    cx.assert_editor_state(indoc! {"
 3876        «2
 3877        1
 3878        added lineˇ»
 3879
 3880        «bbbb
 3881        aaaaa
 3882        added lineˇ»
 3883    "});
 3884
 3885    // Removing lines on each selection
 3886    cx.set_state(indoc! {"
 3887 3888        1ˇ»
 3889
 3890        bb«bb
 3891        aaaˇ»aa
 3892    "});
 3893    cx.update_editor(|e, window, cx| {
 3894        e.manipulate_lines(window, cx, |lines| {
 3895            lines.pop();
 3896        })
 3897    });
 3898    cx.assert_editor_state(indoc! {"
 3899        «2ˇ»
 3900
 3901        «bbbbˇ»
 3902    "});
 3903}
 3904
 3905#[gpui::test]
 3906async fn test_manipulate_text(cx: &mut TestAppContext) {
 3907    init_test(cx, |_| {});
 3908
 3909    let mut cx = EditorTestContext::new(cx).await;
 3910
 3911    // Test convert_to_upper_case()
 3912    cx.set_state(indoc! {"
 3913        «hello worldˇ»
 3914    "});
 3915    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3916    cx.assert_editor_state(indoc! {"
 3917        «HELLO WORLDˇ»
 3918    "});
 3919
 3920    // Test convert_to_lower_case()
 3921    cx.set_state(indoc! {"
 3922        «HELLO WORLDˇ»
 3923    "});
 3924    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3925    cx.assert_editor_state(indoc! {"
 3926        «hello worldˇ»
 3927    "});
 3928
 3929    // Test multiple line, single selection case
 3930    cx.set_state(indoc! {"
 3931        «The quick brown
 3932        fox jumps over
 3933        the lazy dogˇ»
 3934    "});
 3935    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3936    cx.assert_editor_state(indoc! {"
 3937        «The Quick Brown
 3938        Fox Jumps Over
 3939        The Lazy Dogˇ»
 3940    "});
 3941
 3942    // Test multiple line, single selection case
 3943    cx.set_state(indoc! {"
 3944        «The quick brown
 3945        fox jumps over
 3946        the lazy dogˇ»
 3947    "});
 3948    cx.update_editor(|e, window, cx| {
 3949        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3950    });
 3951    cx.assert_editor_state(indoc! {"
 3952        «TheQuickBrown
 3953        FoxJumpsOver
 3954        TheLazyDogˇ»
 3955    "});
 3956
 3957    // From here on out, test more complex cases of manipulate_text()
 3958
 3959    // Test no selection case - should affect words cursors are in
 3960    // Cursor at beginning, middle, and end of word
 3961    cx.set_state(indoc! {"
 3962        ˇhello big beauˇtiful worldˇ
 3963    "});
 3964    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3965    cx.assert_editor_state(indoc! {"
 3966        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3967    "});
 3968
 3969    // Test multiple selections on a single line and across multiple lines
 3970    cx.set_state(indoc! {"
 3971        «Theˇ» quick «brown
 3972        foxˇ» jumps «overˇ»
 3973        the «lazyˇ» dog
 3974    "});
 3975    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3976    cx.assert_editor_state(indoc! {"
 3977        «THEˇ» quick «BROWN
 3978        FOXˇ» jumps «OVERˇ»
 3979        the «LAZYˇ» dog
 3980    "});
 3981
 3982    // Test case where text length grows
 3983    cx.set_state(indoc! {"
 3984        «tschüߡ»
 3985    "});
 3986    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3987    cx.assert_editor_state(indoc! {"
 3988        «TSCHÜSSˇ»
 3989    "});
 3990
 3991    // Test to make sure we don't crash when text shrinks
 3992    cx.set_state(indoc! {"
 3993        aaa_bbbˇ
 3994    "});
 3995    cx.update_editor(|e, window, cx| {
 3996        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3997    });
 3998    cx.assert_editor_state(indoc! {"
 3999        «aaaBbbˇ»
 4000    "});
 4001
 4002    // Test to make sure we all aware of the fact that each word can grow and shrink
 4003    // Final selections should be aware of this fact
 4004    cx.set_state(indoc! {"
 4005        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4006    "});
 4007    cx.update_editor(|e, window, cx| {
 4008        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4009    });
 4010    cx.assert_editor_state(indoc! {"
 4011        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4012    "});
 4013
 4014    cx.set_state(indoc! {"
 4015        «hElLo, WoRld!ˇ»
 4016    "});
 4017    cx.update_editor(|e, window, cx| {
 4018        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4019    });
 4020    cx.assert_editor_state(indoc! {"
 4021        «HeLlO, wOrLD!ˇ»
 4022    "});
 4023}
 4024
 4025#[gpui::test]
 4026fn test_duplicate_line(cx: &mut TestAppContext) {
 4027    init_test(cx, |_| {});
 4028
 4029    let editor = cx.add_window(|window, cx| {
 4030        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4031        build_editor(buffer, window, cx)
 4032    });
 4033    _ = editor.update(cx, |editor, window, cx| {
 4034        editor.change_selections(None, window, cx, |s| {
 4035            s.select_display_ranges([
 4036                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4037                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4038                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4039                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4040            ])
 4041        });
 4042        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4043        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4044        assert_eq!(
 4045            editor.selections.display_ranges(cx),
 4046            vec![
 4047                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4048                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4049                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4050                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4051            ]
 4052        );
 4053    });
 4054
 4055    let editor = cx.add_window(|window, cx| {
 4056        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4057        build_editor(buffer, window, cx)
 4058    });
 4059    _ = editor.update(cx, |editor, window, cx| {
 4060        editor.change_selections(None, window, cx, |s| {
 4061            s.select_display_ranges([
 4062                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4063                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4064            ])
 4065        });
 4066        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4067        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4068        assert_eq!(
 4069            editor.selections.display_ranges(cx),
 4070            vec![
 4071                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4072                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4073            ]
 4074        );
 4075    });
 4076
 4077    // With `move_upwards` the selections stay in place, except for
 4078    // the lines inserted above them
 4079    let editor = cx.add_window(|window, cx| {
 4080        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4081        build_editor(buffer, window, cx)
 4082    });
 4083    _ = editor.update(cx, |editor, window, cx| {
 4084        editor.change_selections(None, window, cx, |s| {
 4085            s.select_display_ranges([
 4086                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4087                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4088                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4089                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4090            ])
 4091        });
 4092        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4093        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4094        assert_eq!(
 4095            editor.selections.display_ranges(cx),
 4096            vec![
 4097                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4098                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4099                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4100                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4101            ]
 4102        );
 4103    });
 4104
 4105    let editor = cx.add_window(|window, cx| {
 4106        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4107        build_editor(buffer, window, cx)
 4108    });
 4109    _ = editor.update(cx, |editor, window, cx| {
 4110        editor.change_selections(None, window, cx, |s| {
 4111            s.select_display_ranges([
 4112                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4113                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4114            ])
 4115        });
 4116        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4117        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4118        assert_eq!(
 4119            editor.selections.display_ranges(cx),
 4120            vec![
 4121                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4122                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4123            ]
 4124        );
 4125    });
 4126
 4127    let editor = cx.add_window(|window, cx| {
 4128        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4129        build_editor(buffer, window, cx)
 4130    });
 4131    _ = editor.update(cx, |editor, window, cx| {
 4132        editor.change_selections(None, window, cx, |s| {
 4133            s.select_display_ranges([
 4134                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4135                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4136            ])
 4137        });
 4138        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4139        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4140        assert_eq!(
 4141            editor.selections.display_ranges(cx),
 4142            vec![
 4143                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4144                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4145            ]
 4146        );
 4147    });
 4148}
 4149
 4150#[gpui::test]
 4151fn test_move_line_up_down(cx: &mut TestAppContext) {
 4152    init_test(cx, |_| {});
 4153
 4154    let editor = cx.add_window(|window, cx| {
 4155        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4156        build_editor(buffer, window, cx)
 4157    });
 4158    _ = editor.update(cx, |editor, window, cx| {
 4159        editor.fold_creases(
 4160            vec![
 4161                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4162                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4163                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4164            ],
 4165            true,
 4166            window,
 4167            cx,
 4168        );
 4169        editor.change_selections(None, window, cx, |s| {
 4170            s.select_display_ranges([
 4171                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4172                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4173                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4174                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4175            ])
 4176        });
 4177        assert_eq!(
 4178            editor.display_text(cx),
 4179            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4180        );
 4181
 4182        editor.move_line_up(&MoveLineUp, window, cx);
 4183        assert_eq!(
 4184            editor.display_text(cx),
 4185            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4186        );
 4187        assert_eq!(
 4188            editor.selections.display_ranges(cx),
 4189            vec![
 4190                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4191                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4192                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4193                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4194            ]
 4195        );
 4196    });
 4197
 4198    _ = editor.update(cx, |editor, window, cx| {
 4199        editor.move_line_down(&MoveLineDown, window, cx);
 4200        assert_eq!(
 4201            editor.display_text(cx),
 4202            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4203        );
 4204        assert_eq!(
 4205            editor.selections.display_ranges(cx),
 4206            vec![
 4207                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4208                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4209                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4210                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4211            ]
 4212        );
 4213    });
 4214
 4215    _ = editor.update(cx, |editor, window, cx| {
 4216        editor.move_line_down(&MoveLineDown, window, cx);
 4217        assert_eq!(
 4218            editor.display_text(cx),
 4219            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4220        );
 4221        assert_eq!(
 4222            editor.selections.display_ranges(cx),
 4223            vec![
 4224                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4225                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4226                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4227                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4228            ]
 4229        );
 4230    });
 4231
 4232    _ = editor.update(cx, |editor, window, cx| {
 4233        editor.move_line_up(&MoveLineUp, window, cx);
 4234        assert_eq!(
 4235            editor.display_text(cx),
 4236            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4237        );
 4238        assert_eq!(
 4239            editor.selections.display_ranges(cx),
 4240            vec![
 4241                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4242                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4243                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4244                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4245            ]
 4246        );
 4247    });
 4248}
 4249
 4250#[gpui::test]
 4251fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4252    init_test(cx, |_| {});
 4253
 4254    let editor = cx.add_window(|window, cx| {
 4255        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4256        build_editor(buffer, window, cx)
 4257    });
 4258    _ = editor.update(cx, |editor, window, cx| {
 4259        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4260        editor.insert_blocks(
 4261            [BlockProperties {
 4262                style: BlockStyle::Fixed,
 4263                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4264                height: 1,
 4265                render: Arc::new(|_| div().into_any()),
 4266                priority: 0,
 4267            }],
 4268            Some(Autoscroll::fit()),
 4269            cx,
 4270        );
 4271        editor.change_selections(None, window, cx, |s| {
 4272            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4273        });
 4274        editor.move_line_down(&MoveLineDown, window, cx);
 4275    });
 4276}
 4277
 4278#[gpui::test]
 4279async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4280    init_test(cx, |_| {});
 4281
 4282    let mut cx = EditorTestContext::new(cx).await;
 4283    cx.set_state(
 4284        &"
 4285            ˇzero
 4286            one
 4287            two
 4288            three
 4289            four
 4290            five
 4291        "
 4292        .unindent(),
 4293    );
 4294
 4295    // Create a four-line block that replaces three lines of text.
 4296    cx.update_editor(|editor, window, cx| {
 4297        let snapshot = editor.snapshot(window, cx);
 4298        let snapshot = &snapshot.buffer_snapshot;
 4299        let placement = BlockPlacement::Replace(
 4300            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4301        );
 4302        editor.insert_blocks(
 4303            [BlockProperties {
 4304                placement,
 4305                height: 4,
 4306                style: BlockStyle::Sticky,
 4307                render: Arc::new(|_| gpui::div().into_any_element()),
 4308                priority: 0,
 4309            }],
 4310            None,
 4311            cx,
 4312        );
 4313    });
 4314
 4315    // Move down so that the cursor touches the block.
 4316    cx.update_editor(|editor, window, cx| {
 4317        editor.move_down(&Default::default(), window, cx);
 4318    });
 4319    cx.assert_editor_state(
 4320        &"
 4321            zero
 4322            «one
 4323            two
 4324            threeˇ»
 4325            four
 4326            five
 4327        "
 4328        .unindent(),
 4329    );
 4330
 4331    // Move down past the block.
 4332    cx.update_editor(|editor, window, cx| {
 4333        editor.move_down(&Default::default(), window, cx);
 4334    });
 4335    cx.assert_editor_state(
 4336        &"
 4337            zero
 4338            one
 4339            two
 4340            three
 4341            ˇfour
 4342            five
 4343        "
 4344        .unindent(),
 4345    );
 4346}
 4347
 4348#[gpui::test]
 4349fn test_transpose(cx: &mut TestAppContext) {
 4350    init_test(cx, |_| {});
 4351
 4352    _ = cx.add_window(|window, cx| {
 4353        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4354        editor.set_style(EditorStyle::default(), window, cx);
 4355        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4356        editor.transpose(&Default::default(), window, cx);
 4357        assert_eq!(editor.text(cx), "bac");
 4358        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4359
 4360        editor.transpose(&Default::default(), window, cx);
 4361        assert_eq!(editor.text(cx), "bca");
 4362        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4363
 4364        editor.transpose(&Default::default(), window, cx);
 4365        assert_eq!(editor.text(cx), "bac");
 4366        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4367
 4368        editor
 4369    });
 4370
 4371    _ = cx.add_window(|window, cx| {
 4372        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4373        editor.set_style(EditorStyle::default(), window, cx);
 4374        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4375        editor.transpose(&Default::default(), window, cx);
 4376        assert_eq!(editor.text(cx), "acb\nde");
 4377        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4378
 4379        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4380        editor.transpose(&Default::default(), window, cx);
 4381        assert_eq!(editor.text(cx), "acbd\ne");
 4382        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4383
 4384        editor.transpose(&Default::default(), window, cx);
 4385        assert_eq!(editor.text(cx), "acbde\n");
 4386        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4387
 4388        editor.transpose(&Default::default(), window, cx);
 4389        assert_eq!(editor.text(cx), "acbd\ne");
 4390        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4391
 4392        editor
 4393    });
 4394
 4395    _ = cx.add_window(|window, cx| {
 4396        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4397        editor.set_style(EditorStyle::default(), window, cx);
 4398        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4399        editor.transpose(&Default::default(), window, cx);
 4400        assert_eq!(editor.text(cx), "bacd\ne");
 4401        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4402
 4403        editor.transpose(&Default::default(), window, cx);
 4404        assert_eq!(editor.text(cx), "bcade\n");
 4405        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4406
 4407        editor.transpose(&Default::default(), window, cx);
 4408        assert_eq!(editor.text(cx), "bcda\ne");
 4409        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4410
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "bcade\n");
 4413        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "bcaed\n");
 4417        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4418
 4419        editor
 4420    });
 4421
 4422    _ = cx.add_window(|window, cx| {
 4423        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4424        editor.set_style(EditorStyle::default(), window, cx);
 4425        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4426        editor.transpose(&Default::default(), window, cx);
 4427        assert_eq!(editor.text(cx), "🏀🍐✋");
 4428        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4429
 4430        editor.transpose(&Default::default(), window, cx);
 4431        assert_eq!(editor.text(cx), "🏀✋🍐");
 4432        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4433
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "🏀🍐✋");
 4436        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4437
 4438        editor
 4439    });
 4440}
 4441
 4442#[gpui::test]
 4443async fn test_rewrap(cx: &mut TestAppContext) {
 4444    init_test(cx, |settings| {
 4445        settings.languages.extend([
 4446            (
 4447                "Markdown".into(),
 4448                LanguageSettingsContent {
 4449                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4450                    ..Default::default()
 4451                },
 4452            ),
 4453            (
 4454                "Plain Text".into(),
 4455                LanguageSettingsContent {
 4456                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4457                    ..Default::default()
 4458                },
 4459            ),
 4460        ])
 4461    });
 4462
 4463    let mut cx = EditorTestContext::new(cx).await;
 4464
 4465    let language_with_c_comments = Arc::new(Language::new(
 4466        LanguageConfig {
 4467            line_comments: vec!["// ".into()],
 4468            ..LanguageConfig::default()
 4469        },
 4470        None,
 4471    ));
 4472    let language_with_pound_comments = Arc::new(Language::new(
 4473        LanguageConfig {
 4474            line_comments: vec!["# ".into()],
 4475            ..LanguageConfig::default()
 4476        },
 4477        None,
 4478    ));
 4479    let markdown_language = Arc::new(Language::new(
 4480        LanguageConfig {
 4481            name: "Markdown".into(),
 4482            ..LanguageConfig::default()
 4483        },
 4484        None,
 4485    ));
 4486    let language_with_doc_comments = Arc::new(Language::new(
 4487        LanguageConfig {
 4488            line_comments: vec!["// ".into(), "/// ".into()],
 4489            ..LanguageConfig::default()
 4490        },
 4491        Some(tree_sitter_rust::LANGUAGE.into()),
 4492    ));
 4493
 4494    let plaintext_language = Arc::new(Language::new(
 4495        LanguageConfig {
 4496            name: "Plain Text".into(),
 4497            ..LanguageConfig::default()
 4498        },
 4499        None,
 4500    ));
 4501
 4502    assert_rewrap(
 4503        indoc! {"
 4504            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4505        "},
 4506        indoc! {"
 4507            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4508            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4509            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4510            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4511            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4512            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4513            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4514            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4515            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4516            // porttitor id. Aliquam id accumsan eros.
 4517        "},
 4518        language_with_c_comments.clone(),
 4519        &mut cx,
 4520    );
 4521
 4522    // Test that rewrapping works inside of a selection
 4523    assert_rewrap(
 4524        indoc! {"
 4525            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4526        "},
 4527        indoc! {"
 4528            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4529            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4530            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4531            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4532            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4533            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4534            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4535            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4536            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4537            // porttitor id. Aliquam id accumsan eros.ˇ»
 4538        "},
 4539        language_with_c_comments.clone(),
 4540        &mut cx,
 4541    );
 4542
 4543    // Test that cursors that expand to the same region are collapsed.
 4544    assert_rewrap(
 4545        indoc! {"
 4546            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4547            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4548            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4549            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4550        "},
 4551        indoc! {"
 4552            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4553            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4554            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4555            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4556            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4557            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4558            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4559            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4560            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4561            // porttitor id. Aliquam id accumsan eros.
 4562        "},
 4563        language_with_c_comments.clone(),
 4564        &mut cx,
 4565    );
 4566
 4567    // Test that non-contiguous selections are treated separately.
 4568    assert_rewrap(
 4569        indoc! {"
 4570            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4571            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4572            //
 4573            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4574            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4575        "},
 4576        indoc! {"
 4577            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4578            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4579            // auctor, eu lacinia sapien scelerisque.
 4580            //
 4581            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4582            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4583            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4584            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4585            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4586            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4587            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4588        "},
 4589        language_with_c_comments.clone(),
 4590        &mut cx,
 4591    );
 4592
 4593    // Test that different comment prefixes are supported.
 4594    assert_rewrap(
 4595        indoc! {"
 4596            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4597        "},
 4598        indoc! {"
 4599            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4600            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4601            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4602            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4603            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4604            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4605            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4606            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4607            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4608            # accumsan eros.
 4609        "},
 4610        language_with_pound_comments.clone(),
 4611        &mut cx,
 4612    );
 4613
 4614    // Test that rewrapping is ignored outside of comments in most languages.
 4615    assert_rewrap(
 4616        indoc! {"
 4617            /// Adds two numbers.
 4618            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4619            fn add(a: u32, b: u32) -> u32 {
 4620                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4621            }
 4622        "},
 4623        indoc! {"
 4624            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4625            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4626            fn add(a: u32, b: u32) -> u32 {
 4627                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4628            }
 4629        "},
 4630        language_with_doc_comments.clone(),
 4631        &mut cx,
 4632    );
 4633
 4634    // Test that rewrapping works in Markdown and Plain Text languages.
 4635    assert_rewrap(
 4636        indoc! {"
 4637            # Hello
 4638
 4639            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4640        "},
 4641        indoc! {"
 4642            # Hello
 4643
 4644            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4645            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4646            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4647            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4648            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4649            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4650            Integer sit amet scelerisque nisi.
 4651        "},
 4652        markdown_language,
 4653        &mut cx,
 4654    );
 4655
 4656    assert_rewrap(
 4657        indoc! {"
 4658            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4659        "},
 4660        indoc! {"
 4661            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4662            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4663            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4664            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4665            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4666            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4667            Integer sit amet scelerisque nisi.
 4668        "},
 4669        plaintext_language,
 4670        &mut cx,
 4671    );
 4672
 4673    // Test rewrapping unaligned comments in a selection.
 4674    assert_rewrap(
 4675        indoc! {"
 4676            fn foo() {
 4677                if true {
 4678            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4679            // Praesent semper egestas tellus id dignissim.ˇ»
 4680                    do_something();
 4681                } else {
 4682                    //
 4683                }
 4684            }
 4685        "},
 4686        indoc! {"
 4687            fn foo() {
 4688                if true {
 4689            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4690                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4691                    // egestas tellus id dignissim.ˇ»
 4692                    do_something();
 4693                } else {
 4694                    //
 4695                }
 4696            }
 4697        "},
 4698        language_with_doc_comments.clone(),
 4699        &mut cx,
 4700    );
 4701
 4702    assert_rewrap(
 4703        indoc! {"
 4704            fn foo() {
 4705                if true {
 4706            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4707            // Praesent semper egestas tellus id dignissim.»
 4708                    do_something();
 4709                } else {
 4710                    //
 4711                }
 4712
 4713            }
 4714        "},
 4715        indoc! {"
 4716            fn foo() {
 4717                if true {
 4718            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4719                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4720                    // egestas tellus id dignissim.»
 4721                    do_something();
 4722                } else {
 4723                    //
 4724                }
 4725
 4726            }
 4727        "},
 4728        language_with_doc_comments.clone(),
 4729        &mut cx,
 4730    );
 4731
 4732    #[track_caller]
 4733    fn assert_rewrap(
 4734        unwrapped_text: &str,
 4735        wrapped_text: &str,
 4736        language: Arc<Language>,
 4737        cx: &mut EditorTestContext,
 4738    ) {
 4739        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4740        cx.set_state(unwrapped_text);
 4741        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4742        cx.assert_editor_state(wrapped_text);
 4743    }
 4744}
 4745
 4746#[gpui::test]
 4747async fn test_hard_wrap(cx: &mut TestAppContext) {
 4748    init_test(cx, |_| {});
 4749    let mut cx = EditorTestContext::new(cx).await;
 4750
 4751    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4752    cx.update_editor(|editor, _, cx| {
 4753        editor.set_hard_wrap(Some(14), cx);
 4754    });
 4755
 4756    cx.set_state(indoc!(
 4757        "
 4758        one two three ˇ
 4759        "
 4760    ));
 4761    cx.simulate_input("four");
 4762    cx.run_until_parked();
 4763
 4764    cx.assert_editor_state(indoc!(
 4765        "
 4766        one two three
 4767        fourˇ
 4768        "
 4769    ));
 4770
 4771    cx.update_editor(|editor, window, cx| {
 4772        editor.newline(&Default::default(), window, cx);
 4773    });
 4774    cx.run_until_parked();
 4775    cx.assert_editor_state(indoc!(
 4776        "
 4777        one two three
 4778        four
 4779        ˇ
 4780        "
 4781    ));
 4782
 4783    cx.simulate_input("five");
 4784    cx.run_until_parked();
 4785    cx.assert_editor_state(indoc!(
 4786        "
 4787        one two three
 4788        four
 4789        fiveˇ
 4790        "
 4791    ));
 4792
 4793    cx.update_editor(|editor, window, cx| {
 4794        editor.newline(&Default::default(), window, cx);
 4795    });
 4796    cx.run_until_parked();
 4797    cx.simulate_input("# ");
 4798    cx.run_until_parked();
 4799    cx.assert_editor_state(indoc!(
 4800        "
 4801        one two three
 4802        four
 4803        five
 4804        # ˇ
 4805        "
 4806    ));
 4807
 4808    cx.update_editor(|editor, window, cx| {
 4809        editor.newline(&Default::default(), window, cx);
 4810    });
 4811    cx.run_until_parked();
 4812    cx.assert_editor_state(indoc!(
 4813        "
 4814        one two three
 4815        four
 4816        five
 4817        #\x20
 4818 4819        "
 4820    ));
 4821
 4822    cx.simulate_input(" 6");
 4823    cx.run_until_parked();
 4824    cx.assert_editor_state(indoc!(
 4825        "
 4826        one two three
 4827        four
 4828        five
 4829        #
 4830        # 6ˇ
 4831        "
 4832    ));
 4833}
 4834
 4835#[gpui::test]
 4836async fn test_clipboard(cx: &mut TestAppContext) {
 4837    init_test(cx, |_| {});
 4838
 4839    let mut cx = EditorTestContext::new(cx).await;
 4840
 4841    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4842    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4843    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4844
 4845    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4846    cx.set_state("two ˇfour ˇsix ˇ");
 4847    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4848    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4849
 4850    // Paste again but with only two cursors. Since the number of cursors doesn't
 4851    // match the number of slices in the clipboard, the entire clipboard text
 4852    // is pasted at each cursor.
 4853    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4854    cx.update_editor(|e, window, cx| {
 4855        e.handle_input("( ", window, cx);
 4856        e.paste(&Paste, window, cx);
 4857        e.handle_input(") ", window, cx);
 4858    });
 4859    cx.assert_editor_state(
 4860        &([
 4861            "( one✅ ",
 4862            "three ",
 4863            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4864            "three ",
 4865            "five ) ˇ",
 4866        ]
 4867        .join("\n")),
 4868    );
 4869
 4870    // Cut with three selections, one of which is full-line.
 4871    cx.set_state(indoc! {"
 4872        1«2ˇ»3
 4873        4ˇ567
 4874        «8ˇ»9"});
 4875    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4876    cx.assert_editor_state(indoc! {"
 4877        1ˇ3
 4878        ˇ9"});
 4879
 4880    // Paste with three selections, noticing how the copied selection that was full-line
 4881    // gets inserted before the second cursor.
 4882    cx.set_state(indoc! {"
 4883        1ˇ3
 4884 4885        «oˇ»ne"});
 4886    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4887    cx.assert_editor_state(indoc! {"
 4888        12ˇ3
 4889        4567
 4890 4891        8ˇne"});
 4892
 4893    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4894    cx.set_state(indoc! {"
 4895        The quick brown
 4896        fox juˇmps over
 4897        the lazy dog"});
 4898    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4899    assert_eq!(
 4900        cx.read_from_clipboard()
 4901            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4902        Some("fox jumps over\n".to_string())
 4903    );
 4904
 4905    // Paste with three selections, noticing how the copied full-line selection is inserted
 4906    // before the empty selections but replaces the selection that is non-empty.
 4907    cx.set_state(indoc! {"
 4908        Tˇhe quick brown
 4909        «foˇ»x jumps over
 4910        tˇhe lazy dog"});
 4911    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4912    cx.assert_editor_state(indoc! {"
 4913        fox jumps over
 4914        Tˇhe quick brown
 4915        fox jumps over
 4916        ˇx jumps over
 4917        fox jumps over
 4918        tˇhe lazy dog"});
 4919}
 4920
 4921#[gpui::test]
 4922async fn test_paste_multiline(cx: &mut TestAppContext) {
 4923    init_test(cx, |_| {});
 4924
 4925    let mut cx = EditorTestContext::new(cx).await;
 4926    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4927
 4928    // Cut an indented block, without the leading whitespace.
 4929    cx.set_state(indoc! {"
 4930        const a: B = (
 4931            c(),
 4932            «d(
 4933                e,
 4934                f
 4935            )ˇ»
 4936        );
 4937    "});
 4938    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4939    cx.assert_editor_state(indoc! {"
 4940        const a: B = (
 4941            c(),
 4942            ˇ
 4943        );
 4944    "});
 4945
 4946    // Paste it at the same position.
 4947    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4948    cx.assert_editor_state(indoc! {"
 4949        const a: B = (
 4950            c(),
 4951            d(
 4952                e,
 4953                f
 4954 4955        );
 4956    "});
 4957
 4958    // Paste it at a line with a lower indent level.
 4959    cx.set_state(indoc! {"
 4960        ˇ
 4961        const a: B = (
 4962            c(),
 4963        );
 4964    "});
 4965    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4966    cx.assert_editor_state(indoc! {"
 4967        d(
 4968            e,
 4969            f
 4970 4971        const a: B = (
 4972            c(),
 4973        );
 4974    "});
 4975
 4976    // Cut an indented block, with the leading whitespace.
 4977    cx.set_state(indoc! {"
 4978        const a: B = (
 4979            c(),
 4980        «    d(
 4981                e,
 4982                f
 4983            )
 4984        ˇ»);
 4985    "});
 4986    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4987    cx.assert_editor_state(indoc! {"
 4988        const a: B = (
 4989            c(),
 4990        ˇ);
 4991    "});
 4992
 4993    // Paste it at the same position.
 4994    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4995    cx.assert_editor_state(indoc! {"
 4996        const a: B = (
 4997            c(),
 4998            d(
 4999                e,
 5000                f
 5001            )
 5002        ˇ);
 5003    "});
 5004
 5005    // Paste it at a line with a higher indent level.
 5006    cx.set_state(indoc! {"
 5007        const a: B = (
 5008            c(),
 5009            d(
 5010                e,
 5011 5012            )
 5013        );
 5014    "});
 5015    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5016    cx.assert_editor_state(indoc! {"
 5017        const a: B = (
 5018            c(),
 5019            d(
 5020                e,
 5021                f    d(
 5022                    e,
 5023                    f
 5024                )
 5025        ˇ
 5026            )
 5027        );
 5028    "});
 5029
 5030    // Copy an indented block, starting mid-line
 5031    cx.set_state(indoc! {"
 5032        const a: B = (
 5033            c(),
 5034            somethin«g(
 5035                e,
 5036                f
 5037            )ˇ»
 5038        );
 5039    "});
 5040    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5041
 5042    // Paste it on a line with a lower indent level
 5043    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5044    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5045    cx.assert_editor_state(indoc! {"
 5046        const a: B = (
 5047            c(),
 5048            something(
 5049                e,
 5050                f
 5051            )
 5052        );
 5053        g(
 5054            e,
 5055            f
 5056"});
 5057}
 5058
 5059#[gpui::test]
 5060async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5061    init_test(cx, |_| {});
 5062
 5063    cx.write_to_clipboard(ClipboardItem::new_string(
 5064        "    d(\n        e\n    );\n".into(),
 5065    ));
 5066
 5067    let mut cx = EditorTestContext::new(cx).await;
 5068    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5069
 5070    cx.set_state(indoc! {"
 5071        fn a() {
 5072            b();
 5073            if c() {
 5074                ˇ
 5075            }
 5076        }
 5077    "});
 5078
 5079    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5080    cx.assert_editor_state(indoc! {"
 5081        fn a() {
 5082            b();
 5083            if c() {
 5084                d(
 5085                    e
 5086                );
 5087        ˇ
 5088            }
 5089        }
 5090    "});
 5091
 5092    cx.set_state(indoc! {"
 5093        fn a() {
 5094            b();
 5095            ˇ
 5096        }
 5097    "});
 5098
 5099    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5100    cx.assert_editor_state(indoc! {"
 5101        fn a() {
 5102            b();
 5103            d(
 5104                e
 5105            );
 5106        ˇ
 5107        }
 5108    "});
 5109}
 5110
 5111#[gpui::test]
 5112fn test_select_all(cx: &mut TestAppContext) {
 5113    init_test(cx, |_| {});
 5114
 5115    let editor = cx.add_window(|window, cx| {
 5116        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5117        build_editor(buffer, window, cx)
 5118    });
 5119    _ = editor.update(cx, |editor, window, cx| {
 5120        editor.select_all(&SelectAll, window, cx);
 5121        assert_eq!(
 5122            editor.selections.display_ranges(cx),
 5123            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5124        );
 5125    });
 5126}
 5127
 5128#[gpui::test]
 5129fn test_select_line(cx: &mut TestAppContext) {
 5130    init_test(cx, |_| {});
 5131
 5132    let editor = cx.add_window(|window, cx| {
 5133        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5134        build_editor(buffer, window, cx)
 5135    });
 5136    _ = editor.update(cx, |editor, window, cx| {
 5137        editor.change_selections(None, window, cx, |s| {
 5138            s.select_display_ranges([
 5139                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5140                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5141                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5142                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5143            ])
 5144        });
 5145        editor.select_line(&SelectLine, window, cx);
 5146        assert_eq!(
 5147            editor.selections.display_ranges(cx),
 5148            vec![
 5149                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5150                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5151            ]
 5152        );
 5153    });
 5154
 5155    _ = editor.update(cx, |editor, window, cx| {
 5156        editor.select_line(&SelectLine, window, cx);
 5157        assert_eq!(
 5158            editor.selections.display_ranges(cx),
 5159            vec![
 5160                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5161                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5162            ]
 5163        );
 5164    });
 5165
 5166    _ = editor.update(cx, |editor, window, cx| {
 5167        editor.select_line(&SelectLine, window, cx);
 5168        assert_eq!(
 5169            editor.selections.display_ranges(cx),
 5170            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5171        );
 5172    });
 5173}
 5174
 5175#[gpui::test]
 5176async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5177    init_test(cx, |_| {});
 5178    let mut cx = EditorTestContext::new(cx).await;
 5179
 5180    #[track_caller]
 5181    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5182        cx.set_state(initial_state);
 5183        cx.update_editor(|e, window, cx| {
 5184            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5185        });
 5186        cx.assert_editor_state(expected_state);
 5187    }
 5188
 5189    // Selection starts and ends at the middle of lines, left-to-right
 5190    test(
 5191        &mut cx,
 5192        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5193        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5194    );
 5195    // Same thing, right-to-left
 5196    test(
 5197        &mut cx,
 5198        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5199        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5200    );
 5201
 5202    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5203    test(
 5204        &mut cx,
 5205        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5206        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5207    );
 5208    // Same thing, right-to-left
 5209    test(
 5210        &mut cx,
 5211        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5212        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5213    );
 5214
 5215    // Whole buffer, left-to-right, last line ends with newline
 5216    test(
 5217        &mut cx,
 5218        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5219        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5220    );
 5221    // Same thing, right-to-left
 5222    test(
 5223        &mut cx,
 5224        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5225        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5226    );
 5227
 5228    // Starts at the end of a line, ends at the start of another
 5229    test(
 5230        &mut cx,
 5231        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5232        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5233    );
 5234}
 5235
 5236#[gpui::test]
 5237async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5238    init_test(cx, |_| {});
 5239
 5240    let editor = cx.add_window(|window, cx| {
 5241        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5242        build_editor(buffer, window, cx)
 5243    });
 5244
 5245    // setup
 5246    _ = editor.update(cx, |editor, window, cx| {
 5247        editor.fold_creases(
 5248            vec![
 5249                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5250                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5251                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5252            ],
 5253            true,
 5254            window,
 5255            cx,
 5256        );
 5257        assert_eq!(
 5258            editor.display_text(cx),
 5259            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5260        );
 5261    });
 5262
 5263    _ = editor.update(cx, |editor, window, cx| {
 5264        editor.change_selections(None, window, cx, |s| {
 5265            s.select_display_ranges([
 5266                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5267                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5268                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5269                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5270            ])
 5271        });
 5272        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5273        assert_eq!(
 5274            editor.display_text(cx),
 5275            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5276        );
 5277    });
 5278    EditorTestContext::for_editor(editor, cx)
 5279        .await
 5280        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5281
 5282    _ = editor.update(cx, |editor, window, cx| {
 5283        editor.change_selections(None, window, cx, |s| {
 5284            s.select_display_ranges([
 5285                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5286            ])
 5287        });
 5288        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5289        assert_eq!(
 5290            editor.display_text(cx),
 5291            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5292        );
 5293        assert_eq!(
 5294            editor.selections.display_ranges(cx),
 5295            [
 5296                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5297                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5298                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5299                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5300                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5301                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5302                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5303            ]
 5304        );
 5305    });
 5306    EditorTestContext::for_editor(editor, cx)
 5307        .await
 5308        .assert_editor_state(
 5309            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5310        );
 5311}
 5312
 5313#[gpui::test]
 5314async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5315    init_test(cx, |_| {});
 5316
 5317    let mut cx = EditorTestContext::new(cx).await;
 5318
 5319    cx.set_state(indoc!(
 5320        r#"abc
 5321           defˇghi
 5322
 5323           jk
 5324           nlmo
 5325           "#
 5326    ));
 5327
 5328    cx.update_editor(|editor, window, cx| {
 5329        editor.add_selection_above(&Default::default(), window, cx);
 5330    });
 5331
 5332    cx.assert_editor_state(indoc!(
 5333        r#"abcˇ
 5334           defˇghi
 5335
 5336           jk
 5337           nlmo
 5338           "#
 5339    ));
 5340
 5341    cx.update_editor(|editor, window, cx| {
 5342        editor.add_selection_above(&Default::default(), window, cx);
 5343    });
 5344
 5345    cx.assert_editor_state(indoc!(
 5346        r#"abcˇ
 5347            defˇghi
 5348
 5349            jk
 5350            nlmo
 5351            "#
 5352    ));
 5353
 5354    cx.update_editor(|editor, window, cx| {
 5355        editor.add_selection_below(&Default::default(), window, cx);
 5356    });
 5357
 5358    cx.assert_editor_state(indoc!(
 5359        r#"abc
 5360           defˇghi
 5361
 5362           jk
 5363           nlmo
 5364           "#
 5365    ));
 5366
 5367    cx.update_editor(|editor, window, cx| {
 5368        editor.undo_selection(&Default::default(), window, cx);
 5369    });
 5370
 5371    cx.assert_editor_state(indoc!(
 5372        r#"abcˇ
 5373           defˇghi
 5374
 5375           jk
 5376           nlmo
 5377           "#
 5378    ));
 5379
 5380    cx.update_editor(|editor, window, cx| {
 5381        editor.redo_selection(&Default::default(), window, cx);
 5382    });
 5383
 5384    cx.assert_editor_state(indoc!(
 5385        r#"abc
 5386           defˇghi
 5387
 5388           jk
 5389           nlmo
 5390           "#
 5391    ));
 5392
 5393    cx.update_editor(|editor, window, cx| {
 5394        editor.add_selection_below(&Default::default(), window, cx);
 5395    });
 5396
 5397    cx.assert_editor_state(indoc!(
 5398        r#"abc
 5399           defˇghi
 5400
 5401           jk
 5402           nlmˇo
 5403           "#
 5404    ));
 5405
 5406    cx.update_editor(|editor, window, cx| {
 5407        editor.add_selection_below(&Default::default(), window, cx);
 5408    });
 5409
 5410    cx.assert_editor_state(indoc!(
 5411        r#"abc
 5412           defˇghi
 5413
 5414           jk
 5415           nlmˇo
 5416           "#
 5417    ));
 5418
 5419    // change selections
 5420    cx.set_state(indoc!(
 5421        r#"abc
 5422           def«ˇg»hi
 5423
 5424           jk
 5425           nlmo
 5426           "#
 5427    ));
 5428
 5429    cx.update_editor(|editor, window, cx| {
 5430        editor.add_selection_below(&Default::default(), window, cx);
 5431    });
 5432
 5433    cx.assert_editor_state(indoc!(
 5434        r#"abc
 5435           def«ˇg»hi
 5436
 5437           jk
 5438           nlm«ˇo»
 5439           "#
 5440    ));
 5441
 5442    cx.update_editor(|editor, window, cx| {
 5443        editor.add_selection_below(&Default::default(), window, cx);
 5444    });
 5445
 5446    cx.assert_editor_state(indoc!(
 5447        r#"abc
 5448           def«ˇg»hi
 5449
 5450           jk
 5451           nlm«ˇo»
 5452           "#
 5453    ));
 5454
 5455    cx.update_editor(|editor, window, cx| {
 5456        editor.add_selection_above(&Default::default(), window, cx);
 5457    });
 5458
 5459    cx.assert_editor_state(indoc!(
 5460        r#"abc
 5461           def«ˇg»hi
 5462
 5463           jk
 5464           nlmo
 5465           "#
 5466    ));
 5467
 5468    cx.update_editor(|editor, window, cx| {
 5469        editor.add_selection_above(&Default::default(), window, cx);
 5470    });
 5471
 5472    cx.assert_editor_state(indoc!(
 5473        r#"abc
 5474           def«ˇg»hi
 5475
 5476           jk
 5477           nlmo
 5478           "#
 5479    ));
 5480
 5481    // Change selections again
 5482    cx.set_state(indoc!(
 5483        r#"a«bc
 5484           defgˇ»hi
 5485
 5486           jk
 5487           nlmo
 5488           "#
 5489    ));
 5490
 5491    cx.update_editor(|editor, window, cx| {
 5492        editor.add_selection_below(&Default::default(), window, cx);
 5493    });
 5494
 5495    cx.assert_editor_state(indoc!(
 5496        r#"a«bcˇ»
 5497           d«efgˇ»hi
 5498
 5499           j«kˇ»
 5500           nlmo
 5501           "#
 5502    ));
 5503
 5504    cx.update_editor(|editor, window, cx| {
 5505        editor.add_selection_below(&Default::default(), window, cx);
 5506    });
 5507    cx.assert_editor_state(indoc!(
 5508        r#"a«bcˇ»
 5509           d«efgˇ»hi
 5510
 5511           j«kˇ»
 5512           n«lmoˇ»
 5513           "#
 5514    ));
 5515    cx.update_editor(|editor, window, cx| {
 5516        editor.add_selection_above(&Default::default(), window, cx);
 5517    });
 5518
 5519    cx.assert_editor_state(indoc!(
 5520        r#"a«bcˇ»
 5521           d«efgˇ»hi
 5522
 5523           j«kˇ»
 5524           nlmo
 5525           "#
 5526    ));
 5527
 5528    // Change selections again
 5529    cx.set_state(indoc!(
 5530        r#"abc
 5531           d«ˇefghi
 5532
 5533           jk
 5534           nlm»o
 5535           "#
 5536    ));
 5537
 5538    cx.update_editor(|editor, window, cx| {
 5539        editor.add_selection_above(&Default::default(), window, cx);
 5540    });
 5541
 5542    cx.assert_editor_state(indoc!(
 5543        r#"a«ˇbc»
 5544           d«ˇef»ghi
 5545
 5546           j«ˇk»
 5547           n«ˇlm»o
 5548           "#
 5549    ));
 5550
 5551    cx.update_editor(|editor, window, cx| {
 5552        editor.add_selection_below(&Default::default(), window, cx);
 5553    });
 5554
 5555    cx.assert_editor_state(indoc!(
 5556        r#"abc
 5557           d«ˇef»ghi
 5558
 5559           j«ˇk»
 5560           n«ˇlm»o
 5561           "#
 5562    ));
 5563}
 5564
 5565#[gpui::test]
 5566async fn test_select_next(cx: &mut TestAppContext) {
 5567    init_test(cx, |_| {});
 5568
 5569    let mut cx = EditorTestContext::new(cx).await;
 5570    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5571
 5572    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5573        .unwrap();
 5574    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5575
 5576    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5577        .unwrap();
 5578    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5579
 5580    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5581    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5582
 5583    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5584    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5585
 5586    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5587        .unwrap();
 5588    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5589
 5590    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5591        .unwrap();
 5592    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5593}
 5594
 5595#[gpui::test]
 5596async fn test_select_all_matches(cx: &mut TestAppContext) {
 5597    init_test(cx, |_| {});
 5598
 5599    let mut cx = EditorTestContext::new(cx).await;
 5600
 5601    // Test caret-only selections
 5602    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5603    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5604        .unwrap();
 5605    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5606
 5607    // Test left-to-right selections
 5608    cx.set_state("abc\n«abcˇ»\nabc");
 5609    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5610        .unwrap();
 5611    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5612
 5613    // Test right-to-left selections
 5614    cx.set_state("abc\n«ˇabc»\nabc");
 5615    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5616        .unwrap();
 5617    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5618
 5619    // Test selecting whitespace with caret selection
 5620    cx.set_state("abc\nˇ   abc\nabc");
 5621    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5622        .unwrap();
 5623    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5624
 5625    // Test selecting whitespace with left-to-right selection
 5626    cx.set_state("abc\n«ˇ  »abc\nabc");
 5627    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5628        .unwrap();
 5629    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5630
 5631    // Test no matches with right-to-left selection
 5632    cx.set_state("abc\n«  ˇ»abc\nabc");
 5633    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5634        .unwrap();
 5635    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5636}
 5637
 5638#[gpui::test]
 5639async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5640    init_test(cx, |_| {});
 5641
 5642    let mut cx = EditorTestContext::new(cx).await;
 5643    cx.set_state(
 5644        r#"let foo = 2;
 5645lˇet foo = 2;
 5646let fooˇ = 2;
 5647let foo = 2;
 5648let foo = ˇ2;"#,
 5649    );
 5650
 5651    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5652        .unwrap();
 5653    cx.assert_editor_state(
 5654        r#"let foo = 2;
 5655«letˇ» foo = 2;
 5656let «fooˇ» = 2;
 5657let foo = 2;
 5658let foo = «2ˇ»;"#,
 5659    );
 5660
 5661    // noop for multiple selections with different contents
 5662    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5663        .unwrap();
 5664    cx.assert_editor_state(
 5665        r#"let foo = 2;
 5666«letˇ» foo = 2;
 5667let «fooˇ» = 2;
 5668let foo = 2;
 5669let foo = «2ˇ»;"#,
 5670    );
 5671}
 5672
 5673#[gpui::test]
 5674async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5675    init_test(cx, |_| {});
 5676
 5677    let mut cx =
 5678        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5679
 5680    cx.assert_editor_state(indoc! {"
 5681        ˇbbb
 5682        ccc
 5683
 5684        bbb
 5685        ccc
 5686        "});
 5687    cx.dispatch_action(SelectPrevious::default());
 5688    cx.assert_editor_state(indoc! {"
 5689                «bbbˇ»
 5690                ccc
 5691
 5692                bbb
 5693                ccc
 5694                "});
 5695    cx.dispatch_action(SelectPrevious::default());
 5696    cx.assert_editor_state(indoc! {"
 5697                «bbbˇ»
 5698                ccc
 5699
 5700                «bbbˇ»
 5701                ccc
 5702                "});
 5703}
 5704
 5705#[gpui::test]
 5706async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5707    init_test(cx, |_| {});
 5708
 5709    let mut cx = EditorTestContext::new(cx).await;
 5710    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5711
 5712    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5713        .unwrap();
 5714    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5715
 5716    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5717        .unwrap();
 5718    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5719
 5720    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5721    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5722
 5723    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5724    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5725
 5726    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5727        .unwrap();
 5728    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5729
 5730    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5731        .unwrap();
 5732    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5733
 5734    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5735        .unwrap();
 5736    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5737}
 5738
 5739#[gpui::test]
 5740async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5741    init_test(cx, |_| {});
 5742
 5743    let mut cx = EditorTestContext::new(cx).await;
 5744    cx.set_state("");
 5745
 5746    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5747        .unwrap();
 5748    cx.assert_editor_state("«aˇ»");
 5749    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5750        .unwrap();
 5751    cx.assert_editor_state("«aˇ»");
 5752}
 5753
 5754#[gpui::test]
 5755async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5756    init_test(cx, |_| {});
 5757
 5758    let mut cx = EditorTestContext::new(cx).await;
 5759    cx.set_state(
 5760        r#"let foo = 2;
 5761lˇet foo = 2;
 5762let fooˇ = 2;
 5763let foo = 2;
 5764let foo = ˇ2;"#,
 5765    );
 5766
 5767    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5768        .unwrap();
 5769    cx.assert_editor_state(
 5770        r#"let foo = 2;
 5771«letˇ» foo = 2;
 5772let «fooˇ» = 2;
 5773let foo = 2;
 5774let foo = «2ˇ»;"#,
 5775    );
 5776
 5777    // noop for multiple selections with different contents
 5778    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5779        .unwrap();
 5780    cx.assert_editor_state(
 5781        r#"let foo = 2;
 5782«letˇ» foo = 2;
 5783let «fooˇ» = 2;
 5784let foo = 2;
 5785let foo = «2ˇ»;"#,
 5786    );
 5787}
 5788
 5789#[gpui::test]
 5790async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5791    init_test(cx, |_| {});
 5792
 5793    let mut cx = EditorTestContext::new(cx).await;
 5794    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5795
 5796    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5797        .unwrap();
 5798    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5799
 5800    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5801        .unwrap();
 5802    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5803
 5804    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5805    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5806
 5807    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5808    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5809
 5810    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5811        .unwrap();
 5812    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5813
 5814    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5815        .unwrap();
 5816    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5817}
 5818
 5819#[gpui::test]
 5820async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5821    init_test(cx, |_| {});
 5822
 5823    let language = Arc::new(Language::new(
 5824        LanguageConfig::default(),
 5825        Some(tree_sitter_rust::LANGUAGE.into()),
 5826    ));
 5827
 5828    let text = r#"
 5829        use mod1::mod2::{mod3, mod4};
 5830
 5831        fn fn_1(param1: bool, param2: &str) {
 5832            let var1 = "text";
 5833        }
 5834    "#
 5835    .unindent();
 5836
 5837    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5838    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5839    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5840
 5841    editor
 5842        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5843        .await;
 5844
 5845    editor.update_in(cx, |editor, window, cx| {
 5846        editor.change_selections(None, window, cx, |s| {
 5847            s.select_display_ranges([
 5848                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5849                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5850                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5851            ]);
 5852        });
 5853        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5854    });
 5855    editor.update(cx, |editor, cx| {
 5856        assert_text_with_selections(
 5857            editor,
 5858            indoc! {r#"
 5859                use mod1::mod2::{mod3, «mod4ˇ»};
 5860
 5861                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5862                    let var1 = "«textˇ»";
 5863                }
 5864            "#},
 5865            cx,
 5866        );
 5867    });
 5868
 5869    editor.update_in(cx, |editor, window, cx| {
 5870        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5871    });
 5872    editor.update(cx, |editor, cx| {
 5873        assert_text_with_selections(
 5874            editor,
 5875            indoc! {r#"
 5876                use mod1::mod2::«{mod3, mod4}ˇ»;
 5877
 5878                «ˇfn fn_1(param1: bool, param2: &str) {
 5879                    let var1 = "text";
 5880 5881            "#},
 5882            cx,
 5883        );
 5884    });
 5885
 5886    editor.update_in(cx, |editor, window, cx| {
 5887        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5888    });
 5889    assert_eq!(
 5890        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5891        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5892    );
 5893
 5894    // Trying to expand the selected syntax node one more time has no effect.
 5895    editor.update_in(cx, |editor, window, cx| {
 5896        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5897    });
 5898    assert_eq!(
 5899        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5900        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5901    );
 5902
 5903    editor.update_in(cx, |editor, window, cx| {
 5904        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5905    });
 5906    editor.update(cx, |editor, cx| {
 5907        assert_text_with_selections(
 5908            editor,
 5909            indoc! {r#"
 5910                use mod1::mod2::«{mod3, mod4}ˇ»;
 5911
 5912                «ˇfn fn_1(param1: bool, param2: &str) {
 5913                    let var1 = "text";
 5914 5915            "#},
 5916            cx,
 5917        );
 5918    });
 5919
 5920    editor.update_in(cx, |editor, window, cx| {
 5921        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5922    });
 5923    editor.update(cx, |editor, cx| {
 5924        assert_text_with_selections(
 5925            editor,
 5926            indoc! {r#"
 5927                use mod1::mod2::{mod3, «mod4ˇ»};
 5928
 5929                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5930                    let var1 = "«textˇ»";
 5931                }
 5932            "#},
 5933            cx,
 5934        );
 5935    });
 5936
 5937    editor.update_in(cx, |editor, window, cx| {
 5938        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5939    });
 5940    editor.update(cx, |editor, cx| {
 5941        assert_text_with_selections(
 5942            editor,
 5943            indoc! {r#"
 5944                use mod1::mod2::{mod3, mo«ˇ»d4};
 5945
 5946                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5947                    let var1 = "te«ˇ»xt";
 5948                }
 5949            "#},
 5950            cx,
 5951        );
 5952    });
 5953
 5954    // Trying to shrink the selected syntax node one more time has no effect.
 5955    editor.update_in(cx, |editor, window, cx| {
 5956        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5957    });
 5958    editor.update_in(cx, |editor, _, cx| {
 5959        assert_text_with_selections(
 5960            editor,
 5961            indoc! {r#"
 5962                use mod1::mod2::{mod3, mo«ˇ»d4};
 5963
 5964                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5965                    let var1 = "te«ˇ»xt";
 5966                }
 5967            "#},
 5968            cx,
 5969        );
 5970    });
 5971
 5972    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5973    // a fold.
 5974    editor.update_in(cx, |editor, window, cx| {
 5975        editor.fold_creases(
 5976            vec![
 5977                Crease::simple(
 5978                    Point::new(0, 21)..Point::new(0, 24),
 5979                    FoldPlaceholder::test(),
 5980                ),
 5981                Crease::simple(
 5982                    Point::new(3, 20)..Point::new(3, 22),
 5983                    FoldPlaceholder::test(),
 5984                ),
 5985            ],
 5986            true,
 5987            window,
 5988            cx,
 5989        );
 5990        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5991    });
 5992    editor.update(cx, |editor, cx| {
 5993        assert_text_with_selections(
 5994            editor,
 5995            indoc! {r#"
 5996                use mod1::mod2::«{mod3, mod4}ˇ»;
 5997
 5998                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5999                    «let var1 = "text";ˇ»
 6000                }
 6001            "#},
 6002            cx,
 6003        );
 6004    });
 6005}
 6006
 6007#[gpui::test]
 6008async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6009    init_test(cx, |_| {});
 6010
 6011    let base_text = r#"
 6012        impl A {
 6013            // this is an uncommitted comment
 6014
 6015            fn b() {
 6016                c();
 6017            }
 6018
 6019            // this is another uncommitted comment
 6020
 6021            fn d() {
 6022                // e
 6023                // f
 6024            }
 6025        }
 6026
 6027        fn g() {
 6028            // h
 6029        }
 6030    "#
 6031    .unindent();
 6032
 6033    let text = r#"
 6034        ˇimpl A {
 6035
 6036            fn b() {
 6037                c();
 6038            }
 6039
 6040            fn d() {
 6041                // e
 6042                // f
 6043            }
 6044        }
 6045
 6046        fn g() {
 6047            // h
 6048        }
 6049    "#
 6050    .unindent();
 6051
 6052    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6053    cx.set_state(&text);
 6054    cx.set_head_text(&base_text);
 6055    cx.update_editor(|editor, window, cx| {
 6056        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6057    });
 6058
 6059    cx.assert_state_with_diff(
 6060        "
 6061        ˇimpl A {
 6062      -     // this is an uncommitted comment
 6063
 6064            fn b() {
 6065                c();
 6066            }
 6067
 6068      -     // this is another uncommitted comment
 6069      -
 6070            fn d() {
 6071                // e
 6072                // f
 6073            }
 6074        }
 6075
 6076        fn g() {
 6077            // h
 6078        }
 6079    "
 6080        .unindent(),
 6081    );
 6082
 6083    let expected_display_text = "
 6084        impl A {
 6085            // this is an uncommitted comment
 6086
 6087            fn b() {
 6088 6089            }
 6090
 6091            // this is another uncommitted comment
 6092
 6093            fn d() {
 6094 6095            }
 6096        }
 6097
 6098        fn g() {
 6099 6100        }
 6101        "
 6102    .unindent();
 6103
 6104    cx.update_editor(|editor, window, cx| {
 6105        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6106        assert_eq!(editor.display_text(cx), expected_display_text);
 6107    });
 6108}
 6109
 6110#[gpui::test]
 6111async fn test_autoindent(cx: &mut TestAppContext) {
 6112    init_test(cx, |_| {});
 6113
 6114    let language = Arc::new(
 6115        Language::new(
 6116            LanguageConfig {
 6117                brackets: BracketPairConfig {
 6118                    pairs: vec![
 6119                        BracketPair {
 6120                            start: "{".to_string(),
 6121                            end: "}".to_string(),
 6122                            close: false,
 6123                            surround: false,
 6124                            newline: true,
 6125                        },
 6126                        BracketPair {
 6127                            start: "(".to_string(),
 6128                            end: ")".to_string(),
 6129                            close: false,
 6130                            surround: false,
 6131                            newline: true,
 6132                        },
 6133                    ],
 6134                    ..Default::default()
 6135                },
 6136                ..Default::default()
 6137            },
 6138            Some(tree_sitter_rust::LANGUAGE.into()),
 6139        )
 6140        .with_indents_query(
 6141            r#"
 6142                (_ "(" ")" @end) @indent
 6143                (_ "{" "}" @end) @indent
 6144            "#,
 6145        )
 6146        .unwrap(),
 6147    );
 6148
 6149    let text = "fn a() {}";
 6150
 6151    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6152    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6153    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6154    editor
 6155        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6156        .await;
 6157
 6158    editor.update_in(cx, |editor, window, cx| {
 6159        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6160        editor.newline(&Newline, window, cx);
 6161        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6162        assert_eq!(
 6163            editor.selections.ranges(cx),
 6164            &[
 6165                Point::new(1, 4)..Point::new(1, 4),
 6166                Point::new(3, 4)..Point::new(3, 4),
 6167                Point::new(5, 0)..Point::new(5, 0)
 6168            ]
 6169        );
 6170    });
 6171}
 6172
 6173#[gpui::test]
 6174async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6175    init_test(cx, |_| {});
 6176
 6177    {
 6178        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6179        cx.set_state(indoc! {"
 6180            impl A {
 6181
 6182                fn b() {}
 6183
 6184            «fn c() {
 6185
 6186            }ˇ»
 6187            }
 6188        "});
 6189
 6190        cx.update_editor(|editor, window, cx| {
 6191            editor.autoindent(&Default::default(), window, cx);
 6192        });
 6193
 6194        cx.assert_editor_state(indoc! {"
 6195            impl A {
 6196
 6197                fn b() {}
 6198
 6199                «fn c() {
 6200
 6201                }ˇ»
 6202            }
 6203        "});
 6204    }
 6205
 6206    {
 6207        let mut cx = EditorTestContext::new_multibuffer(
 6208            cx,
 6209            [indoc! { "
 6210                impl A {
 6211                «
 6212                // a
 6213                fn b(){}
 6214                »
 6215                «
 6216                    }
 6217                    fn c(){}
 6218                »
 6219            "}],
 6220        );
 6221
 6222        let buffer = cx.update_editor(|editor, _, cx| {
 6223            let buffer = editor.buffer().update(cx, |buffer, _| {
 6224                buffer.all_buffers().iter().next().unwrap().clone()
 6225            });
 6226            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6227            buffer
 6228        });
 6229
 6230        cx.run_until_parked();
 6231        cx.update_editor(|editor, window, cx| {
 6232            editor.select_all(&Default::default(), window, cx);
 6233            editor.autoindent(&Default::default(), window, cx)
 6234        });
 6235        cx.run_until_parked();
 6236
 6237        cx.update(|_, cx| {
 6238            pretty_assertions::assert_eq!(
 6239                buffer.read(cx).text(),
 6240                indoc! { "
 6241                    impl A {
 6242
 6243                        // a
 6244                        fn b(){}
 6245
 6246
 6247                    }
 6248                    fn c(){}
 6249
 6250                " }
 6251            )
 6252        });
 6253    }
 6254}
 6255
 6256#[gpui::test]
 6257async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6258    init_test(cx, |_| {});
 6259
 6260    let mut cx = EditorTestContext::new(cx).await;
 6261
 6262    let language = Arc::new(Language::new(
 6263        LanguageConfig {
 6264            brackets: BracketPairConfig {
 6265                pairs: vec![
 6266                    BracketPair {
 6267                        start: "{".to_string(),
 6268                        end: "}".to_string(),
 6269                        close: true,
 6270                        surround: true,
 6271                        newline: true,
 6272                    },
 6273                    BracketPair {
 6274                        start: "(".to_string(),
 6275                        end: ")".to_string(),
 6276                        close: true,
 6277                        surround: true,
 6278                        newline: true,
 6279                    },
 6280                    BracketPair {
 6281                        start: "/*".to_string(),
 6282                        end: " */".to_string(),
 6283                        close: true,
 6284                        surround: true,
 6285                        newline: true,
 6286                    },
 6287                    BracketPair {
 6288                        start: "[".to_string(),
 6289                        end: "]".to_string(),
 6290                        close: false,
 6291                        surround: false,
 6292                        newline: true,
 6293                    },
 6294                    BracketPair {
 6295                        start: "\"".to_string(),
 6296                        end: "\"".to_string(),
 6297                        close: true,
 6298                        surround: true,
 6299                        newline: false,
 6300                    },
 6301                    BracketPair {
 6302                        start: "<".to_string(),
 6303                        end: ">".to_string(),
 6304                        close: false,
 6305                        surround: true,
 6306                        newline: true,
 6307                    },
 6308                ],
 6309                ..Default::default()
 6310            },
 6311            autoclose_before: "})]".to_string(),
 6312            ..Default::default()
 6313        },
 6314        Some(tree_sitter_rust::LANGUAGE.into()),
 6315    ));
 6316
 6317    cx.language_registry().add(language.clone());
 6318    cx.update_buffer(|buffer, cx| {
 6319        buffer.set_language(Some(language), cx);
 6320    });
 6321
 6322    cx.set_state(
 6323        &r#"
 6324            🏀ˇ
 6325            εˇ
 6326            ❤️ˇ
 6327        "#
 6328        .unindent(),
 6329    );
 6330
 6331    // autoclose multiple nested brackets at multiple cursors
 6332    cx.update_editor(|editor, window, cx| {
 6333        editor.handle_input("{", window, cx);
 6334        editor.handle_input("{", window, cx);
 6335        editor.handle_input("{", window, cx);
 6336    });
 6337    cx.assert_editor_state(
 6338        &"
 6339            🏀{{{ˇ}}}
 6340            ε{{{ˇ}}}
 6341            ❤️{{{ˇ}}}
 6342        "
 6343        .unindent(),
 6344    );
 6345
 6346    // insert a different closing bracket
 6347    cx.update_editor(|editor, window, cx| {
 6348        editor.handle_input(")", window, cx);
 6349    });
 6350    cx.assert_editor_state(
 6351        &"
 6352            🏀{{{)ˇ}}}
 6353            ε{{{)ˇ}}}
 6354            ❤️{{{)ˇ}}}
 6355        "
 6356        .unindent(),
 6357    );
 6358
 6359    // skip over the auto-closed brackets when typing a closing bracket
 6360    cx.update_editor(|editor, window, cx| {
 6361        editor.move_right(&MoveRight, window, cx);
 6362        editor.handle_input("}", window, cx);
 6363        editor.handle_input("}", window, cx);
 6364        editor.handle_input("}", window, cx);
 6365    });
 6366    cx.assert_editor_state(
 6367        &"
 6368            🏀{{{)}}}}ˇ
 6369            ε{{{)}}}}ˇ
 6370            ❤️{{{)}}}}ˇ
 6371        "
 6372        .unindent(),
 6373    );
 6374
 6375    // autoclose multi-character pairs
 6376    cx.set_state(
 6377        &"
 6378            ˇ
 6379            ˇ
 6380        "
 6381        .unindent(),
 6382    );
 6383    cx.update_editor(|editor, window, cx| {
 6384        editor.handle_input("/", window, cx);
 6385        editor.handle_input("*", window, cx);
 6386    });
 6387    cx.assert_editor_state(
 6388        &"
 6389            /*ˇ */
 6390            /*ˇ */
 6391        "
 6392        .unindent(),
 6393    );
 6394
 6395    // one cursor autocloses a multi-character pair, one cursor
 6396    // does not autoclose.
 6397    cx.set_state(
 6398        &"
 6399 6400            ˇ
 6401        "
 6402        .unindent(),
 6403    );
 6404    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6405    cx.assert_editor_state(
 6406        &"
 6407            /*ˇ */
 6408 6409        "
 6410        .unindent(),
 6411    );
 6412
 6413    // Don't autoclose if the next character isn't whitespace and isn't
 6414    // listed in the language's "autoclose_before" section.
 6415    cx.set_state("ˇa b");
 6416    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6417    cx.assert_editor_state("{ˇa b");
 6418
 6419    // Don't autoclose if `close` is false for the bracket pair
 6420    cx.set_state("ˇ");
 6421    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6422    cx.assert_editor_state("");
 6423
 6424    // Surround with brackets if text is selected
 6425    cx.set_state("«aˇ» b");
 6426    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6427    cx.assert_editor_state("{«aˇ»} b");
 6428
 6429    // Autoclose when not immediately after a word character
 6430    cx.set_state("a ˇ");
 6431    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6432    cx.assert_editor_state("a \"ˇ\"");
 6433
 6434    // Autoclose pair where the start and end characters are the same
 6435    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6436    cx.assert_editor_state("a \"\"ˇ");
 6437
 6438    // Don't autoclose when immediately after a word character
 6439    cx.set_state("");
 6440    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6441    cx.assert_editor_state("a\"ˇ");
 6442
 6443    // Do autoclose when after a non-word character
 6444    cx.set_state("");
 6445    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6446    cx.assert_editor_state("{\"ˇ\"");
 6447
 6448    // Non identical pairs autoclose regardless of preceding character
 6449    cx.set_state("");
 6450    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6451    cx.assert_editor_state("a{ˇ}");
 6452
 6453    // Don't autoclose pair if autoclose is disabled
 6454    cx.set_state("ˇ");
 6455    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6456    cx.assert_editor_state("");
 6457
 6458    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6459    cx.set_state("«aˇ» b");
 6460    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6461    cx.assert_editor_state("<«aˇ»> b");
 6462}
 6463
 6464#[gpui::test]
 6465async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6466    init_test(cx, |settings| {
 6467        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6468    });
 6469
 6470    let mut cx = EditorTestContext::new(cx).await;
 6471
 6472    let language = Arc::new(Language::new(
 6473        LanguageConfig {
 6474            brackets: BracketPairConfig {
 6475                pairs: vec![
 6476                    BracketPair {
 6477                        start: "{".to_string(),
 6478                        end: "}".to_string(),
 6479                        close: true,
 6480                        surround: true,
 6481                        newline: true,
 6482                    },
 6483                    BracketPair {
 6484                        start: "(".to_string(),
 6485                        end: ")".to_string(),
 6486                        close: true,
 6487                        surround: true,
 6488                        newline: true,
 6489                    },
 6490                    BracketPair {
 6491                        start: "[".to_string(),
 6492                        end: "]".to_string(),
 6493                        close: false,
 6494                        surround: false,
 6495                        newline: true,
 6496                    },
 6497                ],
 6498                ..Default::default()
 6499            },
 6500            autoclose_before: "})]".to_string(),
 6501            ..Default::default()
 6502        },
 6503        Some(tree_sitter_rust::LANGUAGE.into()),
 6504    ));
 6505
 6506    cx.language_registry().add(language.clone());
 6507    cx.update_buffer(|buffer, cx| {
 6508        buffer.set_language(Some(language), cx);
 6509    });
 6510
 6511    cx.set_state(
 6512        &"
 6513            ˇ
 6514            ˇ
 6515            ˇ
 6516        "
 6517        .unindent(),
 6518    );
 6519
 6520    // ensure only matching closing brackets are skipped over
 6521    cx.update_editor(|editor, window, cx| {
 6522        editor.handle_input("}", window, cx);
 6523        editor.move_left(&MoveLeft, window, cx);
 6524        editor.handle_input(")", window, cx);
 6525        editor.move_left(&MoveLeft, window, cx);
 6526    });
 6527    cx.assert_editor_state(
 6528        &"
 6529            ˇ)}
 6530            ˇ)}
 6531            ˇ)}
 6532        "
 6533        .unindent(),
 6534    );
 6535
 6536    // skip-over closing brackets at multiple cursors
 6537    cx.update_editor(|editor, window, cx| {
 6538        editor.handle_input(")", window, cx);
 6539        editor.handle_input("}", window, cx);
 6540    });
 6541    cx.assert_editor_state(
 6542        &"
 6543            )}ˇ
 6544            )}ˇ
 6545            )}ˇ
 6546        "
 6547        .unindent(),
 6548    );
 6549
 6550    // ignore non-close brackets
 6551    cx.update_editor(|editor, window, cx| {
 6552        editor.handle_input("]", window, cx);
 6553        editor.move_left(&MoveLeft, window, cx);
 6554        editor.handle_input("]", window, cx);
 6555    });
 6556    cx.assert_editor_state(
 6557        &"
 6558            )}]ˇ]
 6559            )}]ˇ]
 6560            )}]ˇ]
 6561        "
 6562        .unindent(),
 6563    );
 6564}
 6565
 6566#[gpui::test]
 6567async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6568    init_test(cx, |_| {});
 6569
 6570    let mut cx = EditorTestContext::new(cx).await;
 6571
 6572    let html_language = Arc::new(
 6573        Language::new(
 6574            LanguageConfig {
 6575                name: "HTML".into(),
 6576                brackets: BracketPairConfig {
 6577                    pairs: vec![
 6578                        BracketPair {
 6579                            start: "<".into(),
 6580                            end: ">".into(),
 6581                            close: true,
 6582                            ..Default::default()
 6583                        },
 6584                        BracketPair {
 6585                            start: "{".into(),
 6586                            end: "}".into(),
 6587                            close: true,
 6588                            ..Default::default()
 6589                        },
 6590                        BracketPair {
 6591                            start: "(".into(),
 6592                            end: ")".into(),
 6593                            close: true,
 6594                            ..Default::default()
 6595                        },
 6596                    ],
 6597                    ..Default::default()
 6598                },
 6599                autoclose_before: "})]>".into(),
 6600                ..Default::default()
 6601            },
 6602            Some(tree_sitter_html::LANGUAGE.into()),
 6603        )
 6604        .with_injection_query(
 6605            r#"
 6606            (script_element
 6607                (raw_text) @injection.content
 6608                (#set! injection.language "javascript"))
 6609            "#,
 6610        )
 6611        .unwrap(),
 6612    );
 6613
 6614    let javascript_language = Arc::new(Language::new(
 6615        LanguageConfig {
 6616            name: "JavaScript".into(),
 6617            brackets: BracketPairConfig {
 6618                pairs: vec![
 6619                    BracketPair {
 6620                        start: "/*".into(),
 6621                        end: " */".into(),
 6622                        close: true,
 6623                        ..Default::default()
 6624                    },
 6625                    BracketPair {
 6626                        start: "{".into(),
 6627                        end: "}".into(),
 6628                        close: true,
 6629                        ..Default::default()
 6630                    },
 6631                    BracketPair {
 6632                        start: "(".into(),
 6633                        end: ")".into(),
 6634                        close: true,
 6635                        ..Default::default()
 6636                    },
 6637                ],
 6638                ..Default::default()
 6639            },
 6640            autoclose_before: "})]>".into(),
 6641            ..Default::default()
 6642        },
 6643        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6644    ));
 6645
 6646    cx.language_registry().add(html_language.clone());
 6647    cx.language_registry().add(javascript_language.clone());
 6648
 6649    cx.update_buffer(|buffer, cx| {
 6650        buffer.set_language(Some(html_language), cx);
 6651    });
 6652
 6653    cx.set_state(
 6654        &r#"
 6655            <body>ˇ
 6656                <script>
 6657                    var x = 1;ˇ
 6658                </script>
 6659            </body>ˇ
 6660        "#
 6661        .unindent(),
 6662    );
 6663
 6664    // Precondition: different languages are active at different locations.
 6665    cx.update_editor(|editor, window, cx| {
 6666        let snapshot = editor.snapshot(window, cx);
 6667        let cursors = editor.selections.ranges::<usize>(cx);
 6668        let languages = cursors
 6669            .iter()
 6670            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6671            .collect::<Vec<_>>();
 6672        assert_eq!(
 6673            languages,
 6674            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6675        );
 6676    });
 6677
 6678    // Angle brackets autoclose in HTML, but not JavaScript.
 6679    cx.update_editor(|editor, window, cx| {
 6680        editor.handle_input("<", window, cx);
 6681        editor.handle_input("a", window, cx);
 6682    });
 6683    cx.assert_editor_state(
 6684        &r#"
 6685            <body><aˇ>
 6686                <script>
 6687                    var x = 1;<aˇ
 6688                </script>
 6689            </body><aˇ>
 6690        "#
 6691        .unindent(),
 6692    );
 6693
 6694    // Curly braces and parens autoclose in both HTML and JavaScript.
 6695    cx.update_editor(|editor, window, cx| {
 6696        editor.handle_input(" b=", window, cx);
 6697        editor.handle_input("{", window, cx);
 6698        editor.handle_input("c", window, cx);
 6699        editor.handle_input("(", window, cx);
 6700    });
 6701    cx.assert_editor_state(
 6702        &r#"
 6703            <body><a b={c(ˇ)}>
 6704                <script>
 6705                    var x = 1;<a b={c(ˇ)}
 6706                </script>
 6707            </body><a b={c(ˇ)}>
 6708        "#
 6709        .unindent(),
 6710    );
 6711
 6712    // Brackets that were already autoclosed are skipped.
 6713    cx.update_editor(|editor, window, cx| {
 6714        editor.handle_input(")", window, cx);
 6715        editor.handle_input("d", window, cx);
 6716        editor.handle_input("}", window, cx);
 6717    });
 6718    cx.assert_editor_state(
 6719        &r#"
 6720            <body><a b={c()d}ˇ>
 6721                <script>
 6722                    var x = 1;<a b={c()d}ˇ
 6723                </script>
 6724            </body><a b={c()d}ˇ>
 6725        "#
 6726        .unindent(),
 6727    );
 6728    cx.update_editor(|editor, window, cx| {
 6729        editor.handle_input(">", window, cx);
 6730    });
 6731    cx.assert_editor_state(
 6732        &r#"
 6733            <body><a b={c()d}>ˇ
 6734                <script>
 6735                    var x = 1;<a b={c()d}>ˇ
 6736                </script>
 6737            </body><a b={c()d}>ˇ
 6738        "#
 6739        .unindent(),
 6740    );
 6741
 6742    // Reset
 6743    cx.set_state(
 6744        &r#"
 6745            <body>ˇ
 6746                <script>
 6747                    var x = 1;ˇ
 6748                </script>
 6749            </body>ˇ
 6750        "#
 6751        .unindent(),
 6752    );
 6753
 6754    cx.update_editor(|editor, window, cx| {
 6755        editor.handle_input("<", window, cx);
 6756    });
 6757    cx.assert_editor_state(
 6758        &r#"
 6759            <body><ˇ>
 6760                <script>
 6761                    var x = 1;<ˇ
 6762                </script>
 6763            </body><ˇ>
 6764        "#
 6765        .unindent(),
 6766    );
 6767
 6768    // When backspacing, the closing angle brackets are removed.
 6769    cx.update_editor(|editor, window, cx| {
 6770        editor.backspace(&Backspace, window, cx);
 6771    });
 6772    cx.assert_editor_state(
 6773        &r#"
 6774            <body>ˇ
 6775                <script>
 6776                    var x = 1;ˇ
 6777                </script>
 6778            </body>ˇ
 6779        "#
 6780        .unindent(),
 6781    );
 6782
 6783    // Block comments autoclose in JavaScript, but not HTML.
 6784    cx.update_editor(|editor, window, cx| {
 6785        editor.handle_input("/", window, cx);
 6786        editor.handle_input("*", window, cx);
 6787    });
 6788    cx.assert_editor_state(
 6789        &r#"
 6790            <body>/*ˇ
 6791                <script>
 6792                    var x = 1;/*ˇ */
 6793                </script>
 6794            </body>/*ˇ
 6795        "#
 6796        .unindent(),
 6797    );
 6798}
 6799
 6800#[gpui::test]
 6801async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6802    init_test(cx, |_| {});
 6803
 6804    let mut cx = EditorTestContext::new(cx).await;
 6805
 6806    let rust_language = Arc::new(
 6807        Language::new(
 6808            LanguageConfig {
 6809                name: "Rust".into(),
 6810                brackets: serde_json::from_value(json!([
 6811                    { "start": "{", "end": "}", "close": true, "newline": true },
 6812                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6813                ]))
 6814                .unwrap(),
 6815                autoclose_before: "})]>".into(),
 6816                ..Default::default()
 6817            },
 6818            Some(tree_sitter_rust::LANGUAGE.into()),
 6819        )
 6820        .with_override_query("(string_literal) @string")
 6821        .unwrap(),
 6822    );
 6823
 6824    cx.language_registry().add(rust_language.clone());
 6825    cx.update_buffer(|buffer, cx| {
 6826        buffer.set_language(Some(rust_language), cx);
 6827    });
 6828
 6829    cx.set_state(
 6830        &r#"
 6831            let x = ˇ
 6832        "#
 6833        .unindent(),
 6834    );
 6835
 6836    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6837    cx.update_editor(|editor, window, cx| {
 6838        editor.handle_input("\"", window, cx);
 6839    });
 6840    cx.assert_editor_state(
 6841        &r#"
 6842            let x = "ˇ"
 6843        "#
 6844        .unindent(),
 6845    );
 6846
 6847    // Inserting another quotation mark. The cursor moves across the existing
 6848    // automatically-inserted quotation mark.
 6849    cx.update_editor(|editor, window, cx| {
 6850        editor.handle_input("\"", window, cx);
 6851    });
 6852    cx.assert_editor_state(
 6853        &r#"
 6854            let x = ""ˇ
 6855        "#
 6856        .unindent(),
 6857    );
 6858
 6859    // Reset
 6860    cx.set_state(
 6861        &r#"
 6862            let x = ˇ
 6863        "#
 6864        .unindent(),
 6865    );
 6866
 6867    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6868    cx.update_editor(|editor, window, cx| {
 6869        editor.handle_input("\"", window, cx);
 6870        editor.handle_input(" ", window, cx);
 6871        editor.move_left(&Default::default(), window, cx);
 6872        editor.handle_input("\\", window, cx);
 6873        editor.handle_input("\"", window, cx);
 6874    });
 6875    cx.assert_editor_state(
 6876        &r#"
 6877            let x = "\"ˇ "
 6878        "#
 6879        .unindent(),
 6880    );
 6881
 6882    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6883    // mark. Nothing is inserted.
 6884    cx.update_editor(|editor, window, cx| {
 6885        editor.move_right(&Default::default(), window, cx);
 6886        editor.handle_input("\"", window, cx);
 6887    });
 6888    cx.assert_editor_state(
 6889        &r#"
 6890            let x = "\" "ˇ
 6891        "#
 6892        .unindent(),
 6893    );
 6894}
 6895
 6896#[gpui::test]
 6897async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6898    init_test(cx, |_| {});
 6899
 6900    let language = Arc::new(Language::new(
 6901        LanguageConfig {
 6902            brackets: BracketPairConfig {
 6903                pairs: vec![
 6904                    BracketPair {
 6905                        start: "{".to_string(),
 6906                        end: "}".to_string(),
 6907                        close: true,
 6908                        surround: true,
 6909                        newline: true,
 6910                    },
 6911                    BracketPair {
 6912                        start: "/* ".to_string(),
 6913                        end: "*/".to_string(),
 6914                        close: true,
 6915                        surround: true,
 6916                        ..Default::default()
 6917                    },
 6918                ],
 6919                ..Default::default()
 6920            },
 6921            ..Default::default()
 6922        },
 6923        Some(tree_sitter_rust::LANGUAGE.into()),
 6924    ));
 6925
 6926    let text = r#"
 6927        a
 6928        b
 6929        c
 6930    "#
 6931    .unindent();
 6932
 6933    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6934    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6935    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6936    editor
 6937        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6938        .await;
 6939
 6940    editor.update_in(cx, |editor, window, cx| {
 6941        editor.change_selections(None, window, cx, |s| {
 6942            s.select_display_ranges([
 6943                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6944                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6945                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6946            ])
 6947        });
 6948
 6949        editor.handle_input("{", window, cx);
 6950        editor.handle_input("{", window, cx);
 6951        editor.handle_input("{", window, cx);
 6952        assert_eq!(
 6953            editor.text(cx),
 6954            "
 6955                {{{a}}}
 6956                {{{b}}}
 6957                {{{c}}}
 6958            "
 6959            .unindent()
 6960        );
 6961        assert_eq!(
 6962            editor.selections.display_ranges(cx),
 6963            [
 6964                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6965                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6966                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6967            ]
 6968        );
 6969
 6970        editor.undo(&Undo, window, cx);
 6971        editor.undo(&Undo, window, cx);
 6972        editor.undo(&Undo, window, cx);
 6973        assert_eq!(
 6974            editor.text(cx),
 6975            "
 6976                a
 6977                b
 6978                c
 6979            "
 6980            .unindent()
 6981        );
 6982        assert_eq!(
 6983            editor.selections.display_ranges(cx),
 6984            [
 6985                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6986                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6987                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6988            ]
 6989        );
 6990
 6991        // Ensure inserting the first character of a multi-byte bracket pair
 6992        // doesn't surround the selections with the bracket.
 6993        editor.handle_input("/", window, cx);
 6994        assert_eq!(
 6995            editor.text(cx),
 6996            "
 6997                /
 6998                /
 6999                /
 7000            "
 7001            .unindent()
 7002        );
 7003        assert_eq!(
 7004            editor.selections.display_ranges(cx),
 7005            [
 7006                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7007                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7008                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7009            ]
 7010        );
 7011
 7012        editor.undo(&Undo, window, cx);
 7013        assert_eq!(
 7014            editor.text(cx),
 7015            "
 7016                a
 7017                b
 7018                c
 7019            "
 7020            .unindent()
 7021        );
 7022        assert_eq!(
 7023            editor.selections.display_ranges(cx),
 7024            [
 7025                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7026                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7027                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7028            ]
 7029        );
 7030
 7031        // Ensure inserting the last character of a multi-byte bracket pair
 7032        // doesn't surround the selections with the bracket.
 7033        editor.handle_input("*", window, cx);
 7034        assert_eq!(
 7035            editor.text(cx),
 7036            "
 7037                *
 7038                *
 7039                *
 7040            "
 7041            .unindent()
 7042        );
 7043        assert_eq!(
 7044            editor.selections.display_ranges(cx),
 7045            [
 7046                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7047                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7048                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7049            ]
 7050        );
 7051    });
 7052}
 7053
 7054#[gpui::test]
 7055async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7056    init_test(cx, |_| {});
 7057
 7058    let language = Arc::new(Language::new(
 7059        LanguageConfig {
 7060            brackets: BracketPairConfig {
 7061                pairs: vec![BracketPair {
 7062                    start: "{".to_string(),
 7063                    end: "}".to_string(),
 7064                    close: true,
 7065                    surround: true,
 7066                    newline: true,
 7067                }],
 7068                ..Default::default()
 7069            },
 7070            autoclose_before: "}".to_string(),
 7071            ..Default::default()
 7072        },
 7073        Some(tree_sitter_rust::LANGUAGE.into()),
 7074    ));
 7075
 7076    let text = r#"
 7077        a
 7078        b
 7079        c
 7080    "#
 7081    .unindent();
 7082
 7083    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7084    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7085    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7086    editor
 7087        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7088        .await;
 7089
 7090    editor.update_in(cx, |editor, window, cx| {
 7091        editor.change_selections(None, window, cx, |s| {
 7092            s.select_ranges([
 7093                Point::new(0, 1)..Point::new(0, 1),
 7094                Point::new(1, 1)..Point::new(1, 1),
 7095                Point::new(2, 1)..Point::new(2, 1),
 7096            ])
 7097        });
 7098
 7099        editor.handle_input("{", window, cx);
 7100        editor.handle_input("{", window, cx);
 7101        editor.handle_input("_", window, cx);
 7102        assert_eq!(
 7103            editor.text(cx),
 7104            "
 7105                a{{_}}
 7106                b{{_}}
 7107                c{{_}}
 7108            "
 7109            .unindent()
 7110        );
 7111        assert_eq!(
 7112            editor.selections.ranges::<Point>(cx),
 7113            [
 7114                Point::new(0, 4)..Point::new(0, 4),
 7115                Point::new(1, 4)..Point::new(1, 4),
 7116                Point::new(2, 4)..Point::new(2, 4)
 7117            ]
 7118        );
 7119
 7120        editor.backspace(&Default::default(), window, cx);
 7121        editor.backspace(&Default::default(), window, cx);
 7122        assert_eq!(
 7123            editor.text(cx),
 7124            "
 7125                a{}
 7126                b{}
 7127                c{}
 7128            "
 7129            .unindent()
 7130        );
 7131        assert_eq!(
 7132            editor.selections.ranges::<Point>(cx),
 7133            [
 7134                Point::new(0, 2)..Point::new(0, 2),
 7135                Point::new(1, 2)..Point::new(1, 2),
 7136                Point::new(2, 2)..Point::new(2, 2)
 7137            ]
 7138        );
 7139
 7140        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7141        assert_eq!(
 7142            editor.text(cx),
 7143            "
 7144                a
 7145                b
 7146                c
 7147            "
 7148            .unindent()
 7149        );
 7150        assert_eq!(
 7151            editor.selections.ranges::<Point>(cx),
 7152            [
 7153                Point::new(0, 1)..Point::new(0, 1),
 7154                Point::new(1, 1)..Point::new(1, 1),
 7155                Point::new(2, 1)..Point::new(2, 1)
 7156            ]
 7157        );
 7158    });
 7159}
 7160
 7161#[gpui::test]
 7162async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7163    init_test(cx, |settings| {
 7164        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7165    });
 7166
 7167    let mut cx = EditorTestContext::new(cx).await;
 7168
 7169    let language = Arc::new(Language::new(
 7170        LanguageConfig {
 7171            brackets: BracketPairConfig {
 7172                pairs: vec![
 7173                    BracketPair {
 7174                        start: "{".to_string(),
 7175                        end: "}".to_string(),
 7176                        close: true,
 7177                        surround: true,
 7178                        newline: true,
 7179                    },
 7180                    BracketPair {
 7181                        start: "(".to_string(),
 7182                        end: ")".to_string(),
 7183                        close: true,
 7184                        surround: true,
 7185                        newline: true,
 7186                    },
 7187                    BracketPair {
 7188                        start: "[".to_string(),
 7189                        end: "]".to_string(),
 7190                        close: false,
 7191                        surround: true,
 7192                        newline: true,
 7193                    },
 7194                ],
 7195                ..Default::default()
 7196            },
 7197            autoclose_before: "})]".to_string(),
 7198            ..Default::default()
 7199        },
 7200        Some(tree_sitter_rust::LANGUAGE.into()),
 7201    ));
 7202
 7203    cx.language_registry().add(language.clone());
 7204    cx.update_buffer(|buffer, cx| {
 7205        buffer.set_language(Some(language), cx);
 7206    });
 7207
 7208    cx.set_state(
 7209        &"
 7210            {(ˇ)}
 7211            [[ˇ]]
 7212            {(ˇ)}
 7213        "
 7214        .unindent(),
 7215    );
 7216
 7217    cx.update_editor(|editor, window, cx| {
 7218        editor.backspace(&Default::default(), window, cx);
 7219        editor.backspace(&Default::default(), window, cx);
 7220    });
 7221
 7222    cx.assert_editor_state(
 7223        &"
 7224            ˇ
 7225            ˇ]]
 7226            ˇ
 7227        "
 7228        .unindent(),
 7229    );
 7230
 7231    cx.update_editor(|editor, window, cx| {
 7232        editor.handle_input("{", window, cx);
 7233        editor.handle_input("{", window, cx);
 7234        editor.move_right(&MoveRight, window, cx);
 7235        editor.move_right(&MoveRight, window, cx);
 7236        editor.move_left(&MoveLeft, window, cx);
 7237        editor.move_left(&MoveLeft, window, cx);
 7238        editor.backspace(&Default::default(), window, cx);
 7239    });
 7240
 7241    cx.assert_editor_state(
 7242        &"
 7243            {ˇ}
 7244            {ˇ}]]
 7245            {ˇ}
 7246        "
 7247        .unindent(),
 7248    );
 7249
 7250    cx.update_editor(|editor, window, cx| {
 7251        editor.backspace(&Default::default(), window, cx);
 7252    });
 7253
 7254    cx.assert_editor_state(
 7255        &"
 7256            ˇ
 7257            ˇ]]
 7258            ˇ
 7259        "
 7260        .unindent(),
 7261    );
 7262}
 7263
 7264#[gpui::test]
 7265async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7266    init_test(cx, |_| {});
 7267
 7268    let language = Arc::new(Language::new(
 7269        LanguageConfig::default(),
 7270        Some(tree_sitter_rust::LANGUAGE.into()),
 7271    ));
 7272
 7273    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7274    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7275    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7276    editor
 7277        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7278        .await;
 7279
 7280    editor.update_in(cx, |editor, window, cx| {
 7281        editor.set_auto_replace_emoji_shortcode(true);
 7282
 7283        editor.handle_input("Hello ", window, cx);
 7284        editor.handle_input(":wave", window, cx);
 7285        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7286
 7287        editor.handle_input(":", window, cx);
 7288        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7289
 7290        editor.handle_input(" :smile", window, cx);
 7291        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7292
 7293        editor.handle_input(":", window, cx);
 7294        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7295
 7296        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7297        editor.handle_input(":wave", window, cx);
 7298        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7299
 7300        editor.handle_input(":", window, cx);
 7301        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7302
 7303        editor.handle_input(":1", window, cx);
 7304        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7305
 7306        editor.handle_input(":", window, cx);
 7307        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7308
 7309        // Ensure shortcode does not get replaced when it is part of a word
 7310        editor.handle_input(" Test:wave", window, cx);
 7311        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7312
 7313        editor.handle_input(":", window, cx);
 7314        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7315
 7316        editor.set_auto_replace_emoji_shortcode(false);
 7317
 7318        // Ensure shortcode does not get replaced when auto replace is off
 7319        editor.handle_input(" :wave", window, cx);
 7320        assert_eq!(
 7321            editor.text(cx),
 7322            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7323        );
 7324
 7325        editor.handle_input(":", window, cx);
 7326        assert_eq!(
 7327            editor.text(cx),
 7328            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7329        );
 7330    });
 7331}
 7332
 7333#[gpui::test]
 7334async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7335    init_test(cx, |_| {});
 7336
 7337    let (text, insertion_ranges) = marked_text_ranges(
 7338        indoc! {"
 7339            ˇ
 7340        "},
 7341        false,
 7342    );
 7343
 7344    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7345    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7346
 7347    _ = editor.update_in(cx, |editor, window, cx| {
 7348        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7349
 7350        editor
 7351            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7352            .unwrap();
 7353
 7354        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7355            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7356            assert_eq!(editor.text(cx), expected_text);
 7357            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7358        }
 7359
 7360        assert(
 7361            editor,
 7362            cx,
 7363            indoc! {"
 7364            type «» =•
 7365            "},
 7366        );
 7367
 7368        assert!(editor.context_menu_visible(), "There should be a matches");
 7369    });
 7370}
 7371
 7372#[gpui::test]
 7373async fn test_snippets(cx: &mut TestAppContext) {
 7374    init_test(cx, |_| {});
 7375
 7376    let (text, insertion_ranges) = marked_text_ranges(
 7377        indoc! {"
 7378            a.ˇ b
 7379            a.ˇ b
 7380            a.ˇ b
 7381        "},
 7382        false,
 7383    );
 7384
 7385    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7386    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7387
 7388    editor.update_in(cx, |editor, window, cx| {
 7389        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7390
 7391        editor
 7392            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7393            .unwrap();
 7394
 7395        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7396            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7397            assert_eq!(editor.text(cx), expected_text);
 7398            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7399        }
 7400
 7401        assert(
 7402            editor,
 7403            cx,
 7404            indoc! {"
 7405                a.f(«one», two, «three») b
 7406                a.f(«one», two, «three») b
 7407                a.f(«one», two, «three») b
 7408            "},
 7409        );
 7410
 7411        // Can't move earlier than the first tab stop
 7412        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7413        assert(
 7414            editor,
 7415            cx,
 7416            indoc! {"
 7417                a.f(«one», two, «three») b
 7418                a.f(«one», two, «three») b
 7419                a.f(«one», two, «three») b
 7420            "},
 7421        );
 7422
 7423        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7424        assert(
 7425            editor,
 7426            cx,
 7427            indoc! {"
 7428                a.f(one, «two», three) b
 7429                a.f(one, «two», three) b
 7430                a.f(one, «two», three) b
 7431            "},
 7432        );
 7433
 7434        editor.move_to_prev_snippet_tabstop(window, cx);
 7435        assert(
 7436            editor,
 7437            cx,
 7438            indoc! {"
 7439                a.f(«one», two, «three») b
 7440                a.f(«one», two, «three») b
 7441                a.f(«one», two, «three») b
 7442            "},
 7443        );
 7444
 7445        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7446        assert(
 7447            editor,
 7448            cx,
 7449            indoc! {"
 7450                a.f(one, «two», three) b
 7451                a.f(one, «two», three) b
 7452                a.f(one, «two», three) b
 7453            "},
 7454        );
 7455        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7456        assert(
 7457            editor,
 7458            cx,
 7459            indoc! {"
 7460                a.f(one, two, three)ˇ b
 7461                a.f(one, two, three)ˇ b
 7462                a.f(one, two, three)ˇ b
 7463            "},
 7464        );
 7465
 7466        // As soon as the last tab stop is reached, snippet state is gone
 7467        editor.move_to_prev_snippet_tabstop(window, cx);
 7468        assert(
 7469            editor,
 7470            cx,
 7471            indoc! {"
 7472                a.f(one, two, three)ˇ b
 7473                a.f(one, two, three)ˇ b
 7474                a.f(one, two, three)ˇ b
 7475            "},
 7476        );
 7477    });
 7478}
 7479
 7480#[gpui::test]
 7481async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7482    init_test(cx, |_| {});
 7483
 7484    let fs = FakeFs::new(cx.executor());
 7485    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7486
 7487    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7488
 7489    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7490    language_registry.add(rust_lang());
 7491    let mut fake_servers = language_registry.register_fake_lsp(
 7492        "Rust",
 7493        FakeLspAdapter {
 7494            capabilities: lsp::ServerCapabilities {
 7495                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7496                ..Default::default()
 7497            },
 7498            ..Default::default()
 7499        },
 7500    );
 7501
 7502    let buffer = project
 7503        .update(cx, |project, cx| {
 7504            project.open_local_buffer(path!("/file.rs"), cx)
 7505        })
 7506        .await
 7507        .unwrap();
 7508
 7509    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7510    let (editor, cx) = cx.add_window_view(|window, cx| {
 7511        build_editor_with_project(project.clone(), buffer, window, cx)
 7512    });
 7513    editor.update_in(cx, |editor, window, cx| {
 7514        editor.set_text("one\ntwo\nthree\n", window, cx)
 7515    });
 7516    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7517
 7518    cx.executor().start_waiting();
 7519    let fake_server = fake_servers.next().await.unwrap();
 7520
 7521    let save = editor
 7522        .update_in(cx, |editor, window, cx| {
 7523            editor.save(true, project.clone(), window, cx)
 7524        })
 7525        .unwrap();
 7526    fake_server
 7527        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7528            assert_eq!(
 7529                params.text_document.uri,
 7530                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7531            );
 7532            assert_eq!(params.options.tab_size, 4);
 7533            Ok(Some(vec![lsp::TextEdit::new(
 7534                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7535                ", ".to_string(),
 7536            )]))
 7537        })
 7538        .next()
 7539        .await;
 7540    cx.executor().start_waiting();
 7541    save.await;
 7542
 7543    assert_eq!(
 7544        editor.update(cx, |editor, cx| editor.text(cx)),
 7545        "one, two\nthree\n"
 7546    );
 7547    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7548
 7549    editor.update_in(cx, |editor, window, cx| {
 7550        editor.set_text("one\ntwo\nthree\n", window, cx)
 7551    });
 7552    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7553
 7554    // Ensure we can still save even if formatting hangs.
 7555    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7556        assert_eq!(
 7557            params.text_document.uri,
 7558            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7559        );
 7560        futures::future::pending::<()>().await;
 7561        unreachable!()
 7562    });
 7563    let save = editor
 7564        .update_in(cx, |editor, window, cx| {
 7565            editor.save(true, project.clone(), window, cx)
 7566        })
 7567        .unwrap();
 7568    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7569    cx.executor().start_waiting();
 7570    save.await;
 7571    assert_eq!(
 7572        editor.update(cx, |editor, cx| editor.text(cx)),
 7573        "one\ntwo\nthree\n"
 7574    );
 7575    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7576
 7577    // For non-dirty buffer, no formatting request should be sent
 7578    let save = editor
 7579        .update_in(cx, |editor, window, cx| {
 7580            editor.save(true, project.clone(), window, cx)
 7581        })
 7582        .unwrap();
 7583    let _pending_format_request = fake_server
 7584        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7585            panic!("Should not be invoked on non-dirty buffer");
 7586        })
 7587        .next();
 7588    cx.executor().start_waiting();
 7589    save.await;
 7590
 7591    // Set rust language override and assert overridden tabsize is sent to language server
 7592    update_test_language_settings(cx, |settings| {
 7593        settings.languages.insert(
 7594            "Rust".into(),
 7595            LanguageSettingsContent {
 7596                tab_size: NonZeroU32::new(8),
 7597                ..Default::default()
 7598            },
 7599        );
 7600    });
 7601
 7602    editor.update_in(cx, |editor, window, cx| {
 7603        editor.set_text("somehting_new\n", window, cx)
 7604    });
 7605    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7606    let save = editor
 7607        .update_in(cx, |editor, window, cx| {
 7608            editor.save(true, project.clone(), window, cx)
 7609        })
 7610        .unwrap();
 7611    fake_server
 7612        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7613            assert_eq!(
 7614                params.text_document.uri,
 7615                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7616            );
 7617            assert_eq!(params.options.tab_size, 8);
 7618            Ok(Some(vec![]))
 7619        })
 7620        .next()
 7621        .await;
 7622    cx.executor().start_waiting();
 7623    save.await;
 7624}
 7625
 7626#[gpui::test]
 7627async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7628    init_test(cx, |_| {});
 7629
 7630    let cols = 4;
 7631    let rows = 10;
 7632    let sample_text_1 = sample_text(rows, cols, 'a');
 7633    assert_eq!(
 7634        sample_text_1,
 7635        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7636    );
 7637    let sample_text_2 = sample_text(rows, cols, 'l');
 7638    assert_eq!(
 7639        sample_text_2,
 7640        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7641    );
 7642    let sample_text_3 = sample_text(rows, cols, 'v');
 7643    assert_eq!(
 7644        sample_text_3,
 7645        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7646    );
 7647
 7648    let fs = FakeFs::new(cx.executor());
 7649    fs.insert_tree(
 7650        path!("/a"),
 7651        json!({
 7652            "main.rs": sample_text_1,
 7653            "other.rs": sample_text_2,
 7654            "lib.rs": sample_text_3,
 7655        }),
 7656    )
 7657    .await;
 7658
 7659    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7660    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7661    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7662
 7663    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7664    language_registry.add(rust_lang());
 7665    let mut fake_servers = language_registry.register_fake_lsp(
 7666        "Rust",
 7667        FakeLspAdapter {
 7668            capabilities: lsp::ServerCapabilities {
 7669                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7670                ..Default::default()
 7671            },
 7672            ..Default::default()
 7673        },
 7674    );
 7675
 7676    let worktree = project.update(cx, |project, cx| {
 7677        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7678        assert_eq!(worktrees.len(), 1);
 7679        worktrees.pop().unwrap()
 7680    });
 7681    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7682
 7683    let buffer_1 = project
 7684        .update(cx, |project, cx| {
 7685            project.open_buffer((worktree_id, "main.rs"), cx)
 7686        })
 7687        .await
 7688        .unwrap();
 7689    let buffer_2 = project
 7690        .update(cx, |project, cx| {
 7691            project.open_buffer((worktree_id, "other.rs"), cx)
 7692        })
 7693        .await
 7694        .unwrap();
 7695    let buffer_3 = project
 7696        .update(cx, |project, cx| {
 7697            project.open_buffer((worktree_id, "lib.rs"), cx)
 7698        })
 7699        .await
 7700        .unwrap();
 7701
 7702    let multi_buffer = cx.new(|cx| {
 7703        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7704        multi_buffer.push_excerpts(
 7705            buffer_1.clone(),
 7706            [
 7707                ExcerptRange {
 7708                    context: Point::new(0, 0)..Point::new(3, 0),
 7709                    primary: None,
 7710                },
 7711                ExcerptRange {
 7712                    context: Point::new(5, 0)..Point::new(7, 0),
 7713                    primary: None,
 7714                },
 7715                ExcerptRange {
 7716                    context: Point::new(9, 0)..Point::new(10, 4),
 7717                    primary: None,
 7718                },
 7719            ],
 7720            cx,
 7721        );
 7722        multi_buffer.push_excerpts(
 7723            buffer_2.clone(),
 7724            [
 7725                ExcerptRange {
 7726                    context: Point::new(0, 0)..Point::new(3, 0),
 7727                    primary: None,
 7728                },
 7729                ExcerptRange {
 7730                    context: Point::new(5, 0)..Point::new(7, 0),
 7731                    primary: None,
 7732                },
 7733                ExcerptRange {
 7734                    context: Point::new(9, 0)..Point::new(10, 4),
 7735                    primary: None,
 7736                },
 7737            ],
 7738            cx,
 7739        );
 7740        multi_buffer.push_excerpts(
 7741            buffer_3.clone(),
 7742            [
 7743                ExcerptRange {
 7744                    context: Point::new(0, 0)..Point::new(3, 0),
 7745                    primary: None,
 7746                },
 7747                ExcerptRange {
 7748                    context: Point::new(5, 0)..Point::new(7, 0),
 7749                    primary: None,
 7750                },
 7751                ExcerptRange {
 7752                    context: Point::new(9, 0)..Point::new(10, 4),
 7753                    primary: None,
 7754                },
 7755            ],
 7756            cx,
 7757        );
 7758        multi_buffer
 7759    });
 7760    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7761        Editor::new(
 7762            EditorMode::Full,
 7763            multi_buffer,
 7764            Some(project.clone()),
 7765            window,
 7766            cx,
 7767        )
 7768    });
 7769
 7770    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7771        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7772            s.select_ranges(Some(1..2))
 7773        });
 7774        editor.insert("|one|two|three|", window, cx);
 7775    });
 7776    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7777    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7778        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7779            s.select_ranges(Some(60..70))
 7780        });
 7781        editor.insert("|four|five|six|", window, cx);
 7782    });
 7783    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7784
 7785    // First two buffers should be edited, but not the third one.
 7786    assert_eq!(
 7787        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7788        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
 7789    );
 7790    buffer_1.update(cx, |buffer, _| {
 7791        assert!(buffer.is_dirty());
 7792        assert_eq!(
 7793            buffer.text(),
 7794            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7795        )
 7796    });
 7797    buffer_2.update(cx, |buffer, _| {
 7798        assert!(buffer.is_dirty());
 7799        assert_eq!(
 7800            buffer.text(),
 7801            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7802        )
 7803    });
 7804    buffer_3.update(cx, |buffer, _| {
 7805        assert!(!buffer.is_dirty());
 7806        assert_eq!(buffer.text(), sample_text_3,)
 7807    });
 7808    cx.executor().run_until_parked();
 7809
 7810    cx.executor().start_waiting();
 7811    let save = multi_buffer_editor
 7812        .update_in(cx, |editor, window, cx| {
 7813            editor.save(true, project.clone(), window, cx)
 7814        })
 7815        .unwrap();
 7816
 7817    let fake_server = fake_servers.next().await.unwrap();
 7818    fake_server
 7819        .server
 7820        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7821            Ok(Some(vec![lsp::TextEdit::new(
 7822                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7823                format!("[{} formatted]", params.text_document.uri),
 7824            )]))
 7825        })
 7826        .detach();
 7827    save.await;
 7828
 7829    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7830    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7831    assert_eq!(
 7832        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7833        uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"),
 7834    );
 7835    buffer_1.update(cx, |buffer, _| {
 7836        assert!(!buffer.is_dirty());
 7837        assert_eq!(
 7838            buffer.text(),
 7839            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7840        )
 7841    });
 7842    buffer_2.update(cx, |buffer, _| {
 7843        assert!(!buffer.is_dirty());
 7844        assert_eq!(
 7845            buffer.text(),
 7846            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7847        )
 7848    });
 7849    buffer_3.update(cx, |buffer, _| {
 7850        assert!(!buffer.is_dirty());
 7851        assert_eq!(buffer.text(), sample_text_3,)
 7852    });
 7853}
 7854
 7855#[gpui::test]
 7856async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7857    init_test(cx, |_| {});
 7858
 7859    let fs = FakeFs::new(cx.executor());
 7860    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7861
 7862    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7863
 7864    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7865    language_registry.add(rust_lang());
 7866    let mut fake_servers = language_registry.register_fake_lsp(
 7867        "Rust",
 7868        FakeLspAdapter {
 7869            capabilities: lsp::ServerCapabilities {
 7870                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7871                ..Default::default()
 7872            },
 7873            ..Default::default()
 7874        },
 7875    );
 7876
 7877    let buffer = project
 7878        .update(cx, |project, cx| {
 7879            project.open_local_buffer(path!("/file.rs"), cx)
 7880        })
 7881        .await
 7882        .unwrap();
 7883
 7884    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7885    let (editor, cx) = cx.add_window_view(|window, cx| {
 7886        build_editor_with_project(project.clone(), buffer, window, cx)
 7887    });
 7888    editor.update_in(cx, |editor, window, cx| {
 7889        editor.set_text("one\ntwo\nthree\n", window, cx)
 7890    });
 7891    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7892
 7893    cx.executor().start_waiting();
 7894    let fake_server = fake_servers.next().await.unwrap();
 7895
 7896    let save = editor
 7897        .update_in(cx, |editor, window, cx| {
 7898            editor.save(true, project.clone(), window, cx)
 7899        })
 7900        .unwrap();
 7901    fake_server
 7902        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7903            assert_eq!(
 7904                params.text_document.uri,
 7905                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7906            );
 7907            assert_eq!(params.options.tab_size, 4);
 7908            Ok(Some(vec![lsp::TextEdit::new(
 7909                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7910                ", ".to_string(),
 7911            )]))
 7912        })
 7913        .next()
 7914        .await;
 7915    cx.executor().start_waiting();
 7916    save.await;
 7917    assert_eq!(
 7918        editor.update(cx, |editor, cx| editor.text(cx)),
 7919        "one, two\nthree\n"
 7920    );
 7921    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7922
 7923    editor.update_in(cx, |editor, window, cx| {
 7924        editor.set_text("one\ntwo\nthree\n", window, cx)
 7925    });
 7926    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7927
 7928    // Ensure we can still save even if formatting hangs.
 7929    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7930        move |params, _| async move {
 7931            assert_eq!(
 7932                params.text_document.uri,
 7933                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7934            );
 7935            futures::future::pending::<()>().await;
 7936            unreachable!()
 7937        },
 7938    );
 7939    let save = editor
 7940        .update_in(cx, |editor, window, cx| {
 7941            editor.save(true, project.clone(), window, cx)
 7942        })
 7943        .unwrap();
 7944    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7945    cx.executor().start_waiting();
 7946    save.await;
 7947    assert_eq!(
 7948        editor.update(cx, |editor, cx| editor.text(cx)),
 7949        "one\ntwo\nthree\n"
 7950    );
 7951    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7952
 7953    // For non-dirty buffer, no formatting request should be sent
 7954    let save = editor
 7955        .update_in(cx, |editor, window, cx| {
 7956            editor.save(true, project.clone(), window, cx)
 7957        })
 7958        .unwrap();
 7959    let _pending_format_request = fake_server
 7960        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7961            panic!("Should not be invoked on non-dirty buffer");
 7962        })
 7963        .next();
 7964    cx.executor().start_waiting();
 7965    save.await;
 7966
 7967    // Set Rust language override and assert overridden tabsize is sent to language server
 7968    update_test_language_settings(cx, |settings| {
 7969        settings.languages.insert(
 7970            "Rust".into(),
 7971            LanguageSettingsContent {
 7972                tab_size: NonZeroU32::new(8),
 7973                ..Default::default()
 7974            },
 7975        );
 7976    });
 7977
 7978    editor.update_in(cx, |editor, window, cx| {
 7979        editor.set_text("somehting_new\n", window, cx)
 7980    });
 7981    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7982    let save = editor
 7983        .update_in(cx, |editor, window, cx| {
 7984            editor.save(true, project.clone(), window, cx)
 7985        })
 7986        .unwrap();
 7987    fake_server
 7988        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7989            assert_eq!(
 7990                params.text_document.uri,
 7991                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7992            );
 7993            assert_eq!(params.options.tab_size, 8);
 7994            Ok(Some(vec![]))
 7995        })
 7996        .next()
 7997        .await;
 7998    cx.executor().start_waiting();
 7999    save.await;
 8000}
 8001
 8002#[gpui::test]
 8003async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8004    init_test(cx, |settings| {
 8005        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8006            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8007        ))
 8008    });
 8009
 8010    let fs = FakeFs::new(cx.executor());
 8011    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8012
 8013    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8014
 8015    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8016    language_registry.add(Arc::new(Language::new(
 8017        LanguageConfig {
 8018            name: "Rust".into(),
 8019            matcher: LanguageMatcher {
 8020                path_suffixes: vec!["rs".to_string()],
 8021                ..Default::default()
 8022            },
 8023            ..LanguageConfig::default()
 8024        },
 8025        Some(tree_sitter_rust::LANGUAGE.into()),
 8026    )));
 8027    update_test_language_settings(cx, |settings| {
 8028        // Enable Prettier formatting for the same buffer, and ensure
 8029        // LSP is called instead of Prettier.
 8030        settings.defaults.prettier = Some(PrettierSettings {
 8031            allowed: true,
 8032            ..PrettierSettings::default()
 8033        });
 8034    });
 8035    let mut fake_servers = language_registry.register_fake_lsp(
 8036        "Rust",
 8037        FakeLspAdapter {
 8038            capabilities: lsp::ServerCapabilities {
 8039                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8040                ..Default::default()
 8041            },
 8042            ..Default::default()
 8043        },
 8044    );
 8045
 8046    let buffer = project
 8047        .update(cx, |project, cx| {
 8048            project.open_local_buffer(path!("/file.rs"), cx)
 8049        })
 8050        .await
 8051        .unwrap();
 8052
 8053    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8054    let (editor, cx) = cx.add_window_view(|window, cx| {
 8055        build_editor_with_project(project.clone(), buffer, window, cx)
 8056    });
 8057    editor.update_in(cx, |editor, window, cx| {
 8058        editor.set_text("one\ntwo\nthree\n", window, cx)
 8059    });
 8060
 8061    cx.executor().start_waiting();
 8062    let fake_server = fake_servers.next().await.unwrap();
 8063
 8064    let format = editor
 8065        .update_in(cx, |editor, window, cx| {
 8066            editor.perform_format(
 8067                project.clone(),
 8068                FormatTrigger::Manual,
 8069                FormatTarget::Buffers,
 8070                window,
 8071                cx,
 8072            )
 8073        })
 8074        .unwrap();
 8075    fake_server
 8076        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8077            assert_eq!(
 8078                params.text_document.uri,
 8079                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8080            );
 8081            assert_eq!(params.options.tab_size, 4);
 8082            Ok(Some(vec![lsp::TextEdit::new(
 8083                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8084                ", ".to_string(),
 8085            )]))
 8086        })
 8087        .next()
 8088        .await;
 8089    cx.executor().start_waiting();
 8090    format.await;
 8091    assert_eq!(
 8092        editor.update(cx, |editor, cx| editor.text(cx)),
 8093        "one, two\nthree\n"
 8094    );
 8095
 8096    editor.update_in(cx, |editor, window, cx| {
 8097        editor.set_text("one\ntwo\nthree\n", window, cx)
 8098    });
 8099    // Ensure we don't lock if formatting hangs.
 8100    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8101        assert_eq!(
 8102            params.text_document.uri,
 8103            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8104        );
 8105        futures::future::pending::<()>().await;
 8106        unreachable!()
 8107    });
 8108    let format = editor
 8109        .update_in(cx, |editor, window, cx| {
 8110            editor.perform_format(
 8111                project,
 8112                FormatTrigger::Manual,
 8113                FormatTarget::Buffers,
 8114                window,
 8115                cx,
 8116            )
 8117        })
 8118        .unwrap();
 8119    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8120    cx.executor().start_waiting();
 8121    format.await;
 8122    assert_eq!(
 8123        editor.update(cx, |editor, cx| editor.text(cx)),
 8124        "one\ntwo\nthree\n"
 8125    );
 8126}
 8127
 8128#[gpui::test]
 8129async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8130    init_test(cx, |settings| {
 8131        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8132            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8133        ))
 8134    });
 8135
 8136    let fs = FakeFs::new(cx.executor());
 8137    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8138
 8139    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8140
 8141    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8142    language_registry.add(Arc::new(Language::new(
 8143        LanguageConfig {
 8144            name: "TypeScript".into(),
 8145            matcher: LanguageMatcher {
 8146                path_suffixes: vec!["ts".to_string()],
 8147                ..Default::default()
 8148            },
 8149            ..LanguageConfig::default()
 8150        },
 8151        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8152    )));
 8153    update_test_language_settings(cx, |settings| {
 8154        settings.defaults.prettier = Some(PrettierSettings {
 8155            allowed: true,
 8156            ..PrettierSettings::default()
 8157        });
 8158    });
 8159    let mut fake_servers = language_registry.register_fake_lsp(
 8160        "TypeScript",
 8161        FakeLspAdapter {
 8162            capabilities: lsp::ServerCapabilities {
 8163                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8164                ..Default::default()
 8165            },
 8166            ..Default::default()
 8167        },
 8168    );
 8169
 8170    let buffer = project
 8171        .update(cx, |project, cx| {
 8172            project.open_local_buffer(path!("/file.ts"), cx)
 8173        })
 8174        .await
 8175        .unwrap();
 8176
 8177    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8178    let (editor, cx) = cx.add_window_view(|window, cx| {
 8179        build_editor_with_project(project.clone(), buffer, window, cx)
 8180    });
 8181    editor.update_in(cx, |editor, window, cx| {
 8182        editor.set_text(
 8183            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8184            window,
 8185            cx,
 8186        )
 8187    });
 8188
 8189    cx.executor().start_waiting();
 8190    let fake_server = fake_servers.next().await.unwrap();
 8191
 8192    let format = editor
 8193        .update_in(cx, |editor, window, cx| {
 8194            editor.perform_code_action_kind(
 8195                project.clone(),
 8196                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8197                window,
 8198                cx,
 8199            )
 8200        })
 8201        .unwrap();
 8202    fake_server
 8203        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8204            assert_eq!(
 8205                params.text_document.uri,
 8206                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8207            );
 8208            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8209                lsp::CodeAction {
 8210                    title: "Organize Imports".to_string(),
 8211                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8212                    edit: Some(lsp::WorkspaceEdit {
 8213                        changes: Some(
 8214                            [(
 8215                                params.text_document.uri.clone(),
 8216                                vec![lsp::TextEdit::new(
 8217                                    lsp::Range::new(
 8218                                        lsp::Position::new(1, 0),
 8219                                        lsp::Position::new(2, 0),
 8220                                    ),
 8221                                    "".to_string(),
 8222                                )],
 8223                            )]
 8224                            .into_iter()
 8225                            .collect(),
 8226                        ),
 8227                        ..Default::default()
 8228                    }),
 8229                    ..Default::default()
 8230                },
 8231            )]))
 8232        })
 8233        .next()
 8234        .await;
 8235    cx.executor().start_waiting();
 8236    format.await;
 8237    assert_eq!(
 8238        editor.update(cx, |editor, cx| editor.text(cx)),
 8239        "import { a } from 'module';\n\nconst x = a;\n"
 8240    );
 8241
 8242    editor.update_in(cx, |editor, window, cx| {
 8243        editor.set_text(
 8244            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8245            window,
 8246            cx,
 8247        )
 8248    });
 8249    // Ensure we don't lock if code action hangs.
 8250    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8251        move |params, _| async move {
 8252            assert_eq!(
 8253                params.text_document.uri,
 8254                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8255            );
 8256            futures::future::pending::<()>().await;
 8257            unreachable!()
 8258        },
 8259    );
 8260    let format = editor
 8261        .update_in(cx, |editor, window, cx| {
 8262            editor.perform_code_action_kind(
 8263                project,
 8264                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8265                window,
 8266                cx,
 8267            )
 8268        })
 8269        .unwrap();
 8270    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8271    cx.executor().start_waiting();
 8272    format.await;
 8273    assert_eq!(
 8274        editor.update(cx, |editor, cx| editor.text(cx)),
 8275        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8276    );
 8277}
 8278
 8279#[gpui::test]
 8280async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8281    init_test(cx, |_| {});
 8282
 8283    let mut cx = EditorLspTestContext::new_rust(
 8284        lsp::ServerCapabilities {
 8285            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8286            ..Default::default()
 8287        },
 8288        cx,
 8289    )
 8290    .await;
 8291
 8292    cx.set_state(indoc! {"
 8293        one.twoˇ
 8294    "});
 8295
 8296    // The format request takes a long time. When it completes, it inserts
 8297    // a newline and an indent before the `.`
 8298    cx.lsp
 8299        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8300            let executor = cx.background_executor().clone();
 8301            async move {
 8302                executor.timer(Duration::from_millis(100)).await;
 8303                Ok(Some(vec![lsp::TextEdit {
 8304                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8305                    new_text: "\n    ".into(),
 8306                }]))
 8307            }
 8308        });
 8309
 8310    // Submit a format request.
 8311    let format_1 = cx
 8312        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8313        .unwrap();
 8314    cx.executor().run_until_parked();
 8315
 8316    // Submit a second format request.
 8317    let format_2 = cx
 8318        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8319        .unwrap();
 8320    cx.executor().run_until_parked();
 8321
 8322    // Wait for both format requests to complete
 8323    cx.executor().advance_clock(Duration::from_millis(200));
 8324    cx.executor().start_waiting();
 8325    format_1.await.unwrap();
 8326    cx.executor().start_waiting();
 8327    format_2.await.unwrap();
 8328
 8329    // The formatting edits only happens once.
 8330    cx.assert_editor_state(indoc! {"
 8331        one
 8332            .twoˇ
 8333    "});
 8334}
 8335
 8336#[gpui::test]
 8337async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8338    init_test(cx, |settings| {
 8339        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8340    });
 8341
 8342    let mut cx = EditorLspTestContext::new_rust(
 8343        lsp::ServerCapabilities {
 8344            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8345            ..Default::default()
 8346        },
 8347        cx,
 8348    )
 8349    .await;
 8350
 8351    // Set up a buffer white some trailing whitespace and no trailing newline.
 8352    cx.set_state(
 8353        &[
 8354            "one ",   //
 8355            "twoˇ",   //
 8356            "three ", //
 8357            "four",   //
 8358        ]
 8359        .join("\n"),
 8360    );
 8361
 8362    // Submit a format request.
 8363    let format = cx
 8364        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8365        .unwrap();
 8366
 8367    // Record which buffer changes have been sent to the language server
 8368    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8369    cx.lsp
 8370        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8371            let buffer_changes = buffer_changes.clone();
 8372            move |params, _| {
 8373                buffer_changes.lock().extend(
 8374                    params
 8375                        .content_changes
 8376                        .into_iter()
 8377                        .map(|e| (e.range.unwrap(), e.text)),
 8378                );
 8379            }
 8380        });
 8381
 8382    // Handle formatting requests to the language server.
 8383    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8384        let buffer_changes = buffer_changes.clone();
 8385        move |_, _| {
 8386            // When formatting is requested, trailing whitespace has already been stripped,
 8387            // and the trailing newline has already been added.
 8388            assert_eq!(
 8389                &buffer_changes.lock()[1..],
 8390                &[
 8391                    (
 8392                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8393                        "".into()
 8394                    ),
 8395                    (
 8396                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8397                        "".into()
 8398                    ),
 8399                    (
 8400                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8401                        "\n".into()
 8402                    ),
 8403                ]
 8404            );
 8405
 8406            // Insert blank lines between each line of the buffer.
 8407            async move {
 8408                Ok(Some(vec![
 8409                    lsp::TextEdit {
 8410                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8411                        new_text: "\n".into(),
 8412                    },
 8413                    lsp::TextEdit {
 8414                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8415                        new_text: "\n".into(),
 8416                    },
 8417                ]))
 8418            }
 8419        }
 8420    });
 8421
 8422    // After formatting the buffer, the trailing whitespace is stripped,
 8423    // a newline is appended, and the edits provided by the language server
 8424    // have been applied.
 8425    format.await.unwrap();
 8426    cx.assert_editor_state(
 8427        &[
 8428            "one",   //
 8429            "",      //
 8430            "twoˇ",  //
 8431            "",      //
 8432            "three", //
 8433            "four",  //
 8434            "",      //
 8435        ]
 8436        .join("\n"),
 8437    );
 8438
 8439    // Undoing the formatting undoes the trailing whitespace removal, the
 8440    // trailing newline, and the LSP edits.
 8441    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8442    cx.assert_editor_state(
 8443        &[
 8444            "one ",   //
 8445            "twoˇ",   //
 8446            "three ", //
 8447            "four",   //
 8448        ]
 8449        .join("\n"),
 8450    );
 8451}
 8452
 8453#[gpui::test]
 8454async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8455    cx: &mut TestAppContext,
 8456) {
 8457    init_test(cx, |_| {});
 8458
 8459    cx.update(|cx| {
 8460        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8461            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8462                settings.auto_signature_help = Some(true);
 8463            });
 8464        });
 8465    });
 8466
 8467    let mut cx = EditorLspTestContext::new_rust(
 8468        lsp::ServerCapabilities {
 8469            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8470                ..Default::default()
 8471            }),
 8472            ..Default::default()
 8473        },
 8474        cx,
 8475    )
 8476    .await;
 8477
 8478    let language = Language::new(
 8479        LanguageConfig {
 8480            name: "Rust".into(),
 8481            brackets: BracketPairConfig {
 8482                pairs: vec![
 8483                    BracketPair {
 8484                        start: "{".to_string(),
 8485                        end: "}".to_string(),
 8486                        close: true,
 8487                        surround: true,
 8488                        newline: true,
 8489                    },
 8490                    BracketPair {
 8491                        start: "(".to_string(),
 8492                        end: ")".to_string(),
 8493                        close: true,
 8494                        surround: true,
 8495                        newline: true,
 8496                    },
 8497                    BracketPair {
 8498                        start: "/*".to_string(),
 8499                        end: " */".to_string(),
 8500                        close: true,
 8501                        surround: true,
 8502                        newline: true,
 8503                    },
 8504                    BracketPair {
 8505                        start: "[".to_string(),
 8506                        end: "]".to_string(),
 8507                        close: false,
 8508                        surround: false,
 8509                        newline: true,
 8510                    },
 8511                    BracketPair {
 8512                        start: "\"".to_string(),
 8513                        end: "\"".to_string(),
 8514                        close: true,
 8515                        surround: true,
 8516                        newline: false,
 8517                    },
 8518                    BracketPair {
 8519                        start: "<".to_string(),
 8520                        end: ">".to_string(),
 8521                        close: false,
 8522                        surround: true,
 8523                        newline: true,
 8524                    },
 8525                ],
 8526                ..Default::default()
 8527            },
 8528            autoclose_before: "})]".to_string(),
 8529            ..Default::default()
 8530        },
 8531        Some(tree_sitter_rust::LANGUAGE.into()),
 8532    );
 8533    let language = Arc::new(language);
 8534
 8535    cx.language_registry().add(language.clone());
 8536    cx.update_buffer(|buffer, cx| {
 8537        buffer.set_language(Some(language), cx);
 8538    });
 8539
 8540    cx.set_state(
 8541        &r#"
 8542            fn main() {
 8543                sampleˇ
 8544            }
 8545        "#
 8546        .unindent(),
 8547    );
 8548
 8549    cx.update_editor(|editor, window, cx| {
 8550        editor.handle_input("(", window, cx);
 8551    });
 8552    cx.assert_editor_state(
 8553        &"
 8554            fn main() {
 8555                sample(ˇ)
 8556            }
 8557        "
 8558        .unindent(),
 8559    );
 8560
 8561    let mocked_response = lsp::SignatureHelp {
 8562        signatures: vec![lsp::SignatureInformation {
 8563            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8564            documentation: None,
 8565            parameters: Some(vec![
 8566                lsp::ParameterInformation {
 8567                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8568                    documentation: None,
 8569                },
 8570                lsp::ParameterInformation {
 8571                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8572                    documentation: None,
 8573                },
 8574            ]),
 8575            active_parameter: None,
 8576        }],
 8577        active_signature: Some(0),
 8578        active_parameter: Some(0),
 8579    };
 8580    handle_signature_help_request(&mut cx, mocked_response).await;
 8581
 8582    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8583        .await;
 8584
 8585    cx.editor(|editor, _, _| {
 8586        let signature_help_state = editor.signature_help_state.popover().cloned();
 8587        assert_eq!(
 8588            signature_help_state.unwrap().label,
 8589            "param1: u8, param2: u8"
 8590        );
 8591    });
 8592}
 8593
 8594#[gpui::test]
 8595async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8596    init_test(cx, |_| {});
 8597
 8598    cx.update(|cx| {
 8599        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8600            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8601                settings.auto_signature_help = Some(false);
 8602                settings.show_signature_help_after_edits = Some(false);
 8603            });
 8604        });
 8605    });
 8606
 8607    let mut cx = EditorLspTestContext::new_rust(
 8608        lsp::ServerCapabilities {
 8609            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8610                ..Default::default()
 8611            }),
 8612            ..Default::default()
 8613        },
 8614        cx,
 8615    )
 8616    .await;
 8617
 8618    let language = Language::new(
 8619        LanguageConfig {
 8620            name: "Rust".into(),
 8621            brackets: BracketPairConfig {
 8622                pairs: vec![
 8623                    BracketPair {
 8624                        start: "{".to_string(),
 8625                        end: "}".to_string(),
 8626                        close: true,
 8627                        surround: true,
 8628                        newline: true,
 8629                    },
 8630                    BracketPair {
 8631                        start: "(".to_string(),
 8632                        end: ")".to_string(),
 8633                        close: true,
 8634                        surround: true,
 8635                        newline: true,
 8636                    },
 8637                    BracketPair {
 8638                        start: "/*".to_string(),
 8639                        end: " */".to_string(),
 8640                        close: true,
 8641                        surround: true,
 8642                        newline: true,
 8643                    },
 8644                    BracketPair {
 8645                        start: "[".to_string(),
 8646                        end: "]".to_string(),
 8647                        close: false,
 8648                        surround: false,
 8649                        newline: true,
 8650                    },
 8651                    BracketPair {
 8652                        start: "\"".to_string(),
 8653                        end: "\"".to_string(),
 8654                        close: true,
 8655                        surround: true,
 8656                        newline: false,
 8657                    },
 8658                    BracketPair {
 8659                        start: "<".to_string(),
 8660                        end: ">".to_string(),
 8661                        close: false,
 8662                        surround: true,
 8663                        newline: true,
 8664                    },
 8665                ],
 8666                ..Default::default()
 8667            },
 8668            autoclose_before: "})]".to_string(),
 8669            ..Default::default()
 8670        },
 8671        Some(tree_sitter_rust::LANGUAGE.into()),
 8672    );
 8673    let language = Arc::new(language);
 8674
 8675    cx.language_registry().add(language.clone());
 8676    cx.update_buffer(|buffer, cx| {
 8677        buffer.set_language(Some(language), cx);
 8678    });
 8679
 8680    // Ensure that signature_help is not called when no signature help is enabled.
 8681    cx.set_state(
 8682        &r#"
 8683            fn main() {
 8684                sampleˇ
 8685            }
 8686        "#
 8687        .unindent(),
 8688    );
 8689    cx.update_editor(|editor, window, cx| {
 8690        editor.handle_input("(", window, cx);
 8691    });
 8692    cx.assert_editor_state(
 8693        &"
 8694            fn main() {
 8695                sample(ˇ)
 8696            }
 8697        "
 8698        .unindent(),
 8699    );
 8700    cx.editor(|editor, _, _| {
 8701        assert!(editor.signature_help_state.task().is_none());
 8702    });
 8703
 8704    let mocked_response = lsp::SignatureHelp {
 8705        signatures: vec![lsp::SignatureInformation {
 8706            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8707            documentation: None,
 8708            parameters: Some(vec![
 8709                lsp::ParameterInformation {
 8710                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8711                    documentation: None,
 8712                },
 8713                lsp::ParameterInformation {
 8714                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8715                    documentation: None,
 8716                },
 8717            ]),
 8718            active_parameter: None,
 8719        }],
 8720        active_signature: Some(0),
 8721        active_parameter: Some(0),
 8722    };
 8723
 8724    // Ensure that signature_help is called when enabled afte edits
 8725    cx.update(|_, cx| {
 8726        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8727            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8728                settings.auto_signature_help = Some(false);
 8729                settings.show_signature_help_after_edits = Some(true);
 8730            });
 8731        });
 8732    });
 8733    cx.set_state(
 8734        &r#"
 8735            fn main() {
 8736                sampleˇ
 8737            }
 8738        "#
 8739        .unindent(),
 8740    );
 8741    cx.update_editor(|editor, window, cx| {
 8742        editor.handle_input("(", window, cx);
 8743    });
 8744    cx.assert_editor_state(
 8745        &"
 8746            fn main() {
 8747                sample(ˇ)
 8748            }
 8749        "
 8750        .unindent(),
 8751    );
 8752    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8753    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8754        .await;
 8755    cx.update_editor(|editor, _, _| {
 8756        let signature_help_state = editor.signature_help_state.popover().cloned();
 8757        assert!(signature_help_state.is_some());
 8758        assert_eq!(
 8759            signature_help_state.unwrap().label,
 8760            "param1: u8, param2: u8"
 8761        );
 8762        editor.signature_help_state = SignatureHelpState::default();
 8763    });
 8764
 8765    // Ensure that signature_help is called when auto signature help override is enabled
 8766    cx.update(|_, cx| {
 8767        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8768            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8769                settings.auto_signature_help = Some(true);
 8770                settings.show_signature_help_after_edits = Some(false);
 8771            });
 8772        });
 8773    });
 8774    cx.set_state(
 8775        &r#"
 8776            fn main() {
 8777                sampleˇ
 8778            }
 8779        "#
 8780        .unindent(),
 8781    );
 8782    cx.update_editor(|editor, window, cx| {
 8783        editor.handle_input("(", window, cx);
 8784    });
 8785    cx.assert_editor_state(
 8786        &"
 8787            fn main() {
 8788                sample(ˇ)
 8789            }
 8790        "
 8791        .unindent(),
 8792    );
 8793    handle_signature_help_request(&mut cx, mocked_response).await;
 8794    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8795        .await;
 8796    cx.editor(|editor, _, _| {
 8797        let signature_help_state = editor.signature_help_state.popover().cloned();
 8798        assert!(signature_help_state.is_some());
 8799        assert_eq!(
 8800            signature_help_state.unwrap().label,
 8801            "param1: u8, param2: u8"
 8802        );
 8803    });
 8804}
 8805
 8806#[gpui::test]
 8807async fn test_signature_help(cx: &mut TestAppContext) {
 8808    init_test(cx, |_| {});
 8809    cx.update(|cx| {
 8810        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8811            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8812                settings.auto_signature_help = Some(true);
 8813            });
 8814        });
 8815    });
 8816
 8817    let mut cx = EditorLspTestContext::new_rust(
 8818        lsp::ServerCapabilities {
 8819            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8820                ..Default::default()
 8821            }),
 8822            ..Default::default()
 8823        },
 8824        cx,
 8825    )
 8826    .await;
 8827
 8828    // A test that directly calls `show_signature_help`
 8829    cx.update_editor(|editor, window, cx| {
 8830        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8831    });
 8832
 8833    let mocked_response = lsp::SignatureHelp {
 8834        signatures: vec![lsp::SignatureInformation {
 8835            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8836            documentation: None,
 8837            parameters: Some(vec![
 8838                lsp::ParameterInformation {
 8839                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8840                    documentation: None,
 8841                },
 8842                lsp::ParameterInformation {
 8843                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8844                    documentation: None,
 8845                },
 8846            ]),
 8847            active_parameter: None,
 8848        }],
 8849        active_signature: Some(0),
 8850        active_parameter: Some(0),
 8851    };
 8852    handle_signature_help_request(&mut cx, mocked_response).await;
 8853
 8854    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8855        .await;
 8856
 8857    cx.editor(|editor, _, _| {
 8858        let signature_help_state = editor.signature_help_state.popover().cloned();
 8859        assert!(signature_help_state.is_some());
 8860        assert_eq!(
 8861            signature_help_state.unwrap().label,
 8862            "param1: u8, param2: u8"
 8863        );
 8864    });
 8865
 8866    // When exiting outside from inside the brackets, `signature_help` is closed.
 8867    cx.set_state(indoc! {"
 8868        fn main() {
 8869            sample(ˇ);
 8870        }
 8871
 8872        fn sample(param1: u8, param2: u8) {}
 8873    "});
 8874
 8875    cx.update_editor(|editor, window, cx| {
 8876        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8877    });
 8878
 8879    let mocked_response = lsp::SignatureHelp {
 8880        signatures: Vec::new(),
 8881        active_signature: None,
 8882        active_parameter: None,
 8883    };
 8884    handle_signature_help_request(&mut cx, mocked_response).await;
 8885
 8886    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8887        .await;
 8888
 8889    cx.editor(|editor, _, _| {
 8890        assert!(!editor.signature_help_state.is_shown());
 8891    });
 8892
 8893    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8894    cx.set_state(indoc! {"
 8895        fn main() {
 8896            sample(ˇ);
 8897        }
 8898
 8899        fn sample(param1: u8, param2: u8) {}
 8900    "});
 8901
 8902    let mocked_response = lsp::SignatureHelp {
 8903        signatures: vec![lsp::SignatureInformation {
 8904            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8905            documentation: None,
 8906            parameters: Some(vec![
 8907                lsp::ParameterInformation {
 8908                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8909                    documentation: None,
 8910                },
 8911                lsp::ParameterInformation {
 8912                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8913                    documentation: None,
 8914                },
 8915            ]),
 8916            active_parameter: None,
 8917        }],
 8918        active_signature: Some(0),
 8919        active_parameter: Some(0),
 8920    };
 8921    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8922    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8923        .await;
 8924    cx.editor(|editor, _, _| {
 8925        assert!(editor.signature_help_state.is_shown());
 8926    });
 8927
 8928    // Restore the popover with more parameter input
 8929    cx.set_state(indoc! {"
 8930        fn main() {
 8931            sample(param1, param2ˇ);
 8932        }
 8933
 8934        fn sample(param1: u8, param2: u8) {}
 8935    "});
 8936
 8937    let mocked_response = lsp::SignatureHelp {
 8938        signatures: vec![lsp::SignatureInformation {
 8939            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8940            documentation: None,
 8941            parameters: Some(vec![
 8942                lsp::ParameterInformation {
 8943                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8944                    documentation: None,
 8945                },
 8946                lsp::ParameterInformation {
 8947                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8948                    documentation: None,
 8949                },
 8950            ]),
 8951            active_parameter: None,
 8952        }],
 8953        active_signature: Some(0),
 8954        active_parameter: Some(1),
 8955    };
 8956    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8957    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8958        .await;
 8959
 8960    // When selecting a range, the popover is gone.
 8961    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8962    cx.update_editor(|editor, window, cx| {
 8963        editor.change_selections(None, window, cx, |s| {
 8964            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8965        })
 8966    });
 8967    cx.assert_editor_state(indoc! {"
 8968        fn main() {
 8969            sample(param1, «ˇparam2»);
 8970        }
 8971
 8972        fn sample(param1: u8, param2: u8) {}
 8973    "});
 8974    cx.editor(|editor, _, _| {
 8975        assert!(!editor.signature_help_state.is_shown());
 8976    });
 8977
 8978    // When unselecting again, the popover is back if within the brackets.
 8979    cx.update_editor(|editor, window, cx| {
 8980        editor.change_selections(None, window, cx, |s| {
 8981            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8982        })
 8983    });
 8984    cx.assert_editor_state(indoc! {"
 8985        fn main() {
 8986            sample(param1, ˇparam2);
 8987        }
 8988
 8989        fn sample(param1: u8, param2: u8) {}
 8990    "});
 8991    handle_signature_help_request(&mut cx, mocked_response).await;
 8992    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8993        .await;
 8994    cx.editor(|editor, _, _| {
 8995        assert!(editor.signature_help_state.is_shown());
 8996    });
 8997
 8998    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8999    cx.update_editor(|editor, window, cx| {
 9000        editor.change_selections(None, window, cx, |s| {
 9001            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9002            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9003        })
 9004    });
 9005    cx.assert_editor_state(indoc! {"
 9006        fn main() {
 9007            sample(param1, ˇparam2);
 9008        }
 9009
 9010        fn sample(param1: u8, param2: u8) {}
 9011    "});
 9012
 9013    let mocked_response = lsp::SignatureHelp {
 9014        signatures: vec![lsp::SignatureInformation {
 9015            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9016            documentation: None,
 9017            parameters: Some(vec![
 9018                lsp::ParameterInformation {
 9019                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9020                    documentation: None,
 9021                },
 9022                lsp::ParameterInformation {
 9023                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9024                    documentation: None,
 9025                },
 9026            ]),
 9027            active_parameter: None,
 9028        }],
 9029        active_signature: Some(0),
 9030        active_parameter: Some(1),
 9031    };
 9032    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9033    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9034        .await;
 9035    cx.update_editor(|editor, _, cx| {
 9036        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9037    });
 9038    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9039        .await;
 9040    cx.update_editor(|editor, window, cx| {
 9041        editor.change_selections(None, window, cx, |s| {
 9042            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9043        })
 9044    });
 9045    cx.assert_editor_state(indoc! {"
 9046        fn main() {
 9047            sample(param1, «ˇparam2»);
 9048        }
 9049
 9050        fn sample(param1: u8, param2: u8) {}
 9051    "});
 9052    cx.update_editor(|editor, window, cx| {
 9053        editor.change_selections(None, window, cx, |s| {
 9054            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9055        })
 9056    });
 9057    cx.assert_editor_state(indoc! {"
 9058        fn main() {
 9059            sample(param1, ˇparam2);
 9060        }
 9061
 9062        fn sample(param1: u8, param2: u8) {}
 9063    "});
 9064    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9065        .await;
 9066}
 9067
 9068#[gpui::test]
 9069async fn test_completion(cx: &mut TestAppContext) {
 9070    init_test(cx, |_| {});
 9071
 9072    let mut cx = EditorLspTestContext::new_rust(
 9073        lsp::ServerCapabilities {
 9074            completion_provider: Some(lsp::CompletionOptions {
 9075                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9076                resolve_provider: Some(true),
 9077                ..Default::default()
 9078            }),
 9079            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9080            ..Default::default()
 9081        },
 9082        cx,
 9083    )
 9084    .await;
 9085    let counter = Arc::new(AtomicUsize::new(0));
 9086
 9087    cx.set_state(indoc! {"
 9088        oneˇ
 9089        two
 9090        three
 9091    "});
 9092    cx.simulate_keystroke(".");
 9093    handle_completion_request(
 9094        &mut cx,
 9095        indoc! {"
 9096            one.|<>
 9097            two
 9098            three
 9099        "},
 9100        vec!["first_completion", "second_completion"],
 9101        counter.clone(),
 9102    )
 9103    .await;
 9104    cx.condition(|editor, _| editor.context_menu_visible())
 9105        .await;
 9106    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9107
 9108    let _handler = handle_signature_help_request(
 9109        &mut cx,
 9110        lsp::SignatureHelp {
 9111            signatures: vec![lsp::SignatureInformation {
 9112                label: "test signature".to_string(),
 9113                documentation: None,
 9114                parameters: Some(vec![lsp::ParameterInformation {
 9115                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9116                    documentation: None,
 9117                }]),
 9118                active_parameter: None,
 9119            }],
 9120            active_signature: None,
 9121            active_parameter: None,
 9122        },
 9123    );
 9124    cx.update_editor(|editor, window, cx| {
 9125        assert!(
 9126            !editor.signature_help_state.is_shown(),
 9127            "No signature help was called for"
 9128        );
 9129        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9130    });
 9131    cx.run_until_parked();
 9132    cx.update_editor(|editor, _, _| {
 9133        assert!(
 9134            !editor.signature_help_state.is_shown(),
 9135            "No signature help should be shown when completions menu is open"
 9136        );
 9137    });
 9138
 9139    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9140        editor.context_menu_next(&Default::default(), window, cx);
 9141        editor
 9142            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9143            .unwrap()
 9144    });
 9145    cx.assert_editor_state(indoc! {"
 9146        one.second_completionˇ
 9147        two
 9148        three
 9149    "});
 9150
 9151    handle_resolve_completion_request(
 9152        &mut cx,
 9153        Some(vec![
 9154            (
 9155                //This overlaps with the primary completion edit which is
 9156                //misbehavior from the LSP spec, test that we filter it out
 9157                indoc! {"
 9158                    one.second_ˇcompletion
 9159                    two
 9160                    threeˇ
 9161                "},
 9162                "overlapping additional edit",
 9163            ),
 9164            (
 9165                indoc! {"
 9166                    one.second_completion
 9167                    two
 9168                    threeˇ
 9169                "},
 9170                "\nadditional edit",
 9171            ),
 9172        ]),
 9173    )
 9174    .await;
 9175    apply_additional_edits.await.unwrap();
 9176    cx.assert_editor_state(indoc! {"
 9177        one.second_completionˇ
 9178        two
 9179        three
 9180        additional edit
 9181    "});
 9182
 9183    cx.set_state(indoc! {"
 9184        one.second_completion
 9185        twoˇ
 9186        threeˇ
 9187        additional edit
 9188    "});
 9189    cx.simulate_keystroke(" ");
 9190    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9191    cx.simulate_keystroke("s");
 9192    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9193
 9194    cx.assert_editor_state(indoc! {"
 9195        one.second_completion
 9196        two sˇ
 9197        three sˇ
 9198        additional edit
 9199    "});
 9200    handle_completion_request(
 9201        &mut cx,
 9202        indoc! {"
 9203            one.second_completion
 9204            two s
 9205            three <s|>
 9206            additional edit
 9207        "},
 9208        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9209        counter.clone(),
 9210    )
 9211    .await;
 9212    cx.condition(|editor, _| editor.context_menu_visible())
 9213        .await;
 9214    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9215
 9216    cx.simulate_keystroke("i");
 9217
 9218    handle_completion_request(
 9219        &mut cx,
 9220        indoc! {"
 9221            one.second_completion
 9222            two si
 9223            three <si|>
 9224            additional edit
 9225        "},
 9226        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9227        counter.clone(),
 9228    )
 9229    .await;
 9230    cx.condition(|editor, _| editor.context_menu_visible())
 9231        .await;
 9232    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9233
 9234    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9235        editor
 9236            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9237            .unwrap()
 9238    });
 9239    cx.assert_editor_state(indoc! {"
 9240        one.second_completion
 9241        two sixth_completionˇ
 9242        three sixth_completionˇ
 9243        additional edit
 9244    "});
 9245
 9246    apply_additional_edits.await.unwrap();
 9247
 9248    update_test_language_settings(&mut cx, |settings| {
 9249        settings.defaults.show_completions_on_input = Some(false);
 9250    });
 9251    cx.set_state("editorˇ");
 9252    cx.simulate_keystroke(".");
 9253    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9254    cx.simulate_keystroke("c");
 9255    cx.simulate_keystroke("l");
 9256    cx.simulate_keystroke("o");
 9257    cx.assert_editor_state("editor.cloˇ");
 9258    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9259    cx.update_editor(|editor, window, cx| {
 9260        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9261    });
 9262    handle_completion_request(
 9263        &mut cx,
 9264        "editor.<clo|>",
 9265        vec!["close", "clobber"],
 9266        counter.clone(),
 9267    )
 9268    .await;
 9269    cx.condition(|editor, _| editor.context_menu_visible())
 9270        .await;
 9271    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9272
 9273    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9274        editor
 9275            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9276            .unwrap()
 9277    });
 9278    cx.assert_editor_state("editor.closeˇ");
 9279    handle_resolve_completion_request(&mut cx, None).await;
 9280    apply_additional_edits.await.unwrap();
 9281}
 9282
 9283#[gpui::test]
 9284async fn test_word_completion(cx: &mut TestAppContext) {
 9285    let lsp_fetch_timeout_ms = 10;
 9286    init_test(cx, |language_settings| {
 9287        language_settings.defaults.completions = Some(CompletionSettings {
 9288            words: WordsCompletionMode::Fallback,
 9289            lsp: true,
 9290            lsp_fetch_timeout_ms: 10,
 9291        });
 9292    });
 9293
 9294    let mut cx = EditorLspTestContext::new_rust(
 9295        lsp::ServerCapabilities {
 9296            completion_provider: Some(lsp::CompletionOptions {
 9297                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9298                ..lsp::CompletionOptions::default()
 9299            }),
 9300            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9301            ..lsp::ServerCapabilities::default()
 9302        },
 9303        cx,
 9304    )
 9305    .await;
 9306
 9307    let throttle_completions = Arc::new(AtomicBool::new(false));
 9308
 9309    let lsp_throttle_completions = throttle_completions.clone();
 9310    let _completion_requests_handler =
 9311        cx.lsp
 9312            .server
 9313            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9314                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9315                let cx = cx.clone();
 9316                async move {
 9317                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9318                        cx.background_executor()
 9319                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9320                            .await;
 9321                    }
 9322                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9323                        lsp::CompletionItem {
 9324                            label: "first".into(),
 9325                            ..lsp::CompletionItem::default()
 9326                        },
 9327                        lsp::CompletionItem {
 9328                            label: "last".into(),
 9329                            ..lsp::CompletionItem::default()
 9330                        },
 9331                    ])))
 9332                }
 9333            });
 9334
 9335    cx.set_state(indoc! {"
 9336        oneˇ
 9337        two
 9338        three
 9339    "});
 9340    cx.simulate_keystroke(".");
 9341    cx.executor().run_until_parked();
 9342    cx.condition(|editor, _| editor.context_menu_visible())
 9343        .await;
 9344    cx.update_editor(|editor, window, cx| {
 9345        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9346        {
 9347            assert_eq!(
 9348                completion_menu_entries(&menu),
 9349                &["first", "last"],
 9350                "When LSP server is fast to reply, no fallback word completions are used"
 9351            );
 9352        } else {
 9353            panic!("expected completion menu to be open");
 9354        }
 9355        editor.cancel(&Cancel, window, cx);
 9356    });
 9357    cx.executor().run_until_parked();
 9358    cx.condition(|editor, _| !editor.context_menu_visible())
 9359        .await;
 9360
 9361    throttle_completions.store(true, atomic::Ordering::Release);
 9362    cx.simulate_keystroke(".");
 9363    cx.executor()
 9364        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9365    cx.executor().run_until_parked();
 9366    cx.condition(|editor, _| editor.context_menu_visible())
 9367        .await;
 9368    cx.update_editor(|editor, _, _| {
 9369        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9370        {
 9371            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9372                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9373        } else {
 9374            panic!("expected completion menu to be open");
 9375        }
 9376    });
 9377}
 9378
 9379#[gpui::test]
 9380async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9381    init_test(cx, |language_settings| {
 9382        language_settings.defaults.completions = Some(CompletionSettings {
 9383            words: WordsCompletionMode::Enabled,
 9384            lsp: true,
 9385            lsp_fetch_timeout_ms: 0,
 9386        });
 9387    });
 9388
 9389    let mut cx = EditorLspTestContext::new_rust(
 9390        lsp::ServerCapabilities {
 9391            completion_provider: Some(lsp::CompletionOptions {
 9392                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9393                ..lsp::CompletionOptions::default()
 9394            }),
 9395            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9396            ..lsp::ServerCapabilities::default()
 9397        },
 9398        cx,
 9399    )
 9400    .await;
 9401
 9402    let _completion_requests_handler =
 9403        cx.lsp
 9404            .server
 9405            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9406                Ok(Some(lsp::CompletionResponse::Array(vec![
 9407                    lsp::CompletionItem {
 9408                        label: "first".into(),
 9409                        ..lsp::CompletionItem::default()
 9410                    },
 9411                    lsp::CompletionItem {
 9412                        label: "last".into(),
 9413                        ..lsp::CompletionItem::default()
 9414                    },
 9415                ])))
 9416            });
 9417
 9418    cx.set_state(indoc! {"ˇ
 9419        first
 9420        last
 9421        second
 9422    "});
 9423    cx.simulate_keystroke(".");
 9424    cx.executor().run_until_parked();
 9425    cx.condition(|editor, _| editor.context_menu_visible())
 9426        .await;
 9427    cx.update_editor(|editor, _, _| {
 9428        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9429        {
 9430            assert_eq!(
 9431                completion_menu_entries(&menu),
 9432                &["first", "last", "second"],
 9433                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9434            );
 9435        } else {
 9436            panic!("expected completion menu to be open");
 9437        }
 9438    });
 9439}
 9440
 9441#[gpui::test]
 9442async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9443    init_test(cx, |language_settings| {
 9444        language_settings.defaults.completions = Some(CompletionSettings {
 9445            words: WordsCompletionMode::Disabled,
 9446            lsp: true,
 9447            lsp_fetch_timeout_ms: 0,
 9448        });
 9449    });
 9450
 9451    let mut cx = EditorLspTestContext::new_rust(
 9452        lsp::ServerCapabilities {
 9453            completion_provider: Some(lsp::CompletionOptions {
 9454                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9455                ..lsp::CompletionOptions::default()
 9456            }),
 9457            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9458            ..lsp::ServerCapabilities::default()
 9459        },
 9460        cx,
 9461    )
 9462    .await;
 9463
 9464    let _completion_requests_handler =
 9465        cx.lsp
 9466            .server
 9467            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9468                panic!("LSP completions should not be queried when dealing with word completions")
 9469            });
 9470
 9471    cx.set_state(indoc! {"ˇ
 9472        first
 9473        last
 9474        second
 9475    "});
 9476    cx.update_editor(|editor, window, cx| {
 9477        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9478    });
 9479    cx.executor().run_until_parked();
 9480    cx.condition(|editor, _| editor.context_menu_visible())
 9481        .await;
 9482    cx.update_editor(|editor, _, _| {
 9483        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9484        {
 9485            assert_eq!(
 9486                completion_menu_entries(&menu),
 9487                &["first", "last", "second"],
 9488                "`ShowWordCompletions` action should show word completions"
 9489            );
 9490        } else {
 9491            panic!("expected completion menu to be open");
 9492        }
 9493    });
 9494
 9495    cx.simulate_keystroke("l");
 9496    cx.executor().run_until_parked();
 9497    cx.condition(|editor, _| editor.context_menu_visible())
 9498        .await;
 9499    cx.update_editor(|editor, _, _| {
 9500        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9501        {
 9502            assert_eq!(
 9503                completion_menu_entries(&menu),
 9504                &["last"],
 9505                "After showing word completions, further editing should filter them and not query the LSP"
 9506            );
 9507        } else {
 9508            panic!("expected completion menu to be open");
 9509        }
 9510    });
 9511}
 9512
 9513#[gpui::test]
 9514async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9515    init_test(cx, |language_settings| {
 9516        language_settings.defaults.completions = Some(CompletionSettings {
 9517            words: WordsCompletionMode::Fallback,
 9518            lsp: false,
 9519            lsp_fetch_timeout_ms: 0,
 9520        });
 9521    });
 9522
 9523    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9524
 9525    cx.set_state(indoc! {"ˇ
 9526        0_usize
 9527        let
 9528        33
 9529        4.5f32
 9530    "});
 9531    cx.update_editor(|editor, window, cx| {
 9532        editor.show_completions(&ShowCompletions::default(), window, cx);
 9533    });
 9534    cx.executor().run_until_parked();
 9535    cx.condition(|editor, _| editor.context_menu_visible())
 9536        .await;
 9537    cx.update_editor(|editor, window, cx| {
 9538        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9539        {
 9540            assert_eq!(
 9541                completion_menu_entries(&menu),
 9542                &["let"],
 9543                "With no digits in the completion query, no digits should be in the word completions"
 9544            );
 9545        } else {
 9546            panic!("expected completion menu to be open");
 9547        }
 9548        editor.cancel(&Cancel, window, cx);
 9549    });
 9550
 9551    cx.set_state(indoc! {" 9552        0_usize
 9553        let
 9554        3
 9555        33.35f32
 9556    "});
 9557    cx.update_editor(|editor, window, cx| {
 9558        editor.show_completions(&ShowCompletions::default(), window, cx);
 9559    });
 9560    cx.executor().run_until_parked();
 9561    cx.condition(|editor, _| editor.context_menu_visible())
 9562        .await;
 9563    cx.update_editor(|editor, _, _| {
 9564        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9565        {
 9566            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9567                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9568        } else {
 9569            panic!("expected completion menu to be open");
 9570        }
 9571    });
 9572}
 9573
 9574#[gpui::test]
 9575async fn test_multiline_completion(cx: &mut TestAppContext) {
 9576    init_test(cx, |_| {});
 9577
 9578    let fs = FakeFs::new(cx.executor());
 9579    fs.insert_tree(
 9580        path!("/a"),
 9581        json!({
 9582            "main.ts": "a",
 9583        }),
 9584    )
 9585    .await;
 9586
 9587    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9588    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9589    let typescript_language = Arc::new(Language::new(
 9590        LanguageConfig {
 9591            name: "TypeScript".into(),
 9592            matcher: LanguageMatcher {
 9593                path_suffixes: vec!["ts".to_string()],
 9594                ..LanguageMatcher::default()
 9595            },
 9596            line_comments: vec!["// ".into()],
 9597            ..LanguageConfig::default()
 9598        },
 9599        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9600    ));
 9601    language_registry.add(typescript_language.clone());
 9602    let mut fake_servers = language_registry.register_fake_lsp(
 9603        "TypeScript",
 9604        FakeLspAdapter {
 9605            capabilities: lsp::ServerCapabilities {
 9606                completion_provider: Some(lsp::CompletionOptions {
 9607                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9608                    ..lsp::CompletionOptions::default()
 9609                }),
 9610                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9611                ..lsp::ServerCapabilities::default()
 9612            },
 9613            // Emulate vtsls label generation
 9614            label_for_completion: Some(Box::new(|item, _| {
 9615                let text = if let Some(description) = item
 9616                    .label_details
 9617                    .as_ref()
 9618                    .and_then(|label_details| label_details.description.as_ref())
 9619                {
 9620                    format!("{} {}", item.label, description)
 9621                } else if let Some(detail) = &item.detail {
 9622                    format!("{} {}", item.label, detail)
 9623                } else {
 9624                    item.label.clone()
 9625                };
 9626                let len = text.len();
 9627                Some(language::CodeLabel {
 9628                    text,
 9629                    runs: Vec::new(),
 9630                    filter_range: 0..len,
 9631                })
 9632            })),
 9633            ..FakeLspAdapter::default()
 9634        },
 9635    );
 9636    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9637    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9638    let worktree_id = workspace
 9639        .update(cx, |workspace, _window, cx| {
 9640            workspace.project().update(cx, |project, cx| {
 9641                project.worktrees(cx).next().unwrap().read(cx).id()
 9642            })
 9643        })
 9644        .unwrap();
 9645    let _buffer = project
 9646        .update(cx, |project, cx| {
 9647            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9648        })
 9649        .await
 9650        .unwrap();
 9651    let editor = workspace
 9652        .update(cx, |workspace, window, cx| {
 9653            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9654        })
 9655        .unwrap()
 9656        .await
 9657        .unwrap()
 9658        .downcast::<Editor>()
 9659        .unwrap();
 9660    let fake_server = fake_servers.next().await.unwrap();
 9661
 9662    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9663    let multiline_label_2 = "a\nb\nc\n";
 9664    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9665    let multiline_description = "d\ne\nf\n";
 9666    let multiline_detail_2 = "g\nh\ni\n";
 9667
 9668    let mut completion_handle =
 9669        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9670            Ok(Some(lsp::CompletionResponse::Array(vec![
 9671                lsp::CompletionItem {
 9672                    label: multiline_label.to_string(),
 9673                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9674                        range: lsp::Range {
 9675                            start: lsp::Position {
 9676                                line: params.text_document_position.position.line,
 9677                                character: params.text_document_position.position.character,
 9678                            },
 9679                            end: lsp::Position {
 9680                                line: params.text_document_position.position.line,
 9681                                character: params.text_document_position.position.character,
 9682                            },
 9683                        },
 9684                        new_text: "new_text_1".to_string(),
 9685                    })),
 9686                    ..lsp::CompletionItem::default()
 9687                },
 9688                lsp::CompletionItem {
 9689                    label: "single line label 1".to_string(),
 9690                    detail: Some(multiline_detail.to_string()),
 9691                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9692                        range: lsp::Range {
 9693                            start: lsp::Position {
 9694                                line: params.text_document_position.position.line,
 9695                                character: params.text_document_position.position.character,
 9696                            },
 9697                            end: lsp::Position {
 9698                                line: params.text_document_position.position.line,
 9699                                character: params.text_document_position.position.character,
 9700                            },
 9701                        },
 9702                        new_text: "new_text_2".to_string(),
 9703                    })),
 9704                    ..lsp::CompletionItem::default()
 9705                },
 9706                lsp::CompletionItem {
 9707                    label: "single line label 2".to_string(),
 9708                    label_details: Some(lsp::CompletionItemLabelDetails {
 9709                        description: Some(multiline_description.to_string()),
 9710                        detail: None,
 9711                    }),
 9712                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9713                        range: lsp::Range {
 9714                            start: lsp::Position {
 9715                                line: params.text_document_position.position.line,
 9716                                character: params.text_document_position.position.character,
 9717                            },
 9718                            end: lsp::Position {
 9719                                line: params.text_document_position.position.line,
 9720                                character: params.text_document_position.position.character,
 9721                            },
 9722                        },
 9723                        new_text: "new_text_2".to_string(),
 9724                    })),
 9725                    ..lsp::CompletionItem::default()
 9726                },
 9727                lsp::CompletionItem {
 9728                    label: multiline_label_2.to_string(),
 9729                    detail: Some(multiline_detail_2.to_string()),
 9730                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9731                        range: lsp::Range {
 9732                            start: lsp::Position {
 9733                                line: params.text_document_position.position.line,
 9734                                character: params.text_document_position.position.character,
 9735                            },
 9736                            end: lsp::Position {
 9737                                line: params.text_document_position.position.line,
 9738                                character: params.text_document_position.position.character,
 9739                            },
 9740                        },
 9741                        new_text: "new_text_3".to_string(),
 9742                    })),
 9743                    ..lsp::CompletionItem::default()
 9744                },
 9745                lsp::CompletionItem {
 9746                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9747                    detail: Some(
 9748                        "Details with many     spaces and \t but without newlines".to_string(),
 9749                    ),
 9750                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9751                        range: lsp::Range {
 9752                            start: lsp::Position {
 9753                                line: params.text_document_position.position.line,
 9754                                character: params.text_document_position.position.character,
 9755                            },
 9756                            end: lsp::Position {
 9757                                line: params.text_document_position.position.line,
 9758                                character: params.text_document_position.position.character,
 9759                            },
 9760                        },
 9761                        new_text: "new_text_4".to_string(),
 9762                    })),
 9763                    ..lsp::CompletionItem::default()
 9764                },
 9765            ])))
 9766        });
 9767
 9768    editor.update_in(cx, |editor, window, cx| {
 9769        cx.focus_self(window);
 9770        editor.move_to_end(&MoveToEnd, window, cx);
 9771        editor.handle_input(".", window, cx);
 9772    });
 9773    cx.run_until_parked();
 9774    completion_handle.next().await.unwrap();
 9775
 9776    editor.update(cx, |editor, _| {
 9777        assert!(editor.context_menu_visible());
 9778        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9779        {
 9780            let completion_labels = menu
 9781                .completions
 9782                .borrow()
 9783                .iter()
 9784                .map(|c| c.label.text.clone())
 9785                .collect::<Vec<_>>();
 9786            assert_eq!(
 9787                completion_labels,
 9788                &[
 9789                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9790                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9791                    "single line label 2 d e f ",
 9792                    "a b c g h i ",
 9793                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9794                ],
 9795                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9796            );
 9797
 9798            for completion in menu
 9799                .completions
 9800                .borrow()
 9801                .iter() {
 9802                    assert_eq!(
 9803                        completion.label.filter_range,
 9804                        0..completion.label.text.len(),
 9805                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9806                    );
 9807                }
 9808
 9809        } else {
 9810            panic!("expected completion menu to be open");
 9811        }
 9812    });
 9813}
 9814
 9815#[gpui::test]
 9816async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9817    init_test(cx, |_| {});
 9818    let mut cx = EditorLspTestContext::new_rust(
 9819        lsp::ServerCapabilities {
 9820            completion_provider: Some(lsp::CompletionOptions {
 9821                trigger_characters: Some(vec![".".to_string()]),
 9822                ..Default::default()
 9823            }),
 9824            ..Default::default()
 9825        },
 9826        cx,
 9827    )
 9828    .await;
 9829    cx.lsp
 9830        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9831            Ok(Some(lsp::CompletionResponse::Array(vec![
 9832                lsp::CompletionItem {
 9833                    label: "first".into(),
 9834                    ..Default::default()
 9835                },
 9836                lsp::CompletionItem {
 9837                    label: "last".into(),
 9838                    ..Default::default()
 9839                },
 9840            ])))
 9841        });
 9842    cx.set_state("variableˇ");
 9843    cx.simulate_keystroke(".");
 9844    cx.executor().run_until_parked();
 9845
 9846    cx.update_editor(|editor, _, _| {
 9847        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9848        {
 9849            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9850        } else {
 9851            panic!("expected completion menu to be open");
 9852        }
 9853    });
 9854
 9855    cx.update_editor(|editor, window, cx| {
 9856        editor.move_page_down(&MovePageDown::default(), window, cx);
 9857        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9858        {
 9859            assert!(
 9860                menu.selected_item == 1,
 9861                "expected PageDown to select the last item from the context menu"
 9862            );
 9863        } else {
 9864            panic!("expected completion menu to stay open after PageDown");
 9865        }
 9866    });
 9867
 9868    cx.update_editor(|editor, window, cx| {
 9869        editor.move_page_up(&MovePageUp::default(), window, cx);
 9870        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9871        {
 9872            assert!(
 9873                menu.selected_item == 0,
 9874                "expected PageUp to select the first item from the context menu"
 9875            );
 9876        } else {
 9877            panic!("expected completion menu to stay open after PageUp");
 9878        }
 9879    });
 9880}
 9881
 9882#[gpui::test]
 9883async fn test_completion_sort(cx: &mut TestAppContext) {
 9884    init_test(cx, |_| {});
 9885    let mut cx = EditorLspTestContext::new_rust(
 9886        lsp::ServerCapabilities {
 9887            completion_provider: Some(lsp::CompletionOptions {
 9888                trigger_characters: Some(vec![".".to_string()]),
 9889                ..Default::default()
 9890            }),
 9891            ..Default::default()
 9892        },
 9893        cx,
 9894    )
 9895    .await;
 9896    cx.lsp
 9897        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9898            Ok(Some(lsp::CompletionResponse::Array(vec![
 9899                lsp::CompletionItem {
 9900                    label: "Range".into(),
 9901                    sort_text: Some("a".into()),
 9902                    ..Default::default()
 9903                },
 9904                lsp::CompletionItem {
 9905                    label: "r".into(),
 9906                    sort_text: Some("b".into()),
 9907                    ..Default::default()
 9908                },
 9909                lsp::CompletionItem {
 9910                    label: "ret".into(),
 9911                    sort_text: Some("c".into()),
 9912                    ..Default::default()
 9913                },
 9914                lsp::CompletionItem {
 9915                    label: "return".into(),
 9916                    sort_text: Some("d".into()),
 9917                    ..Default::default()
 9918                },
 9919                lsp::CompletionItem {
 9920                    label: "slice".into(),
 9921                    sort_text: Some("d".into()),
 9922                    ..Default::default()
 9923                },
 9924            ])))
 9925        });
 9926    cx.set_state("");
 9927    cx.executor().run_until_parked();
 9928    cx.update_editor(|editor, window, cx| {
 9929        editor.show_completions(
 9930            &ShowCompletions {
 9931                trigger: Some("r".into()),
 9932            },
 9933            window,
 9934            cx,
 9935        );
 9936    });
 9937    cx.executor().run_until_parked();
 9938
 9939    cx.update_editor(|editor, _, _| {
 9940        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9941        {
 9942            assert_eq!(
 9943                completion_menu_entries(&menu),
 9944                &["r", "ret", "Range", "return"]
 9945            );
 9946        } else {
 9947            panic!("expected completion menu to be open");
 9948        }
 9949    });
 9950}
 9951
 9952#[gpui::test]
 9953async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9954    init_test(cx, |_| {});
 9955
 9956    let mut cx = EditorLspTestContext::new_rust(
 9957        lsp::ServerCapabilities {
 9958            completion_provider: Some(lsp::CompletionOptions {
 9959                trigger_characters: Some(vec![".".to_string()]),
 9960                resolve_provider: Some(true),
 9961                ..Default::default()
 9962            }),
 9963            ..Default::default()
 9964        },
 9965        cx,
 9966    )
 9967    .await;
 9968
 9969    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9970    cx.simulate_keystroke(".");
 9971    let completion_item = lsp::CompletionItem {
 9972        label: "Some".into(),
 9973        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9974        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9975        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9976            kind: lsp::MarkupKind::Markdown,
 9977            value: "```rust\nSome(2)\n```".to_string(),
 9978        })),
 9979        deprecated: Some(false),
 9980        sort_text: Some("Some".to_string()),
 9981        filter_text: Some("Some".to_string()),
 9982        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9983        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9984            range: lsp::Range {
 9985                start: lsp::Position {
 9986                    line: 0,
 9987                    character: 22,
 9988                },
 9989                end: lsp::Position {
 9990                    line: 0,
 9991                    character: 22,
 9992                },
 9993            },
 9994            new_text: "Some(2)".to_string(),
 9995        })),
 9996        additional_text_edits: Some(vec![lsp::TextEdit {
 9997            range: lsp::Range {
 9998                start: lsp::Position {
 9999                    line: 0,
10000                    character: 20,
10001                },
10002                end: lsp::Position {
10003                    line: 0,
10004                    character: 22,
10005                },
10006            },
10007            new_text: "".to_string(),
10008        }]),
10009        ..Default::default()
10010    };
10011
10012    let closure_completion_item = completion_item.clone();
10013    let counter = Arc::new(AtomicUsize::new(0));
10014    let counter_clone = counter.clone();
10015    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10016        let task_completion_item = closure_completion_item.clone();
10017        counter_clone.fetch_add(1, atomic::Ordering::Release);
10018        async move {
10019            Ok(Some(lsp::CompletionResponse::Array(vec![
10020                task_completion_item,
10021            ])))
10022        }
10023    });
10024
10025    cx.condition(|editor, _| editor.context_menu_visible())
10026        .await;
10027    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
10028    assert!(request.next().await.is_some());
10029    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10030
10031    cx.simulate_keystroke("S");
10032    cx.simulate_keystroke("o");
10033    cx.simulate_keystroke("m");
10034    cx.condition(|editor, _| editor.context_menu_visible())
10035        .await;
10036    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
10037    assert!(request.next().await.is_some());
10038    assert!(request.next().await.is_some());
10039    assert!(request.next().await.is_some());
10040    request.close();
10041    assert!(request.next().await.is_none());
10042    assert_eq!(
10043        counter.load(atomic::Ordering::Acquire),
10044        4,
10045        "With the completions menu open, only one LSP request should happen per input"
10046    );
10047}
10048
10049#[gpui::test]
10050async fn test_toggle_comment(cx: &mut TestAppContext) {
10051    init_test(cx, |_| {});
10052    let mut cx = EditorTestContext::new(cx).await;
10053    let language = Arc::new(Language::new(
10054        LanguageConfig {
10055            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10056            ..Default::default()
10057        },
10058        Some(tree_sitter_rust::LANGUAGE.into()),
10059    ));
10060    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10061
10062    // If multiple selections intersect a line, the line is only toggled once.
10063    cx.set_state(indoc! {"
10064        fn a() {
10065            «//b();
10066            ˇ»// «c();
10067            //ˇ»  d();
10068        }
10069    "});
10070
10071    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10072
10073    cx.assert_editor_state(indoc! {"
10074        fn a() {
10075            «b();
10076            c();
10077            ˇ» d();
10078        }
10079    "});
10080
10081    // The comment prefix is inserted at the same column for every line in a
10082    // selection.
10083    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10084
10085    cx.assert_editor_state(indoc! {"
10086        fn a() {
10087            // «b();
10088            // c();
10089            ˇ»//  d();
10090        }
10091    "});
10092
10093    // If a selection ends at the beginning of a line, that line is not toggled.
10094    cx.set_selections_state(indoc! {"
10095        fn a() {
10096            // b();
10097            «// c();
10098        ˇ»    //  d();
10099        }
10100    "});
10101
10102    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10103
10104    cx.assert_editor_state(indoc! {"
10105        fn a() {
10106            // b();
10107            «c();
10108        ˇ»    //  d();
10109        }
10110    "});
10111
10112    // If a selection span a single line and is empty, the line is toggled.
10113    cx.set_state(indoc! {"
10114        fn a() {
10115            a();
10116            b();
10117        ˇ
10118        }
10119    "});
10120
10121    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10122
10123    cx.assert_editor_state(indoc! {"
10124        fn a() {
10125            a();
10126            b();
10127        //•ˇ
10128        }
10129    "});
10130
10131    // If a selection span multiple lines, empty lines are not toggled.
10132    cx.set_state(indoc! {"
10133        fn a() {
10134            «a();
10135
10136            c();ˇ»
10137        }
10138    "});
10139
10140    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10141
10142    cx.assert_editor_state(indoc! {"
10143        fn a() {
10144            // «a();
10145
10146            // c();ˇ»
10147        }
10148    "});
10149
10150    // If a selection includes multiple comment prefixes, all lines are uncommented.
10151    cx.set_state(indoc! {"
10152        fn a() {
10153            «// a();
10154            /// b();
10155            //! c();ˇ»
10156        }
10157    "});
10158
10159    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10160
10161    cx.assert_editor_state(indoc! {"
10162        fn a() {
10163            «a();
10164            b();
10165            c();ˇ»
10166        }
10167    "});
10168}
10169
10170#[gpui::test]
10171async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10172    init_test(cx, |_| {});
10173    let mut cx = EditorTestContext::new(cx).await;
10174    let language = Arc::new(Language::new(
10175        LanguageConfig {
10176            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10177            ..Default::default()
10178        },
10179        Some(tree_sitter_rust::LANGUAGE.into()),
10180    ));
10181    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10182
10183    let toggle_comments = &ToggleComments {
10184        advance_downwards: false,
10185        ignore_indent: true,
10186    };
10187
10188    // If multiple selections intersect a line, the line is only toggled once.
10189    cx.set_state(indoc! {"
10190        fn a() {
10191        //    «b();
10192        //    c();
10193        //    ˇ» d();
10194        }
10195    "});
10196
10197    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10198
10199    cx.assert_editor_state(indoc! {"
10200        fn a() {
10201            «b();
10202            c();
10203            ˇ» d();
10204        }
10205    "});
10206
10207    // The comment prefix is inserted at the beginning of each line
10208    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10209
10210    cx.assert_editor_state(indoc! {"
10211        fn a() {
10212        //    «b();
10213        //    c();
10214        //    ˇ» d();
10215        }
10216    "});
10217
10218    // If a selection ends at the beginning of a line, that line is not toggled.
10219    cx.set_selections_state(indoc! {"
10220        fn a() {
10221        //    b();
10222        //    «c();
10223        ˇ»//     d();
10224        }
10225    "});
10226
10227    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10228
10229    cx.assert_editor_state(indoc! {"
10230        fn a() {
10231        //    b();
10232            «c();
10233        ˇ»//     d();
10234        }
10235    "});
10236
10237    // If a selection span a single line and is empty, the line is toggled.
10238    cx.set_state(indoc! {"
10239        fn a() {
10240            a();
10241            b();
10242        ˇ
10243        }
10244    "});
10245
10246    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10247
10248    cx.assert_editor_state(indoc! {"
10249        fn a() {
10250            a();
10251            b();
10252        //ˇ
10253        }
10254    "});
10255
10256    // If a selection span multiple lines, empty lines are not toggled.
10257    cx.set_state(indoc! {"
10258        fn a() {
10259            «a();
10260
10261            c();ˇ»
10262        }
10263    "});
10264
10265    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10266
10267    cx.assert_editor_state(indoc! {"
10268        fn a() {
10269        //    «a();
10270
10271        //    c();ˇ»
10272        }
10273    "});
10274
10275    // If a selection includes multiple comment prefixes, all lines are uncommented.
10276    cx.set_state(indoc! {"
10277        fn a() {
10278        //    «a();
10279        ///    b();
10280        //!    c();ˇ»
10281        }
10282    "});
10283
10284    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10285
10286    cx.assert_editor_state(indoc! {"
10287        fn a() {
10288            «a();
10289            b();
10290            c();ˇ»
10291        }
10292    "});
10293}
10294
10295#[gpui::test]
10296async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10297    init_test(cx, |_| {});
10298
10299    let language = Arc::new(Language::new(
10300        LanguageConfig {
10301            line_comments: vec!["// ".into()],
10302            ..Default::default()
10303        },
10304        Some(tree_sitter_rust::LANGUAGE.into()),
10305    ));
10306
10307    let mut cx = EditorTestContext::new(cx).await;
10308
10309    cx.language_registry().add(language.clone());
10310    cx.update_buffer(|buffer, cx| {
10311        buffer.set_language(Some(language), cx);
10312    });
10313
10314    let toggle_comments = &ToggleComments {
10315        advance_downwards: true,
10316        ignore_indent: false,
10317    };
10318
10319    // Single cursor on one line -> advance
10320    // Cursor moves horizontally 3 characters as well on non-blank line
10321    cx.set_state(indoc!(
10322        "fn a() {
10323             ˇdog();
10324             cat();
10325        }"
10326    ));
10327    cx.update_editor(|editor, window, cx| {
10328        editor.toggle_comments(toggle_comments, window, cx);
10329    });
10330    cx.assert_editor_state(indoc!(
10331        "fn a() {
10332             // dog();
10333             catˇ();
10334        }"
10335    ));
10336
10337    // Single selection on one line -> don't advance
10338    cx.set_state(indoc!(
10339        "fn a() {
10340             «dog()ˇ»;
10341             cat();
10342        }"
10343    ));
10344    cx.update_editor(|editor, window, cx| {
10345        editor.toggle_comments(toggle_comments, window, cx);
10346    });
10347    cx.assert_editor_state(indoc!(
10348        "fn a() {
10349             // «dog()ˇ»;
10350             cat();
10351        }"
10352    ));
10353
10354    // Multiple cursors on one line -> advance
10355    cx.set_state(indoc!(
10356        "fn a() {
10357             ˇdˇog();
10358             cat();
10359        }"
10360    ));
10361    cx.update_editor(|editor, window, cx| {
10362        editor.toggle_comments(toggle_comments, window, cx);
10363    });
10364    cx.assert_editor_state(indoc!(
10365        "fn a() {
10366             // dog();
10367             catˇ(ˇ);
10368        }"
10369    ));
10370
10371    // Multiple cursors on one line, with selection -> don't advance
10372    cx.set_state(indoc!(
10373        "fn a() {
10374             ˇdˇog«()ˇ»;
10375             cat();
10376        }"
10377    ));
10378    cx.update_editor(|editor, window, cx| {
10379        editor.toggle_comments(toggle_comments, window, cx);
10380    });
10381    cx.assert_editor_state(indoc!(
10382        "fn a() {
10383             // ˇdˇog«()ˇ»;
10384             cat();
10385        }"
10386    ));
10387
10388    // Single cursor on one line -> advance
10389    // Cursor moves to column 0 on blank line
10390    cx.set_state(indoc!(
10391        "fn a() {
10392             ˇdog();
10393
10394             cat();
10395        }"
10396    ));
10397    cx.update_editor(|editor, window, cx| {
10398        editor.toggle_comments(toggle_comments, window, cx);
10399    });
10400    cx.assert_editor_state(indoc!(
10401        "fn a() {
10402             // dog();
10403        ˇ
10404             cat();
10405        }"
10406    ));
10407
10408    // Single cursor on one line -> advance
10409    // Cursor starts and ends at column 0
10410    cx.set_state(indoc!(
10411        "fn a() {
10412         ˇ    dog();
10413             cat();
10414        }"
10415    ));
10416    cx.update_editor(|editor, window, cx| {
10417        editor.toggle_comments(toggle_comments, window, cx);
10418    });
10419    cx.assert_editor_state(indoc!(
10420        "fn a() {
10421             // dog();
10422         ˇ    cat();
10423        }"
10424    ));
10425}
10426
10427#[gpui::test]
10428async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10429    init_test(cx, |_| {});
10430
10431    let mut cx = EditorTestContext::new(cx).await;
10432
10433    let html_language = Arc::new(
10434        Language::new(
10435            LanguageConfig {
10436                name: "HTML".into(),
10437                block_comment: Some(("<!-- ".into(), " -->".into())),
10438                ..Default::default()
10439            },
10440            Some(tree_sitter_html::LANGUAGE.into()),
10441        )
10442        .with_injection_query(
10443            r#"
10444            (script_element
10445                (raw_text) @injection.content
10446                (#set! injection.language "javascript"))
10447            "#,
10448        )
10449        .unwrap(),
10450    );
10451
10452    let javascript_language = Arc::new(Language::new(
10453        LanguageConfig {
10454            name: "JavaScript".into(),
10455            line_comments: vec!["// ".into()],
10456            ..Default::default()
10457        },
10458        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10459    ));
10460
10461    cx.language_registry().add(html_language.clone());
10462    cx.language_registry().add(javascript_language.clone());
10463    cx.update_buffer(|buffer, cx| {
10464        buffer.set_language(Some(html_language), cx);
10465    });
10466
10467    // Toggle comments for empty selections
10468    cx.set_state(
10469        &r#"
10470            <p>A</p>ˇ
10471            <p>B</p>ˇ
10472            <p>C</p>ˇ
10473        "#
10474        .unindent(),
10475    );
10476    cx.update_editor(|editor, window, cx| {
10477        editor.toggle_comments(&ToggleComments::default(), window, cx)
10478    });
10479    cx.assert_editor_state(
10480        &r#"
10481            <!-- <p>A</p>ˇ -->
10482            <!-- <p>B</p>ˇ -->
10483            <!-- <p>C</p>ˇ -->
10484        "#
10485        .unindent(),
10486    );
10487    cx.update_editor(|editor, window, cx| {
10488        editor.toggle_comments(&ToggleComments::default(), window, cx)
10489    });
10490    cx.assert_editor_state(
10491        &r#"
10492            <p>A</p>ˇ
10493            <p>B</p>ˇ
10494            <p>C</p>ˇ
10495        "#
10496        .unindent(),
10497    );
10498
10499    // Toggle comments for mixture of empty and non-empty selections, where
10500    // multiple selections occupy a given line.
10501    cx.set_state(
10502        &r#"
10503            <p>A«</p>
10504            <p>ˇ»B</p>ˇ
10505            <p>C«</p>
10506            <p>ˇ»D</p>ˇ
10507        "#
10508        .unindent(),
10509    );
10510
10511    cx.update_editor(|editor, window, cx| {
10512        editor.toggle_comments(&ToggleComments::default(), window, cx)
10513    });
10514    cx.assert_editor_state(
10515        &r#"
10516            <!-- <p>A«</p>
10517            <p>ˇ»B</p>ˇ -->
10518            <!-- <p>C«</p>
10519            <p>ˇ»D</p>ˇ -->
10520        "#
10521        .unindent(),
10522    );
10523    cx.update_editor(|editor, window, cx| {
10524        editor.toggle_comments(&ToggleComments::default(), window, cx)
10525    });
10526    cx.assert_editor_state(
10527        &r#"
10528            <p>A«</p>
10529            <p>ˇ»B</p>ˇ
10530            <p>C«</p>
10531            <p>ˇ»D</p>ˇ
10532        "#
10533        .unindent(),
10534    );
10535
10536    // Toggle comments when different languages are active for different
10537    // selections.
10538    cx.set_state(
10539        &r#"
10540            ˇ<script>
10541                ˇvar x = new Y();
10542            ˇ</script>
10543        "#
10544        .unindent(),
10545    );
10546    cx.executor().run_until_parked();
10547    cx.update_editor(|editor, window, cx| {
10548        editor.toggle_comments(&ToggleComments::default(), window, cx)
10549    });
10550    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10551    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10552    cx.assert_editor_state(
10553        &r#"
10554            <!-- ˇ<script> -->
10555                // ˇvar x = new Y();
10556            <!-- ˇ</script> -->
10557        "#
10558        .unindent(),
10559    );
10560}
10561
10562#[gpui::test]
10563fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10564    init_test(cx, |_| {});
10565
10566    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10567    let multibuffer = cx.new(|cx| {
10568        let mut multibuffer = MultiBuffer::new(ReadWrite);
10569        multibuffer.push_excerpts(
10570            buffer.clone(),
10571            [
10572                ExcerptRange {
10573                    context: Point::new(0, 0)..Point::new(0, 4),
10574                    primary: None,
10575                },
10576                ExcerptRange {
10577                    context: Point::new(1, 0)..Point::new(1, 4),
10578                    primary: None,
10579                },
10580            ],
10581            cx,
10582        );
10583        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10584        multibuffer
10585    });
10586
10587    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10588    editor.update_in(cx, |editor, window, cx| {
10589        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10590        editor.change_selections(None, window, cx, |s| {
10591            s.select_ranges([
10592                Point::new(0, 0)..Point::new(0, 0),
10593                Point::new(1, 0)..Point::new(1, 0),
10594            ])
10595        });
10596
10597        editor.handle_input("X", window, cx);
10598        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10599        assert_eq!(
10600            editor.selections.ranges(cx),
10601            [
10602                Point::new(0, 1)..Point::new(0, 1),
10603                Point::new(1, 1)..Point::new(1, 1),
10604            ]
10605        );
10606
10607        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10608        editor.change_selections(None, window, cx, |s| {
10609            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10610        });
10611        editor.backspace(&Default::default(), window, cx);
10612        assert_eq!(editor.text(cx), "Xa\nbbb");
10613        assert_eq!(
10614            editor.selections.ranges(cx),
10615            [Point::new(1, 0)..Point::new(1, 0)]
10616        );
10617
10618        editor.change_selections(None, window, cx, |s| {
10619            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10620        });
10621        editor.backspace(&Default::default(), window, cx);
10622        assert_eq!(editor.text(cx), "X\nbb");
10623        assert_eq!(
10624            editor.selections.ranges(cx),
10625            [Point::new(0, 1)..Point::new(0, 1)]
10626        );
10627    });
10628}
10629
10630#[gpui::test]
10631fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10632    init_test(cx, |_| {});
10633
10634    let markers = vec![('[', ']').into(), ('(', ')').into()];
10635    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10636        indoc! {"
10637            [aaaa
10638            (bbbb]
10639            cccc)",
10640        },
10641        markers.clone(),
10642    );
10643    let excerpt_ranges = markers.into_iter().map(|marker| {
10644        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10645        ExcerptRange {
10646            context,
10647            primary: None,
10648        }
10649    });
10650    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10651    let multibuffer = cx.new(|cx| {
10652        let mut multibuffer = MultiBuffer::new(ReadWrite);
10653        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10654        multibuffer
10655    });
10656
10657    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10658    editor.update_in(cx, |editor, window, cx| {
10659        let (expected_text, selection_ranges) = marked_text_ranges(
10660            indoc! {"
10661                aaaa
10662                bˇbbb
10663                bˇbbˇb
10664                cccc"
10665            },
10666            true,
10667        );
10668        assert_eq!(editor.text(cx), expected_text);
10669        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10670
10671        editor.handle_input("X", window, cx);
10672
10673        let (expected_text, expected_selections) = marked_text_ranges(
10674            indoc! {"
10675                aaaa
10676                bXˇbbXb
10677                bXˇbbXˇb
10678                cccc"
10679            },
10680            false,
10681        );
10682        assert_eq!(editor.text(cx), expected_text);
10683        assert_eq!(editor.selections.ranges(cx), expected_selections);
10684
10685        editor.newline(&Newline, window, cx);
10686        let (expected_text, expected_selections) = marked_text_ranges(
10687            indoc! {"
10688                aaaa
10689                bX
10690                ˇbbX
10691                b
10692                bX
10693                ˇbbX
10694                ˇb
10695                cccc"
10696            },
10697            false,
10698        );
10699        assert_eq!(editor.text(cx), expected_text);
10700        assert_eq!(editor.selections.ranges(cx), expected_selections);
10701    });
10702}
10703
10704#[gpui::test]
10705fn test_refresh_selections(cx: &mut TestAppContext) {
10706    init_test(cx, |_| {});
10707
10708    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10709    let mut excerpt1_id = None;
10710    let multibuffer = cx.new(|cx| {
10711        let mut multibuffer = MultiBuffer::new(ReadWrite);
10712        excerpt1_id = multibuffer
10713            .push_excerpts(
10714                buffer.clone(),
10715                [
10716                    ExcerptRange {
10717                        context: Point::new(0, 0)..Point::new(1, 4),
10718                        primary: None,
10719                    },
10720                    ExcerptRange {
10721                        context: Point::new(1, 0)..Point::new(2, 4),
10722                        primary: None,
10723                    },
10724                ],
10725                cx,
10726            )
10727            .into_iter()
10728            .next();
10729        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10730        multibuffer
10731    });
10732
10733    let editor = cx.add_window(|window, cx| {
10734        let mut editor = build_editor(multibuffer.clone(), window, cx);
10735        let snapshot = editor.snapshot(window, cx);
10736        editor.change_selections(None, window, cx, |s| {
10737            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10738        });
10739        editor.begin_selection(
10740            Point::new(2, 1).to_display_point(&snapshot),
10741            true,
10742            1,
10743            window,
10744            cx,
10745        );
10746        assert_eq!(
10747            editor.selections.ranges(cx),
10748            [
10749                Point::new(1, 3)..Point::new(1, 3),
10750                Point::new(2, 1)..Point::new(2, 1),
10751            ]
10752        );
10753        editor
10754    });
10755
10756    // Refreshing selections is a no-op when excerpts haven't changed.
10757    _ = editor.update(cx, |editor, window, cx| {
10758        editor.change_selections(None, window, cx, |s| s.refresh());
10759        assert_eq!(
10760            editor.selections.ranges(cx),
10761            [
10762                Point::new(1, 3)..Point::new(1, 3),
10763                Point::new(2, 1)..Point::new(2, 1),
10764            ]
10765        );
10766    });
10767
10768    multibuffer.update(cx, |multibuffer, cx| {
10769        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10770    });
10771    _ = editor.update(cx, |editor, window, cx| {
10772        // Removing an excerpt causes the first selection to become degenerate.
10773        assert_eq!(
10774            editor.selections.ranges(cx),
10775            [
10776                Point::new(0, 0)..Point::new(0, 0),
10777                Point::new(0, 1)..Point::new(0, 1)
10778            ]
10779        );
10780
10781        // Refreshing selections will relocate the first selection to the original buffer
10782        // location.
10783        editor.change_selections(None, window, cx, |s| s.refresh());
10784        assert_eq!(
10785            editor.selections.ranges(cx),
10786            [
10787                Point::new(0, 1)..Point::new(0, 1),
10788                Point::new(0, 3)..Point::new(0, 3)
10789            ]
10790        );
10791        assert!(editor.selections.pending_anchor().is_some());
10792    });
10793}
10794
10795#[gpui::test]
10796fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10797    init_test(cx, |_| {});
10798
10799    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10800    let mut excerpt1_id = None;
10801    let multibuffer = cx.new(|cx| {
10802        let mut multibuffer = MultiBuffer::new(ReadWrite);
10803        excerpt1_id = multibuffer
10804            .push_excerpts(
10805                buffer.clone(),
10806                [
10807                    ExcerptRange {
10808                        context: Point::new(0, 0)..Point::new(1, 4),
10809                        primary: None,
10810                    },
10811                    ExcerptRange {
10812                        context: Point::new(1, 0)..Point::new(2, 4),
10813                        primary: None,
10814                    },
10815                ],
10816                cx,
10817            )
10818            .into_iter()
10819            .next();
10820        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10821        multibuffer
10822    });
10823
10824    let editor = cx.add_window(|window, cx| {
10825        let mut editor = build_editor(multibuffer.clone(), window, cx);
10826        let snapshot = editor.snapshot(window, cx);
10827        editor.begin_selection(
10828            Point::new(1, 3).to_display_point(&snapshot),
10829            false,
10830            1,
10831            window,
10832            cx,
10833        );
10834        assert_eq!(
10835            editor.selections.ranges(cx),
10836            [Point::new(1, 3)..Point::new(1, 3)]
10837        );
10838        editor
10839    });
10840
10841    multibuffer.update(cx, |multibuffer, cx| {
10842        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10843    });
10844    _ = editor.update(cx, |editor, window, cx| {
10845        assert_eq!(
10846            editor.selections.ranges(cx),
10847            [Point::new(0, 0)..Point::new(0, 0)]
10848        );
10849
10850        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10851        editor.change_selections(None, window, cx, |s| s.refresh());
10852        assert_eq!(
10853            editor.selections.ranges(cx),
10854            [Point::new(0, 3)..Point::new(0, 3)]
10855        );
10856        assert!(editor.selections.pending_anchor().is_some());
10857    });
10858}
10859
10860#[gpui::test]
10861async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10862    init_test(cx, |_| {});
10863
10864    let language = Arc::new(
10865        Language::new(
10866            LanguageConfig {
10867                brackets: BracketPairConfig {
10868                    pairs: vec![
10869                        BracketPair {
10870                            start: "{".to_string(),
10871                            end: "}".to_string(),
10872                            close: true,
10873                            surround: true,
10874                            newline: true,
10875                        },
10876                        BracketPair {
10877                            start: "/* ".to_string(),
10878                            end: " */".to_string(),
10879                            close: true,
10880                            surround: true,
10881                            newline: true,
10882                        },
10883                    ],
10884                    ..Default::default()
10885                },
10886                ..Default::default()
10887            },
10888            Some(tree_sitter_rust::LANGUAGE.into()),
10889        )
10890        .with_indents_query("")
10891        .unwrap(),
10892    );
10893
10894    let text = concat!(
10895        "{   }\n",     //
10896        "  x\n",       //
10897        "  /*   */\n", //
10898        "x\n",         //
10899        "{{} }\n",     //
10900    );
10901
10902    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10903    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10904    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10905    editor
10906        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10907        .await;
10908
10909    editor.update_in(cx, |editor, window, cx| {
10910        editor.change_selections(None, window, cx, |s| {
10911            s.select_display_ranges([
10912                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10913                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10914                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10915            ])
10916        });
10917        editor.newline(&Newline, window, cx);
10918
10919        assert_eq!(
10920            editor.buffer().read(cx).read(cx).text(),
10921            concat!(
10922                "{ \n",    // Suppress rustfmt
10923                "\n",      //
10924                "}\n",     //
10925                "  x\n",   //
10926                "  /* \n", //
10927                "  \n",    //
10928                "  */\n",  //
10929                "x\n",     //
10930                "{{} \n",  //
10931                "}\n",     //
10932            )
10933        );
10934    });
10935}
10936
10937#[gpui::test]
10938fn test_highlighted_ranges(cx: &mut TestAppContext) {
10939    init_test(cx, |_| {});
10940
10941    let editor = cx.add_window(|window, cx| {
10942        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10943        build_editor(buffer.clone(), window, cx)
10944    });
10945
10946    _ = editor.update(cx, |editor, window, cx| {
10947        struct Type1;
10948        struct Type2;
10949
10950        let buffer = editor.buffer.read(cx).snapshot(cx);
10951
10952        let anchor_range =
10953            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10954
10955        editor.highlight_background::<Type1>(
10956            &[
10957                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10958                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10959                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10960                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10961            ],
10962            |_| Hsla::red(),
10963            cx,
10964        );
10965        editor.highlight_background::<Type2>(
10966            &[
10967                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10968                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10969                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10970                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10971            ],
10972            |_| Hsla::green(),
10973            cx,
10974        );
10975
10976        let snapshot = editor.snapshot(window, cx);
10977        let mut highlighted_ranges = editor.background_highlights_in_range(
10978            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10979            &snapshot,
10980            cx.theme().colors(),
10981        );
10982        // Enforce a consistent ordering based on color without relying on the ordering of the
10983        // highlight's `TypeId` which is non-executor.
10984        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10985        assert_eq!(
10986            highlighted_ranges,
10987            &[
10988                (
10989                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10990                    Hsla::red(),
10991                ),
10992                (
10993                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10994                    Hsla::red(),
10995                ),
10996                (
10997                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10998                    Hsla::green(),
10999                ),
11000                (
11001                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11002                    Hsla::green(),
11003                ),
11004            ]
11005        );
11006        assert_eq!(
11007            editor.background_highlights_in_range(
11008                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11009                &snapshot,
11010                cx.theme().colors(),
11011            ),
11012            &[(
11013                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11014                Hsla::red(),
11015            )]
11016        );
11017    });
11018}
11019
11020#[gpui::test]
11021async fn test_following(cx: &mut TestAppContext) {
11022    init_test(cx, |_| {});
11023
11024    let fs = FakeFs::new(cx.executor());
11025    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11026
11027    let buffer = project.update(cx, |project, cx| {
11028        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11029        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11030    });
11031    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11032    let follower = cx.update(|cx| {
11033        cx.open_window(
11034            WindowOptions {
11035                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11036                    gpui::Point::new(px(0.), px(0.)),
11037                    gpui::Point::new(px(10.), px(80.)),
11038                ))),
11039                ..Default::default()
11040            },
11041            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11042        )
11043        .unwrap()
11044    });
11045
11046    let is_still_following = Rc::new(RefCell::new(true));
11047    let follower_edit_event_count = Rc::new(RefCell::new(0));
11048    let pending_update = Rc::new(RefCell::new(None));
11049    let leader_entity = leader.root(cx).unwrap();
11050    let follower_entity = follower.root(cx).unwrap();
11051    _ = follower.update(cx, {
11052        let update = pending_update.clone();
11053        let is_still_following = is_still_following.clone();
11054        let follower_edit_event_count = follower_edit_event_count.clone();
11055        |_, window, cx| {
11056            cx.subscribe_in(
11057                &leader_entity,
11058                window,
11059                move |_, leader, event, window, cx| {
11060                    leader.read(cx).add_event_to_update_proto(
11061                        event,
11062                        &mut update.borrow_mut(),
11063                        window,
11064                        cx,
11065                    );
11066                },
11067            )
11068            .detach();
11069
11070            cx.subscribe_in(
11071                &follower_entity,
11072                window,
11073                move |_, _, event: &EditorEvent, _window, _cx| {
11074                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11075                        *is_still_following.borrow_mut() = false;
11076                    }
11077
11078                    if let EditorEvent::BufferEdited = event {
11079                        *follower_edit_event_count.borrow_mut() += 1;
11080                    }
11081                },
11082            )
11083            .detach();
11084        }
11085    });
11086
11087    // Update the selections only
11088    _ = leader.update(cx, |leader, window, cx| {
11089        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11090    });
11091    follower
11092        .update(cx, |follower, window, cx| {
11093            follower.apply_update_proto(
11094                &project,
11095                pending_update.borrow_mut().take().unwrap(),
11096                window,
11097                cx,
11098            )
11099        })
11100        .unwrap()
11101        .await
11102        .unwrap();
11103    _ = follower.update(cx, |follower, _, cx| {
11104        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11105    });
11106    assert!(*is_still_following.borrow());
11107    assert_eq!(*follower_edit_event_count.borrow(), 0);
11108
11109    // Update the scroll position only
11110    _ = leader.update(cx, |leader, window, cx| {
11111        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11112    });
11113    follower
11114        .update(cx, |follower, window, cx| {
11115            follower.apply_update_proto(
11116                &project,
11117                pending_update.borrow_mut().take().unwrap(),
11118                window,
11119                cx,
11120            )
11121        })
11122        .unwrap()
11123        .await
11124        .unwrap();
11125    assert_eq!(
11126        follower
11127            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11128            .unwrap(),
11129        gpui::Point::new(1.5, 3.5)
11130    );
11131    assert!(*is_still_following.borrow());
11132    assert_eq!(*follower_edit_event_count.borrow(), 0);
11133
11134    // Update the selections and scroll position. The follower's scroll position is updated
11135    // via autoscroll, not via the leader's exact scroll position.
11136    _ = leader.update(cx, |leader, window, cx| {
11137        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11138        leader.request_autoscroll(Autoscroll::newest(), cx);
11139        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11140    });
11141    follower
11142        .update(cx, |follower, window, cx| {
11143            follower.apply_update_proto(
11144                &project,
11145                pending_update.borrow_mut().take().unwrap(),
11146                window,
11147                cx,
11148            )
11149        })
11150        .unwrap()
11151        .await
11152        .unwrap();
11153    _ = follower.update(cx, |follower, _, cx| {
11154        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11155        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11156    });
11157    assert!(*is_still_following.borrow());
11158
11159    // Creating a pending selection that precedes another selection
11160    _ = leader.update(cx, |leader, window, cx| {
11161        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11162        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11163    });
11164    follower
11165        .update(cx, |follower, window, cx| {
11166            follower.apply_update_proto(
11167                &project,
11168                pending_update.borrow_mut().take().unwrap(),
11169                window,
11170                cx,
11171            )
11172        })
11173        .unwrap()
11174        .await
11175        .unwrap();
11176    _ = follower.update(cx, |follower, _, cx| {
11177        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11178    });
11179    assert!(*is_still_following.borrow());
11180
11181    // Extend the pending selection so that it surrounds another selection
11182    _ = leader.update(cx, |leader, window, cx| {
11183        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11184    });
11185    follower
11186        .update(cx, |follower, window, cx| {
11187            follower.apply_update_proto(
11188                &project,
11189                pending_update.borrow_mut().take().unwrap(),
11190                window,
11191                cx,
11192            )
11193        })
11194        .unwrap()
11195        .await
11196        .unwrap();
11197    _ = follower.update(cx, |follower, _, cx| {
11198        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11199    });
11200
11201    // Scrolling locally breaks the follow
11202    _ = follower.update(cx, |follower, window, cx| {
11203        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11204        follower.set_scroll_anchor(
11205            ScrollAnchor {
11206                anchor: top_anchor,
11207                offset: gpui::Point::new(0.0, 0.5),
11208            },
11209            window,
11210            cx,
11211        );
11212    });
11213    assert!(!(*is_still_following.borrow()));
11214}
11215
11216#[gpui::test]
11217async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11218    init_test(cx, |_| {});
11219
11220    let fs = FakeFs::new(cx.executor());
11221    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11222    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11223    let pane = workspace
11224        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11225        .unwrap();
11226
11227    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11228
11229    let leader = pane.update_in(cx, |_, window, cx| {
11230        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11231        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11232    });
11233
11234    // Start following the editor when it has no excerpts.
11235    let mut state_message =
11236        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11237    let workspace_entity = workspace.root(cx).unwrap();
11238    let follower_1 = cx
11239        .update_window(*workspace.deref(), |_, window, cx| {
11240            Editor::from_state_proto(
11241                workspace_entity,
11242                ViewId {
11243                    creator: Default::default(),
11244                    id: 0,
11245                },
11246                &mut state_message,
11247                window,
11248                cx,
11249            )
11250        })
11251        .unwrap()
11252        .unwrap()
11253        .await
11254        .unwrap();
11255
11256    let update_message = Rc::new(RefCell::new(None));
11257    follower_1.update_in(cx, {
11258        let update = update_message.clone();
11259        |_, window, cx| {
11260            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11261                leader.read(cx).add_event_to_update_proto(
11262                    event,
11263                    &mut update.borrow_mut(),
11264                    window,
11265                    cx,
11266                );
11267            })
11268            .detach();
11269        }
11270    });
11271
11272    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11273        (
11274            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11275            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11276        )
11277    });
11278
11279    // Insert some excerpts.
11280    leader.update(cx, |leader, cx| {
11281        leader.buffer.update(cx, |multibuffer, cx| {
11282            let excerpt_ids = multibuffer.push_excerpts(
11283                buffer_1.clone(),
11284                [
11285                    ExcerptRange {
11286                        context: 1..6,
11287                        primary: None,
11288                    },
11289                    ExcerptRange {
11290                        context: 12..15,
11291                        primary: None,
11292                    },
11293                    ExcerptRange {
11294                        context: 0..3,
11295                        primary: None,
11296                    },
11297                ],
11298                cx,
11299            );
11300            multibuffer.insert_excerpts_after(
11301                excerpt_ids[0],
11302                buffer_2.clone(),
11303                [
11304                    ExcerptRange {
11305                        context: 8..12,
11306                        primary: None,
11307                    },
11308                    ExcerptRange {
11309                        context: 0..6,
11310                        primary: None,
11311                    },
11312                ],
11313                cx,
11314            );
11315        });
11316    });
11317
11318    // Apply the update of adding the excerpts.
11319    follower_1
11320        .update_in(cx, |follower, window, cx| {
11321            follower.apply_update_proto(
11322                &project,
11323                update_message.borrow().clone().unwrap(),
11324                window,
11325                cx,
11326            )
11327        })
11328        .await
11329        .unwrap();
11330    assert_eq!(
11331        follower_1.update(cx, |editor, cx| editor.text(cx)),
11332        leader.update(cx, |editor, cx| editor.text(cx))
11333    );
11334    update_message.borrow_mut().take();
11335
11336    // Start following separately after it already has excerpts.
11337    let mut state_message =
11338        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11339    let workspace_entity = workspace.root(cx).unwrap();
11340    let follower_2 = cx
11341        .update_window(*workspace.deref(), |_, window, cx| {
11342            Editor::from_state_proto(
11343                workspace_entity,
11344                ViewId {
11345                    creator: Default::default(),
11346                    id: 0,
11347                },
11348                &mut state_message,
11349                window,
11350                cx,
11351            )
11352        })
11353        .unwrap()
11354        .unwrap()
11355        .await
11356        .unwrap();
11357    assert_eq!(
11358        follower_2.update(cx, |editor, cx| editor.text(cx)),
11359        leader.update(cx, |editor, cx| editor.text(cx))
11360    );
11361
11362    // Remove some excerpts.
11363    leader.update(cx, |leader, cx| {
11364        leader.buffer.update(cx, |multibuffer, cx| {
11365            let excerpt_ids = multibuffer.excerpt_ids();
11366            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11367            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11368        });
11369    });
11370
11371    // Apply the update of removing the excerpts.
11372    follower_1
11373        .update_in(cx, |follower, window, cx| {
11374            follower.apply_update_proto(
11375                &project,
11376                update_message.borrow().clone().unwrap(),
11377                window,
11378                cx,
11379            )
11380        })
11381        .await
11382        .unwrap();
11383    follower_2
11384        .update_in(cx, |follower, window, cx| {
11385            follower.apply_update_proto(
11386                &project,
11387                update_message.borrow().clone().unwrap(),
11388                window,
11389                cx,
11390            )
11391        })
11392        .await
11393        .unwrap();
11394    update_message.borrow_mut().take();
11395    assert_eq!(
11396        follower_1.update(cx, |editor, cx| editor.text(cx)),
11397        leader.update(cx, |editor, cx| editor.text(cx))
11398    );
11399}
11400
11401#[gpui::test]
11402async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11403    init_test(cx, |_| {});
11404
11405    let mut cx = EditorTestContext::new(cx).await;
11406    let lsp_store =
11407        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11408
11409    cx.set_state(indoc! {"
11410        ˇfn func(abc def: i32) -> u32 {
11411        }
11412    "});
11413
11414    cx.update(|_, cx| {
11415        lsp_store.update(cx, |lsp_store, cx| {
11416            lsp_store
11417                .update_diagnostics(
11418                    LanguageServerId(0),
11419                    lsp::PublishDiagnosticsParams {
11420                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11421                        version: None,
11422                        diagnostics: vec![
11423                            lsp::Diagnostic {
11424                                range: lsp::Range::new(
11425                                    lsp::Position::new(0, 11),
11426                                    lsp::Position::new(0, 12),
11427                                ),
11428                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11429                                ..Default::default()
11430                            },
11431                            lsp::Diagnostic {
11432                                range: lsp::Range::new(
11433                                    lsp::Position::new(0, 12),
11434                                    lsp::Position::new(0, 15),
11435                                ),
11436                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11437                                ..Default::default()
11438                            },
11439                            lsp::Diagnostic {
11440                                range: lsp::Range::new(
11441                                    lsp::Position::new(0, 25),
11442                                    lsp::Position::new(0, 28),
11443                                ),
11444                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11445                                ..Default::default()
11446                            },
11447                        ],
11448                    },
11449                    &[],
11450                    cx,
11451                )
11452                .unwrap()
11453        });
11454    });
11455
11456    executor.run_until_parked();
11457
11458    cx.update_editor(|editor, window, cx| {
11459        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11460    });
11461
11462    cx.assert_editor_state(indoc! {"
11463        fn func(abc def: i32) -> ˇu32 {
11464        }
11465    "});
11466
11467    cx.update_editor(|editor, window, cx| {
11468        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11469    });
11470
11471    cx.assert_editor_state(indoc! {"
11472        fn func(abc ˇdef: i32) -> u32 {
11473        }
11474    "});
11475
11476    cx.update_editor(|editor, window, cx| {
11477        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11478    });
11479
11480    cx.assert_editor_state(indoc! {"
11481        fn func(abcˇ def: i32) -> u32 {
11482        }
11483    "});
11484
11485    cx.update_editor(|editor, window, cx| {
11486        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11487    });
11488
11489    cx.assert_editor_state(indoc! {"
11490        fn func(abc def: i32) -> ˇu32 {
11491        }
11492    "});
11493}
11494
11495#[gpui::test]
11496async fn cycle_through_same_place_diagnostics(
11497    executor: BackgroundExecutor,
11498    cx: &mut TestAppContext,
11499) {
11500    init_test(cx, |_| {});
11501
11502    let mut cx = EditorTestContext::new(cx).await;
11503    let lsp_store =
11504        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11505
11506    cx.set_state(indoc! {"
11507        ˇfn func(abc def: i32) -> u32 {
11508        }
11509    "});
11510
11511    cx.update(|_, cx| {
11512        lsp_store.update(cx, |lsp_store, cx| {
11513            lsp_store
11514                .update_diagnostics(
11515                    LanguageServerId(0),
11516                    lsp::PublishDiagnosticsParams {
11517                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11518                        version: None,
11519                        diagnostics: vec![
11520                            lsp::Diagnostic {
11521                                range: lsp::Range::new(
11522                                    lsp::Position::new(0, 11),
11523                                    lsp::Position::new(0, 12),
11524                                ),
11525                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11526                                ..Default::default()
11527                            },
11528                            lsp::Diagnostic {
11529                                range: lsp::Range::new(
11530                                    lsp::Position::new(0, 12),
11531                                    lsp::Position::new(0, 15),
11532                                ),
11533                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11534                                ..Default::default()
11535                            },
11536                            lsp::Diagnostic {
11537                                range: lsp::Range::new(
11538                                    lsp::Position::new(0, 12),
11539                                    lsp::Position::new(0, 15),
11540                                ),
11541                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11542                                ..Default::default()
11543                            },
11544                            lsp::Diagnostic {
11545                                range: lsp::Range::new(
11546                                    lsp::Position::new(0, 25),
11547                                    lsp::Position::new(0, 28),
11548                                ),
11549                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11550                                ..Default::default()
11551                            },
11552                        ],
11553                    },
11554                    &[],
11555                    cx,
11556                )
11557                .unwrap()
11558        });
11559    });
11560    executor.run_until_parked();
11561
11562    //// Backward
11563
11564    // Fourth diagnostic
11565    cx.update_editor(|editor, window, cx| {
11566        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11567    });
11568    cx.assert_editor_state(indoc! {"
11569        fn func(abc def: i32) -> ˇu32 {
11570        }
11571    "});
11572
11573    // Third diagnostic
11574    cx.update_editor(|editor, window, cx| {
11575        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11576    });
11577    cx.assert_editor_state(indoc! {"
11578        fn func(abc ˇdef: i32) -> u32 {
11579        }
11580    "});
11581
11582    // Second diagnostic, same place
11583    cx.update_editor(|editor, window, cx| {
11584        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11585    });
11586    cx.assert_editor_state(indoc! {"
11587        fn func(abc ˇdef: i32) -> u32 {
11588        }
11589    "});
11590
11591    // First diagnostic
11592    cx.update_editor(|editor, window, cx| {
11593        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11594    });
11595    cx.assert_editor_state(indoc! {"
11596        fn func(abcˇ def: i32) -> u32 {
11597        }
11598    "});
11599
11600    // Wrapped over, fourth diagnostic
11601    cx.update_editor(|editor, window, cx| {
11602        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11603    });
11604    cx.assert_editor_state(indoc! {"
11605        fn func(abc def: i32) -> ˇu32 {
11606        }
11607    "});
11608
11609    cx.update_editor(|editor, window, cx| {
11610        editor.move_to_beginning(&MoveToBeginning, window, cx);
11611    });
11612    cx.assert_editor_state(indoc! {"
11613        ˇfn func(abc def: i32) -> u32 {
11614        }
11615    "});
11616
11617    //// Forward
11618
11619    // First diagnostic
11620    cx.update_editor(|editor, window, cx| {
11621        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11622    });
11623    cx.assert_editor_state(indoc! {"
11624        fn func(abcˇ def: i32) -> u32 {
11625        }
11626    "});
11627
11628    // Second diagnostic
11629    cx.update_editor(|editor, window, cx| {
11630        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11631    });
11632    cx.assert_editor_state(indoc! {"
11633        fn func(abc ˇdef: i32) -> u32 {
11634        }
11635    "});
11636
11637    // Third diagnostic, same place
11638    cx.update_editor(|editor, window, cx| {
11639        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11640    });
11641    cx.assert_editor_state(indoc! {"
11642        fn func(abc ˇdef: i32) -> u32 {
11643        }
11644    "});
11645
11646    // Fourth diagnostic
11647    cx.update_editor(|editor, window, cx| {
11648        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11649    });
11650    cx.assert_editor_state(indoc! {"
11651        fn func(abc def: i32) -> ˇu32 {
11652        }
11653    "});
11654
11655    // Wrapped around, first diagnostic
11656    cx.update_editor(|editor, window, cx| {
11657        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11658    });
11659    cx.assert_editor_state(indoc! {"
11660        fn func(abcˇ def: i32) -> u32 {
11661        }
11662    "});
11663}
11664
11665#[gpui::test]
11666async fn active_diagnostics_dismiss_after_invalidation(
11667    executor: BackgroundExecutor,
11668    cx: &mut TestAppContext,
11669) {
11670    init_test(cx, |_| {});
11671
11672    let mut cx = EditorTestContext::new(cx).await;
11673    let lsp_store =
11674        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11675
11676    cx.set_state(indoc! {"
11677        ˇfn func(abc def: i32) -> u32 {
11678        }
11679    "});
11680
11681    let message = "Something's wrong!";
11682    cx.update(|_, cx| {
11683        lsp_store.update(cx, |lsp_store, cx| {
11684            lsp_store
11685                .update_diagnostics(
11686                    LanguageServerId(0),
11687                    lsp::PublishDiagnosticsParams {
11688                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11689                        version: None,
11690                        diagnostics: vec![lsp::Diagnostic {
11691                            range: lsp::Range::new(
11692                                lsp::Position::new(0, 11),
11693                                lsp::Position::new(0, 12),
11694                            ),
11695                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11696                            message: message.to_string(),
11697                            ..Default::default()
11698                        }],
11699                    },
11700                    &[],
11701                    cx,
11702                )
11703                .unwrap()
11704        });
11705    });
11706    executor.run_until_parked();
11707
11708    cx.update_editor(|editor, window, cx| {
11709        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11710        assert_eq!(
11711            editor
11712                .active_diagnostics
11713                .as_ref()
11714                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11715            Some(message),
11716            "Should have a diagnostics group activated"
11717        );
11718    });
11719    cx.assert_editor_state(indoc! {"
11720        fn func(abcˇ def: i32) -> u32 {
11721        }
11722    "});
11723
11724    cx.update(|_, cx| {
11725        lsp_store.update(cx, |lsp_store, cx| {
11726            lsp_store
11727                .update_diagnostics(
11728                    LanguageServerId(0),
11729                    lsp::PublishDiagnosticsParams {
11730                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11731                        version: None,
11732                        diagnostics: Vec::new(),
11733                    },
11734                    &[],
11735                    cx,
11736                )
11737                .unwrap()
11738        });
11739    });
11740    executor.run_until_parked();
11741    cx.update_editor(|editor, _, _| {
11742        assert_eq!(
11743            editor.active_diagnostics, None,
11744            "After no diagnostics set to the editor, no diagnostics should be active"
11745        );
11746    });
11747    cx.assert_editor_state(indoc! {"
11748        fn func(abcˇ def: i32) -> u32 {
11749        }
11750    "});
11751
11752    cx.update_editor(|editor, window, cx| {
11753        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11754        assert_eq!(
11755            editor.active_diagnostics, None,
11756            "Should be no diagnostics to go to and activate"
11757        );
11758    });
11759    cx.assert_editor_state(indoc! {"
11760        fn func(abcˇ def: i32) -> u32 {
11761        }
11762    "});
11763}
11764
11765#[gpui::test]
11766async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11767    init_test(cx, |_| {});
11768
11769    let mut cx = EditorTestContext::new(cx).await;
11770
11771    cx.set_state(indoc! {"
11772        fn func(abˇc def: i32) -> u32 {
11773        }
11774    "});
11775    let lsp_store =
11776        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11777
11778    cx.update(|_, cx| {
11779        lsp_store.update(cx, |lsp_store, cx| {
11780            lsp_store.update_diagnostics(
11781                LanguageServerId(0),
11782                lsp::PublishDiagnosticsParams {
11783                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11784                    version: None,
11785                    diagnostics: vec![lsp::Diagnostic {
11786                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11787                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11788                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11789                        ..Default::default()
11790                    }],
11791                },
11792                &[],
11793                cx,
11794            )
11795        })
11796    }).unwrap();
11797    cx.run_until_parked();
11798    cx.update_editor(|editor, window, cx| {
11799        hover_popover::hover(editor, &Default::default(), window, cx)
11800    });
11801    cx.run_until_parked();
11802    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11803}
11804
11805#[gpui::test]
11806async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11807    init_test(cx, |_| {});
11808
11809    let mut cx = EditorTestContext::new(cx).await;
11810
11811    let diff_base = r#"
11812        use some::mod;
11813
11814        const A: u32 = 42;
11815
11816        fn main() {
11817            println!("hello");
11818
11819            println!("world");
11820        }
11821        "#
11822    .unindent();
11823
11824    // Edits are modified, removed, modified, added
11825    cx.set_state(
11826        &r#"
11827        use some::modified;
11828
11829        ˇ
11830        fn main() {
11831            println!("hello there");
11832
11833            println!("around the");
11834            println!("world");
11835        }
11836        "#
11837        .unindent(),
11838    );
11839
11840    cx.set_head_text(&diff_base);
11841    executor.run_until_parked();
11842
11843    cx.update_editor(|editor, window, cx| {
11844        //Wrap around the bottom of the buffer
11845        for _ in 0..3 {
11846            editor.go_to_next_hunk(&GoToHunk, window, cx);
11847        }
11848    });
11849
11850    cx.assert_editor_state(
11851        &r#"
11852        ˇuse some::modified;
11853
11854
11855        fn main() {
11856            println!("hello there");
11857
11858            println!("around the");
11859            println!("world");
11860        }
11861        "#
11862        .unindent(),
11863    );
11864
11865    cx.update_editor(|editor, window, cx| {
11866        //Wrap around the top of the buffer
11867        for _ in 0..2 {
11868            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11869        }
11870    });
11871
11872    cx.assert_editor_state(
11873        &r#"
11874        use some::modified;
11875
11876
11877        fn main() {
11878        ˇ    println!("hello there");
11879
11880            println!("around the");
11881            println!("world");
11882        }
11883        "#
11884        .unindent(),
11885    );
11886
11887    cx.update_editor(|editor, window, cx| {
11888        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11889    });
11890
11891    cx.assert_editor_state(
11892        &r#"
11893        use some::modified;
11894
11895        ˇ
11896        fn main() {
11897            println!("hello there");
11898
11899            println!("around the");
11900            println!("world");
11901        }
11902        "#
11903        .unindent(),
11904    );
11905
11906    cx.update_editor(|editor, window, cx| {
11907        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11908    });
11909
11910    cx.assert_editor_state(
11911        &r#"
11912        ˇuse some::modified;
11913
11914
11915        fn main() {
11916            println!("hello there");
11917
11918            println!("around the");
11919            println!("world");
11920        }
11921        "#
11922        .unindent(),
11923    );
11924
11925    cx.update_editor(|editor, window, cx| {
11926        for _ in 0..2 {
11927            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11928        }
11929    });
11930
11931    cx.assert_editor_state(
11932        &r#"
11933        use some::modified;
11934
11935
11936        fn main() {
11937        ˇ    println!("hello there");
11938
11939            println!("around the");
11940            println!("world");
11941        }
11942        "#
11943        .unindent(),
11944    );
11945
11946    cx.update_editor(|editor, window, cx| {
11947        editor.fold(&Fold, window, cx);
11948    });
11949
11950    cx.update_editor(|editor, window, cx| {
11951        editor.go_to_next_hunk(&GoToHunk, window, cx);
11952    });
11953
11954    cx.assert_editor_state(
11955        &r#"
11956        ˇuse some::modified;
11957
11958
11959        fn main() {
11960            println!("hello there");
11961
11962            println!("around the");
11963            println!("world");
11964        }
11965        "#
11966        .unindent(),
11967    );
11968}
11969
11970#[test]
11971fn test_split_words() {
11972    fn split(text: &str) -> Vec<&str> {
11973        split_words(text).collect()
11974    }
11975
11976    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11977    assert_eq!(split("hello_world"), &["hello_", "world"]);
11978    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11979    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11980    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11981    assert_eq!(split("helloworld"), &["helloworld"]);
11982
11983    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11984}
11985
11986#[gpui::test]
11987async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11988    init_test(cx, |_| {});
11989
11990    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11991    let mut assert = |before, after| {
11992        let _state_context = cx.set_state(before);
11993        cx.run_until_parked();
11994        cx.update_editor(|editor, window, cx| {
11995            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11996        });
11997        cx.run_until_parked();
11998        cx.assert_editor_state(after);
11999    };
12000
12001    // Outside bracket jumps to outside of matching bracket
12002    assert("console.logˇ(var);", "console.log(var)ˇ;");
12003    assert("console.log(var)ˇ;", "console.logˇ(var);");
12004
12005    // Inside bracket jumps to inside of matching bracket
12006    assert("console.log(ˇvar);", "console.log(varˇ);");
12007    assert("console.log(varˇ);", "console.log(ˇvar);");
12008
12009    // When outside a bracket and inside, favor jumping to the inside bracket
12010    assert(
12011        "console.log('foo', [1, 2, 3]ˇ);",
12012        "console.log(ˇ'foo', [1, 2, 3]);",
12013    );
12014    assert(
12015        "console.log(ˇ'foo', [1, 2, 3]);",
12016        "console.log('foo', [1, 2, 3]ˇ);",
12017    );
12018
12019    // Bias forward if two options are equally likely
12020    assert(
12021        "let result = curried_fun()ˇ();",
12022        "let result = curried_fun()()ˇ;",
12023    );
12024
12025    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12026    assert(
12027        indoc! {"
12028            function test() {
12029                console.log('test')ˇ
12030            }"},
12031        indoc! {"
12032            function test() {
12033                console.logˇ('test')
12034            }"},
12035    );
12036}
12037
12038#[gpui::test]
12039async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12040    init_test(cx, |_| {});
12041
12042    let fs = FakeFs::new(cx.executor());
12043    fs.insert_tree(
12044        path!("/a"),
12045        json!({
12046            "main.rs": "fn main() { let a = 5; }",
12047            "other.rs": "// Test file",
12048        }),
12049    )
12050    .await;
12051    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12052
12053    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12054    language_registry.add(Arc::new(Language::new(
12055        LanguageConfig {
12056            name: "Rust".into(),
12057            matcher: LanguageMatcher {
12058                path_suffixes: vec!["rs".to_string()],
12059                ..Default::default()
12060            },
12061            brackets: BracketPairConfig {
12062                pairs: vec![BracketPair {
12063                    start: "{".to_string(),
12064                    end: "}".to_string(),
12065                    close: true,
12066                    surround: true,
12067                    newline: true,
12068                }],
12069                disabled_scopes_by_bracket_ix: Vec::new(),
12070            },
12071            ..Default::default()
12072        },
12073        Some(tree_sitter_rust::LANGUAGE.into()),
12074    )));
12075    let mut fake_servers = language_registry.register_fake_lsp(
12076        "Rust",
12077        FakeLspAdapter {
12078            capabilities: lsp::ServerCapabilities {
12079                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12080                    first_trigger_character: "{".to_string(),
12081                    more_trigger_character: None,
12082                }),
12083                ..Default::default()
12084            },
12085            ..Default::default()
12086        },
12087    );
12088
12089    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12090
12091    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12092
12093    let worktree_id = workspace
12094        .update(cx, |workspace, _, cx| {
12095            workspace.project().update(cx, |project, cx| {
12096                project.worktrees(cx).next().unwrap().read(cx).id()
12097            })
12098        })
12099        .unwrap();
12100
12101    let buffer = project
12102        .update(cx, |project, cx| {
12103            project.open_local_buffer(path!("/a/main.rs"), cx)
12104        })
12105        .await
12106        .unwrap();
12107    let editor_handle = workspace
12108        .update(cx, |workspace, window, cx| {
12109            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12110        })
12111        .unwrap()
12112        .await
12113        .unwrap()
12114        .downcast::<Editor>()
12115        .unwrap();
12116
12117    cx.executor().start_waiting();
12118    let fake_server = fake_servers.next().await.unwrap();
12119
12120    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
12121        assert_eq!(
12122            params.text_document_position.text_document.uri,
12123            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12124        );
12125        assert_eq!(
12126            params.text_document_position.position,
12127            lsp::Position::new(0, 21),
12128        );
12129
12130        Ok(Some(vec![lsp::TextEdit {
12131            new_text: "]".to_string(),
12132            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12133        }]))
12134    });
12135
12136    editor_handle.update_in(cx, |editor, window, cx| {
12137        window.focus(&editor.focus_handle(cx));
12138        editor.change_selections(None, window, cx, |s| {
12139            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12140        });
12141        editor.handle_input("{", window, cx);
12142    });
12143
12144    cx.executor().run_until_parked();
12145
12146    buffer.update(cx, |buffer, _| {
12147        assert_eq!(
12148            buffer.text(),
12149            "fn main() { let a = {5}; }",
12150            "No extra braces from on type formatting should appear in the buffer"
12151        )
12152    });
12153}
12154
12155#[gpui::test]
12156async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12157    init_test(cx, |_| {});
12158
12159    let fs = FakeFs::new(cx.executor());
12160    fs.insert_tree(
12161        path!("/a"),
12162        json!({
12163            "main.rs": "fn main() { let a = 5; }",
12164            "other.rs": "// Test file",
12165        }),
12166    )
12167    .await;
12168
12169    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12170
12171    let server_restarts = Arc::new(AtomicUsize::new(0));
12172    let closure_restarts = Arc::clone(&server_restarts);
12173    let language_server_name = "test language server";
12174    let language_name: LanguageName = "Rust".into();
12175
12176    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12177    language_registry.add(Arc::new(Language::new(
12178        LanguageConfig {
12179            name: language_name.clone(),
12180            matcher: LanguageMatcher {
12181                path_suffixes: vec!["rs".to_string()],
12182                ..Default::default()
12183            },
12184            ..Default::default()
12185        },
12186        Some(tree_sitter_rust::LANGUAGE.into()),
12187    )));
12188    let mut fake_servers = language_registry.register_fake_lsp(
12189        "Rust",
12190        FakeLspAdapter {
12191            name: language_server_name,
12192            initialization_options: Some(json!({
12193                "testOptionValue": true
12194            })),
12195            initializer: Some(Box::new(move |fake_server| {
12196                let task_restarts = Arc::clone(&closure_restarts);
12197                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12198                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12199                    futures::future::ready(Ok(()))
12200                });
12201            })),
12202            ..Default::default()
12203        },
12204    );
12205
12206    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12207    let _buffer = project
12208        .update(cx, |project, cx| {
12209            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12210        })
12211        .await
12212        .unwrap();
12213    let _fake_server = fake_servers.next().await.unwrap();
12214    update_test_language_settings(cx, |language_settings| {
12215        language_settings.languages.insert(
12216            language_name.clone(),
12217            LanguageSettingsContent {
12218                tab_size: NonZeroU32::new(8),
12219                ..Default::default()
12220            },
12221        );
12222    });
12223    cx.executor().run_until_parked();
12224    assert_eq!(
12225        server_restarts.load(atomic::Ordering::Acquire),
12226        0,
12227        "Should not restart LSP server on an unrelated change"
12228    );
12229
12230    update_test_project_settings(cx, |project_settings| {
12231        project_settings.lsp.insert(
12232            "Some other server name".into(),
12233            LspSettings {
12234                binary: None,
12235                settings: None,
12236                initialization_options: Some(json!({
12237                    "some other init value": false
12238                })),
12239            },
12240        );
12241    });
12242    cx.executor().run_until_parked();
12243    assert_eq!(
12244        server_restarts.load(atomic::Ordering::Acquire),
12245        0,
12246        "Should not restart LSP server on an unrelated LSP settings change"
12247    );
12248
12249    update_test_project_settings(cx, |project_settings| {
12250        project_settings.lsp.insert(
12251            language_server_name.into(),
12252            LspSettings {
12253                binary: None,
12254                settings: None,
12255                initialization_options: Some(json!({
12256                    "anotherInitValue": false
12257                })),
12258            },
12259        );
12260    });
12261    cx.executor().run_until_parked();
12262    assert_eq!(
12263        server_restarts.load(atomic::Ordering::Acquire),
12264        1,
12265        "Should restart LSP server on a related LSP settings change"
12266    );
12267
12268    update_test_project_settings(cx, |project_settings| {
12269        project_settings.lsp.insert(
12270            language_server_name.into(),
12271            LspSettings {
12272                binary: None,
12273                settings: None,
12274                initialization_options: Some(json!({
12275                    "anotherInitValue": false
12276                })),
12277            },
12278        );
12279    });
12280    cx.executor().run_until_parked();
12281    assert_eq!(
12282        server_restarts.load(atomic::Ordering::Acquire),
12283        1,
12284        "Should not restart LSP server on a related LSP settings change that is the same"
12285    );
12286
12287    update_test_project_settings(cx, |project_settings| {
12288        project_settings.lsp.insert(
12289            language_server_name.into(),
12290            LspSettings {
12291                binary: None,
12292                settings: None,
12293                initialization_options: None,
12294            },
12295        );
12296    });
12297    cx.executor().run_until_parked();
12298    assert_eq!(
12299        server_restarts.load(atomic::Ordering::Acquire),
12300        2,
12301        "Should restart LSP server on another related LSP settings change"
12302    );
12303}
12304
12305#[gpui::test]
12306async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12307    init_test(cx, |_| {});
12308
12309    let mut cx = EditorLspTestContext::new_rust(
12310        lsp::ServerCapabilities {
12311            completion_provider: Some(lsp::CompletionOptions {
12312                trigger_characters: Some(vec![".".to_string()]),
12313                resolve_provider: Some(true),
12314                ..Default::default()
12315            }),
12316            ..Default::default()
12317        },
12318        cx,
12319    )
12320    .await;
12321
12322    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12323    cx.simulate_keystroke(".");
12324    let completion_item = lsp::CompletionItem {
12325        label: "some".into(),
12326        kind: Some(lsp::CompletionItemKind::SNIPPET),
12327        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12328        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12329            kind: lsp::MarkupKind::Markdown,
12330            value: "```rust\nSome(2)\n```".to_string(),
12331        })),
12332        deprecated: Some(false),
12333        sort_text: Some("fffffff2".to_string()),
12334        filter_text: Some("some".to_string()),
12335        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12336        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12337            range: lsp::Range {
12338                start: lsp::Position {
12339                    line: 0,
12340                    character: 22,
12341                },
12342                end: lsp::Position {
12343                    line: 0,
12344                    character: 22,
12345                },
12346            },
12347            new_text: "Some(2)".to_string(),
12348        })),
12349        additional_text_edits: Some(vec![lsp::TextEdit {
12350            range: lsp::Range {
12351                start: lsp::Position {
12352                    line: 0,
12353                    character: 20,
12354                },
12355                end: lsp::Position {
12356                    line: 0,
12357                    character: 22,
12358                },
12359            },
12360            new_text: "".to_string(),
12361        }]),
12362        ..Default::default()
12363    };
12364
12365    let closure_completion_item = completion_item.clone();
12366    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12367        let task_completion_item = closure_completion_item.clone();
12368        async move {
12369            Ok(Some(lsp::CompletionResponse::Array(vec![
12370                task_completion_item,
12371            ])))
12372        }
12373    });
12374
12375    request.next().await;
12376
12377    cx.condition(|editor, _| editor.context_menu_visible())
12378        .await;
12379    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12380        editor
12381            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12382            .unwrap()
12383    });
12384    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12385
12386    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12387        let task_completion_item = completion_item.clone();
12388        async move { Ok(task_completion_item) }
12389    })
12390    .next()
12391    .await
12392    .unwrap();
12393    apply_additional_edits.await.unwrap();
12394    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12395}
12396
12397#[gpui::test]
12398async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12399    init_test(cx, |_| {});
12400
12401    let mut cx = EditorLspTestContext::new_rust(
12402        lsp::ServerCapabilities {
12403            completion_provider: Some(lsp::CompletionOptions {
12404                trigger_characters: Some(vec![".".to_string()]),
12405                resolve_provider: Some(true),
12406                ..Default::default()
12407            }),
12408            ..Default::default()
12409        },
12410        cx,
12411    )
12412    .await;
12413
12414    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12415    cx.simulate_keystroke(".");
12416
12417    let item1 = lsp::CompletionItem {
12418        label: "method id()".to_string(),
12419        filter_text: Some("id".to_string()),
12420        detail: None,
12421        documentation: None,
12422        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12423            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12424            new_text: ".id".to_string(),
12425        })),
12426        ..lsp::CompletionItem::default()
12427    };
12428
12429    let item2 = lsp::CompletionItem {
12430        label: "other".to_string(),
12431        filter_text: Some("other".to_string()),
12432        detail: None,
12433        documentation: None,
12434        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12435            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12436            new_text: ".other".to_string(),
12437        })),
12438        ..lsp::CompletionItem::default()
12439    };
12440
12441    let item1 = item1.clone();
12442    cx.handle_request::<lsp::request::Completion, _, _>({
12443        let item1 = item1.clone();
12444        move |_, _, _| {
12445            let item1 = item1.clone();
12446            let item2 = item2.clone();
12447            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12448        }
12449    })
12450    .next()
12451    .await;
12452
12453    cx.condition(|editor, _| editor.context_menu_visible())
12454        .await;
12455    cx.update_editor(|editor, _, _| {
12456        let context_menu = editor.context_menu.borrow_mut();
12457        let context_menu = context_menu
12458            .as_ref()
12459            .expect("Should have the context menu deployed");
12460        match context_menu {
12461            CodeContextMenu::Completions(completions_menu) => {
12462                let completions = completions_menu.completions.borrow_mut();
12463                assert_eq!(
12464                    completions
12465                        .iter()
12466                        .map(|completion| &completion.label.text)
12467                        .collect::<Vec<_>>(),
12468                    vec!["method id()", "other"]
12469                )
12470            }
12471            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12472        }
12473    });
12474
12475    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12476        let item1 = item1.clone();
12477        move |_, item_to_resolve, _| {
12478            let item1 = item1.clone();
12479            async move {
12480                if item1 == item_to_resolve {
12481                    Ok(lsp::CompletionItem {
12482                        label: "method id()".to_string(),
12483                        filter_text: Some("id".to_string()),
12484                        detail: Some("Now resolved!".to_string()),
12485                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12486                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12487                            range: lsp::Range::new(
12488                                lsp::Position::new(0, 22),
12489                                lsp::Position::new(0, 22),
12490                            ),
12491                            new_text: ".id".to_string(),
12492                        })),
12493                        ..lsp::CompletionItem::default()
12494                    })
12495                } else {
12496                    Ok(item_to_resolve)
12497                }
12498            }
12499        }
12500    })
12501    .next()
12502    .await
12503    .unwrap();
12504    cx.run_until_parked();
12505
12506    cx.update_editor(|editor, window, cx| {
12507        editor.context_menu_next(&Default::default(), window, cx);
12508    });
12509
12510    cx.update_editor(|editor, _, _| {
12511        let context_menu = editor.context_menu.borrow_mut();
12512        let context_menu = context_menu
12513            .as_ref()
12514            .expect("Should have the context menu deployed");
12515        match context_menu {
12516            CodeContextMenu::Completions(completions_menu) => {
12517                let completions = completions_menu.completions.borrow_mut();
12518                assert_eq!(
12519                    completions
12520                        .iter()
12521                        .map(|completion| &completion.label.text)
12522                        .collect::<Vec<_>>(),
12523                    vec!["method id() Now resolved!", "other"],
12524                    "Should update first completion label, but not second as the filter text did not match."
12525                );
12526            }
12527            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12528        }
12529    });
12530}
12531
12532#[gpui::test]
12533async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12534    init_test(cx, |_| {});
12535
12536    let mut cx = EditorLspTestContext::new_rust(
12537        lsp::ServerCapabilities {
12538            completion_provider: Some(lsp::CompletionOptions {
12539                trigger_characters: Some(vec![".".to_string()]),
12540                resolve_provider: Some(true),
12541                ..Default::default()
12542            }),
12543            ..Default::default()
12544        },
12545        cx,
12546    )
12547    .await;
12548
12549    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12550    cx.simulate_keystroke(".");
12551
12552    let unresolved_item_1 = lsp::CompletionItem {
12553        label: "id".to_string(),
12554        filter_text: Some("id".to_string()),
12555        detail: None,
12556        documentation: None,
12557        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12558            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12559            new_text: ".id".to_string(),
12560        })),
12561        ..lsp::CompletionItem::default()
12562    };
12563    let resolved_item_1 = lsp::CompletionItem {
12564        additional_text_edits: Some(vec![lsp::TextEdit {
12565            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12566            new_text: "!!".to_string(),
12567        }]),
12568        ..unresolved_item_1.clone()
12569    };
12570    let unresolved_item_2 = lsp::CompletionItem {
12571        label: "other".to_string(),
12572        filter_text: Some("other".to_string()),
12573        detail: None,
12574        documentation: None,
12575        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12576            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12577            new_text: ".other".to_string(),
12578        })),
12579        ..lsp::CompletionItem::default()
12580    };
12581    let resolved_item_2 = lsp::CompletionItem {
12582        additional_text_edits: Some(vec![lsp::TextEdit {
12583            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12584            new_text: "??".to_string(),
12585        }]),
12586        ..unresolved_item_2.clone()
12587    };
12588
12589    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12590    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12591    cx.lsp
12592        .server
12593        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12594            let unresolved_item_1 = unresolved_item_1.clone();
12595            let resolved_item_1 = resolved_item_1.clone();
12596            let unresolved_item_2 = unresolved_item_2.clone();
12597            let resolved_item_2 = resolved_item_2.clone();
12598            let resolve_requests_1 = resolve_requests_1.clone();
12599            let resolve_requests_2 = resolve_requests_2.clone();
12600            move |unresolved_request, _| {
12601                let unresolved_item_1 = unresolved_item_1.clone();
12602                let resolved_item_1 = resolved_item_1.clone();
12603                let unresolved_item_2 = unresolved_item_2.clone();
12604                let resolved_item_2 = resolved_item_2.clone();
12605                let resolve_requests_1 = resolve_requests_1.clone();
12606                let resolve_requests_2 = resolve_requests_2.clone();
12607                async move {
12608                    if unresolved_request == unresolved_item_1 {
12609                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12610                        Ok(resolved_item_1.clone())
12611                    } else if unresolved_request == unresolved_item_2 {
12612                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12613                        Ok(resolved_item_2.clone())
12614                    } else {
12615                        panic!("Unexpected completion item {unresolved_request:?}")
12616                    }
12617                }
12618            }
12619        })
12620        .detach();
12621
12622    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12623        let unresolved_item_1 = unresolved_item_1.clone();
12624        let unresolved_item_2 = unresolved_item_2.clone();
12625        async move {
12626            Ok(Some(lsp::CompletionResponse::Array(vec![
12627                unresolved_item_1,
12628                unresolved_item_2,
12629            ])))
12630        }
12631    })
12632    .next()
12633    .await;
12634
12635    cx.condition(|editor, _| editor.context_menu_visible())
12636        .await;
12637    cx.update_editor(|editor, _, _| {
12638        let context_menu = editor.context_menu.borrow_mut();
12639        let context_menu = context_menu
12640            .as_ref()
12641            .expect("Should have the context menu deployed");
12642        match context_menu {
12643            CodeContextMenu::Completions(completions_menu) => {
12644                let completions = completions_menu.completions.borrow_mut();
12645                assert_eq!(
12646                    completions
12647                        .iter()
12648                        .map(|completion| &completion.label.text)
12649                        .collect::<Vec<_>>(),
12650                    vec!["id", "other"]
12651                )
12652            }
12653            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12654        }
12655    });
12656    cx.run_until_parked();
12657
12658    cx.update_editor(|editor, window, cx| {
12659        editor.context_menu_next(&ContextMenuNext, window, cx);
12660    });
12661    cx.run_until_parked();
12662    cx.update_editor(|editor, window, cx| {
12663        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12664    });
12665    cx.run_until_parked();
12666    cx.update_editor(|editor, window, cx| {
12667        editor.context_menu_next(&ContextMenuNext, window, cx);
12668    });
12669    cx.run_until_parked();
12670    cx.update_editor(|editor, window, cx| {
12671        editor
12672            .compose_completion(&ComposeCompletion::default(), window, cx)
12673            .expect("No task returned")
12674    })
12675    .await
12676    .expect("Completion failed");
12677    cx.run_until_parked();
12678
12679    cx.update_editor(|editor, _, cx| {
12680        assert_eq!(
12681            resolve_requests_1.load(atomic::Ordering::Acquire),
12682            1,
12683            "Should always resolve once despite multiple selections"
12684        );
12685        assert_eq!(
12686            resolve_requests_2.load(atomic::Ordering::Acquire),
12687            1,
12688            "Should always resolve once after multiple selections and applying the completion"
12689        );
12690        assert_eq!(
12691            editor.text(cx),
12692            "fn main() { let a = ??.other; }",
12693            "Should use resolved data when applying the completion"
12694        );
12695    });
12696}
12697
12698#[gpui::test]
12699async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12700    init_test(cx, |_| {});
12701
12702    let item_0 = lsp::CompletionItem {
12703        label: "abs".into(),
12704        insert_text: Some("abs".into()),
12705        data: Some(json!({ "very": "special"})),
12706        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12707        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12708            lsp::InsertReplaceEdit {
12709                new_text: "abs".to_string(),
12710                insert: lsp::Range::default(),
12711                replace: lsp::Range::default(),
12712            },
12713        )),
12714        ..lsp::CompletionItem::default()
12715    };
12716    let items = iter::once(item_0.clone())
12717        .chain((11..51).map(|i| lsp::CompletionItem {
12718            label: format!("item_{}", i),
12719            insert_text: Some(format!("item_{}", i)),
12720            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12721            ..lsp::CompletionItem::default()
12722        }))
12723        .collect::<Vec<_>>();
12724
12725    let default_commit_characters = vec!["?".to_string()];
12726    let default_data = json!({ "default": "data"});
12727    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12728    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12729    let default_edit_range = lsp::Range {
12730        start: lsp::Position {
12731            line: 0,
12732            character: 5,
12733        },
12734        end: lsp::Position {
12735            line: 0,
12736            character: 5,
12737        },
12738    };
12739
12740    let mut cx = EditorLspTestContext::new_rust(
12741        lsp::ServerCapabilities {
12742            completion_provider: Some(lsp::CompletionOptions {
12743                trigger_characters: Some(vec![".".to_string()]),
12744                resolve_provider: Some(true),
12745                ..Default::default()
12746            }),
12747            ..Default::default()
12748        },
12749        cx,
12750    )
12751    .await;
12752
12753    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12754    cx.simulate_keystroke(".");
12755
12756    let completion_data = default_data.clone();
12757    let completion_characters = default_commit_characters.clone();
12758    let completion_items = items.clone();
12759    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12760        let default_data = completion_data.clone();
12761        let default_commit_characters = completion_characters.clone();
12762        let items = completion_items.clone();
12763        async move {
12764            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12765                items,
12766                item_defaults: Some(lsp::CompletionListItemDefaults {
12767                    data: Some(default_data.clone()),
12768                    commit_characters: Some(default_commit_characters.clone()),
12769                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12770                        default_edit_range,
12771                    )),
12772                    insert_text_format: Some(default_insert_text_format),
12773                    insert_text_mode: Some(default_insert_text_mode),
12774                }),
12775                ..lsp::CompletionList::default()
12776            })))
12777        }
12778    })
12779    .next()
12780    .await;
12781
12782    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12783    cx.lsp
12784        .server
12785        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12786            let closure_resolved_items = resolved_items.clone();
12787            move |item_to_resolve, _| {
12788                let closure_resolved_items = closure_resolved_items.clone();
12789                async move {
12790                    closure_resolved_items.lock().push(item_to_resolve.clone());
12791                    Ok(item_to_resolve)
12792                }
12793            }
12794        })
12795        .detach();
12796
12797    cx.condition(|editor, _| editor.context_menu_visible())
12798        .await;
12799    cx.run_until_parked();
12800    cx.update_editor(|editor, _, _| {
12801        let menu = editor.context_menu.borrow_mut();
12802        match menu.as_ref().expect("should have the completions menu") {
12803            CodeContextMenu::Completions(completions_menu) => {
12804                assert_eq!(
12805                    completions_menu
12806                        .entries
12807                        .borrow()
12808                        .iter()
12809                        .map(|mat| mat.string.clone())
12810                        .collect::<Vec<String>>(),
12811                    items
12812                        .iter()
12813                        .map(|completion| completion.label.clone())
12814                        .collect::<Vec<String>>()
12815                );
12816            }
12817            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12818        }
12819    });
12820    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12821    // with 4 from the end.
12822    assert_eq!(
12823        *resolved_items.lock(),
12824        [&items[0..16], &items[items.len() - 4..items.len()]]
12825            .concat()
12826            .iter()
12827            .cloned()
12828            .map(|mut item| {
12829                if item.data.is_none() {
12830                    item.data = Some(default_data.clone());
12831                }
12832                item
12833            })
12834            .collect::<Vec<lsp::CompletionItem>>(),
12835        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12836    );
12837    resolved_items.lock().clear();
12838
12839    cx.update_editor(|editor, window, cx| {
12840        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12841    });
12842    cx.run_until_parked();
12843    // Completions that have already been resolved are skipped.
12844    assert_eq!(
12845        *resolved_items.lock(),
12846        items[items.len() - 16..items.len() - 4]
12847            .iter()
12848            .cloned()
12849            .map(|mut item| {
12850                if item.data.is_none() {
12851                    item.data = Some(default_data.clone());
12852                }
12853                item
12854            })
12855            .collect::<Vec<lsp::CompletionItem>>()
12856    );
12857    resolved_items.lock().clear();
12858}
12859
12860#[gpui::test]
12861async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12862    init_test(cx, |_| {});
12863
12864    let mut cx = EditorLspTestContext::new(
12865        Language::new(
12866            LanguageConfig {
12867                matcher: LanguageMatcher {
12868                    path_suffixes: vec!["jsx".into()],
12869                    ..Default::default()
12870                },
12871                overrides: [(
12872                    "element".into(),
12873                    LanguageConfigOverride {
12874                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
12875                        ..Default::default()
12876                    },
12877                )]
12878                .into_iter()
12879                .collect(),
12880                ..Default::default()
12881            },
12882            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12883        )
12884        .with_override_query("(jsx_self_closing_element) @element")
12885        .unwrap(),
12886        lsp::ServerCapabilities {
12887            completion_provider: Some(lsp::CompletionOptions {
12888                trigger_characters: Some(vec![":".to_string()]),
12889                ..Default::default()
12890            }),
12891            ..Default::default()
12892        },
12893        cx,
12894    )
12895    .await;
12896
12897    cx.lsp
12898        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12899            Ok(Some(lsp::CompletionResponse::Array(vec![
12900                lsp::CompletionItem {
12901                    label: "bg-blue".into(),
12902                    ..Default::default()
12903                },
12904                lsp::CompletionItem {
12905                    label: "bg-red".into(),
12906                    ..Default::default()
12907                },
12908                lsp::CompletionItem {
12909                    label: "bg-yellow".into(),
12910                    ..Default::default()
12911                },
12912            ])))
12913        });
12914
12915    cx.set_state(r#"<p class="bgˇ" />"#);
12916
12917    // Trigger completion when typing a dash, because the dash is an extra
12918    // word character in the 'element' scope, which contains the cursor.
12919    cx.simulate_keystroke("-");
12920    cx.executor().run_until_parked();
12921    cx.update_editor(|editor, _, _| {
12922        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12923        {
12924            assert_eq!(
12925                completion_menu_entries(&menu),
12926                &["bg-red", "bg-blue", "bg-yellow"]
12927            );
12928        } else {
12929            panic!("expected completion menu to be open");
12930        }
12931    });
12932
12933    cx.simulate_keystroke("l");
12934    cx.executor().run_until_parked();
12935    cx.update_editor(|editor, _, _| {
12936        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12937        {
12938            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12939        } else {
12940            panic!("expected completion menu to be open");
12941        }
12942    });
12943
12944    // When filtering completions, consider the character after the '-' to
12945    // be the start of a subword.
12946    cx.set_state(r#"<p class="yelˇ" />"#);
12947    cx.simulate_keystroke("l");
12948    cx.executor().run_until_parked();
12949    cx.update_editor(|editor, _, _| {
12950        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12951        {
12952            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12953        } else {
12954            panic!("expected completion menu to be open");
12955        }
12956    });
12957}
12958
12959fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12960    let entries = menu.entries.borrow();
12961    entries.iter().map(|mat| mat.string.clone()).collect()
12962}
12963
12964#[gpui::test]
12965async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12966    init_test(cx, |settings| {
12967        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12968            FormatterList(vec![Formatter::Prettier].into()),
12969        ))
12970    });
12971
12972    let fs = FakeFs::new(cx.executor());
12973    fs.insert_file(path!("/file.ts"), Default::default()).await;
12974
12975    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12976    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12977
12978    language_registry.add(Arc::new(Language::new(
12979        LanguageConfig {
12980            name: "TypeScript".into(),
12981            matcher: LanguageMatcher {
12982                path_suffixes: vec!["ts".to_string()],
12983                ..Default::default()
12984            },
12985            ..Default::default()
12986        },
12987        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12988    )));
12989    update_test_language_settings(cx, |settings| {
12990        settings.defaults.prettier = Some(PrettierSettings {
12991            allowed: true,
12992            ..PrettierSettings::default()
12993        });
12994    });
12995
12996    let test_plugin = "test_plugin";
12997    let _ = language_registry.register_fake_lsp(
12998        "TypeScript",
12999        FakeLspAdapter {
13000            prettier_plugins: vec![test_plugin],
13001            ..Default::default()
13002        },
13003    );
13004
13005    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13006    let buffer = project
13007        .update(cx, |project, cx| {
13008            project.open_local_buffer(path!("/file.ts"), cx)
13009        })
13010        .await
13011        .unwrap();
13012
13013    let buffer_text = "one\ntwo\nthree\n";
13014    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13015    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13016    editor.update_in(cx, |editor, window, cx| {
13017        editor.set_text(buffer_text, window, cx)
13018    });
13019
13020    editor
13021        .update_in(cx, |editor, window, cx| {
13022            editor.perform_format(
13023                project.clone(),
13024                FormatTrigger::Manual,
13025                FormatTarget::Buffers,
13026                window,
13027                cx,
13028            )
13029        })
13030        .unwrap()
13031        .await;
13032    assert_eq!(
13033        editor.update(cx, |editor, cx| editor.text(cx)),
13034        buffer_text.to_string() + prettier_format_suffix,
13035        "Test prettier formatting was not applied to the original buffer text",
13036    );
13037
13038    update_test_language_settings(cx, |settings| {
13039        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13040    });
13041    let format = editor.update_in(cx, |editor, window, cx| {
13042        editor.perform_format(
13043            project.clone(),
13044            FormatTrigger::Manual,
13045            FormatTarget::Buffers,
13046            window,
13047            cx,
13048        )
13049    });
13050    format.await.unwrap();
13051    assert_eq!(
13052        editor.update(cx, |editor, cx| editor.text(cx)),
13053        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13054        "Autoformatting (via test prettier) was not applied to the original buffer text",
13055    );
13056}
13057
13058#[gpui::test]
13059async fn test_addition_reverts(cx: &mut TestAppContext) {
13060    init_test(cx, |_| {});
13061    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13062    let base_text = indoc! {r#"
13063        struct Row;
13064        struct Row1;
13065        struct Row2;
13066
13067        struct Row4;
13068        struct Row5;
13069        struct Row6;
13070
13071        struct Row8;
13072        struct Row9;
13073        struct Row10;"#};
13074
13075    // When addition hunks are not adjacent to carets, no hunk revert is performed
13076    assert_hunk_revert(
13077        indoc! {r#"struct Row;
13078                   struct Row1;
13079                   struct Row1.1;
13080                   struct Row1.2;
13081                   struct Row2;ˇ
13082
13083                   struct Row4;
13084                   struct Row5;
13085                   struct Row6;
13086
13087                   struct Row8;
13088                   ˇstruct Row9;
13089                   struct Row9.1;
13090                   struct Row9.2;
13091                   struct Row9.3;
13092                   struct Row10;"#},
13093        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13094        indoc! {r#"struct Row;
13095                   struct Row1;
13096                   struct Row1.1;
13097                   struct Row1.2;
13098                   struct Row2;ˇ
13099
13100                   struct Row4;
13101                   struct Row5;
13102                   struct Row6;
13103
13104                   struct Row8;
13105                   ˇstruct Row9;
13106                   struct Row9.1;
13107                   struct Row9.2;
13108                   struct Row9.3;
13109                   struct Row10;"#},
13110        base_text,
13111        &mut cx,
13112    );
13113    // Same for selections
13114    assert_hunk_revert(
13115        indoc! {r#"struct Row;
13116                   struct Row1;
13117                   struct Row2;
13118                   struct Row2.1;
13119                   struct Row2.2;
13120                   «ˇ
13121                   struct Row4;
13122                   struct» Row5;
13123                   «struct Row6;
13124                   ˇ»
13125                   struct Row9.1;
13126                   struct Row9.2;
13127                   struct Row9.3;
13128                   struct Row8;
13129                   struct Row9;
13130                   struct Row10;"#},
13131        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13132        indoc! {r#"struct Row;
13133                   struct Row1;
13134                   struct Row2;
13135                   struct Row2.1;
13136                   struct Row2.2;
13137                   «ˇ
13138                   struct Row4;
13139                   struct» Row5;
13140                   «struct Row6;
13141                   ˇ»
13142                   struct Row9.1;
13143                   struct Row9.2;
13144                   struct Row9.3;
13145                   struct Row8;
13146                   struct Row9;
13147                   struct Row10;"#},
13148        base_text,
13149        &mut cx,
13150    );
13151
13152    // When carets and selections intersect the addition hunks, those are reverted.
13153    // Adjacent carets got merged.
13154    assert_hunk_revert(
13155        indoc! {r#"struct Row;
13156                   ˇ// something on the top
13157                   struct Row1;
13158                   struct Row2;
13159                   struct Roˇw3.1;
13160                   struct Row2.2;
13161                   struct Row2.3;ˇ
13162
13163                   struct Row4;
13164                   struct ˇRow5.1;
13165                   struct Row5.2;
13166                   struct «Rowˇ»5.3;
13167                   struct Row5;
13168                   struct Row6;
13169                   ˇ
13170                   struct Row9.1;
13171                   struct «Rowˇ»9.2;
13172                   struct «ˇRow»9.3;
13173                   struct Row8;
13174                   struct Row9;
13175                   «ˇ// something on bottom»
13176                   struct Row10;"#},
13177        vec![
13178            DiffHunkStatusKind::Added,
13179            DiffHunkStatusKind::Added,
13180            DiffHunkStatusKind::Added,
13181            DiffHunkStatusKind::Added,
13182            DiffHunkStatusKind::Added,
13183        ],
13184        indoc! {r#"struct Row;
13185                   ˇstruct Row1;
13186                   struct Row2;
13187                   ˇ
13188                   struct Row4;
13189                   ˇstruct Row5;
13190                   struct Row6;
13191                   ˇ
13192                   ˇstruct Row8;
13193                   struct Row9;
13194                   ˇstruct Row10;"#},
13195        base_text,
13196        &mut cx,
13197    );
13198}
13199
13200#[gpui::test]
13201async fn test_modification_reverts(cx: &mut TestAppContext) {
13202    init_test(cx, |_| {});
13203    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13204    let base_text = indoc! {r#"
13205        struct Row;
13206        struct Row1;
13207        struct Row2;
13208
13209        struct Row4;
13210        struct Row5;
13211        struct Row6;
13212
13213        struct Row8;
13214        struct Row9;
13215        struct Row10;"#};
13216
13217    // Modification hunks behave the same as the addition ones.
13218    assert_hunk_revert(
13219        indoc! {r#"struct Row;
13220                   struct Row1;
13221                   struct Row33;
13222                   ˇ
13223                   struct Row4;
13224                   struct Row5;
13225                   struct Row6;
13226                   ˇ
13227                   struct Row99;
13228                   struct Row9;
13229                   struct Row10;"#},
13230        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13231        indoc! {r#"struct Row;
13232                   struct Row1;
13233                   struct Row33;
13234                   ˇ
13235                   struct Row4;
13236                   struct Row5;
13237                   struct Row6;
13238                   ˇ
13239                   struct Row99;
13240                   struct Row9;
13241                   struct Row10;"#},
13242        base_text,
13243        &mut cx,
13244    );
13245    assert_hunk_revert(
13246        indoc! {r#"struct Row;
13247                   struct Row1;
13248                   struct Row33;
13249                   «ˇ
13250                   struct Row4;
13251                   struct» Row5;
13252                   «struct Row6;
13253                   ˇ»
13254                   struct Row99;
13255                   struct Row9;
13256                   struct Row10;"#},
13257        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13258        indoc! {r#"struct Row;
13259                   struct Row1;
13260                   struct Row33;
13261                   «ˇ
13262                   struct Row4;
13263                   struct» Row5;
13264                   «struct Row6;
13265                   ˇ»
13266                   struct Row99;
13267                   struct Row9;
13268                   struct Row10;"#},
13269        base_text,
13270        &mut cx,
13271    );
13272
13273    assert_hunk_revert(
13274        indoc! {r#"ˇstruct Row1.1;
13275                   struct Row1;
13276                   «ˇstr»uct Row22;
13277
13278                   struct ˇRow44;
13279                   struct Row5;
13280                   struct «Rˇ»ow66;ˇ
13281
13282                   «struˇ»ct Row88;
13283                   struct Row9;
13284                   struct Row1011;ˇ"#},
13285        vec![
13286            DiffHunkStatusKind::Modified,
13287            DiffHunkStatusKind::Modified,
13288            DiffHunkStatusKind::Modified,
13289            DiffHunkStatusKind::Modified,
13290            DiffHunkStatusKind::Modified,
13291            DiffHunkStatusKind::Modified,
13292        ],
13293        indoc! {r#"struct Row;
13294                   ˇstruct Row1;
13295                   struct Row2;
13296                   ˇ
13297                   struct Row4;
13298                   ˇstruct Row5;
13299                   struct Row6;
13300                   ˇ
13301                   struct Row8;
13302                   ˇstruct Row9;
13303                   struct Row10;ˇ"#},
13304        base_text,
13305        &mut cx,
13306    );
13307}
13308
13309#[gpui::test]
13310async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13311    init_test(cx, |_| {});
13312    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13313    let base_text = indoc! {r#"
13314        one
13315
13316        two
13317        three
13318        "#};
13319
13320    cx.set_head_text(base_text);
13321    cx.set_state("\nˇ\n");
13322    cx.executor().run_until_parked();
13323    cx.update_editor(|editor, _window, cx| {
13324        editor.expand_selected_diff_hunks(cx);
13325    });
13326    cx.executor().run_until_parked();
13327    cx.update_editor(|editor, window, cx| {
13328        editor.backspace(&Default::default(), window, cx);
13329    });
13330    cx.run_until_parked();
13331    cx.assert_state_with_diff(
13332        indoc! {r#"
13333
13334        - two
13335        - threeˇ
13336        +
13337        "#}
13338        .to_string(),
13339    );
13340}
13341
13342#[gpui::test]
13343async fn test_deletion_reverts(cx: &mut TestAppContext) {
13344    init_test(cx, |_| {});
13345    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13346    let base_text = indoc! {r#"struct Row;
13347struct Row1;
13348struct Row2;
13349
13350struct Row4;
13351struct Row5;
13352struct Row6;
13353
13354struct Row8;
13355struct Row9;
13356struct Row10;"#};
13357
13358    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13359    assert_hunk_revert(
13360        indoc! {r#"struct Row;
13361                   struct Row2;
13362
13363                   ˇstruct Row4;
13364                   struct Row5;
13365                   struct Row6;
13366                   ˇ
13367                   struct Row8;
13368                   struct Row10;"#},
13369        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13370        indoc! {r#"struct Row;
13371                   struct Row2;
13372
13373                   ˇstruct Row4;
13374                   struct Row5;
13375                   struct Row6;
13376                   ˇ
13377                   struct Row8;
13378                   struct Row10;"#},
13379        base_text,
13380        &mut cx,
13381    );
13382    assert_hunk_revert(
13383        indoc! {r#"struct Row;
13384                   struct Row2;
13385
13386                   «ˇstruct Row4;
13387                   struct» Row5;
13388                   «struct Row6;
13389                   ˇ»
13390                   struct Row8;
13391                   struct Row10;"#},
13392        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13393        indoc! {r#"struct Row;
13394                   struct Row2;
13395
13396                   «ˇstruct Row4;
13397                   struct» Row5;
13398                   «struct Row6;
13399                   ˇ»
13400                   struct Row8;
13401                   struct Row10;"#},
13402        base_text,
13403        &mut cx,
13404    );
13405
13406    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13407    assert_hunk_revert(
13408        indoc! {r#"struct Row;
13409                   ˇstruct Row2;
13410
13411                   struct Row4;
13412                   struct Row5;
13413                   struct Row6;
13414
13415                   struct Row8;ˇ
13416                   struct Row10;"#},
13417        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13418        indoc! {r#"struct Row;
13419                   struct Row1;
13420                   ˇstruct Row2;
13421
13422                   struct Row4;
13423                   struct Row5;
13424                   struct Row6;
13425
13426                   struct Row8;ˇ
13427                   struct Row9;
13428                   struct Row10;"#},
13429        base_text,
13430        &mut cx,
13431    );
13432    assert_hunk_revert(
13433        indoc! {r#"struct Row;
13434                   struct Row2«ˇ;
13435                   struct Row4;
13436                   struct» Row5;
13437                   «struct Row6;
13438
13439                   struct Row8;ˇ»
13440                   struct Row10;"#},
13441        vec![
13442            DiffHunkStatusKind::Deleted,
13443            DiffHunkStatusKind::Deleted,
13444            DiffHunkStatusKind::Deleted,
13445        ],
13446        indoc! {r#"struct Row;
13447                   struct Row1;
13448                   struct Row2«ˇ;
13449
13450                   struct Row4;
13451                   struct» Row5;
13452                   «struct Row6;
13453
13454                   struct Row8;ˇ»
13455                   struct Row9;
13456                   struct Row10;"#},
13457        base_text,
13458        &mut cx,
13459    );
13460}
13461
13462#[gpui::test]
13463async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13464    init_test(cx, |_| {});
13465
13466    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13467    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13468    let base_text_3 =
13469        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13470
13471    let text_1 = edit_first_char_of_every_line(base_text_1);
13472    let text_2 = edit_first_char_of_every_line(base_text_2);
13473    let text_3 = edit_first_char_of_every_line(base_text_3);
13474
13475    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13476    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13477    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13478
13479    let multibuffer = cx.new(|cx| {
13480        let mut multibuffer = MultiBuffer::new(ReadWrite);
13481        multibuffer.push_excerpts(
13482            buffer_1.clone(),
13483            [
13484                ExcerptRange {
13485                    context: Point::new(0, 0)..Point::new(3, 0),
13486                    primary: None,
13487                },
13488                ExcerptRange {
13489                    context: Point::new(5, 0)..Point::new(7, 0),
13490                    primary: None,
13491                },
13492                ExcerptRange {
13493                    context: Point::new(9, 0)..Point::new(10, 4),
13494                    primary: None,
13495                },
13496            ],
13497            cx,
13498        );
13499        multibuffer.push_excerpts(
13500            buffer_2.clone(),
13501            [
13502                ExcerptRange {
13503                    context: Point::new(0, 0)..Point::new(3, 0),
13504                    primary: None,
13505                },
13506                ExcerptRange {
13507                    context: Point::new(5, 0)..Point::new(7, 0),
13508                    primary: None,
13509                },
13510                ExcerptRange {
13511                    context: Point::new(9, 0)..Point::new(10, 4),
13512                    primary: None,
13513                },
13514            ],
13515            cx,
13516        );
13517        multibuffer.push_excerpts(
13518            buffer_3.clone(),
13519            [
13520                ExcerptRange {
13521                    context: Point::new(0, 0)..Point::new(3, 0),
13522                    primary: None,
13523                },
13524                ExcerptRange {
13525                    context: Point::new(5, 0)..Point::new(7, 0),
13526                    primary: None,
13527                },
13528                ExcerptRange {
13529                    context: Point::new(9, 0)..Point::new(10, 4),
13530                    primary: None,
13531                },
13532            ],
13533            cx,
13534        );
13535        multibuffer
13536    });
13537
13538    let fs = FakeFs::new(cx.executor());
13539    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13540    let (editor, cx) = cx
13541        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13542    editor.update_in(cx, |editor, _window, cx| {
13543        for (buffer, diff_base) in [
13544            (buffer_1.clone(), base_text_1),
13545            (buffer_2.clone(), base_text_2),
13546            (buffer_3.clone(), base_text_3),
13547        ] {
13548            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13549            editor
13550                .buffer
13551                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13552        }
13553    });
13554    cx.executor().run_until_parked();
13555
13556    editor.update_in(cx, |editor, window, cx| {
13557        assert_eq!(editor.text(cx), "Xaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
13558        editor.select_all(&SelectAll, window, cx);
13559        editor.git_restore(&Default::default(), window, cx);
13560    });
13561    cx.executor().run_until_parked();
13562
13563    // When all ranges are selected, all buffer hunks are reverted.
13564    editor.update(cx, |editor, cx| {
13565        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");
13566    });
13567    buffer_1.update(cx, |buffer, _| {
13568        assert_eq!(buffer.text(), base_text_1);
13569    });
13570    buffer_2.update(cx, |buffer, _| {
13571        assert_eq!(buffer.text(), base_text_2);
13572    });
13573    buffer_3.update(cx, |buffer, _| {
13574        assert_eq!(buffer.text(), base_text_3);
13575    });
13576
13577    editor.update_in(cx, |editor, window, cx| {
13578        editor.undo(&Default::default(), window, cx);
13579    });
13580
13581    editor.update_in(cx, |editor, window, cx| {
13582        editor.change_selections(None, window, cx, |s| {
13583            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13584        });
13585        editor.git_restore(&Default::default(), window, cx);
13586    });
13587
13588    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13589    // but not affect buffer_2 and its related excerpts.
13590    editor.update(cx, |editor, cx| {
13591        assert_eq!(
13592            editor.text(cx),
13593            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
13594        );
13595    });
13596    buffer_1.update(cx, |buffer, _| {
13597        assert_eq!(buffer.text(), base_text_1);
13598    });
13599    buffer_2.update(cx, |buffer, _| {
13600        assert_eq!(
13601            buffer.text(),
13602            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13603        );
13604    });
13605    buffer_3.update(cx, |buffer, _| {
13606        assert_eq!(
13607            buffer.text(),
13608            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13609        );
13610    });
13611
13612    fn edit_first_char_of_every_line(text: &str) -> String {
13613        text.split('\n')
13614            .map(|line| format!("X{}", &line[1..]))
13615            .collect::<Vec<_>>()
13616            .join("\n")
13617    }
13618}
13619
13620#[gpui::test]
13621async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13622    init_test(cx, |_| {});
13623
13624    let cols = 4;
13625    let rows = 10;
13626    let sample_text_1 = sample_text(rows, cols, 'a');
13627    assert_eq!(
13628        sample_text_1,
13629        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13630    );
13631    let sample_text_2 = sample_text(rows, cols, 'l');
13632    assert_eq!(
13633        sample_text_2,
13634        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13635    );
13636    let sample_text_3 = sample_text(rows, cols, 'v');
13637    assert_eq!(
13638        sample_text_3,
13639        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13640    );
13641
13642    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13643    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13644    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13645
13646    let multi_buffer = cx.new(|cx| {
13647        let mut multibuffer = MultiBuffer::new(ReadWrite);
13648        multibuffer.push_excerpts(
13649            buffer_1.clone(),
13650            [
13651                ExcerptRange {
13652                    context: Point::new(0, 0)..Point::new(3, 0),
13653                    primary: None,
13654                },
13655                ExcerptRange {
13656                    context: Point::new(5, 0)..Point::new(7, 0),
13657                    primary: None,
13658                },
13659                ExcerptRange {
13660                    context: Point::new(9, 0)..Point::new(10, 4),
13661                    primary: None,
13662                },
13663            ],
13664            cx,
13665        );
13666        multibuffer.push_excerpts(
13667            buffer_2.clone(),
13668            [
13669                ExcerptRange {
13670                    context: Point::new(0, 0)..Point::new(3, 0),
13671                    primary: None,
13672                },
13673                ExcerptRange {
13674                    context: Point::new(5, 0)..Point::new(7, 0),
13675                    primary: None,
13676                },
13677                ExcerptRange {
13678                    context: Point::new(9, 0)..Point::new(10, 4),
13679                    primary: None,
13680                },
13681            ],
13682            cx,
13683        );
13684        multibuffer.push_excerpts(
13685            buffer_3.clone(),
13686            [
13687                ExcerptRange {
13688                    context: Point::new(0, 0)..Point::new(3, 0),
13689                    primary: None,
13690                },
13691                ExcerptRange {
13692                    context: Point::new(5, 0)..Point::new(7, 0),
13693                    primary: None,
13694                },
13695                ExcerptRange {
13696                    context: Point::new(9, 0)..Point::new(10, 4),
13697                    primary: None,
13698                },
13699            ],
13700            cx,
13701        );
13702        multibuffer
13703    });
13704
13705    let fs = FakeFs::new(cx.executor());
13706    fs.insert_tree(
13707        "/a",
13708        json!({
13709            "main.rs": sample_text_1,
13710            "other.rs": sample_text_2,
13711            "lib.rs": sample_text_3,
13712        }),
13713    )
13714    .await;
13715    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13716    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13717    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13718    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13719        Editor::new(
13720            EditorMode::Full,
13721            multi_buffer,
13722            Some(project.clone()),
13723            window,
13724            cx,
13725        )
13726    });
13727    let multibuffer_item_id = workspace
13728        .update(cx, |workspace, window, cx| {
13729            assert!(
13730                workspace.active_item(cx).is_none(),
13731                "active item should be None before the first item is added"
13732            );
13733            workspace.add_item_to_active_pane(
13734                Box::new(multi_buffer_editor.clone()),
13735                None,
13736                true,
13737                window,
13738                cx,
13739            );
13740            let active_item = workspace
13741                .active_item(cx)
13742                .expect("should have an active item after adding the multi buffer");
13743            assert!(
13744                !active_item.is_singleton(cx),
13745                "A multi buffer was expected to active after adding"
13746            );
13747            active_item.item_id()
13748        })
13749        .unwrap();
13750    cx.executor().run_until_parked();
13751
13752    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13753        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13754            s.select_ranges(Some(1..2))
13755        });
13756        editor.open_excerpts(&OpenExcerpts, window, cx);
13757    });
13758    cx.executor().run_until_parked();
13759    let first_item_id = workspace
13760        .update(cx, |workspace, window, cx| {
13761            let active_item = workspace
13762                .active_item(cx)
13763                .expect("should have an active item after navigating into the 1st buffer");
13764            let first_item_id = active_item.item_id();
13765            assert_ne!(
13766                first_item_id, multibuffer_item_id,
13767                "Should navigate into the 1st buffer and activate it"
13768            );
13769            assert!(
13770                active_item.is_singleton(cx),
13771                "New active item should be a singleton buffer"
13772            );
13773            assert_eq!(
13774                active_item
13775                    .act_as::<Editor>(cx)
13776                    .expect("should have navigated into an editor for the 1st buffer")
13777                    .read(cx)
13778                    .text(cx),
13779                sample_text_1
13780            );
13781
13782            workspace
13783                .go_back(workspace.active_pane().downgrade(), window, cx)
13784                .detach_and_log_err(cx);
13785
13786            first_item_id
13787        })
13788        .unwrap();
13789    cx.executor().run_until_parked();
13790    workspace
13791        .update(cx, |workspace, _, cx| {
13792            let active_item = workspace
13793                .active_item(cx)
13794                .expect("should have an active item after navigating back");
13795            assert_eq!(
13796                active_item.item_id(),
13797                multibuffer_item_id,
13798                "Should navigate back to the multi buffer"
13799            );
13800            assert!(!active_item.is_singleton(cx));
13801        })
13802        .unwrap();
13803
13804    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13805        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13806            s.select_ranges(Some(39..40))
13807        });
13808        editor.open_excerpts(&OpenExcerpts, window, cx);
13809    });
13810    cx.executor().run_until_parked();
13811    let second_item_id = workspace
13812        .update(cx, |workspace, window, cx| {
13813            let active_item = workspace
13814                .active_item(cx)
13815                .expect("should have an active item after navigating into the 2nd buffer");
13816            let second_item_id = active_item.item_id();
13817            assert_ne!(
13818                second_item_id, multibuffer_item_id,
13819                "Should navigate away from the multibuffer"
13820            );
13821            assert_ne!(
13822                second_item_id, first_item_id,
13823                "Should navigate into the 2nd buffer and activate it"
13824            );
13825            assert!(
13826                active_item.is_singleton(cx),
13827                "New active item should be a singleton buffer"
13828            );
13829            assert_eq!(
13830                active_item
13831                    .act_as::<Editor>(cx)
13832                    .expect("should have navigated into an editor")
13833                    .read(cx)
13834                    .text(cx),
13835                sample_text_2
13836            );
13837
13838            workspace
13839                .go_back(workspace.active_pane().downgrade(), window, cx)
13840                .detach_and_log_err(cx);
13841
13842            second_item_id
13843        })
13844        .unwrap();
13845    cx.executor().run_until_parked();
13846    workspace
13847        .update(cx, |workspace, _, cx| {
13848            let active_item = workspace
13849                .active_item(cx)
13850                .expect("should have an active item after navigating back from the 2nd buffer");
13851            assert_eq!(
13852                active_item.item_id(),
13853                multibuffer_item_id,
13854                "Should navigate back from the 2nd buffer to the multi buffer"
13855            );
13856            assert!(!active_item.is_singleton(cx));
13857        })
13858        .unwrap();
13859
13860    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13861        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13862            s.select_ranges(Some(70..70))
13863        });
13864        editor.open_excerpts(&OpenExcerpts, window, cx);
13865    });
13866    cx.executor().run_until_parked();
13867    workspace
13868        .update(cx, |workspace, window, cx| {
13869            let active_item = workspace
13870                .active_item(cx)
13871                .expect("should have an active item after navigating into the 3rd buffer");
13872            let third_item_id = active_item.item_id();
13873            assert_ne!(
13874                third_item_id, multibuffer_item_id,
13875                "Should navigate into the 3rd buffer and activate it"
13876            );
13877            assert_ne!(third_item_id, first_item_id);
13878            assert_ne!(third_item_id, second_item_id);
13879            assert!(
13880                active_item.is_singleton(cx),
13881                "New active item should be a singleton buffer"
13882            );
13883            assert_eq!(
13884                active_item
13885                    .act_as::<Editor>(cx)
13886                    .expect("should have navigated into an editor")
13887                    .read(cx)
13888                    .text(cx),
13889                sample_text_3
13890            );
13891
13892            workspace
13893                .go_back(workspace.active_pane().downgrade(), window, cx)
13894                .detach_and_log_err(cx);
13895        })
13896        .unwrap();
13897    cx.executor().run_until_parked();
13898    workspace
13899        .update(cx, |workspace, _, cx| {
13900            let active_item = workspace
13901                .active_item(cx)
13902                .expect("should have an active item after navigating back from the 3rd buffer");
13903            assert_eq!(
13904                active_item.item_id(),
13905                multibuffer_item_id,
13906                "Should navigate back from the 3rd buffer to the multi buffer"
13907            );
13908            assert!(!active_item.is_singleton(cx));
13909        })
13910        .unwrap();
13911}
13912
13913#[gpui::test]
13914async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13915    init_test(cx, |_| {});
13916
13917    let mut cx = EditorTestContext::new(cx).await;
13918
13919    let diff_base = r#"
13920        use some::mod;
13921
13922        const A: u32 = 42;
13923
13924        fn main() {
13925            println!("hello");
13926
13927            println!("world");
13928        }
13929        "#
13930    .unindent();
13931
13932    cx.set_state(
13933        &r#"
13934        use some::modified;
13935
13936        ˇ
13937        fn main() {
13938            println!("hello there");
13939
13940            println!("around the");
13941            println!("world");
13942        }
13943        "#
13944        .unindent(),
13945    );
13946
13947    cx.set_head_text(&diff_base);
13948    executor.run_until_parked();
13949
13950    cx.update_editor(|editor, window, cx| {
13951        editor.go_to_next_hunk(&GoToHunk, window, cx);
13952        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13953    });
13954    executor.run_until_parked();
13955    cx.assert_state_with_diff(
13956        r#"
13957          use some::modified;
13958
13959
13960          fn main() {
13961        -     println!("hello");
13962        + ˇ    println!("hello there");
13963
13964              println!("around the");
13965              println!("world");
13966          }
13967        "#
13968        .unindent(),
13969    );
13970
13971    cx.update_editor(|editor, window, cx| {
13972        for _ in 0..2 {
13973            editor.go_to_next_hunk(&GoToHunk, window, cx);
13974            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13975        }
13976    });
13977    executor.run_until_parked();
13978    cx.assert_state_with_diff(
13979        r#"
13980        - use some::mod;
13981        + ˇuse some::modified;
13982
13983
13984          fn main() {
13985        -     println!("hello");
13986        +     println!("hello there");
13987
13988        +     println!("around the");
13989              println!("world");
13990          }
13991        "#
13992        .unindent(),
13993    );
13994
13995    cx.update_editor(|editor, window, cx| {
13996        editor.go_to_next_hunk(&GoToHunk, window, cx);
13997        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13998    });
13999    executor.run_until_parked();
14000    cx.assert_state_with_diff(
14001        r#"
14002        - use some::mod;
14003        + use some::modified;
14004
14005        - const A: u32 = 42;
14006          ˇ
14007          fn main() {
14008        -     println!("hello");
14009        +     println!("hello there");
14010
14011        +     println!("around the");
14012              println!("world");
14013          }
14014        "#
14015        .unindent(),
14016    );
14017
14018    cx.update_editor(|editor, window, cx| {
14019        editor.cancel(&Cancel, window, cx);
14020    });
14021
14022    cx.assert_state_with_diff(
14023        r#"
14024          use some::modified;
14025
14026          ˇ
14027          fn main() {
14028              println!("hello there");
14029
14030              println!("around the");
14031              println!("world");
14032          }
14033        "#
14034        .unindent(),
14035    );
14036}
14037
14038#[gpui::test]
14039async fn test_diff_base_change_with_expanded_diff_hunks(
14040    executor: BackgroundExecutor,
14041    cx: &mut TestAppContext,
14042) {
14043    init_test(cx, |_| {});
14044
14045    let mut cx = EditorTestContext::new(cx).await;
14046
14047    let diff_base = r#"
14048        use some::mod1;
14049        use some::mod2;
14050
14051        const A: u32 = 42;
14052        const B: u32 = 42;
14053        const C: u32 = 42;
14054
14055        fn main() {
14056            println!("hello");
14057
14058            println!("world");
14059        }
14060        "#
14061    .unindent();
14062
14063    cx.set_state(
14064        &r#"
14065        use some::mod2;
14066
14067        const A: u32 = 42;
14068        const C: u32 = 42;
14069
14070        fn main(ˇ) {
14071            //println!("hello");
14072
14073            println!("world");
14074            //
14075            //
14076        }
14077        "#
14078        .unindent(),
14079    );
14080
14081    cx.set_head_text(&diff_base);
14082    executor.run_until_parked();
14083
14084    cx.update_editor(|editor, window, cx| {
14085        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14086    });
14087    executor.run_until_parked();
14088    cx.assert_state_with_diff(
14089        r#"
14090        - use some::mod1;
14091          use some::mod2;
14092
14093          const A: u32 = 42;
14094        - const B: u32 = 42;
14095          const C: u32 = 42;
14096
14097          fn main(ˇ) {
14098        -     println!("hello");
14099        +     //println!("hello");
14100
14101              println!("world");
14102        +     //
14103        +     //
14104          }
14105        "#
14106        .unindent(),
14107    );
14108
14109    cx.set_head_text("new diff base!");
14110    executor.run_until_parked();
14111    cx.assert_state_with_diff(
14112        r#"
14113        - new diff base!
14114        + use some::mod2;
14115        +
14116        + const A: u32 = 42;
14117        + const C: u32 = 42;
14118        +
14119        + fn main(ˇ) {
14120        +     //println!("hello");
14121        +
14122        +     println!("world");
14123        +     //
14124        +     //
14125        + }
14126        "#
14127        .unindent(),
14128    );
14129}
14130
14131#[gpui::test]
14132async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14133    init_test(cx, |_| {});
14134
14135    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14136    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14137    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14138    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14139    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14140    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14141
14142    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14143    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14144    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14145
14146    let multi_buffer = cx.new(|cx| {
14147        let mut multibuffer = MultiBuffer::new(ReadWrite);
14148        multibuffer.push_excerpts(
14149            buffer_1.clone(),
14150            [
14151                ExcerptRange {
14152                    context: Point::new(0, 0)..Point::new(3, 0),
14153                    primary: None,
14154                },
14155                ExcerptRange {
14156                    context: Point::new(5, 0)..Point::new(7, 0),
14157                    primary: None,
14158                },
14159                ExcerptRange {
14160                    context: Point::new(9, 0)..Point::new(10, 3),
14161                    primary: None,
14162                },
14163            ],
14164            cx,
14165        );
14166        multibuffer.push_excerpts(
14167            buffer_2.clone(),
14168            [
14169                ExcerptRange {
14170                    context: Point::new(0, 0)..Point::new(3, 0),
14171                    primary: None,
14172                },
14173                ExcerptRange {
14174                    context: Point::new(5, 0)..Point::new(7, 0),
14175                    primary: None,
14176                },
14177                ExcerptRange {
14178                    context: Point::new(9, 0)..Point::new(10, 3),
14179                    primary: None,
14180                },
14181            ],
14182            cx,
14183        );
14184        multibuffer.push_excerpts(
14185            buffer_3.clone(),
14186            [
14187                ExcerptRange {
14188                    context: Point::new(0, 0)..Point::new(3, 0),
14189                    primary: None,
14190                },
14191                ExcerptRange {
14192                    context: Point::new(5, 0)..Point::new(7, 0),
14193                    primary: None,
14194                },
14195                ExcerptRange {
14196                    context: Point::new(9, 0)..Point::new(10, 3),
14197                    primary: None,
14198                },
14199            ],
14200            cx,
14201        );
14202        multibuffer
14203    });
14204
14205    let editor =
14206        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14207    editor
14208        .update(cx, |editor, _window, cx| {
14209            for (buffer, diff_base) in [
14210                (buffer_1.clone(), file_1_old),
14211                (buffer_2.clone(), file_2_old),
14212                (buffer_3.clone(), file_3_old),
14213            ] {
14214                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14215                editor
14216                    .buffer
14217                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14218            }
14219        })
14220        .unwrap();
14221
14222    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14223    cx.run_until_parked();
14224
14225    cx.assert_editor_state(
14226        &"
14227            ˇaaa
14228            ccc
14229            ddd
14230
14231            ggg
14232            hhh
14233
14234
14235            lll
14236            mmm
14237            NNN
14238
14239            qqq
14240            rrr
14241
14242            uuu
14243            111
14244            222
14245            333
14246
14247            666
14248            777
14249
14250            000
14251            !!!"
14252        .unindent(),
14253    );
14254
14255    cx.update_editor(|editor, window, cx| {
14256        editor.select_all(&SelectAll, window, cx);
14257        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14258    });
14259    cx.executor().run_until_parked();
14260
14261    cx.assert_state_with_diff(
14262        "
14263            «aaa
14264          - bbb
14265            ccc
14266            ddd
14267
14268            ggg
14269            hhh
14270
14271
14272            lll
14273            mmm
14274          - nnn
14275          + NNN
14276
14277            qqq
14278            rrr
14279
14280            uuu
14281            111
14282            222
14283            333
14284
14285          + 666
14286            777
14287
14288            000
14289            !!!ˇ»"
14290            .unindent(),
14291    );
14292}
14293
14294#[gpui::test]
14295async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14296    init_test(cx, |_| {});
14297
14298    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14299    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14300
14301    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14302    let multi_buffer = cx.new(|cx| {
14303        let mut multibuffer = MultiBuffer::new(ReadWrite);
14304        multibuffer.push_excerpts(
14305            buffer.clone(),
14306            [
14307                ExcerptRange {
14308                    context: Point::new(0, 0)..Point::new(2, 0),
14309                    primary: None,
14310                },
14311                ExcerptRange {
14312                    context: Point::new(4, 0)..Point::new(7, 0),
14313                    primary: None,
14314                },
14315                ExcerptRange {
14316                    context: Point::new(9, 0)..Point::new(10, 0),
14317                    primary: None,
14318                },
14319            ],
14320            cx,
14321        );
14322        multibuffer
14323    });
14324
14325    let editor =
14326        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14327    editor
14328        .update(cx, |editor, _window, cx| {
14329            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14330            editor
14331                .buffer
14332                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14333        })
14334        .unwrap();
14335
14336    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14337    cx.run_until_parked();
14338
14339    cx.update_editor(|editor, window, cx| {
14340        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14341    });
14342    cx.executor().run_until_parked();
14343
14344    // When the start of a hunk coincides with the start of its excerpt,
14345    // the hunk is expanded. When the start of a a hunk is earlier than
14346    // the start of its excerpt, the hunk is not expanded.
14347    cx.assert_state_with_diff(
14348        "
14349            ˇaaa
14350          - bbb
14351          + BBB
14352
14353          - ddd
14354          - eee
14355          + DDD
14356          + EEE
14357            fff
14358
14359            iii
14360        "
14361        .unindent(),
14362    );
14363}
14364
14365#[gpui::test]
14366async fn test_edits_around_expanded_insertion_hunks(
14367    executor: BackgroundExecutor,
14368    cx: &mut TestAppContext,
14369) {
14370    init_test(cx, |_| {});
14371
14372    let mut cx = EditorTestContext::new(cx).await;
14373
14374    let diff_base = r#"
14375        use some::mod1;
14376        use some::mod2;
14377
14378        const A: u32 = 42;
14379
14380        fn main() {
14381            println!("hello");
14382
14383            println!("world");
14384        }
14385        "#
14386    .unindent();
14387    executor.run_until_parked();
14388    cx.set_state(
14389        &r#"
14390        use some::mod1;
14391        use some::mod2;
14392
14393        const A: u32 = 42;
14394        const B: u32 = 42;
14395        const C: u32 = 42;
14396        ˇ
14397
14398        fn main() {
14399            println!("hello");
14400
14401            println!("world");
14402        }
14403        "#
14404        .unindent(),
14405    );
14406
14407    cx.set_head_text(&diff_base);
14408    executor.run_until_parked();
14409
14410    cx.update_editor(|editor, window, cx| {
14411        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14412    });
14413    executor.run_until_parked();
14414
14415    cx.assert_state_with_diff(
14416        r#"
14417        use some::mod1;
14418        use some::mod2;
14419
14420        const A: u32 = 42;
14421      + const B: u32 = 42;
14422      + const C: u32 = 42;
14423      + ˇ
14424
14425        fn main() {
14426            println!("hello");
14427
14428            println!("world");
14429        }
14430      "#
14431        .unindent(),
14432    );
14433
14434    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14435    executor.run_until_parked();
14436
14437    cx.assert_state_with_diff(
14438        r#"
14439        use some::mod1;
14440        use some::mod2;
14441
14442        const A: u32 = 42;
14443      + const B: u32 = 42;
14444      + const C: u32 = 42;
14445      + const D: u32 = 42;
14446      + ˇ
14447
14448        fn main() {
14449            println!("hello");
14450
14451            println!("world");
14452        }
14453      "#
14454        .unindent(),
14455    );
14456
14457    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14458    executor.run_until_parked();
14459
14460    cx.assert_state_with_diff(
14461        r#"
14462        use some::mod1;
14463        use some::mod2;
14464
14465        const A: u32 = 42;
14466      + const B: u32 = 42;
14467      + const C: u32 = 42;
14468      + const D: u32 = 42;
14469      + const E: u32 = 42;
14470      + ˇ
14471
14472        fn main() {
14473            println!("hello");
14474
14475            println!("world");
14476        }
14477      "#
14478        .unindent(),
14479    );
14480
14481    cx.update_editor(|editor, window, cx| {
14482        editor.delete_line(&DeleteLine, window, cx);
14483    });
14484    executor.run_until_parked();
14485
14486    cx.assert_state_with_diff(
14487        r#"
14488        use some::mod1;
14489        use some::mod2;
14490
14491        const A: u32 = 42;
14492      + const B: u32 = 42;
14493      + const C: u32 = 42;
14494      + const D: u32 = 42;
14495      + const E: u32 = 42;
14496        ˇ
14497        fn main() {
14498            println!("hello");
14499
14500            println!("world");
14501        }
14502      "#
14503        .unindent(),
14504    );
14505
14506    cx.update_editor(|editor, window, cx| {
14507        editor.move_up(&MoveUp, window, cx);
14508        editor.delete_line(&DeleteLine, window, cx);
14509        editor.move_up(&MoveUp, window, cx);
14510        editor.delete_line(&DeleteLine, window, cx);
14511        editor.move_up(&MoveUp, window, cx);
14512        editor.delete_line(&DeleteLine, window, cx);
14513    });
14514    executor.run_until_parked();
14515    cx.assert_state_with_diff(
14516        r#"
14517        use some::mod1;
14518        use some::mod2;
14519
14520        const A: u32 = 42;
14521      + const B: u32 = 42;
14522        ˇ
14523        fn main() {
14524            println!("hello");
14525
14526            println!("world");
14527        }
14528      "#
14529        .unindent(),
14530    );
14531
14532    cx.update_editor(|editor, window, cx| {
14533        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14534        editor.delete_line(&DeleteLine, window, cx);
14535    });
14536    executor.run_until_parked();
14537    cx.assert_state_with_diff(
14538        r#"
14539        ˇ
14540        fn main() {
14541            println!("hello");
14542
14543            println!("world");
14544        }
14545      "#
14546        .unindent(),
14547    );
14548}
14549
14550#[gpui::test]
14551async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14552    init_test(cx, |_| {});
14553
14554    let mut cx = EditorTestContext::new(cx).await;
14555    cx.set_head_text(indoc! { "
14556        one
14557        two
14558        three
14559        four
14560        five
14561        "
14562    });
14563    cx.set_state(indoc! { "
14564        one
14565        ˇthree
14566        five
14567    "});
14568    cx.run_until_parked();
14569    cx.update_editor(|editor, window, cx| {
14570        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14571    });
14572    cx.assert_state_with_diff(
14573        indoc! { "
14574        one
14575      - two
14576        ˇthree
14577      - four
14578        five
14579    "}
14580        .to_string(),
14581    );
14582    cx.update_editor(|editor, window, cx| {
14583        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14584    });
14585
14586    cx.assert_state_with_diff(
14587        indoc! { "
14588        one
14589        ˇthree
14590        five
14591    "}
14592        .to_string(),
14593    );
14594
14595    cx.set_state(indoc! { "
14596        one
14597        ˇTWO
14598        three
14599        four
14600        five
14601    "});
14602    cx.run_until_parked();
14603    cx.update_editor(|editor, window, cx| {
14604        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14605    });
14606
14607    cx.assert_state_with_diff(
14608        indoc! { "
14609            one
14610          - two
14611          + ˇTWO
14612            three
14613            four
14614            five
14615        "}
14616        .to_string(),
14617    );
14618    cx.update_editor(|editor, window, cx| {
14619        editor.move_up(&Default::default(), window, cx);
14620        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14621    });
14622    cx.assert_state_with_diff(
14623        indoc! { "
14624            one
14625            ˇTWO
14626            three
14627            four
14628            five
14629        "}
14630        .to_string(),
14631    );
14632}
14633
14634#[gpui::test]
14635async fn test_edits_around_expanded_deletion_hunks(
14636    executor: BackgroundExecutor,
14637    cx: &mut TestAppContext,
14638) {
14639    init_test(cx, |_| {});
14640
14641    let mut cx = EditorTestContext::new(cx).await;
14642
14643    let diff_base = r#"
14644        use some::mod1;
14645        use some::mod2;
14646
14647        const A: u32 = 42;
14648        const B: u32 = 42;
14649        const C: u32 = 42;
14650
14651
14652        fn main() {
14653            println!("hello");
14654
14655            println!("world");
14656        }
14657    "#
14658    .unindent();
14659    executor.run_until_parked();
14660    cx.set_state(
14661        &r#"
14662        use some::mod1;
14663        use some::mod2;
14664
14665        ˇconst B: u32 = 42;
14666        const C: u32 = 42;
14667
14668
14669        fn main() {
14670            println!("hello");
14671
14672            println!("world");
14673        }
14674        "#
14675        .unindent(),
14676    );
14677
14678    cx.set_head_text(&diff_base);
14679    executor.run_until_parked();
14680
14681    cx.update_editor(|editor, window, cx| {
14682        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14683    });
14684    executor.run_until_parked();
14685
14686    cx.assert_state_with_diff(
14687        r#"
14688        use some::mod1;
14689        use some::mod2;
14690
14691      - const A: u32 = 42;
14692        ˇconst B: u32 = 42;
14693        const C: u32 = 42;
14694
14695
14696        fn main() {
14697            println!("hello");
14698
14699            println!("world");
14700        }
14701      "#
14702        .unindent(),
14703    );
14704
14705    cx.update_editor(|editor, window, cx| {
14706        editor.delete_line(&DeleteLine, window, cx);
14707    });
14708    executor.run_until_parked();
14709    cx.assert_state_with_diff(
14710        r#"
14711        use some::mod1;
14712        use some::mod2;
14713
14714      - const A: u32 = 42;
14715      - const B: u32 = 42;
14716        ˇconst C: u32 = 42;
14717
14718
14719        fn main() {
14720            println!("hello");
14721
14722            println!("world");
14723        }
14724      "#
14725        .unindent(),
14726    );
14727
14728    cx.update_editor(|editor, window, cx| {
14729        editor.delete_line(&DeleteLine, window, cx);
14730    });
14731    executor.run_until_parked();
14732    cx.assert_state_with_diff(
14733        r#"
14734        use some::mod1;
14735        use some::mod2;
14736
14737      - const A: u32 = 42;
14738      - const B: u32 = 42;
14739      - const C: u32 = 42;
14740        ˇ
14741
14742        fn main() {
14743            println!("hello");
14744
14745            println!("world");
14746        }
14747      "#
14748        .unindent(),
14749    );
14750
14751    cx.update_editor(|editor, window, cx| {
14752        editor.handle_input("replacement", window, cx);
14753    });
14754    executor.run_until_parked();
14755    cx.assert_state_with_diff(
14756        r#"
14757        use some::mod1;
14758        use some::mod2;
14759
14760      - const A: u32 = 42;
14761      - const B: u32 = 42;
14762      - const C: u32 = 42;
14763      -
14764      + replacementˇ
14765
14766        fn main() {
14767            println!("hello");
14768
14769            println!("world");
14770        }
14771      "#
14772        .unindent(),
14773    );
14774}
14775
14776#[gpui::test]
14777async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14778    init_test(cx, |_| {});
14779
14780    let mut cx = EditorTestContext::new(cx).await;
14781
14782    let base_text = r#"
14783        one
14784        two
14785        three
14786        four
14787        five
14788    "#
14789    .unindent();
14790    executor.run_until_parked();
14791    cx.set_state(
14792        &r#"
14793        one
14794        two
14795        fˇour
14796        five
14797        "#
14798        .unindent(),
14799    );
14800
14801    cx.set_head_text(&base_text);
14802    executor.run_until_parked();
14803
14804    cx.update_editor(|editor, window, cx| {
14805        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14806    });
14807    executor.run_until_parked();
14808
14809    cx.assert_state_with_diff(
14810        r#"
14811          one
14812          two
14813        - three
14814          fˇour
14815          five
14816        "#
14817        .unindent(),
14818    );
14819
14820    cx.update_editor(|editor, window, cx| {
14821        editor.backspace(&Backspace, window, cx);
14822        editor.backspace(&Backspace, window, cx);
14823    });
14824    executor.run_until_parked();
14825    cx.assert_state_with_diff(
14826        r#"
14827          one
14828          two
14829        - threeˇ
14830        - four
14831        + our
14832          five
14833        "#
14834        .unindent(),
14835    );
14836}
14837
14838#[gpui::test]
14839async fn test_edit_after_expanded_modification_hunk(
14840    executor: BackgroundExecutor,
14841    cx: &mut TestAppContext,
14842) {
14843    init_test(cx, |_| {});
14844
14845    let mut cx = EditorTestContext::new(cx).await;
14846
14847    let diff_base = r#"
14848        use some::mod1;
14849        use some::mod2;
14850
14851        const A: u32 = 42;
14852        const B: u32 = 42;
14853        const C: u32 = 42;
14854        const D: u32 = 42;
14855
14856
14857        fn main() {
14858            println!("hello");
14859
14860            println!("world");
14861        }"#
14862    .unindent();
14863
14864    cx.set_state(
14865        &r#"
14866        use some::mod1;
14867        use some::mod2;
14868
14869        const A: u32 = 42;
14870        const B: u32 = 42;
14871        const C: u32 = 43ˇ
14872        const D: u32 = 42;
14873
14874
14875        fn main() {
14876            println!("hello");
14877
14878            println!("world");
14879        }"#
14880        .unindent(),
14881    );
14882
14883    cx.set_head_text(&diff_base);
14884    executor.run_until_parked();
14885    cx.update_editor(|editor, window, cx| {
14886        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14887    });
14888    executor.run_until_parked();
14889
14890    cx.assert_state_with_diff(
14891        r#"
14892        use some::mod1;
14893        use some::mod2;
14894
14895        const A: u32 = 42;
14896        const B: u32 = 42;
14897      - const C: u32 = 42;
14898      + const C: u32 = 43ˇ
14899        const D: u32 = 42;
14900
14901
14902        fn main() {
14903            println!("hello");
14904
14905            println!("world");
14906        }"#
14907        .unindent(),
14908    );
14909
14910    cx.update_editor(|editor, window, cx| {
14911        editor.handle_input("\nnew_line\n", window, cx);
14912    });
14913    executor.run_until_parked();
14914
14915    cx.assert_state_with_diff(
14916        r#"
14917        use some::mod1;
14918        use some::mod2;
14919
14920        const A: u32 = 42;
14921        const B: u32 = 42;
14922      - const C: u32 = 42;
14923      + const C: u32 = 43
14924      + new_line
14925      + ˇ
14926        const D: u32 = 42;
14927
14928
14929        fn main() {
14930            println!("hello");
14931
14932            println!("world");
14933        }"#
14934        .unindent(),
14935    );
14936}
14937
14938#[gpui::test]
14939async fn test_stage_and_unstage_added_file_hunk(
14940    executor: BackgroundExecutor,
14941    cx: &mut TestAppContext,
14942) {
14943    init_test(cx, |_| {});
14944
14945    let mut cx = EditorTestContext::new(cx).await;
14946    cx.update_editor(|editor, _, cx| {
14947        editor.set_expand_all_diff_hunks(cx);
14948    });
14949
14950    let working_copy = r#"
14951            ˇfn main() {
14952                println!("hello, world!");
14953            }
14954        "#
14955    .unindent();
14956
14957    cx.set_state(&working_copy);
14958    executor.run_until_parked();
14959
14960    cx.assert_state_with_diff(
14961        r#"
14962            + ˇfn main() {
14963            +     println!("hello, world!");
14964            + }
14965        "#
14966        .unindent(),
14967    );
14968    cx.assert_index_text(None);
14969
14970    cx.update_editor(|editor, window, cx| {
14971        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14972    });
14973    executor.run_until_parked();
14974    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14975    cx.assert_state_with_diff(
14976        r#"
14977            + ˇfn main() {
14978            +     println!("hello, world!");
14979            + }
14980        "#
14981        .unindent(),
14982    );
14983
14984    cx.update_editor(|editor, window, cx| {
14985        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14986    });
14987    executor.run_until_parked();
14988    cx.assert_index_text(None);
14989}
14990
14991async fn setup_indent_guides_editor(
14992    text: &str,
14993    cx: &mut TestAppContext,
14994) -> (BufferId, EditorTestContext) {
14995    init_test(cx, |_| {});
14996
14997    let mut cx = EditorTestContext::new(cx).await;
14998
14999    let buffer_id = cx.update_editor(|editor, window, cx| {
15000        editor.set_text(text, window, cx);
15001        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15002
15003        buffer_ids[0]
15004    });
15005
15006    (buffer_id, cx)
15007}
15008
15009fn assert_indent_guides(
15010    range: Range<u32>,
15011    expected: Vec<IndentGuide>,
15012    active_indices: Option<Vec<usize>>,
15013    cx: &mut EditorTestContext,
15014) {
15015    let indent_guides = cx.update_editor(|editor, window, cx| {
15016        let snapshot = editor.snapshot(window, cx).display_snapshot;
15017        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15018            editor,
15019            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15020            true,
15021            &snapshot,
15022            cx,
15023        );
15024
15025        indent_guides.sort_by(|a, b| {
15026            a.depth.cmp(&b.depth).then(
15027                a.start_row
15028                    .cmp(&b.start_row)
15029                    .then(a.end_row.cmp(&b.end_row)),
15030            )
15031        });
15032        indent_guides
15033    });
15034
15035    if let Some(expected) = active_indices {
15036        let active_indices = cx.update_editor(|editor, window, cx| {
15037            let snapshot = editor.snapshot(window, cx).display_snapshot;
15038            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15039        });
15040
15041        assert_eq!(
15042            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15043            expected,
15044            "Active indent guide indices do not match"
15045        );
15046    }
15047
15048    assert_eq!(indent_guides, expected, "Indent guides do not match");
15049}
15050
15051fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15052    IndentGuide {
15053        buffer_id,
15054        start_row: MultiBufferRow(start_row),
15055        end_row: MultiBufferRow(end_row),
15056        depth,
15057        tab_size: 4,
15058        settings: IndentGuideSettings {
15059            enabled: true,
15060            line_width: 1,
15061            active_line_width: 1,
15062            ..Default::default()
15063        },
15064    }
15065}
15066
15067#[gpui::test]
15068async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15069    let (buffer_id, mut cx) = setup_indent_guides_editor(
15070        &"
15071    fn main() {
15072        let a = 1;
15073    }"
15074        .unindent(),
15075        cx,
15076    )
15077    .await;
15078
15079    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15080}
15081
15082#[gpui::test]
15083async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15084    let (buffer_id, mut cx) = setup_indent_guides_editor(
15085        &"
15086    fn main() {
15087        let a = 1;
15088        let b = 2;
15089    }"
15090        .unindent(),
15091        cx,
15092    )
15093    .await;
15094
15095    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15096}
15097
15098#[gpui::test]
15099async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15100    let (buffer_id, mut cx) = setup_indent_guides_editor(
15101        &"
15102    fn main() {
15103        let a = 1;
15104        if a == 3 {
15105            let b = 2;
15106        } else {
15107            let c = 3;
15108        }
15109    }"
15110        .unindent(),
15111        cx,
15112    )
15113    .await;
15114
15115    assert_indent_guides(
15116        0..8,
15117        vec![
15118            indent_guide(buffer_id, 1, 6, 0),
15119            indent_guide(buffer_id, 3, 3, 1),
15120            indent_guide(buffer_id, 5, 5, 1),
15121        ],
15122        None,
15123        &mut cx,
15124    );
15125}
15126
15127#[gpui::test]
15128async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15129    let (buffer_id, mut cx) = setup_indent_guides_editor(
15130        &"
15131    fn main() {
15132        let a = 1;
15133            let b = 2;
15134        let c = 3;
15135    }"
15136        .unindent(),
15137        cx,
15138    )
15139    .await;
15140
15141    assert_indent_guides(
15142        0..5,
15143        vec![
15144            indent_guide(buffer_id, 1, 3, 0),
15145            indent_guide(buffer_id, 2, 2, 1),
15146        ],
15147        None,
15148        &mut cx,
15149    );
15150}
15151
15152#[gpui::test]
15153async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15154    let (buffer_id, mut cx) = setup_indent_guides_editor(
15155        &"
15156        fn main() {
15157            let a = 1;
15158
15159            let c = 3;
15160        }"
15161        .unindent(),
15162        cx,
15163    )
15164    .await;
15165
15166    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15167}
15168
15169#[gpui::test]
15170async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15171    let (buffer_id, mut cx) = setup_indent_guides_editor(
15172        &"
15173        fn main() {
15174            let a = 1;
15175
15176            let c = 3;
15177
15178            if a == 3 {
15179                let b = 2;
15180            } else {
15181                let c = 3;
15182            }
15183        }"
15184        .unindent(),
15185        cx,
15186    )
15187    .await;
15188
15189    assert_indent_guides(
15190        0..11,
15191        vec![
15192            indent_guide(buffer_id, 1, 9, 0),
15193            indent_guide(buffer_id, 6, 6, 1),
15194            indent_guide(buffer_id, 8, 8, 1),
15195        ],
15196        None,
15197        &mut cx,
15198    );
15199}
15200
15201#[gpui::test]
15202async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15203    let (buffer_id, mut cx) = setup_indent_guides_editor(
15204        &"
15205        fn main() {
15206            let a = 1;
15207
15208            let c = 3;
15209
15210            if a == 3 {
15211                let b = 2;
15212            } else {
15213                let c = 3;
15214            }
15215        }"
15216        .unindent(),
15217        cx,
15218    )
15219    .await;
15220
15221    assert_indent_guides(
15222        1..11,
15223        vec![
15224            indent_guide(buffer_id, 1, 9, 0),
15225            indent_guide(buffer_id, 6, 6, 1),
15226            indent_guide(buffer_id, 8, 8, 1),
15227        ],
15228        None,
15229        &mut cx,
15230    );
15231}
15232
15233#[gpui::test]
15234async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15235    let (buffer_id, mut cx) = setup_indent_guides_editor(
15236        &"
15237        fn main() {
15238            let a = 1;
15239
15240            let c = 3;
15241
15242            if a == 3 {
15243                let b = 2;
15244            } else {
15245                let c = 3;
15246            }
15247        }"
15248        .unindent(),
15249        cx,
15250    )
15251    .await;
15252
15253    assert_indent_guides(
15254        1..10,
15255        vec![
15256            indent_guide(buffer_id, 1, 9, 0),
15257            indent_guide(buffer_id, 6, 6, 1),
15258            indent_guide(buffer_id, 8, 8, 1),
15259        ],
15260        None,
15261        &mut cx,
15262    );
15263}
15264
15265#[gpui::test]
15266async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15267    let (buffer_id, mut cx) = setup_indent_guides_editor(
15268        &"
15269        block1
15270            block2
15271                block3
15272                    block4
15273            block2
15274        block1
15275        block1"
15276            .unindent(),
15277        cx,
15278    )
15279    .await;
15280
15281    assert_indent_guides(
15282        1..10,
15283        vec![
15284            indent_guide(buffer_id, 1, 4, 0),
15285            indent_guide(buffer_id, 2, 3, 1),
15286            indent_guide(buffer_id, 3, 3, 2),
15287        ],
15288        None,
15289        &mut cx,
15290    );
15291}
15292
15293#[gpui::test]
15294async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15295    let (buffer_id, mut cx) = setup_indent_guides_editor(
15296        &"
15297        block1
15298            block2
15299                block3
15300
15301        block1
15302        block1"
15303            .unindent(),
15304        cx,
15305    )
15306    .await;
15307
15308    assert_indent_guides(
15309        0..6,
15310        vec![
15311            indent_guide(buffer_id, 1, 2, 0),
15312            indent_guide(buffer_id, 2, 2, 1),
15313        ],
15314        None,
15315        &mut cx,
15316    );
15317}
15318
15319#[gpui::test]
15320async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15321    let (buffer_id, mut cx) = setup_indent_guides_editor(
15322        &"
15323        block1
15324
15325
15326
15327            block2
15328        "
15329        .unindent(),
15330        cx,
15331    )
15332    .await;
15333
15334    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15335}
15336
15337#[gpui::test]
15338async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15339    let (buffer_id, mut cx) = setup_indent_guides_editor(
15340        &"
15341        def a:
15342        \tb = 3
15343        \tif True:
15344        \t\tc = 4
15345        \t\td = 5
15346        \tprint(b)
15347        "
15348        .unindent(),
15349        cx,
15350    )
15351    .await;
15352
15353    assert_indent_guides(
15354        0..6,
15355        vec![
15356            indent_guide(buffer_id, 1, 6, 0),
15357            indent_guide(buffer_id, 3, 4, 1),
15358        ],
15359        None,
15360        &mut cx,
15361    );
15362}
15363
15364#[gpui::test]
15365async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15366    let (buffer_id, mut cx) = setup_indent_guides_editor(
15367        &"
15368    fn main() {
15369        let a = 1;
15370    }"
15371        .unindent(),
15372        cx,
15373    )
15374    .await;
15375
15376    cx.update_editor(|editor, window, cx| {
15377        editor.change_selections(None, window, cx, |s| {
15378            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15379        });
15380    });
15381
15382    assert_indent_guides(
15383        0..3,
15384        vec![indent_guide(buffer_id, 1, 1, 0)],
15385        Some(vec![0]),
15386        &mut cx,
15387    );
15388}
15389
15390#[gpui::test]
15391async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15392    let (buffer_id, mut cx) = setup_indent_guides_editor(
15393        &"
15394    fn main() {
15395        if 1 == 2 {
15396            let a = 1;
15397        }
15398    }"
15399        .unindent(),
15400        cx,
15401    )
15402    .await;
15403
15404    cx.update_editor(|editor, window, cx| {
15405        editor.change_selections(None, window, cx, |s| {
15406            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15407        });
15408    });
15409
15410    assert_indent_guides(
15411        0..4,
15412        vec![
15413            indent_guide(buffer_id, 1, 3, 0),
15414            indent_guide(buffer_id, 2, 2, 1),
15415        ],
15416        Some(vec![1]),
15417        &mut cx,
15418    );
15419
15420    cx.update_editor(|editor, window, cx| {
15421        editor.change_selections(None, window, cx, |s| {
15422            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15423        });
15424    });
15425
15426    assert_indent_guides(
15427        0..4,
15428        vec![
15429            indent_guide(buffer_id, 1, 3, 0),
15430            indent_guide(buffer_id, 2, 2, 1),
15431        ],
15432        Some(vec![1]),
15433        &mut cx,
15434    );
15435
15436    cx.update_editor(|editor, window, cx| {
15437        editor.change_selections(None, window, cx, |s| {
15438            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15439        });
15440    });
15441
15442    assert_indent_guides(
15443        0..4,
15444        vec![
15445            indent_guide(buffer_id, 1, 3, 0),
15446            indent_guide(buffer_id, 2, 2, 1),
15447        ],
15448        Some(vec![0]),
15449        &mut cx,
15450    );
15451}
15452
15453#[gpui::test]
15454async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15455    let (buffer_id, mut cx) = setup_indent_guides_editor(
15456        &"
15457    fn main() {
15458        let a = 1;
15459
15460        let b = 2;
15461    }"
15462        .unindent(),
15463        cx,
15464    )
15465    .await;
15466
15467    cx.update_editor(|editor, window, cx| {
15468        editor.change_selections(None, window, cx, |s| {
15469            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15470        });
15471    });
15472
15473    assert_indent_guides(
15474        0..5,
15475        vec![indent_guide(buffer_id, 1, 3, 0)],
15476        Some(vec![0]),
15477        &mut cx,
15478    );
15479}
15480
15481#[gpui::test]
15482async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15483    let (buffer_id, mut cx) = setup_indent_guides_editor(
15484        &"
15485    def m:
15486        a = 1
15487        pass"
15488            .unindent(),
15489        cx,
15490    )
15491    .await;
15492
15493    cx.update_editor(|editor, window, cx| {
15494        editor.change_selections(None, window, cx, |s| {
15495            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15496        });
15497    });
15498
15499    assert_indent_guides(
15500        0..3,
15501        vec![indent_guide(buffer_id, 1, 2, 0)],
15502        Some(vec![0]),
15503        &mut cx,
15504    );
15505}
15506
15507#[gpui::test]
15508async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15509    init_test(cx, |_| {});
15510    let mut cx = EditorTestContext::new(cx).await;
15511    let text = indoc! {
15512        "
15513        impl A {
15514            fn b() {
15515                0;
15516                3;
15517                5;
15518                6;
15519                7;
15520            }
15521        }
15522        "
15523    };
15524    let base_text = indoc! {
15525        "
15526        impl A {
15527            fn b() {
15528                0;
15529                1;
15530                2;
15531                3;
15532                4;
15533            }
15534            fn c() {
15535                5;
15536                6;
15537                7;
15538            }
15539        }
15540        "
15541    };
15542
15543    cx.update_editor(|editor, window, cx| {
15544        editor.set_text(text, window, cx);
15545
15546        editor.buffer().update(cx, |multibuffer, cx| {
15547            let buffer = multibuffer.as_singleton().unwrap();
15548            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15549
15550            multibuffer.set_all_diff_hunks_expanded(cx);
15551            multibuffer.add_diff(diff, cx);
15552
15553            buffer.read(cx).remote_id()
15554        })
15555    });
15556    cx.run_until_parked();
15557
15558    cx.assert_state_with_diff(
15559        indoc! { "
15560          impl A {
15561              fn b() {
15562                  0;
15563        -         1;
15564        -         2;
15565                  3;
15566        -         4;
15567        -     }
15568        -     fn c() {
15569                  5;
15570                  6;
15571                  7;
15572              }
15573          }
15574          ˇ"
15575        }
15576        .to_string(),
15577    );
15578
15579    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15580        editor
15581            .snapshot(window, cx)
15582            .buffer_snapshot
15583            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15584            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15585            .collect::<Vec<_>>()
15586    });
15587    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15588    assert_eq!(
15589        actual_guides,
15590        vec![
15591            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15592            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15593            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15594        ]
15595    );
15596}
15597
15598#[gpui::test]
15599async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15600    init_test(cx, |_| {});
15601    let mut cx = EditorTestContext::new(cx).await;
15602
15603    let diff_base = r#"
15604        a
15605        b
15606        c
15607        "#
15608    .unindent();
15609
15610    cx.set_state(
15611        &r#"
15612        ˇA
15613        b
15614        C
15615        "#
15616        .unindent(),
15617    );
15618    cx.set_head_text(&diff_base);
15619    cx.update_editor(|editor, window, cx| {
15620        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15621    });
15622    executor.run_until_parked();
15623
15624    let both_hunks_expanded = r#"
15625        - a
15626        + ˇA
15627          b
15628        - c
15629        + C
15630        "#
15631    .unindent();
15632
15633    cx.assert_state_with_diff(both_hunks_expanded.clone());
15634
15635    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15636        let snapshot = editor.snapshot(window, cx);
15637        let hunks = editor
15638            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15639            .collect::<Vec<_>>();
15640        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15641        let buffer_id = hunks[0].buffer_id;
15642        hunks
15643            .into_iter()
15644            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15645            .collect::<Vec<_>>()
15646    });
15647    assert_eq!(hunk_ranges.len(), 2);
15648
15649    cx.update_editor(|editor, _, cx| {
15650        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15651    });
15652    executor.run_until_parked();
15653
15654    let second_hunk_expanded = r#"
15655          ˇA
15656          b
15657        - c
15658        + C
15659        "#
15660    .unindent();
15661
15662    cx.assert_state_with_diff(second_hunk_expanded);
15663
15664    cx.update_editor(|editor, _, cx| {
15665        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15666    });
15667    executor.run_until_parked();
15668
15669    cx.assert_state_with_diff(both_hunks_expanded.clone());
15670
15671    cx.update_editor(|editor, _, cx| {
15672        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15673    });
15674    executor.run_until_parked();
15675
15676    let first_hunk_expanded = r#"
15677        - a
15678        + ˇA
15679          b
15680          C
15681        "#
15682    .unindent();
15683
15684    cx.assert_state_with_diff(first_hunk_expanded);
15685
15686    cx.update_editor(|editor, _, cx| {
15687        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15688    });
15689    executor.run_until_parked();
15690
15691    cx.assert_state_with_diff(both_hunks_expanded);
15692
15693    cx.set_state(
15694        &r#"
15695        ˇA
15696        b
15697        "#
15698        .unindent(),
15699    );
15700    cx.run_until_parked();
15701
15702    // TODO this cursor position seems bad
15703    cx.assert_state_with_diff(
15704        r#"
15705        - ˇa
15706        + A
15707          b
15708        "#
15709        .unindent(),
15710    );
15711
15712    cx.update_editor(|editor, window, cx| {
15713        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15714    });
15715
15716    cx.assert_state_with_diff(
15717        r#"
15718            - ˇa
15719            + A
15720              b
15721            - c
15722            "#
15723        .unindent(),
15724    );
15725
15726    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15727        let snapshot = editor.snapshot(window, cx);
15728        let hunks = editor
15729            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15730            .collect::<Vec<_>>();
15731        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15732        let buffer_id = hunks[0].buffer_id;
15733        hunks
15734            .into_iter()
15735            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15736            .collect::<Vec<_>>()
15737    });
15738    assert_eq!(hunk_ranges.len(), 2);
15739
15740    cx.update_editor(|editor, _, cx| {
15741        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15742    });
15743    executor.run_until_parked();
15744
15745    cx.assert_state_with_diff(
15746        r#"
15747        - ˇa
15748        + A
15749          b
15750        "#
15751        .unindent(),
15752    );
15753}
15754
15755#[gpui::test]
15756async fn test_toggle_deletion_hunk_at_start_of_file(
15757    executor: BackgroundExecutor,
15758    cx: &mut TestAppContext,
15759) {
15760    init_test(cx, |_| {});
15761    let mut cx = EditorTestContext::new(cx).await;
15762
15763    let diff_base = r#"
15764        a
15765        b
15766        c
15767        "#
15768    .unindent();
15769
15770    cx.set_state(
15771        &r#"
15772        ˇb
15773        c
15774        "#
15775        .unindent(),
15776    );
15777    cx.set_head_text(&diff_base);
15778    cx.update_editor(|editor, window, cx| {
15779        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15780    });
15781    executor.run_until_parked();
15782
15783    let hunk_expanded = r#"
15784        - a
15785          ˇb
15786          c
15787        "#
15788    .unindent();
15789
15790    cx.assert_state_with_diff(hunk_expanded.clone());
15791
15792    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15793        let snapshot = editor.snapshot(window, cx);
15794        let hunks = editor
15795            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15796            .collect::<Vec<_>>();
15797        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15798        let buffer_id = hunks[0].buffer_id;
15799        hunks
15800            .into_iter()
15801            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15802            .collect::<Vec<_>>()
15803    });
15804    assert_eq!(hunk_ranges.len(), 1);
15805
15806    cx.update_editor(|editor, _, cx| {
15807        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15808    });
15809    executor.run_until_parked();
15810
15811    let hunk_collapsed = r#"
15812          ˇb
15813          c
15814        "#
15815    .unindent();
15816
15817    cx.assert_state_with_diff(hunk_collapsed);
15818
15819    cx.update_editor(|editor, _, cx| {
15820        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15821    });
15822    executor.run_until_parked();
15823
15824    cx.assert_state_with_diff(hunk_expanded.clone());
15825}
15826
15827#[gpui::test]
15828async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15829    init_test(cx, |_| {});
15830
15831    let fs = FakeFs::new(cx.executor());
15832    fs.insert_tree(
15833        path!("/test"),
15834        json!({
15835            ".git": {},
15836            "file-1": "ONE\n",
15837            "file-2": "TWO\n",
15838            "file-3": "THREE\n",
15839        }),
15840    )
15841    .await;
15842
15843    fs.set_head_for_repo(
15844        path!("/test/.git").as_ref(),
15845        &[
15846            ("file-1".into(), "one\n".into()),
15847            ("file-2".into(), "two\n".into()),
15848            ("file-3".into(), "three\n".into()),
15849        ],
15850    );
15851
15852    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15853    let mut buffers = vec![];
15854    for i in 1..=3 {
15855        let buffer = project
15856            .update(cx, |project, cx| {
15857                let path = format!(path!("/test/file-{}"), i);
15858                project.open_local_buffer(path, cx)
15859            })
15860            .await
15861            .unwrap();
15862        buffers.push(buffer);
15863    }
15864
15865    let multibuffer = cx.new(|cx| {
15866        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15867        multibuffer.set_all_diff_hunks_expanded(cx);
15868        for buffer in &buffers {
15869            let snapshot = buffer.read(cx).snapshot();
15870            multibuffer.set_excerpts_for_path(
15871                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15872                buffer.clone(),
15873                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15874                DEFAULT_MULTIBUFFER_CONTEXT,
15875                cx,
15876            );
15877        }
15878        multibuffer
15879    });
15880
15881    let editor = cx.add_window(|window, cx| {
15882        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15883    });
15884    cx.run_until_parked();
15885
15886    let snapshot = editor
15887        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15888        .unwrap();
15889    let hunks = snapshot
15890        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15891        .map(|hunk| match hunk {
15892            DisplayDiffHunk::Unfolded {
15893                display_row_range, ..
15894            } => display_row_range,
15895            DisplayDiffHunk::Folded { .. } => unreachable!(),
15896        })
15897        .collect::<Vec<_>>();
15898    assert_eq!(
15899        hunks,
15900        [
15901            DisplayRow(2)..DisplayRow(4),
15902            DisplayRow(7)..DisplayRow(9),
15903            DisplayRow(12)..DisplayRow(14),
15904        ]
15905    );
15906}
15907
15908#[gpui::test]
15909async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15910    init_test(cx, |_| {});
15911
15912    let mut cx = EditorTestContext::new(cx).await;
15913    cx.set_head_text(indoc! { "
15914        one
15915        two
15916        three
15917        four
15918        five
15919        "
15920    });
15921    cx.set_index_text(indoc! { "
15922        one
15923        two
15924        three
15925        four
15926        five
15927        "
15928    });
15929    cx.set_state(indoc! {"
15930        one
15931        TWO
15932        ˇTHREE
15933        FOUR
15934        five
15935    "});
15936    cx.run_until_parked();
15937    cx.update_editor(|editor, window, cx| {
15938        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15939    });
15940    cx.run_until_parked();
15941    cx.assert_index_text(Some(indoc! {"
15942        one
15943        TWO
15944        THREE
15945        FOUR
15946        five
15947    "}));
15948    cx.set_state(indoc! { "
15949        one
15950        TWO
15951        ˇTHREE-HUNDRED
15952        FOUR
15953        five
15954    "});
15955    cx.run_until_parked();
15956    cx.update_editor(|editor, window, cx| {
15957        let snapshot = editor.snapshot(window, cx);
15958        let hunks = editor
15959            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15960            .collect::<Vec<_>>();
15961        assert_eq!(hunks.len(), 1);
15962        assert_eq!(
15963            hunks[0].status(),
15964            DiffHunkStatus {
15965                kind: DiffHunkStatusKind::Modified,
15966                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15967            }
15968        );
15969
15970        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15971    });
15972    cx.run_until_parked();
15973    cx.assert_index_text(Some(indoc! {"
15974        one
15975        TWO
15976        THREE-HUNDRED
15977        FOUR
15978        five
15979    "}));
15980}
15981
15982#[gpui::test]
15983fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15984    init_test(cx, |_| {});
15985
15986    let editor = cx.add_window(|window, cx| {
15987        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15988        build_editor(buffer, window, cx)
15989    });
15990
15991    let render_args = Arc::new(Mutex::new(None));
15992    let snapshot = editor
15993        .update(cx, |editor, window, cx| {
15994            let snapshot = editor.buffer().read(cx).snapshot(cx);
15995            let range =
15996                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15997
15998            struct RenderArgs {
15999                row: MultiBufferRow,
16000                folded: bool,
16001                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16002            }
16003
16004            let crease = Crease::inline(
16005                range,
16006                FoldPlaceholder::test(),
16007                {
16008                    let toggle_callback = render_args.clone();
16009                    move |row, folded, callback, _window, _cx| {
16010                        *toggle_callback.lock() = Some(RenderArgs {
16011                            row,
16012                            folded,
16013                            callback,
16014                        });
16015                        div()
16016                    }
16017                },
16018                |_row, _folded, _window, _cx| div(),
16019            );
16020
16021            editor.insert_creases(Some(crease), cx);
16022            let snapshot = editor.snapshot(window, cx);
16023            let _div = snapshot.render_crease_toggle(
16024                MultiBufferRow(1),
16025                false,
16026                cx.entity().clone(),
16027                window,
16028                cx,
16029            );
16030            snapshot
16031        })
16032        .unwrap();
16033
16034    let render_args = render_args.lock().take().unwrap();
16035    assert_eq!(render_args.row, MultiBufferRow(1));
16036    assert!(!render_args.folded);
16037    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16038
16039    cx.update_window(*editor, |_, window, cx| {
16040        (render_args.callback)(true, window, cx)
16041    })
16042    .unwrap();
16043    let snapshot = editor
16044        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16045        .unwrap();
16046    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16047
16048    cx.update_window(*editor, |_, window, cx| {
16049        (render_args.callback)(false, window, cx)
16050    })
16051    .unwrap();
16052    let snapshot = editor
16053        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16054        .unwrap();
16055    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16056}
16057
16058#[gpui::test]
16059async fn test_input_text(cx: &mut TestAppContext) {
16060    init_test(cx, |_| {});
16061    let mut cx = EditorTestContext::new(cx).await;
16062
16063    cx.set_state(
16064        &r#"ˇone
16065        two
16066
16067        three
16068        fourˇ
16069        five
16070
16071        siˇx"#
16072            .unindent(),
16073    );
16074
16075    cx.dispatch_action(HandleInput(String::new()));
16076    cx.assert_editor_state(
16077        &r#"ˇone
16078        two
16079
16080        three
16081        fourˇ
16082        five
16083
16084        siˇx"#
16085            .unindent(),
16086    );
16087
16088    cx.dispatch_action(HandleInput("AAAA".to_string()));
16089    cx.assert_editor_state(
16090        &r#"AAAAˇone
16091        two
16092
16093        three
16094        fourAAAAˇ
16095        five
16096
16097        siAAAAˇx"#
16098            .unindent(),
16099    );
16100}
16101
16102#[gpui::test]
16103async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16104    init_test(cx, |_| {});
16105
16106    let mut cx = EditorTestContext::new(cx).await;
16107    cx.set_state(
16108        r#"let foo = 1;
16109let foo = 2;
16110let foo = 3;
16111let fooˇ = 4;
16112let foo = 5;
16113let foo = 6;
16114let foo = 7;
16115let foo = 8;
16116let foo = 9;
16117let foo = 10;
16118let foo = 11;
16119let foo = 12;
16120let foo = 13;
16121let foo = 14;
16122let foo = 15;"#,
16123    );
16124
16125    cx.update_editor(|e, window, cx| {
16126        assert_eq!(
16127            e.next_scroll_position,
16128            NextScrollCursorCenterTopBottom::Center,
16129            "Default next scroll direction is center",
16130        );
16131
16132        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16133        assert_eq!(
16134            e.next_scroll_position,
16135            NextScrollCursorCenterTopBottom::Top,
16136            "After center, next scroll direction should be top",
16137        );
16138
16139        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16140        assert_eq!(
16141            e.next_scroll_position,
16142            NextScrollCursorCenterTopBottom::Bottom,
16143            "After top, next scroll direction should be bottom",
16144        );
16145
16146        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16147        assert_eq!(
16148            e.next_scroll_position,
16149            NextScrollCursorCenterTopBottom::Center,
16150            "After bottom, scrolling should start over",
16151        );
16152
16153        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16154        assert_eq!(
16155            e.next_scroll_position,
16156            NextScrollCursorCenterTopBottom::Top,
16157            "Scrolling continues if retriggered fast enough"
16158        );
16159    });
16160
16161    cx.executor()
16162        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16163    cx.executor().run_until_parked();
16164    cx.update_editor(|e, _, _| {
16165        assert_eq!(
16166            e.next_scroll_position,
16167            NextScrollCursorCenterTopBottom::Center,
16168            "If scrolling is not triggered fast enough, it should reset"
16169        );
16170    });
16171}
16172
16173#[gpui::test]
16174async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16175    init_test(cx, |_| {});
16176    let mut cx = EditorLspTestContext::new_rust(
16177        lsp::ServerCapabilities {
16178            definition_provider: Some(lsp::OneOf::Left(true)),
16179            references_provider: Some(lsp::OneOf::Left(true)),
16180            ..lsp::ServerCapabilities::default()
16181        },
16182        cx,
16183    )
16184    .await;
16185
16186    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16187        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16188            move |params, _| async move {
16189                if empty_go_to_definition {
16190                    Ok(None)
16191                } else {
16192                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16193                        uri: params.text_document_position_params.text_document.uri,
16194                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16195                    })))
16196                }
16197            },
16198        );
16199        let references =
16200            cx.lsp
16201                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16202                    Ok(Some(vec![lsp::Location {
16203                        uri: params.text_document_position.text_document.uri,
16204                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16205                    }]))
16206                });
16207        (go_to_definition, references)
16208    };
16209
16210    cx.set_state(
16211        &r#"fn one() {
16212            let mut a = ˇtwo();
16213        }
16214
16215        fn two() {}"#
16216            .unindent(),
16217    );
16218    set_up_lsp_handlers(false, &mut cx);
16219    let navigated = cx
16220        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16221        .await
16222        .expect("Failed to navigate to definition");
16223    assert_eq!(
16224        navigated,
16225        Navigated::Yes,
16226        "Should have navigated to definition from the GetDefinition response"
16227    );
16228    cx.assert_editor_state(
16229        &r#"fn one() {
16230            let mut a = two();
16231        }
16232
16233        fn «twoˇ»() {}"#
16234            .unindent(),
16235    );
16236
16237    let editors = cx.update_workspace(|workspace, _, cx| {
16238        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16239    });
16240    cx.update_editor(|_, _, test_editor_cx| {
16241        assert_eq!(
16242            editors.len(),
16243            1,
16244            "Initially, only one, test, editor should be open in the workspace"
16245        );
16246        assert_eq!(
16247            test_editor_cx.entity(),
16248            editors.last().expect("Asserted len is 1").clone()
16249        );
16250    });
16251
16252    set_up_lsp_handlers(true, &mut cx);
16253    let navigated = cx
16254        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16255        .await
16256        .expect("Failed to navigate to lookup references");
16257    assert_eq!(
16258        navigated,
16259        Navigated::Yes,
16260        "Should have navigated to references as a fallback after empty GoToDefinition response"
16261    );
16262    // We should not change the selections in the existing file,
16263    // if opening another milti buffer with the references
16264    cx.assert_editor_state(
16265        &r#"fn one() {
16266            let mut a = two();
16267        }
16268
16269        fn «twoˇ»() {}"#
16270            .unindent(),
16271    );
16272    let editors = cx.update_workspace(|workspace, _, cx| {
16273        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16274    });
16275    cx.update_editor(|_, _, test_editor_cx| {
16276        assert_eq!(
16277            editors.len(),
16278            2,
16279            "After falling back to references search, we open a new editor with the results"
16280        );
16281        let references_fallback_text = editors
16282            .into_iter()
16283            .find(|new_editor| *new_editor != test_editor_cx.entity())
16284            .expect("Should have one non-test editor now")
16285            .read(test_editor_cx)
16286            .text(test_editor_cx);
16287        assert_eq!(
16288            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16289            "Should use the range from the references response and not the GoToDefinition one"
16290        );
16291    });
16292}
16293
16294#[gpui::test]
16295async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16296    init_test(cx, |_| {});
16297
16298    let language = Arc::new(Language::new(
16299        LanguageConfig::default(),
16300        Some(tree_sitter_rust::LANGUAGE.into()),
16301    ));
16302
16303    let text = r#"
16304        #[cfg(test)]
16305        mod tests() {
16306            #[test]
16307            fn runnable_1() {
16308                let a = 1;
16309            }
16310
16311            #[test]
16312            fn runnable_2() {
16313                let a = 1;
16314                let b = 2;
16315            }
16316        }
16317    "#
16318    .unindent();
16319
16320    let fs = FakeFs::new(cx.executor());
16321    fs.insert_file("/file.rs", Default::default()).await;
16322
16323    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16324    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16325    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16326    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16327    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16328
16329    let editor = cx.new_window_entity(|window, cx| {
16330        Editor::new(
16331            EditorMode::Full,
16332            multi_buffer,
16333            Some(project.clone()),
16334            window,
16335            cx,
16336        )
16337    });
16338
16339    editor.update_in(cx, |editor, window, cx| {
16340        let snapshot = editor.buffer().read(cx).snapshot(cx);
16341        editor.tasks.insert(
16342            (buffer.read(cx).remote_id(), 3),
16343            RunnableTasks {
16344                templates: vec![],
16345                offset: snapshot.anchor_before(43),
16346                column: 0,
16347                extra_variables: HashMap::default(),
16348                context_range: BufferOffset(43)..BufferOffset(85),
16349            },
16350        );
16351        editor.tasks.insert(
16352            (buffer.read(cx).remote_id(), 8),
16353            RunnableTasks {
16354                templates: vec![],
16355                offset: snapshot.anchor_before(86),
16356                column: 0,
16357                extra_variables: HashMap::default(),
16358                context_range: BufferOffset(86)..BufferOffset(191),
16359            },
16360        );
16361
16362        // Test finding task when cursor is inside function body
16363        editor.change_selections(None, window, cx, |s| {
16364            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16365        });
16366        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16367        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16368
16369        // Test finding task when cursor is on function name
16370        editor.change_selections(None, window, cx, |s| {
16371            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16372        });
16373        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16374        assert_eq!(row, 8, "Should find task when cursor is on function name");
16375    });
16376}
16377
16378#[gpui::test]
16379async fn test_folding_buffers(cx: &mut TestAppContext) {
16380    init_test(cx, |_| {});
16381
16382    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16383    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16384    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16385
16386    let fs = FakeFs::new(cx.executor());
16387    fs.insert_tree(
16388        path!("/a"),
16389        json!({
16390            "first.rs": sample_text_1,
16391            "second.rs": sample_text_2,
16392            "third.rs": sample_text_3,
16393        }),
16394    )
16395    .await;
16396    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16397    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16398    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16399    let worktree = project.update(cx, |project, cx| {
16400        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16401        assert_eq!(worktrees.len(), 1);
16402        worktrees.pop().unwrap()
16403    });
16404    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16405
16406    let buffer_1 = project
16407        .update(cx, |project, cx| {
16408            project.open_buffer((worktree_id, "first.rs"), cx)
16409        })
16410        .await
16411        .unwrap();
16412    let buffer_2 = project
16413        .update(cx, |project, cx| {
16414            project.open_buffer((worktree_id, "second.rs"), cx)
16415        })
16416        .await
16417        .unwrap();
16418    let buffer_3 = project
16419        .update(cx, |project, cx| {
16420            project.open_buffer((worktree_id, "third.rs"), cx)
16421        })
16422        .await
16423        .unwrap();
16424
16425    let multi_buffer = cx.new(|cx| {
16426        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16427        multi_buffer.push_excerpts(
16428            buffer_1.clone(),
16429            [
16430                ExcerptRange {
16431                    context: Point::new(0, 0)..Point::new(3, 0),
16432                    primary: None,
16433                },
16434                ExcerptRange {
16435                    context: Point::new(5, 0)..Point::new(7, 0),
16436                    primary: None,
16437                },
16438                ExcerptRange {
16439                    context: Point::new(9, 0)..Point::new(10, 4),
16440                    primary: None,
16441                },
16442            ],
16443            cx,
16444        );
16445        multi_buffer.push_excerpts(
16446            buffer_2.clone(),
16447            [
16448                ExcerptRange {
16449                    context: Point::new(0, 0)..Point::new(3, 0),
16450                    primary: None,
16451                },
16452                ExcerptRange {
16453                    context: Point::new(5, 0)..Point::new(7, 0),
16454                    primary: None,
16455                },
16456                ExcerptRange {
16457                    context: Point::new(9, 0)..Point::new(10, 4),
16458                    primary: None,
16459                },
16460            ],
16461            cx,
16462        );
16463        multi_buffer.push_excerpts(
16464            buffer_3.clone(),
16465            [
16466                ExcerptRange {
16467                    context: Point::new(0, 0)..Point::new(3, 0),
16468                    primary: None,
16469                },
16470                ExcerptRange {
16471                    context: Point::new(5, 0)..Point::new(7, 0),
16472                    primary: None,
16473                },
16474                ExcerptRange {
16475                    context: Point::new(9, 0)..Point::new(10, 4),
16476                    primary: None,
16477                },
16478            ],
16479            cx,
16480        );
16481        multi_buffer
16482    });
16483    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16484        Editor::new(
16485            EditorMode::Full,
16486            multi_buffer.clone(),
16487            Some(project.clone()),
16488            window,
16489            cx,
16490        )
16491    });
16492
16493    assert_eq!(
16494        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16495        "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16496    );
16497
16498    multi_buffer_editor.update(cx, |editor, cx| {
16499        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16500    });
16501    assert_eq!(
16502        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16503        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16504        "After folding the first buffer, its text should not be displayed"
16505    );
16506
16507    multi_buffer_editor.update(cx, |editor, cx| {
16508        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16509    });
16510    assert_eq!(
16511        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16512        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16513        "After folding the second buffer, its text should not be displayed"
16514    );
16515
16516    multi_buffer_editor.update(cx, |editor, cx| {
16517        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16518    });
16519    assert_eq!(
16520        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16521        "\n\n\n\n\n",
16522        "After folding the third buffer, its text should not be displayed"
16523    );
16524
16525    // Emulate selection inside the fold logic, that should work
16526    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16527        editor
16528            .snapshot(window, cx)
16529            .next_line_boundary(Point::new(0, 4));
16530    });
16531
16532    multi_buffer_editor.update(cx, |editor, cx| {
16533        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16534    });
16535    assert_eq!(
16536        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16537        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16538        "After unfolding the second buffer, its text should be displayed"
16539    );
16540
16541    // Typing inside of buffer 1 causes that buffer to be unfolded.
16542    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16543        assert_eq!(
16544            multi_buffer
16545                .read(cx)
16546                .snapshot(cx)
16547                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16548                .collect::<String>(),
16549            "bbbb"
16550        );
16551        editor.change_selections(None, window, cx, |selections| {
16552            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16553        });
16554        editor.handle_input("B", window, cx);
16555    });
16556
16557    assert_eq!(
16558        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16559        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16560        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16561    );
16562
16563    multi_buffer_editor.update(cx, |editor, cx| {
16564        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16565    });
16566    assert_eq!(
16567        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16568        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16569        "After unfolding the all buffers, all original text should be displayed"
16570    );
16571}
16572
16573#[gpui::test]
16574async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16575    init_test(cx, |_| {});
16576
16577    let sample_text_1 = "1111\n2222\n3333".to_string();
16578    let sample_text_2 = "4444\n5555\n6666".to_string();
16579    let sample_text_3 = "7777\n8888\n9999".to_string();
16580
16581    let fs = FakeFs::new(cx.executor());
16582    fs.insert_tree(
16583        path!("/a"),
16584        json!({
16585            "first.rs": sample_text_1,
16586            "second.rs": sample_text_2,
16587            "third.rs": sample_text_3,
16588        }),
16589    )
16590    .await;
16591    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16592    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16593    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16594    let worktree = project.update(cx, |project, cx| {
16595        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16596        assert_eq!(worktrees.len(), 1);
16597        worktrees.pop().unwrap()
16598    });
16599    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16600
16601    let buffer_1 = project
16602        .update(cx, |project, cx| {
16603            project.open_buffer((worktree_id, "first.rs"), cx)
16604        })
16605        .await
16606        .unwrap();
16607    let buffer_2 = project
16608        .update(cx, |project, cx| {
16609            project.open_buffer((worktree_id, "second.rs"), cx)
16610        })
16611        .await
16612        .unwrap();
16613    let buffer_3 = project
16614        .update(cx, |project, cx| {
16615            project.open_buffer((worktree_id, "third.rs"), cx)
16616        })
16617        .await
16618        .unwrap();
16619
16620    let multi_buffer = cx.new(|cx| {
16621        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16622        multi_buffer.push_excerpts(
16623            buffer_1.clone(),
16624            [ExcerptRange {
16625                context: Point::new(0, 0)..Point::new(3, 0),
16626                primary: None,
16627            }],
16628            cx,
16629        );
16630        multi_buffer.push_excerpts(
16631            buffer_2.clone(),
16632            [ExcerptRange {
16633                context: Point::new(0, 0)..Point::new(3, 0),
16634                primary: None,
16635            }],
16636            cx,
16637        );
16638        multi_buffer.push_excerpts(
16639            buffer_3.clone(),
16640            [ExcerptRange {
16641                context: Point::new(0, 0)..Point::new(3, 0),
16642                primary: None,
16643            }],
16644            cx,
16645        );
16646        multi_buffer
16647    });
16648
16649    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16650        Editor::new(
16651            EditorMode::Full,
16652            multi_buffer,
16653            Some(project.clone()),
16654            window,
16655            cx,
16656        )
16657    });
16658
16659    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16660    assert_eq!(
16661        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16662        full_text,
16663    );
16664
16665    multi_buffer_editor.update(cx, |editor, cx| {
16666        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16667    });
16668    assert_eq!(
16669        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16670        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16671        "After folding the first buffer, its text should not be displayed"
16672    );
16673
16674    multi_buffer_editor.update(cx, |editor, cx| {
16675        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16676    });
16677
16678    assert_eq!(
16679        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16680        "\n\n\n\n\n\n7777\n8888\n9999",
16681        "After folding the second buffer, its text should not be displayed"
16682    );
16683
16684    multi_buffer_editor.update(cx, |editor, cx| {
16685        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16686    });
16687    assert_eq!(
16688        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16689        "\n\n\n\n\n",
16690        "After folding the third buffer, its text should not be displayed"
16691    );
16692
16693    multi_buffer_editor.update(cx, |editor, cx| {
16694        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16695    });
16696    assert_eq!(
16697        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16698        "\n\n\n\n4444\n5555\n6666\n\n",
16699        "After unfolding the second buffer, its text should be displayed"
16700    );
16701
16702    multi_buffer_editor.update(cx, |editor, cx| {
16703        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16704    });
16705    assert_eq!(
16706        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16707        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16708        "After unfolding the first buffer, its text should be displayed"
16709    );
16710
16711    multi_buffer_editor.update(cx, |editor, cx| {
16712        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16713    });
16714    assert_eq!(
16715        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16716        full_text,
16717        "After unfolding all buffers, all original text should be displayed"
16718    );
16719}
16720
16721#[gpui::test]
16722async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16723    init_test(cx, |_| {});
16724
16725    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16726
16727    let fs = FakeFs::new(cx.executor());
16728    fs.insert_tree(
16729        path!("/a"),
16730        json!({
16731            "main.rs": sample_text,
16732        }),
16733    )
16734    .await;
16735    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16736    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16737    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16738    let worktree = project.update(cx, |project, cx| {
16739        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16740        assert_eq!(worktrees.len(), 1);
16741        worktrees.pop().unwrap()
16742    });
16743    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16744
16745    let buffer_1 = project
16746        .update(cx, |project, cx| {
16747            project.open_buffer((worktree_id, "main.rs"), cx)
16748        })
16749        .await
16750        .unwrap();
16751
16752    let multi_buffer = cx.new(|cx| {
16753        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16754        multi_buffer.push_excerpts(
16755            buffer_1.clone(),
16756            [ExcerptRange {
16757                context: Point::new(0, 0)
16758                    ..Point::new(
16759                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16760                        0,
16761                    ),
16762                primary: None,
16763            }],
16764            cx,
16765        );
16766        multi_buffer
16767    });
16768    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16769        Editor::new(
16770            EditorMode::Full,
16771            multi_buffer,
16772            Some(project.clone()),
16773            window,
16774            cx,
16775        )
16776    });
16777
16778    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16779    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16780        enum TestHighlight {}
16781        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16782        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16783        editor.highlight_text::<TestHighlight>(
16784            vec![highlight_range.clone()],
16785            HighlightStyle::color(Hsla::green()),
16786            cx,
16787        );
16788        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16789    });
16790
16791    let full_text = format!("\n\n{sample_text}");
16792    assert_eq!(
16793        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16794        full_text,
16795    );
16796}
16797
16798#[gpui::test]
16799async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16800    init_test(cx, |_| {});
16801    cx.update(|cx| {
16802        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16803            "keymaps/default-linux.json",
16804            cx,
16805        )
16806        .unwrap();
16807        cx.bind_keys(default_key_bindings);
16808    });
16809
16810    let (editor, cx) = cx.add_window_view(|window, cx| {
16811        let multi_buffer = MultiBuffer::build_multi(
16812            [
16813                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16814                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16815                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16816                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16817            ],
16818            cx,
16819        );
16820        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16821
16822        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16823        // fold all but the second buffer, so that we test navigating between two
16824        // adjacent folded buffers, as well as folded buffers at the start and
16825        // end the multibuffer
16826        editor.fold_buffer(buffer_ids[0], cx);
16827        editor.fold_buffer(buffer_ids[2], cx);
16828        editor.fold_buffer(buffer_ids[3], cx);
16829
16830        editor
16831    });
16832    cx.simulate_resize(size(px(1000.), px(1000.)));
16833
16834    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16835    cx.assert_excerpts_with_selections(indoc! {"
16836        [EXCERPT]
16837        ˇ[FOLDED]
16838        [EXCERPT]
16839        a1
16840        b1
16841        [EXCERPT]
16842        [FOLDED]
16843        [EXCERPT]
16844        [FOLDED]
16845        "
16846    });
16847    cx.simulate_keystroke("down");
16848    cx.assert_excerpts_with_selections(indoc! {"
16849        [EXCERPT]
16850        [FOLDED]
16851        [EXCERPT]
16852        ˇa1
16853        b1
16854        [EXCERPT]
16855        [FOLDED]
16856        [EXCERPT]
16857        [FOLDED]
16858        "
16859    });
16860    cx.simulate_keystroke("down");
16861    cx.assert_excerpts_with_selections(indoc! {"
16862        [EXCERPT]
16863        [FOLDED]
16864        [EXCERPT]
16865        a1
16866        ˇb1
16867        [EXCERPT]
16868        [FOLDED]
16869        [EXCERPT]
16870        [FOLDED]
16871        "
16872    });
16873    cx.simulate_keystroke("down");
16874    cx.assert_excerpts_with_selections(indoc! {"
16875        [EXCERPT]
16876        [FOLDED]
16877        [EXCERPT]
16878        a1
16879        b1
16880        ˇ[EXCERPT]
16881        [FOLDED]
16882        [EXCERPT]
16883        [FOLDED]
16884        "
16885    });
16886    cx.simulate_keystroke("down");
16887    cx.assert_excerpts_with_selections(indoc! {"
16888        [EXCERPT]
16889        [FOLDED]
16890        [EXCERPT]
16891        a1
16892        b1
16893        [EXCERPT]
16894        ˇ[FOLDED]
16895        [EXCERPT]
16896        [FOLDED]
16897        "
16898    });
16899    for _ in 0..5 {
16900        cx.simulate_keystroke("down");
16901        cx.assert_excerpts_with_selections(indoc! {"
16902            [EXCERPT]
16903            [FOLDED]
16904            [EXCERPT]
16905            a1
16906            b1
16907            [EXCERPT]
16908            [FOLDED]
16909            [EXCERPT]
16910            ˇ[FOLDED]
16911            "
16912        });
16913    }
16914
16915    cx.simulate_keystroke("up");
16916    cx.assert_excerpts_with_selections(indoc! {"
16917        [EXCERPT]
16918        [FOLDED]
16919        [EXCERPT]
16920        a1
16921        b1
16922        [EXCERPT]
16923        ˇ[FOLDED]
16924        [EXCERPT]
16925        [FOLDED]
16926        "
16927    });
16928    cx.simulate_keystroke("up");
16929    cx.assert_excerpts_with_selections(indoc! {"
16930        [EXCERPT]
16931        [FOLDED]
16932        [EXCERPT]
16933        a1
16934        b1
16935        ˇ[EXCERPT]
16936        [FOLDED]
16937        [EXCERPT]
16938        [FOLDED]
16939        "
16940    });
16941    cx.simulate_keystroke("up");
16942    cx.assert_excerpts_with_selections(indoc! {"
16943        [EXCERPT]
16944        [FOLDED]
16945        [EXCERPT]
16946        a1
16947        ˇb1
16948        [EXCERPT]
16949        [FOLDED]
16950        [EXCERPT]
16951        [FOLDED]
16952        "
16953    });
16954    cx.simulate_keystroke("up");
16955    cx.assert_excerpts_with_selections(indoc! {"
16956        [EXCERPT]
16957        [FOLDED]
16958        [EXCERPT]
16959        ˇa1
16960        b1
16961        [EXCERPT]
16962        [FOLDED]
16963        [EXCERPT]
16964        [FOLDED]
16965        "
16966    });
16967    for _ in 0..5 {
16968        cx.simulate_keystroke("up");
16969        cx.assert_excerpts_with_selections(indoc! {"
16970            [EXCERPT]
16971            ˇ[FOLDED]
16972            [EXCERPT]
16973            a1
16974            b1
16975            [EXCERPT]
16976            [FOLDED]
16977            [EXCERPT]
16978            [FOLDED]
16979            "
16980        });
16981    }
16982}
16983
16984#[gpui::test]
16985async fn test_inline_completion_text(cx: &mut TestAppContext) {
16986    init_test(cx, |_| {});
16987
16988    // Simple insertion
16989    assert_highlighted_edits(
16990        "Hello, world!",
16991        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16992        true,
16993        cx,
16994        |highlighted_edits, cx| {
16995            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16996            assert_eq!(highlighted_edits.highlights.len(), 1);
16997            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16998            assert_eq!(
16999                highlighted_edits.highlights[0].1.background_color,
17000                Some(cx.theme().status().created_background)
17001            );
17002        },
17003    )
17004    .await;
17005
17006    // Replacement
17007    assert_highlighted_edits(
17008        "This is a test.",
17009        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17010        false,
17011        cx,
17012        |highlighted_edits, cx| {
17013            assert_eq!(highlighted_edits.text, "That is a test.");
17014            assert_eq!(highlighted_edits.highlights.len(), 1);
17015            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17016            assert_eq!(
17017                highlighted_edits.highlights[0].1.background_color,
17018                Some(cx.theme().status().created_background)
17019            );
17020        },
17021    )
17022    .await;
17023
17024    // Multiple edits
17025    assert_highlighted_edits(
17026        "Hello, world!",
17027        vec![
17028            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17029            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17030        ],
17031        false,
17032        cx,
17033        |highlighted_edits, cx| {
17034            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17035            assert_eq!(highlighted_edits.highlights.len(), 2);
17036            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17037            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17038            assert_eq!(
17039                highlighted_edits.highlights[0].1.background_color,
17040                Some(cx.theme().status().created_background)
17041            );
17042            assert_eq!(
17043                highlighted_edits.highlights[1].1.background_color,
17044                Some(cx.theme().status().created_background)
17045            );
17046        },
17047    )
17048    .await;
17049
17050    // Multiple lines with edits
17051    assert_highlighted_edits(
17052        "First line\nSecond line\nThird line\nFourth line",
17053        vec![
17054            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17055            (
17056                Point::new(2, 0)..Point::new(2, 10),
17057                "New third line".to_string(),
17058            ),
17059            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17060        ],
17061        false,
17062        cx,
17063        |highlighted_edits, cx| {
17064            assert_eq!(
17065                highlighted_edits.text,
17066                "Second modified\nNew third line\nFourth updated line"
17067            );
17068            assert_eq!(highlighted_edits.highlights.len(), 3);
17069            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17070            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17071            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17072            for highlight in &highlighted_edits.highlights {
17073                assert_eq!(
17074                    highlight.1.background_color,
17075                    Some(cx.theme().status().created_background)
17076                );
17077            }
17078        },
17079    )
17080    .await;
17081}
17082
17083#[gpui::test]
17084async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17085    init_test(cx, |_| {});
17086
17087    // Deletion
17088    assert_highlighted_edits(
17089        "Hello, world!",
17090        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17091        true,
17092        cx,
17093        |highlighted_edits, cx| {
17094            assert_eq!(highlighted_edits.text, "Hello, world!");
17095            assert_eq!(highlighted_edits.highlights.len(), 1);
17096            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17097            assert_eq!(
17098                highlighted_edits.highlights[0].1.background_color,
17099                Some(cx.theme().status().deleted_background)
17100            );
17101        },
17102    )
17103    .await;
17104
17105    // Insertion
17106    assert_highlighted_edits(
17107        "Hello, world!",
17108        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17109        true,
17110        cx,
17111        |highlighted_edits, cx| {
17112            assert_eq!(highlighted_edits.highlights.len(), 1);
17113            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17114            assert_eq!(
17115                highlighted_edits.highlights[0].1.background_color,
17116                Some(cx.theme().status().created_background)
17117            );
17118        },
17119    )
17120    .await;
17121}
17122
17123async fn assert_highlighted_edits(
17124    text: &str,
17125    edits: Vec<(Range<Point>, String)>,
17126    include_deletions: bool,
17127    cx: &mut TestAppContext,
17128    assertion_fn: impl Fn(HighlightedText, &App),
17129) {
17130    let window = cx.add_window(|window, cx| {
17131        let buffer = MultiBuffer::build_simple(text, cx);
17132        Editor::new(EditorMode::Full, buffer, None, window, cx)
17133    });
17134    let cx = &mut VisualTestContext::from_window(*window, cx);
17135
17136    let (buffer, snapshot) = window
17137        .update(cx, |editor, _window, cx| {
17138            (
17139                editor.buffer().clone(),
17140                editor.buffer().read(cx).snapshot(cx),
17141            )
17142        })
17143        .unwrap();
17144
17145    let edits = edits
17146        .into_iter()
17147        .map(|(range, edit)| {
17148            (
17149                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17150                edit,
17151            )
17152        })
17153        .collect::<Vec<_>>();
17154
17155    let text_anchor_edits = edits
17156        .clone()
17157        .into_iter()
17158        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17159        .collect::<Vec<_>>();
17160
17161    let edit_preview = window
17162        .update(cx, |_, _window, cx| {
17163            buffer
17164                .read(cx)
17165                .as_singleton()
17166                .unwrap()
17167                .read(cx)
17168                .preview_edits(text_anchor_edits.into(), cx)
17169        })
17170        .unwrap()
17171        .await;
17172
17173    cx.update(|_window, cx| {
17174        let highlighted_edits = inline_completion_edit_text(
17175            &snapshot.as_singleton().unwrap().2,
17176            &edits,
17177            &edit_preview,
17178            include_deletions,
17179            cx,
17180        );
17181        assertion_fn(highlighted_edits, cx)
17182    });
17183}
17184
17185#[track_caller]
17186fn assert_breakpoint(
17187    breakpoints: &BTreeMap<Arc<Path>, Vec<SerializedBreakpoint>>,
17188    path: &Arc<Path>,
17189    expected: Vec<(u32, BreakpointKind)>,
17190) {
17191    if expected.len() == 0usize {
17192        assert!(!breakpoints.contains_key(path));
17193    } else {
17194        let mut breakpoint = breakpoints
17195            .get(path)
17196            .unwrap()
17197            .into_iter()
17198            .map(|breakpoint| (breakpoint.position, breakpoint.kind.clone()))
17199            .collect::<Vec<_>>();
17200
17201        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17202
17203        assert_eq!(expected, breakpoint);
17204    }
17205}
17206
17207fn add_log_breakpoint_at_cursor(
17208    editor: &mut Editor,
17209    log_message: &str,
17210    window: &mut Window,
17211    cx: &mut Context<Editor>,
17212) {
17213    let (anchor, bp) = editor
17214        .breakpoint_at_cursor_head(window, cx)
17215        .unwrap_or_else(|| {
17216            let cursor_position: Point = editor.selections.newest(cx).head();
17217
17218            let breakpoint_position = editor
17219                .snapshot(window, cx)
17220                .display_snapshot
17221                .buffer_snapshot
17222                .anchor_before(Point::new(cursor_position.row, 0));
17223
17224            let kind = BreakpointKind::Log(Arc::from(log_message));
17225
17226            (breakpoint_position, Breakpoint { kind })
17227        });
17228
17229    editor.edit_breakpoint_at_anchor(
17230        anchor,
17231        bp.kind,
17232        BreakpointEditAction::EditLogMessage(log_message.into()),
17233        cx,
17234    );
17235}
17236
17237#[gpui::test]
17238async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17239    init_test(cx, |_| {});
17240
17241    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17242    let fs = FakeFs::new(cx.executor());
17243    fs.insert_tree(
17244        path!("/a"),
17245        json!({
17246            "main.rs": sample_text,
17247        }),
17248    )
17249    .await;
17250    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17251    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17252    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17253
17254    let fs = FakeFs::new(cx.executor());
17255    fs.insert_tree(
17256        path!("/a"),
17257        json!({
17258            "main.rs": sample_text,
17259        }),
17260    )
17261    .await;
17262    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17263    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17264    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17265    let worktree_id = workspace
17266        .update(cx, |workspace, _window, cx| {
17267            workspace.project().update(cx, |project, cx| {
17268                project.worktrees(cx).next().unwrap().read(cx).id()
17269            })
17270        })
17271        .unwrap();
17272
17273    let buffer = project
17274        .update(cx, |project, cx| {
17275            project.open_buffer((worktree_id, "main.rs"), cx)
17276        })
17277        .await
17278        .unwrap();
17279
17280    let (editor, cx) = cx.add_window_view(|window, cx| {
17281        Editor::new(
17282            EditorMode::Full,
17283            MultiBuffer::build_from_buffer(buffer, cx),
17284            Some(project.clone()),
17285            window,
17286            cx,
17287        )
17288    });
17289
17290    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17291    let abs_path = project.read_with(cx, |project, cx| {
17292        project
17293            .absolute_path(&project_path, cx)
17294            .map(|path_buf| Arc::from(path_buf.to_owned()))
17295            .unwrap()
17296    });
17297
17298    // assert we can add breakpoint on the first line
17299    editor.update_in(cx, |editor, window, cx| {
17300        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17301        editor.move_to_end(&MoveToEnd, window, cx);
17302        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17303    });
17304
17305    let breakpoints = editor.update(cx, |editor, cx| {
17306        editor
17307            .breakpoint_store()
17308            .as_ref()
17309            .unwrap()
17310            .read(cx)
17311            .all_breakpoints(cx)
17312            .clone()
17313    });
17314
17315    assert_eq!(1, breakpoints.len());
17316    assert_breakpoint(
17317        &breakpoints,
17318        &abs_path,
17319        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17320    );
17321
17322    editor.update_in(cx, |editor, window, cx| {
17323        editor.move_to_beginning(&MoveToBeginning, window, cx);
17324        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17325    });
17326
17327    let breakpoints = editor.update(cx, |editor, cx| {
17328        editor
17329            .breakpoint_store()
17330            .as_ref()
17331            .unwrap()
17332            .read(cx)
17333            .all_breakpoints(cx)
17334            .clone()
17335    });
17336
17337    assert_eq!(1, breakpoints.len());
17338    assert_breakpoint(&breakpoints, &abs_path, vec![(3, BreakpointKind::Standard)]);
17339
17340    editor.update_in(cx, |editor, window, cx| {
17341        editor.move_to_end(&MoveToEnd, window, cx);
17342        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17343    });
17344
17345    let breakpoints = editor.update(cx, |editor, cx| {
17346        editor
17347            .breakpoint_store()
17348            .as_ref()
17349            .unwrap()
17350            .read(cx)
17351            .all_breakpoints(cx)
17352            .clone()
17353    });
17354
17355    assert_eq!(0, breakpoints.len());
17356    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17357}
17358
17359#[gpui::test]
17360async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17361    init_test(cx, |_| {});
17362
17363    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17364
17365    let fs = FakeFs::new(cx.executor());
17366    fs.insert_tree(
17367        path!("/a"),
17368        json!({
17369            "main.rs": sample_text,
17370        }),
17371    )
17372    .await;
17373    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17374    let (workspace, cx) =
17375        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17376
17377    let worktree_id = workspace.update(cx, |workspace, cx| {
17378        workspace.project().update(cx, |project, cx| {
17379            project.worktrees(cx).next().unwrap().read(cx).id()
17380        })
17381    });
17382
17383    let buffer = project
17384        .update(cx, |project, cx| {
17385            project.open_buffer((worktree_id, "main.rs"), cx)
17386        })
17387        .await
17388        .unwrap();
17389
17390    let (editor, cx) = cx.add_window_view(|window, cx| {
17391        Editor::new(
17392            EditorMode::Full,
17393            MultiBuffer::build_from_buffer(buffer, cx),
17394            Some(project.clone()),
17395            window,
17396            cx,
17397        )
17398    });
17399
17400    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17401    let abs_path = project.read_with(cx, |project, cx| {
17402        project
17403            .absolute_path(&project_path, cx)
17404            .map(|path_buf| Arc::from(path_buf.to_owned()))
17405            .unwrap()
17406    });
17407
17408    editor.update_in(cx, |editor, window, cx| {
17409        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17410    });
17411
17412    let breakpoints = editor.update(cx, |editor, cx| {
17413        editor
17414            .breakpoint_store()
17415            .as_ref()
17416            .unwrap()
17417            .read(cx)
17418            .all_breakpoints(cx)
17419            .clone()
17420    });
17421
17422    assert_breakpoint(
17423        &breakpoints,
17424        &abs_path,
17425        vec![(0, BreakpointKind::Log("hello world".into()))],
17426    );
17427
17428    // Removing a log message from a log breakpoint should remove it
17429    editor.update_in(cx, |editor, window, cx| {
17430        add_log_breakpoint_at_cursor(editor, "", window, cx);
17431    });
17432
17433    let breakpoints = editor.update(cx, |editor, cx| {
17434        editor
17435            .breakpoint_store()
17436            .as_ref()
17437            .unwrap()
17438            .read(cx)
17439            .all_breakpoints(cx)
17440            .clone()
17441    });
17442
17443    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17444
17445    editor.update_in(cx, |editor, window, cx| {
17446        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17447        editor.move_to_end(&MoveToEnd, window, cx);
17448        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17449        // Not adding a log message to a standard breakpoint shouldn't remove it
17450        add_log_breakpoint_at_cursor(editor, "", window, cx);
17451    });
17452
17453    let breakpoints = editor.update(cx, |editor, cx| {
17454        editor
17455            .breakpoint_store()
17456            .as_ref()
17457            .unwrap()
17458            .read(cx)
17459            .all_breakpoints(cx)
17460            .clone()
17461    });
17462
17463    assert_breakpoint(
17464        &breakpoints,
17465        &abs_path,
17466        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17467    );
17468
17469    editor.update_in(cx, |editor, window, cx| {
17470        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17471    });
17472
17473    let breakpoints = editor.update(cx, |editor, cx| {
17474        editor
17475            .breakpoint_store()
17476            .as_ref()
17477            .unwrap()
17478            .read(cx)
17479            .all_breakpoints(cx)
17480            .clone()
17481    });
17482
17483    assert_breakpoint(
17484        &breakpoints,
17485        &abs_path,
17486        vec![
17487            (0, BreakpointKind::Standard),
17488            (3, BreakpointKind::Log("hello world".into())),
17489        ],
17490    );
17491
17492    editor.update_in(cx, |editor, window, cx| {
17493        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17494    });
17495
17496    let breakpoints = editor.update(cx, |editor, cx| {
17497        editor
17498            .breakpoint_store()
17499            .as_ref()
17500            .unwrap()
17501            .read(cx)
17502            .all_breakpoints(cx)
17503            .clone()
17504    });
17505
17506    assert_breakpoint(
17507        &breakpoints,
17508        &abs_path,
17509        vec![
17510            (0, BreakpointKind::Standard),
17511            (3, BreakpointKind::Log("hello Earth !!".into())),
17512        ],
17513    );
17514}
17515
17516#[gpui::test]
17517async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17518    init_test(cx, |_| {});
17519    let capabilities = lsp::ServerCapabilities {
17520        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17521            prepare_provider: Some(true),
17522            work_done_progress_options: Default::default(),
17523        })),
17524        ..Default::default()
17525    };
17526    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17527
17528    cx.set_state(indoc! {"
17529        struct Fˇoo {}
17530    "});
17531
17532    cx.update_editor(|editor, _, cx| {
17533        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17534        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17535        editor.highlight_background::<DocumentHighlightRead>(
17536            &[highlight_range],
17537            |c| c.editor_document_highlight_read_background,
17538            cx,
17539        );
17540    });
17541
17542    let mut prepare_rename_handler =
17543        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17544            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17545                start: lsp::Position {
17546                    line: 0,
17547                    character: 7,
17548                },
17549                end: lsp::Position {
17550                    line: 0,
17551                    character: 10,
17552                },
17553            })))
17554        });
17555    let prepare_rename_task = cx
17556        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17557        .expect("Prepare rename was not started");
17558    prepare_rename_handler.next().await.unwrap();
17559    prepare_rename_task.await.expect("Prepare rename failed");
17560
17561    let mut rename_handler =
17562        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17563            let edit = lsp::TextEdit {
17564                range: lsp::Range {
17565                    start: lsp::Position {
17566                        line: 0,
17567                        character: 7,
17568                    },
17569                    end: lsp::Position {
17570                        line: 0,
17571                        character: 10,
17572                    },
17573                },
17574                new_text: "FooRenamed".to_string(),
17575            };
17576            Ok(Some(lsp::WorkspaceEdit::new(
17577                // Specify the same edit twice
17578                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17579            )))
17580        });
17581    let rename_task = cx
17582        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17583        .expect("Confirm rename was not started");
17584    rename_handler.next().await.unwrap();
17585    rename_task.await.expect("Confirm rename failed");
17586    cx.run_until_parked();
17587
17588    // Despite two edits, only one is actually applied as those are identical
17589    cx.assert_editor_state(indoc! {"
17590        struct FooRenamedˇ {}
17591    "});
17592}
17593
17594#[gpui::test]
17595async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17596    init_test(cx, |_| {});
17597    // These capabilities indicate that the server does not support prepare rename.
17598    let capabilities = lsp::ServerCapabilities {
17599        rename_provider: Some(lsp::OneOf::Left(true)),
17600        ..Default::default()
17601    };
17602    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17603
17604    cx.set_state(indoc! {"
17605        struct Fˇoo {}
17606    "});
17607
17608    cx.update_editor(|editor, _window, cx| {
17609        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17610        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17611        editor.highlight_background::<DocumentHighlightRead>(
17612            &[highlight_range],
17613            |c| c.editor_document_highlight_read_background,
17614            cx,
17615        );
17616    });
17617
17618    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17619        .expect("Prepare rename was not started")
17620        .await
17621        .expect("Prepare rename failed");
17622
17623    let mut rename_handler =
17624        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17625            let edit = lsp::TextEdit {
17626                range: lsp::Range {
17627                    start: lsp::Position {
17628                        line: 0,
17629                        character: 7,
17630                    },
17631                    end: lsp::Position {
17632                        line: 0,
17633                        character: 10,
17634                    },
17635                },
17636                new_text: "FooRenamed".to_string(),
17637            };
17638            Ok(Some(lsp::WorkspaceEdit::new(
17639                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17640            )))
17641        });
17642    let rename_task = cx
17643        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17644        .expect("Confirm rename was not started");
17645    rename_handler.next().await.unwrap();
17646    rename_task.await.expect("Confirm rename failed");
17647    cx.run_until_parked();
17648
17649    // Correct range is renamed, as `surrounding_word` is used to find it.
17650    cx.assert_editor_state(indoc! {"
17651        struct FooRenamedˇ {}
17652    "});
17653}
17654
17655#[gpui::test]
17656async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17657    init_test(cx, |_| {});
17658    let mut cx = EditorTestContext::new(cx).await;
17659
17660    let language = Arc::new(
17661        Language::new(
17662            LanguageConfig::default(),
17663            Some(tree_sitter_html::LANGUAGE.into()),
17664        )
17665        .with_brackets_query(
17666            r#"
17667            ("<" @open "/>" @close)
17668            ("</" @open ">" @close)
17669            ("<" @open ">" @close)
17670            ("\"" @open "\"" @close)
17671            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17672        "#,
17673        )
17674        .unwrap(),
17675    );
17676    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17677
17678    cx.set_state(indoc! {"
17679        <span>ˇ</span>
17680    "});
17681    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17682    cx.assert_editor_state(indoc! {"
17683        <span>
17684        ˇ
17685        </span>
17686    "});
17687
17688    cx.set_state(indoc! {"
17689        <span><span></span>ˇ</span>
17690    "});
17691    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17692    cx.assert_editor_state(indoc! {"
17693        <span><span></span>
17694        ˇ</span>
17695    "});
17696
17697    cx.set_state(indoc! {"
17698        <span>ˇ
17699        </span>
17700    "});
17701    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17702    cx.assert_editor_state(indoc! {"
17703        <span>
17704        ˇ
17705        </span>
17706    "});
17707}
17708
17709#[gpui::test(iterations = 10)]
17710async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17711    init_test(cx, |_| {});
17712
17713    let fs = FakeFs::new(cx.executor());
17714    fs.insert_tree(
17715        path!("/dir"),
17716        json!({
17717            "a.ts": "a",
17718        }),
17719    )
17720    .await;
17721
17722    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17723    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17724    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17725
17726    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17727    language_registry.add(Arc::new(Language::new(
17728        LanguageConfig {
17729            name: "TypeScript".into(),
17730            matcher: LanguageMatcher {
17731                path_suffixes: vec!["ts".to_string()],
17732                ..Default::default()
17733            },
17734            ..Default::default()
17735        },
17736        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17737    )));
17738    let mut fake_language_servers = language_registry.register_fake_lsp(
17739        "TypeScript",
17740        FakeLspAdapter {
17741            capabilities: lsp::ServerCapabilities {
17742                code_lens_provider: Some(lsp::CodeLensOptions {
17743                    resolve_provider: Some(true),
17744                }),
17745                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17746                    commands: vec!["_the/command".to_string()],
17747                    ..lsp::ExecuteCommandOptions::default()
17748                }),
17749                ..lsp::ServerCapabilities::default()
17750            },
17751            ..FakeLspAdapter::default()
17752        },
17753    );
17754
17755    let (buffer, _handle) = project
17756        .update(cx, |p, cx| {
17757            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17758        })
17759        .await
17760        .unwrap();
17761    cx.executor().run_until_parked();
17762
17763    let fake_server = fake_language_servers.next().await.unwrap();
17764
17765    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17766    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17767    drop(buffer_snapshot);
17768    let actions = cx
17769        .update_window(*workspace, |_, window, cx| {
17770            project.code_actions(&buffer, anchor..anchor, window, cx)
17771        })
17772        .unwrap();
17773
17774    fake_server
17775        .handle_request::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17776            Ok(Some(vec![
17777                lsp::CodeLens {
17778                    range: lsp::Range::default(),
17779                    command: Some(lsp::Command {
17780                        title: "Code lens command".to_owned(),
17781                        command: "_the/command".to_owned(),
17782                        arguments: None,
17783                    }),
17784                    data: None,
17785                },
17786                lsp::CodeLens {
17787                    range: lsp::Range::default(),
17788                    command: Some(lsp::Command {
17789                        title: "Command not in capabilities".to_owned(),
17790                        command: "not in capabilities".to_owned(),
17791                        arguments: None,
17792                    }),
17793                    data: None,
17794                },
17795                lsp::CodeLens {
17796                    range: lsp::Range {
17797                        start: lsp::Position {
17798                            line: 1,
17799                            character: 1,
17800                        },
17801                        end: lsp::Position {
17802                            line: 1,
17803                            character: 1,
17804                        },
17805                    },
17806                    command: Some(lsp::Command {
17807                        title: "Command not in range".to_owned(),
17808                        command: "_the/command".to_owned(),
17809                        arguments: None,
17810                    }),
17811                    data: None,
17812                },
17813            ]))
17814        })
17815        .next()
17816        .await;
17817
17818    let actions = actions.await.unwrap();
17819    assert_eq!(
17820        actions.len(),
17821        1,
17822        "Should have only one valid action for the 0..0 range"
17823    );
17824    let action = actions[0].clone();
17825    let apply = project.update(cx, |project, cx| {
17826        project.apply_code_action(buffer.clone(), action, true, cx)
17827    });
17828
17829    // Resolving the code action does not populate its edits. In absence of
17830    // edits, we must execute the given command.
17831    fake_server.handle_request::<lsp::request::CodeLensResolve, _, _>(|mut lens, _| async move {
17832        let lens_command = lens.command.as_mut().expect("should have a command");
17833        assert_eq!(lens_command.title, "Code lens command");
17834        lens_command.arguments = Some(vec![json!("the-argument")]);
17835        Ok(lens)
17836    });
17837
17838    // While executing the command, the language server sends the editor
17839    // a `workspaceEdit` request.
17840    fake_server
17841        .handle_request::<lsp::request::ExecuteCommand, _, _>({
17842            let fake = fake_server.clone();
17843            move |params, _| {
17844                assert_eq!(params.command, "_the/command");
17845                let fake = fake.clone();
17846                async move {
17847                    fake.server
17848                        .request::<lsp::request::ApplyWorkspaceEdit>(
17849                            lsp::ApplyWorkspaceEditParams {
17850                                label: None,
17851                                edit: lsp::WorkspaceEdit {
17852                                    changes: Some(
17853                                        [(
17854                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
17855                                            vec![lsp::TextEdit {
17856                                                range: lsp::Range::new(
17857                                                    lsp::Position::new(0, 0),
17858                                                    lsp::Position::new(0, 0),
17859                                                ),
17860                                                new_text: "X".into(),
17861                                            }],
17862                                        )]
17863                                        .into_iter()
17864                                        .collect(),
17865                                    ),
17866                                    ..Default::default()
17867                                },
17868                            },
17869                        )
17870                        .await
17871                        .unwrap();
17872                    Ok(Some(json!(null)))
17873                }
17874            }
17875        })
17876        .next()
17877        .await;
17878
17879    // Applying the code lens command returns a project transaction containing the edits
17880    // sent by the language server in its `workspaceEdit` request.
17881    let transaction = apply.await.unwrap();
17882    assert!(transaction.0.contains_key(&buffer));
17883    buffer.update(cx, |buffer, cx| {
17884        assert_eq!(buffer.text(), "Xa");
17885        buffer.undo(cx);
17886        assert_eq!(buffer.text(), "a");
17887    });
17888}
17889
17890mod autoclose_tags {
17891    use super::*;
17892    use language::language_settings::JsxTagAutoCloseSettings;
17893    use languages::language;
17894
17895    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17896        init_test(cx, |settings| {
17897            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17898        });
17899
17900        let mut cx = EditorTestContext::new(cx).await;
17901        cx.update_buffer(|buffer, cx| {
17902            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17903
17904            buffer.set_language(Some(language), cx)
17905        });
17906
17907        cx
17908    }
17909
17910    macro_rules! check {
17911        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17912            #[gpui::test]
17913            async fn $name(cx: &mut TestAppContext) {
17914                let mut cx = test_setup(cx).await;
17915                cx.set_state($initial);
17916                cx.run_until_parked();
17917
17918                cx.update_editor(|editor, window, cx| {
17919                    editor.handle_input($input, window, cx);
17920                });
17921                cx.run_until_parked();
17922                cx.assert_editor_state($expected);
17923            }
17924        };
17925    }
17926
17927    check!(
17928        test_basic,
17929        "<divˇ" + ">" => "<div>ˇ</div>"
17930    );
17931
17932    check!(
17933        test_basic_nested,
17934        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17935    );
17936
17937    check!(
17938        test_basic_ignore_already_closed,
17939        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17940    );
17941
17942    check!(
17943        test_doesnt_autoclose_closing_tag,
17944        "</divˇ" + ">" => "</div>ˇ"
17945    );
17946
17947    check!(
17948        test_jsx_attr,
17949        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17950    );
17951
17952    check!(
17953        test_ignores_closing_tags_in_expr_block,
17954        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17955    );
17956
17957    check!(
17958        test_doesnt_autoclose_on_gt_in_expr,
17959        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17960    );
17961
17962    check!(
17963        test_ignores_closing_tags_with_different_tag_names,
17964        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17965    );
17966
17967    check!(
17968        test_autocloses_in_jsx_expression,
17969        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17970    );
17971
17972    check!(
17973        test_doesnt_autoclose_already_closed_in_jsx_expression,
17974        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17975    );
17976
17977    check!(
17978        test_autocloses_fragment,
17979        "" + ">" => "<>ˇ</>"
17980    );
17981
17982    check!(
17983        test_does_not_include_type_argument_in_autoclose_tag_name,
17984        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17985    );
17986
17987    check!(
17988        test_does_not_autoclose_doctype,
17989        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17990    );
17991
17992    check!(
17993        test_does_not_autoclose_comment,
17994        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17995    );
17996
17997    check!(
17998        test_multi_cursor_autoclose_same_tag,
17999        r#"
18000        <divˇ
18001        <divˇ
18002        "#
18003        + ">" =>
18004        r#"
18005        <div>ˇ</div>
18006        <div>ˇ</div>
18007        "#
18008    );
18009
18010    check!(
18011        test_multi_cursor_autoclose_different_tags,
18012        r#"
18013        <divˇ
18014        <spanˇ
18015        "#
18016        + ">" =>
18017        r#"
18018        <div>ˇ</div>
18019        <span>ˇ</span>
18020        "#
18021    );
18022
18023    check!(
18024        test_multi_cursor_autoclose_some_dont_autoclose_others,
18025        r#"
18026        <divˇ
18027        <div /ˇ
18028        <spanˇ</span>
18029        <!DOCTYPE htmlˇ
18030        </headˇ
18031        <Component<T>ˇ
18032        ˇ
18033        "#
18034        + ">" =>
18035        r#"
18036        <div>ˇ</div>
18037        <div />ˇ
18038        <span>ˇ</span>
18039        <!DOCTYPE html>ˇ
18040        </head>ˇ
18041        <Component<T>>ˇ</Component>
1804218043        "#
18044    );
18045
18046    check!(
18047        test_doesnt_mess_up_trailing_text,
18048        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
18049    );
18050
18051    #[gpui::test]
18052    async fn test_multibuffer(cx: &mut TestAppContext) {
18053        init_test(cx, |settings| {
18054            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
18055        });
18056
18057        let buffer_a = cx.new(|cx| {
18058            let mut buf = language::Buffer::local("<div", cx);
18059            buf.set_language(
18060                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
18061                cx,
18062            );
18063            buf
18064        });
18065        let buffer_b = cx.new(|cx| {
18066            let mut buf = language::Buffer::local("<pre", cx);
18067            buf.set_language(
18068                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
18069                cx,
18070            );
18071            buf
18072        });
18073        let buffer_c = cx.new(|cx| {
18074            let buf = language::Buffer::local("<span", cx);
18075            buf
18076        });
18077        let buffer = cx.new(|cx| {
18078            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
18079            buf.push_excerpts(
18080                buffer_a,
18081                [ExcerptRange {
18082                    context: text::Anchor::MIN..text::Anchor::MAX,
18083                    primary: None,
18084                }],
18085                cx,
18086            );
18087            buf.push_excerpts(
18088                buffer_b,
18089                [ExcerptRange {
18090                    context: text::Anchor::MIN..text::Anchor::MAX,
18091                    primary: None,
18092                }],
18093                cx,
18094            );
18095            buf.push_excerpts(
18096                buffer_c,
18097                [ExcerptRange {
18098                    context: text::Anchor::MIN..text::Anchor::MAX,
18099                    primary: None,
18100                }],
18101                cx,
18102            );
18103            buf
18104        });
18105        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
18106
18107        let mut cx = EditorTestContext::for_editor(editor, cx).await;
18108
18109        cx.update_editor(|editor, window, cx| {
18110            editor.change_selections(None, window, cx, |selections| {
18111                selections.select(vec![
18112                    Selection::from_offset(4),
18113                    Selection::from_offset(9),
18114                    Selection::from_offset(15),
18115                ])
18116            })
18117        });
18118        cx.run_until_parked();
18119
18120        cx.update_editor(|editor, window, cx| {
18121            editor.handle_input(">", window, cx);
18122        });
18123        cx.run_until_parked();
18124
18125        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
18126    }
18127}
18128
18129fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18130    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18131    point..point
18132}
18133
18134fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18135    let (text, ranges) = marked_text_ranges(marked_text, true);
18136    assert_eq!(editor.text(cx), text);
18137    assert_eq!(
18138        editor.selections.ranges(cx),
18139        ranges,
18140        "Assert selections are {}",
18141        marked_text
18142    );
18143}
18144
18145pub fn handle_signature_help_request(
18146    cx: &mut EditorLspTestContext,
18147    mocked_response: lsp::SignatureHelp,
18148) -> impl Future<Output = ()> {
18149    let mut request =
18150        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18151            let mocked_response = mocked_response.clone();
18152            async move { Ok(Some(mocked_response)) }
18153        });
18154
18155    async move {
18156        request.next().await;
18157    }
18158}
18159
18160/// Handle completion request passing a marked string specifying where the completion
18161/// should be triggered from using '|' character, what range should be replaced, and what completions
18162/// should be returned using '<' and '>' to delimit the range
18163pub fn handle_completion_request(
18164    cx: &mut EditorLspTestContext,
18165    marked_string: &str,
18166    completions: Vec<&'static str>,
18167    counter: Arc<AtomicUsize>,
18168) -> impl Future<Output = ()> {
18169    let complete_from_marker: TextRangeMarker = '|'.into();
18170    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18171    let (_, mut marked_ranges) = marked_text_ranges_by(
18172        marked_string,
18173        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18174    );
18175
18176    let complete_from_position =
18177        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18178    let replace_range =
18179        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18180
18181    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
18182        let completions = completions.clone();
18183        counter.fetch_add(1, atomic::Ordering::Release);
18184        async move {
18185            assert_eq!(params.text_document_position.text_document.uri, url.clone());
18186            assert_eq!(
18187                params.text_document_position.position,
18188                complete_from_position
18189            );
18190            Ok(Some(lsp::CompletionResponse::Array(
18191                completions
18192                    .iter()
18193                    .map(|completion_text| lsp::CompletionItem {
18194                        label: completion_text.to_string(),
18195                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18196                            range: replace_range,
18197                            new_text: completion_text.to_string(),
18198                        })),
18199                        ..Default::default()
18200                    })
18201                    .collect(),
18202            )))
18203        }
18204    });
18205
18206    async move {
18207        request.next().await;
18208    }
18209}
18210
18211fn handle_resolve_completion_request(
18212    cx: &mut EditorLspTestContext,
18213    edits: Option<Vec<(&'static str, &'static str)>>,
18214) -> impl Future<Output = ()> {
18215    let edits = edits.map(|edits| {
18216        edits
18217            .iter()
18218            .map(|(marked_string, new_text)| {
18219                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18220                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18221                lsp::TextEdit::new(replace_range, new_text.to_string())
18222            })
18223            .collect::<Vec<_>>()
18224    });
18225
18226    let mut request =
18227        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18228            let edits = edits.clone();
18229            async move {
18230                Ok(lsp::CompletionItem {
18231                    additional_text_edits: edits,
18232                    ..Default::default()
18233                })
18234            }
18235        });
18236
18237    async move {
18238        request.next().await;
18239    }
18240}
18241
18242pub(crate) fn update_test_language_settings(
18243    cx: &mut TestAppContext,
18244    f: impl Fn(&mut AllLanguageSettingsContent),
18245) {
18246    cx.update(|cx| {
18247        SettingsStore::update_global(cx, |store, cx| {
18248            store.update_user_settings::<AllLanguageSettings>(cx, f);
18249        });
18250    });
18251}
18252
18253pub(crate) fn update_test_project_settings(
18254    cx: &mut TestAppContext,
18255    f: impl Fn(&mut ProjectSettings),
18256) {
18257    cx.update(|cx| {
18258        SettingsStore::update_global(cx, |store, cx| {
18259            store.update_user_settings::<ProjectSettings>(cx, f);
18260        });
18261    });
18262}
18263
18264pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18265    cx.update(|cx| {
18266        assets::Assets.load_test_fonts(cx);
18267        let store = SettingsStore::test(cx);
18268        cx.set_global(store);
18269        theme::init(theme::LoadThemes::JustBase, cx);
18270        release_channel::init(SemanticVersion::default(), cx);
18271        client::init_settings(cx);
18272        language::init(cx);
18273        Project::init_settings(cx);
18274        workspace::init_settings(cx);
18275        crate::init(cx);
18276    });
18277
18278    update_test_language_settings(cx, f);
18279}
18280
18281#[track_caller]
18282fn assert_hunk_revert(
18283    not_reverted_text_with_selections: &str,
18284    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18285    expected_reverted_text_with_selections: &str,
18286    base_text: &str,
18287    cx: &mut EditorLspTestContext,
18288) {
18289    cx.set_state(not_reverted_text_with_selections);
18290    cx.set_head_text(base_text);
18291    cx.executor().run_until_parked();
18292
18293    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18294        let snapshot = editor.snapshot(window, cx);
18295        let reverted_hunk_statuses = snapshot
18296            .buffer_snapshot
18297            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18298            .map(|hunk| hunk.status().kind)
18299            .collect::<Vec<_>>();
18300
18301        editor.git_restore(&Default::default(), window, cx);
18302        reverted_hunk_statuses
18303    });
18304    cx.executor().run_until_parked();
18305    cx.assert_editor_state(expected_reverted_text_with_selections);
18306    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18307}