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                async move {
 9316                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9317                        cx.background_executor()
 9318                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9319                            .await;
 9320                    }
 9321                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9322                        lsp::CompletionItem {
 9323                            label: "first".into(),
 9324                            ..lsp::CompletionItem::default()
 9325                        },
 9326                        lsp::CompletionItem {
 9327                            label: "last".into(),
 9328                            ..lsp::CompletionItem::default()
 9329                        },
 9330                    ])))
 9331                }
 9332            });
 9333
 9334    cx.set_state(indoc! {"
 9335        oneˇ
 9336        two
 9337        three
 9338    "});
 9339    cx.simulate_keystroke(".");
 9340    cx.executor().run_until_parked();
 9341    cx.condition(|editor, _| editor.context_menu_visible())
 9342        .await;
 9343    cx.update_editor(|editor, window, cx| {
 9344        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9345        {
 9346            assert_eq!(
 9347                completion_menu_entries(&menu),
 9348                &["first", "last"],
 9349                "When LSP server is fast to reply, no fallback word completions are used"
 9350            );
 9351        } else {
 9352            panic!("expected completion menu to be open");
 9353        }
 9354        editor.cancel(&Cancel, window, cx);
 9355    });
 9356    cx.executor().run_until_parked();
 9357    cx.condition(|editor, _| !editor.context_menu_visible())
 9358        .await;
 9359
 9360    throttle_completions.store(true, atomic::Ordering::Release);
 9361    cx.simulate_keystroke(".");
 9362    cx.executor()
 9363        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9364    cx.executor().run_until_parked();
 9365    cx.condition(|editor, _| editor.context_menu_visible())
 9366        .await;
 9367    cx.update_editor(|editor, _, _| {
 9368        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9369        {
 9370            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9371                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9372        } else {
 9373            panic!("expected completion menu to be open");
 9374        }
 9375    });
 9376}
 9377
 9378#[gpui::test]
 9379async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9380    init_test(cx, |language_settings| {
 9381        language_settings.defaults.completions = Some(CompletionSettings {
 9382            words: WordsCompletionMode::Enabled,
 9383            lsp: true,
 9384            lsp_fetch_timeout_ms: 0,
 9385        });
 9386    });
 9387
 9388    let mut cx = EditorLspTestContext::new_rust(
 9389        lsp::ServerCapabilities {
 9390            completion_provider: Some(lsp::CompletionOptions {
 9391                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9392                ..lsp::CompletionOptions::default()
 9393            }),
 9394            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9395            ..lsp::ServerCapabilities::default()
 9396        },
 9397        cx,
 9398    )
 9399    .await;
 9400
 9401    let _completion_requests_handler =
 9402        cx.lsp
 9403            .server
 9404            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9405                Ok(Some(lsp::CompletionResponse::Array(vec![
 9406                    lsp::CompletionItem {
 9407                        label: "first".into(),
 9408                        ..lsp::CompletionItem::default()
 9409                    },
 9410                    lsp::CompletionItem {
 9411                        label: "last".into(),
 9412                        ..lsp::CompletionItem::default()
 9413                    },
 9414                ])))
 9415            });
 9416
 9417    cx.set_state(indoc! {"ˇ
 9418        first
 9419        last
 9420        second
 9421    "});
 9422    cx.simulate_keystroke(".");
 9423    cx.executor().run_until_parked();
 9424    cx.condition(|editor, _| editor.context_menu_visible())
 9425        .await;
 9426    cx.update_editor(|editor, _, _| {
 9427        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9428        {
 9429            assert_eq!(
 9430                completion_menu_entries(&menu),
 9431                &["first", "last", "second"],
 9432                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9433            );
 9434        } else {
 9435            panic!("expected completion menu to be open");
 9436        }
 9437    });
 9438}
 9439
 9440#[gpui::test]
 9441async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9442    init_test(cx, |language_settings| {
 9443        language_settings.defaults.completions = Some(CompletionSettings {
 9444            words: WordsCompletionMode::Disabled,
 9445            lsp: true,
 9446            lsp_fetch_timeout_ms: 0,
 9447        });
 9448    });
 9449
 9450    let mut cx = EditorLspTestContext::new_rust(
 9451        lsp::ServerCapabilities {
 9452            completion_provider: Some(lsp::CompletionOptions {
 9453                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9454                ..lsp::CompletionOptions::default()
 9455            }),
 9456            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9457            ..lsp::ServerCapabilities::default()
 9458        },
 9459        cx,
 9460    )
 9461    .await;
 9462
 9463    let _completion_requests_handler =
 9464        cx.lsp
 9465            .server
 9466            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9467                panic!("LSP completions should not be queried when dealing with word completions")
 9468            });
 9469
 9470    cx.set_state(indoc! {"ˇ
 9471        first
 9472        last
 9473        second
 9474    "});
 9475    cx.update_editor(|editor, window, cx| {
 9476        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9477    });
 9478    cx.executor().run_until_parked();
 9479    cx.condition(|editor, _| editor.context_menu_visible())
 9480        .await;
 9481    cx.update_editor(|editor, _, _| {
 9482        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9483        {
 9484            assert_eq!(
 9485                completion_menu_entries(&menu),
 9486                &["first", "last", "second"],
 9487                "`ShowWordCompletions` action should show word completions"
 9488            );
 9489        } else {
 9490            panic!("expected completion menu to be open");
 9491        }
 9492    });
 9493
 9494    cx.simulate_keystroke("s");
 9495    cx.executor().run_until_parked();
 9496    cx.condition(|editor, _| editor.context_menu_visible())
 9497        .await;
 9498    cx.update_editor(|editor, _, _| {
 9499        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9500        {
 9501            assert_eq!(
 9502                completion_menu_entries(&menu),
 9503                &["second"],
 9504                "After showing word completions, further editing should filter them and not query the LSP"
 9505            );
 9506        } else {
 9507            panic!("expected completion menu to be open");
 9508        }
 9509    });
 9510}
 9511
 9512#[gpui::test]
 9513async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9514    init_test(cx, |language_settings| {
 9515        language_settings.defaults.completions = Some(CompletionSettings {
 9516            words: WordsCompletionMode::Fallback,
 9517            lsp: false,
 9518            lsp_fetch_timeout_ms: 0,
 9519        });
 9520    });
 9521
 9522    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9523
 9524    cx.set_state(indoc! {"ˇ
 9525        0_usize
 9526        let
 9527        33
 9528        4.5f32
 9529    "});
 9530    cx.update_editor(|editor, window, cx| {
 9531        editor.show_completions(&ShowCompletions::default(), window, cx);
 9532    });
 9533    cx.executor().run_until_parked();
 9534    cx.condition(|editor, _| editor.context_menu_visible())
 9535        .await;
 9536    cx.update_editor(|editor, window, cx| {
 9537        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9538        {
 9539            assert_eq!(
 9540                completion_menu_entries(&menu),
 9541                &["let"],
 9542                "With no digits in the completion query, no digits should be in the word completions"
 9543            );
 9544        } else {
 9545            panic!("expected completion menu to be open");
 9546        }
 9547        editor.cancel(&Cancel, window, cx);
 9548    });
 9549
 9550    cx.set_state(indoc! {" 9551        0_usize
 9552        let
 9553        3
 9554        33.35f32
 9555    "});
 9556    cx.update_editor(|editor, window, cx| {
 9557        editor.show_completions(&ShowCompletions::default(), window, cx);
 9558    });
 9559    cx.executor().run_until_parked();
 9560    cx.condition(|editor, _| editor.context_menu_visible())
 9561        .await;
 9562    cx.update_editor(|editor, _, _| {
 9563        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9564        {
 9565            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9566                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9567        } else {
 9568            panic!("expected completion menu to be open");
 9569        }
 9570    });
 9571}
 9572
 9573#[gpui::test]
 9574async fn test_multiline_completion(cx: &mut TestAppContext) {
 9575    init_test(cx, |_| {});
 9576
 9577    let fs = FakeFs::new(cx.executor());
 9578    fs.insert_tree(
 9579        path!("/a"),
 9580        json!({
 9581            "main.ts": "a",
 9582        }),
 9583    )
 9584    .await;
 9585
 9586    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9587    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9588    let typescript_language = Arc::new(Language::new(
 9589        LanguageConfig {
 9590            name: "TypeScript".into(),
 9591            matcher: LanguageMatcher {
 9592                path_suffixes: vec!["ts".to_string()],
 9593                ..LanguageMatcher::default()
 9594            },
 9595            line_comments: vec!["// ".into()],
 9596            ..LanguageConfig::default()
 9597        },
 9598        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9599    ));
 9600    language_registry.add(typescript_language.clone());
 9601    let mut fake_servers = language_registry.register_fake_lsp(
 9602        "TypeScript",
 9603        FakeLspAdapter {
 9604            capabilities: lsp::ServerCapabilities {
 9605                completion_provider: Some(lsp::CompletionOptions {
 9606                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9607                    ..lsp::CompletionOptions::default()
 9608                }),
 9609                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9610                ..lsp::ServerCapabilities::default()
 9611            },
 9612            // Emulate vtsls label generation
 9613            label_for_completion: Some(Box::new(|item, _| {
 9614                let text = if let Some(description) = item
 9615                    .label_details
 9616                    .as_ref()
 9617                    .and_then(|label_details| label_details.description.as_ref())
 9618                {
 9619                    format!("{} {}", item.label, description)
 9620                } else if let Some(detail) = &item.detail {
 9621                    format!("{} {}", item.label, detail)
 9622                } else {
 9623                    item.label.clone()
 9624                };
 9625                let len = text.len();
 9626                Some(language::CodeLabel {
 9627                    text,
 9628                    runs: Vec::new(),
 9629                    filter_range: 0..len,
 9630                })
 9631            })),
 9632            ..FakeLspAdapter::default()
 9633        },
 9634    );
 9635    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9636    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9637    let worktree_id = workspace
 9638        .update(cx, |workspace, _window, cx| {
 9639            workspace.project().update(cx, |project, cx| {
 9640                project.worktrees(cx).next().unwrap().read(cx).id()
 9641            })
 9642        })
 9643        .unwrap();
 9644    let _buffer = project
 9645        .update(cx, |project, cx| {
 9646            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9647        })
 9648        .await
 9649        .unwrap();
 9650    let editor = workspace
 9651        .update(cx, |workspace, window, cx| {
 9652            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9653        })
 9654        .unwrap()
 9655        .await
 9656        .unwrap()
 9657        .downcast::<Editor>()
 9658        .unwrap();
 9659    let fake_server = fake_servers.next().await.unwrap();
 9660
 9661    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9662    let multiline_label_2 = "a\nb\nc\n";
 9663    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9664    let multiline_description = "d\ne\nf\n";
 9665    let multiline_detail_2 = "g\nh\ni\n";
 9666
 9667    let mut completion_handle =
 9668        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9669            Ok(Some(lsp::CompletionResponse::Array(vec![
 9670                lsp::CompletionItem {
 9671                    label: multiline_label.to_string(),
 9672                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9673                        range: lsp::Range {
 9674                            start: lsp::Position {
 9675                                line: params.text_document_position.position.line,
 9676                                character: params.text_document_position.position.character,
 9677                            },
 9678                            end: lsp::Position {
 9679                                line: params.text_document_position.position.line,
 9680                                character: params.text_document_position.position.character,
 9681                            },
 9682                        },
 9683                        new_text: "new_text_1".to_string(),
 9684                    })),
 9685                    ..lsp::CompletionItem::default()
 9686                },
 9687                lsp::CompletionItem {
 9688                    label: "single line label 1".to_string(),
 9689                    detail: Some(multiline_detail.to_string()),
 9690                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9691                        range: lsp::Range {
 9692                            start: lsp::Position {
 9693                                line: params.text_document_position.position.line,
 9694                                character: params.text_document_position.position.character,
 9695                            },
 9696                            end: lsp::Position {
 9697                                line: params.text_document_position.position.line,
 9698                                character: params.text_document_position.position.character,
 9699                            },
 9700                        },
 9701                        new_text: "new_text_2".to_string(),
 9702                    })),
 9703                    ..lsp::CompletionItem::default()
 9704                },
 9705                lsp::CompletionItem {
 9706                    label: "single line label 2".to_string(),
 9707                    label_details: Some(lsp::CompletionItemLabelDetails {
 9708                        description: Some(multiline_description.to_string()),
 9709                        detail: None,
 9710                    }),
 9711                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9712                        range: lsp::Range {
 9713                            start: lsp::Position {
 9714                                line: params.text_document_position.position.line,
 9715                                character: params.text_document_position.position.character,
 9716                            },
 9717                            end: lsp::Position {
 9718                                line: params.text_document_position.position.line,
 9719                                character: params.text_document_position.position.character,
 9720                            },
 9721                        },
 9722                        new_text: "new_text_2".to_string(),
 9723                    })),
 9724                    ..lsp::CompletionItem::default()
 9725                },
 9726                lsp::CompletionItem {
 9727                    label: multiline_label_2.to_string(),
 9728                    detail: Some(multiline_detail_2.to_string()),
 9729                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9730                        range: lsp::Range {
 9731                            start: lsp::Position {
 9732                                line: params.text_document_position.position.line,
 9733                                character: params.text_document_position.position.character,
 9734                            },
 9735                            end: lsp::Position {
 9736                                line: params.text_document_position.position.line,
 9737                                character: params.text_document_position.position.character,
 9738                            },
 9739                        },
 9740                        new_text: "new_text_3".to_string(),
 9741                    })),
 9742                    ..lsp::CompletionItem::default()
 9743                },
 9744                lsp::CompletionItem {
 9745                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9746                    detail: Some(
 9747                        "Details with many     spaces and \t but without newlines".to_string(),
 9748                    ),
 9749                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9750                        range: lsp::Range {
 9751                            start: lsp::Position {
 9752                                line: params.text_document_position.position.line,
 9753                                character: params.text_document_position.position.character,
 9754                            },
 9755                            end: lsp::Position {
 9756                                line: params.text_document_position.position.line,
 9757                                character: params.text_document_position.position.character,
 9758                            },
 9759                        },
 9760                        new_text: "new_text_4".to_string(),
 9761                    })),
 9762                    ..lsp::CompletionItem::default()
 9763                },
 9764            ])))
 9765        });
 9766
 9767    editor.update_in(cx, |editor, window, cx| {
 9768        cx.focus_self(window);
 9769        editor.move_to_end(&MoveToEnd, window, cx);
 9770        editor.handle_input(".", window, cx);
 9771    });
 9772    cx.run_until_parked();
 9773    completion_handle.next().await.unwrap();
 9774
 9775    editor.update(cx, |editor, _| {
 9776        assert!(editor.context_menu_visible());
 9777        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9778        {
 9779            let completion_labels = menu
 9780                .completions
 9781                .borrow()
 9782                .iter()
 9783                .map(|c| c.label.text.clone())
 9784                .collect::<Vec<_>>();
 9785            assert_eq!(
 9786                completion_labels,
 9787                &[
 9788                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9789                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9790                    "single line label 2 d e f ",
 9791                    "a b c g h i ",
 9792                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9793                ],
 9794                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9795            );
 9796
 9797            for completion in menu
 9798                .completions
 9799                .borrow()
 9800                .iter() {
 9801                    assert_eq!(
 9802                        completion.label.filter_range,
 9803                        0..completion.label.text.len(),
 9804                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9805                    );
 9806                }
 9807
 9808        } else {
 9809            panic!("expected completion menu to be open");
 9810        }
 9811    });
 9812}
 9813
 9814#[gpui::test]
 9815async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9816    init_test(cx, |_| {});
 9817    let mut cx = EditorLspTestContext::new_rust(
 9818        lsp::ServerCapabilities {
 9819            completion_provider: Some(lsp::CompletionOptions {
 9820                trigger_characters: Some(vec![".".to_string()]),
 9821                ..Default::default()
 9822            }),
 9823            ..Default::default()
 9824        },
 9825        cx,
 9826    )
 9827    .await;
 9828    cx.lsp
 9829        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9830            Ok(Some(lsp::CompletionResponse::Array(vec![
 9831                lsp::CompletionItem {
 9832                    label: "first".into(),
 9833                    ..Default::default()
 9834                },
 9835                lsp::CompletionItem {
 9836                    label: "last".into(),
 9837                    ..Default::default()
 9838                },
 9839            ])))
 9840        });
 9841    cx.set_state("variableˇ");
 9842    cx.simulate_keystroke(".");
 9843    cx.executor().run_until_parked();
 9844
 9845    cx.update_editor(|editor, _, _| {
 9846        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9847        {
 9848            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9849        } else {
 9850            panic!("expected completion menu to be open");
 9851        }
 9852    });
 9853
 9854    cx.update_editor(|editor, window, cx| {
 9855        editor.move_page_down(&MovePageDown::default(), window, cx);
 9856        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9857        {
 9858            assert!(
 9859                menu.selected_item == 1,
 9860                "expected PageDown to select the last item from the context menu"
 9861            );
 9862        } else {
 9863            panic!("expected completion menu to stay open after PageDown");
 9864        }
 9865    });
 9866
 9867    cx.update_editor(|editor, window, cx| {
 9868        editor.move_page_up(&MovePageUp::default(), window, cx);
 9869        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9870        {
 9871            assert!(
 9872                menu.selected_item == 0,
 9873                "expected PageUp to select the first item from the context menu"
 9874            );
 9875        } else {
 9876            panic!("expected completion menu to stay open after PageUp");
 9877        }
 9878    });
 9879}
 9880
 9881#[gpui::test]
 9882async fn test_completion_sort(cx: &mut TestAppContext) {
 9883    init_test(cx, |_| {});
 9884    let mut cx = EditorLspTestContext::new_rust(
 9885        lsp::ServerCapabilities {
 9886            completion_provider: Some(lsp::CompletionOptions {
 9887                trigger_characters: Some(vec![".".to_string()]),
 9888                ..Default::default()
 9889            }),
 9890            ..Default::default()
 9891        },
 9892        cx,
 9893    )
 9894    .await;
 9895    cx.lsp
 9896        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9897            Ok(Some(lsp::CompletionResponse::Array(vec![
 9898                lsp::CompletionItem {
 9899                    label: "Range".into(),
 9900                    sort_text: Some("a".into()),
 9901                    ..Default::default()
 9902                },
 9903                lsp::CompletionItem {
 9904                    label: "r".into(),
 9905                    sort_text: Some("b".into()),
 9906                    ..Default::default()
 9907                },
 9908                lsp::CompletionItem {
 9909                    label: "ret".into(),
 9910                    sort_text: Some("c".into()),
 9911                    ..Default::default()
 9912                },
 9913                lsp::CompletionItem {
 9914                    label: "return".into(),
 9915                    sort_text: Some("d".into()),
 9916                    ..Default::default()
 9917                },
 9918                lsp::CompletionItem {
 9919                    label: "slice".into(),
 9920                    sort_text: Some("d".into()),
 9921                    ..Default::default()
 9922                },
 9923            ])))
 9924        });
 9925    cx.set_state("");
 9926    cx.executor().run_until_parked();
 9927    cx.update_editor(|editor, window, cx| {
 9928        editor.show_completions(
 9929            &ShowCompletions {
 9930                trigger: Some("r".into()),
 9931            },
 9932            window,
 9933            cx,
 9934        );
 9935    });
 9936    cx.executor().run_until_parked();
 9937
 9938    cx.update_editor(|editor, _, _| {
 9939        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9940        {
 9941            assert_eq!(
 9942                completion_menu_entries(&menu),
 9943                &["r", "ret", "Range", "return"]
 9944            );
 9945        } else {
 9946            panic!("expected completion menu to be open");
 9947        }
 9948    });
 9949}
 9950
 9951#[gpui::test]
 9952async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9953    init_test(cx, |_| {});
 9954
 9955    let mut cx = EditorLspTestContext::new_rust(
 9956        lsp::ServerCapabilities {
 9957            completion_provider: Some(lsp::CompletionOptions {
 9958                trigger_characters: Some(vec![".".to_string()]),
 9959                resolve_provider: Some(true),
 9960                ..Default::default()
 9961            }),
 9962            ..Default::default()
 9963        },
 9964        cx,
 9965    )
 9966    .await;
 9967
 9968    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9969    cx.simulate_keystroke(".");
 9970    let completion_item = lsp::CompletionItem {
 9971        label: "Some".into(),
 9972        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9973        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9974        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9975            kind: lsp::MarkupKind::Markdown,
 9976            value: "```rust\nSome(2)\n```".to_string(),
 9977        })),
 9978        deprecated: Some(false),
 9979        sort_text: Some("Some".to_string()),
 9980        filter_text: Some("Some".to_string()),
 9981        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9982        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9983            range: lsp::Range {
 9984                start: lsp::Position {
 9985                    line: 0,
 9986                    character: 22,
 9987                },
 9988                end: lsp::Position {
 9989                    line: 0,
 9990                    character: 22,
 9991                },
 9992            },
 9993            new_text: "Some(2)".to_string(),
 9994        })),
 9995        additional_text_edits: Some(vec![lsp::TextEdit {
 9996            range: lsp::Range {
 9997                start: lsp::Position {
 9998                    line: 0,
 9999                    character: 20,
10000                },
10001                end: lsp::Position {
10002                    line: 0,
10003                    character: 22,
10004                },
10005            },
10006            new_text: "".to_string(),
10007        }]),
10008        ..Default::default()
10009    };
10010
10011    let closure_completion_item = completion_item.clone();
10012    let counter = Arc::new(AtomicUsize::new(0));
10013    let counter_clone = counter.clone();
10014    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10015        let task_completion_item = closure_completion_item.clone();
10016        counter_clone.fetch_add(1, atomic::Ordering::Release);
10017        async move {
10018            Ok(Some(lsp::CompletionResponse::Array(vec![
10019                task_completion_item,
10020            ])))
10021        }
10022    });
10023
10024    cx.condition(|editor, _| editor.context_menu_visible())
10025        .await;
10026    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
10027    assert!(request.next().await.is_some());
10028    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10029
10030    cx.simulate_keystroke("S");
10031    cx.simulate_keystroke("o");
10032    cx.simulate_keystroke("m");
10033    cx.condition(|editor, _| editor.context_menu_visible())
10034        .await;
10035    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
10036    assert!(request.next().await.is_some());
10037    assert!(request.next().await.is_some());
10038    assert!(request.next().await.is_some());
10039    request.close();
10040    assert!(request.next().await.is_none());
10041    assert_eq!(
10042        counter.load(atomic::Ordering::Acquire),
10043        4,
10044        "With the completions menu open, only one LSP request should happen per input"
10045    );
10046}
10047
10048#[gpui::test]
10049async fn test_toggle_comment(cx: &mut TestAppContext) {
10050    init_test(cx, |_| {});
10051    let mut cx = EditorTestContext::new(cx).await;
10052    let language = Arc::new(Language::new(
10053        LanguageConfig {
10054            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10055            ..Default::default()
10056        },
10057        Some(tree_sitter_rust::LANGUAGE.into()),
10058    ));
10059    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10060
10061    // If multiple selections intersect a line, the line is only toggled once.
10062    cx.set_state(indoc! {"
10063        fn a() {
10064            «//b();
10065            ˇ»// «c();
10066            //ˇ»  d();
10067        }
10068    "});
10069
10070    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10071
10072    cx.assert_editor_state(indoc! {"
10073        fn a() {
10074            «b();
10075            c();
10076            ˇ» d();
10077        }
10078    "});
10079
10080    // The comment prefix is inserted at the same column for every line in a
10081    // selection.
10082    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10083
10084    cx.assert_editor_state(indoc! {"
10085        fn a() {
10086            // «b();
10087            // c();
10088            ˇ»//  d();
10089        }
10090    "});
10091
10092    // If a selection ends at the beginning of a line, that line is not toggled.
10093    cx.set_selections_state(indoc! {"
10094        fn a() {
10095            // b();
10096            «// c();
10097        ˇ»    //  d();
10098        }
10099    "});
10100
10101    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10102
10103    cx.assert_editor_state(indoc! {"
10104        fn a() {
10105            // b();
10106            «c();
10107        ˇ»    //  d();
10108        }
10109    "});
10110
10111    // If a selection span a single line and is empty, the line is toggled.
10112    cx.set_state(indoc! {"
10113        fn a() {
10114            a();
10115            b();
10116        ˇ
10117        }
10118    "});
10119
10120    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10121
10122    cx.assert_editor_state(indoc! {"
10123        fn a() {
10124            a();
10125            b();
10126        //•ˇ
10127        }
10128    "});
10129
10130    // If a selection span multiple lines, empty lines are not toggled.
10131    cx.set_state(indoc! {"
10132        fn a() {
10133            «a();
10134
10135            c();ˇ»
10136        }
10137    "});
10138
10139    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10140
10141    cx.assert_editor_state(indoc! {"
10142        fn a() {
10143            // «a();
10144
10145            // c();ˇ»
10146        }
10147    "});
10148
10149    // If a selection includes multiple comment prefixes, all lines are uncommented.
10150    cx.set_state(indoc! {"
10151        fn a() {
10152            «// a();
10153            /// b();
10154            //! c();ˇ»
10155        }
10156    "});
10157
10158    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10159
10160    cx.assert_editor_state(indoc! {"
10161        fn a() {
10162            «a();
10163            b();
10164            c();ˇ»
10165        }
10166    "});
10167}
10168
10169#[gpui::test]
10170async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10171    init_test(cx, |_| {});
10172    let mut cx = EditorTestContext::new(cx).await;
10173    let language = Arc::new(Language::new(
10174        LanguageConfig {
10175            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10176            ..Default::default()
10177        },
10178        Some(tree_sitter_rust::LANGUAGE.into()),
10179    ));
10180    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10181
10182    let toggle_comments = &ToggleComments {
10183        advance_downwards: false,
10184        ignore_indent: true,
10185    };
10186
10187    // If multiple selections intersect a line, the line is only toggled once.
10188    cx.set_state(indoc! {"
10189        fn a() {
10190        //    «b();
10191        //    c();
10192        //    ˇ» d();
10193        }
10194    "});
10195
10196    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10197
10198    cx.assert_editor_state(indoc! {"
10199        fn a() {
10200            «b();
10201            c();
10202            ˇ» d();
10203        }
10204    "});
10205
10206    // The comment prefix is inserted at the beginning of each line
10207    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10208
10209    cx.assert_editor_state(indoc! {"
10210        fn a() {
10211        //    «b();
10212        //    c();
10213        //    ˇ» d();
10214        }
10215    "});
10216
10217    // If a selection ends at the beginning of a line, that line is not toggled.
10218    cx.set_selections_state(indoc! {"
10219        fn a() {
10220        //    b();
10221        //    «c();
10222        ˇ»//     d();
10223        }
10224    "});
10225
10226    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10227
10228    cx.assert_editor_state(indoc! {"
10229        fn a() {
10230        //    b();
10231            «c();
10232        ˇ»//     d();
10233        }
10234    "});
10235
10236    // If a selection span a single line and is empty, the line is toggled.
10237    cx.set_state(indoc! {"
10238        fn a() {
10239            a();
10240            b();
10241        ˇ
10242        }
10243    "});
10244
10245    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10246
10247    cx.assert_editor_state(indoc! {"
10248        fn a() {
10249            a();
10250            b();
10251        //ˇ
10252        }
10253    "});
10254
10255    // If a selection span multiple lines, empty lines are not toggled.
10256    cx.set_state(indoc! {"
10257        fn a() {
10258            «a();
10259
10260            c();ˇ»
10261        }
10262    "});
10263
10264    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10265
10266    cx.assert_editor_state(indoc! {"
10267        fn a() {
10268        //    «a();
10269
10270        //    c();ˇ»
10271        }
10272    "});
10273
10274    // If a selection includes multiple comment prefixes, all lines are uncommented.
10275    cx.set_state(indoc! {"
10276        fn a() {
10277        //    «a();
10278        ///    b();
10279        //!    c();ˇ»
10280        }
10281    "});
10282
10283    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10284
10285    cx.assert_editor_state(indoc! {"
10286        fn a() {
10287            «a();
10288            b();
10289            c();ˇ»
10290        }
10291    "});
10292}
10293
10294#[gpui::test]
10295async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10296    init_test(cx, |_| {});
10297
10298    let language = Arc::new(Language::new(
10299        LanguageConfig {
10300            line_comments: vec!["// ".into()],
10301            ..Default::default()
10302        },
10303        Some(tree_sitter_rust::LANGUAGE.into()),
10304    ));
10305
10306    let mut cx = EditorTestContext::new(cx).await;
10307
10308    cx.language_registry().add(language.clone());
10309    cx.update_buffer(|buffer, cx| {
10310        buffer.set_language(Some(language), cx);
10311    });
10312
10313    let toggle_comments = &ToggleComments {
10314        advance_downwards: true,
10315        ignore_indent: false,
10316    };
10317
10318    // Single cursor on one line -> advance
10319    // Cursor moves horizontally 3 characters as well on non-blank line
10320    cx.set_state(indoc!(
10321        "fn a() {
10322             ˇdog();
10323             cat();
10324        }"
10325    ));
10326    cx.update_editor(|editor, window, cx| {
10327        editor.toggle_comments(toggle_comments, window, cx);
10328    });
10329    cx.assert_editor_state(indoc!(
10330        "fn a() {
10331             // dog();
10332             catˇ();
10333        }"
10334    ));
10335
10336    // Single selection on one line -> don't advance
10337    cx.set_state(indoc!(
10338        "fn a() {
10339             «dog()ˇ»;
10340             cat();
10341        }"
10342    ));
10343    cx.update_editor(|editor, window, cx| {
10344        editor.toggle_comments(toggle_comments, window, cx);
10345    });
10346    cx.assert_editor_state(indoc!(
10347        "fn a() {
10348             // «dog()ˇ»;
10349             cat();
10350        }"
10351    ));
10352
10353    // Multiple cursors on one line -> advance
10354    cx.set_state(indoc!(
10355        "fn a() {
10356             ˇdˇog();
10357             cat();
10358        }"
10359    ));
10360    cx.update_editor(|editor, window, cx| {
10361        editor.toggle_comments(toggle_comments, window, cx);
10362    });
10363    cx.assert_editor_state(indoc!(
10364        "fn a() {
10365             // dog();
10366             catˇ(ˇ);
10367        }"
10368    ));
10369
10370    // Multiple cursors on one line, with selection -> don't advance
10371    cx.set_state(indoc!(
10372        "fn a() {
10373             ˇdˇog«()ˇ»;
10374             cat();
10375        }"
10376    ));
10377    cx.update_editor(|editor, window, cx| {
10378        editor.toggle_comments(toggle_comments, window, cx);
10379    });
10380    cx.assert_editor_state(indoc!(
10381        "fn a() {
10382             // ˇdˇog«()ˇ»;
10383             cat();
10384        }"
10385    ));
10386
10387    // Single cursor on one line -> advance
10388    // Cursor moves to column 0 on blank line
10389    cx.set_state(indoc!(
10390        "fn a() {
10391             ˇdog();
10392
10393             cat();
10394        }"
10395    ));
10396    cx.update_editor(|editor, window, cx| {
10397        editor.toggle_comments(toggle_comments, window, cx);
10398    });
10399    cx.assert_editor_state(indoc!(
10400        "fn a() {
10401             // dog();
10402        ˇ
10403             cat();
10404        }"
10405    ));
10406
10407    // Single cursor on one line -> advance
10408    // Cursor starts and ends at column 0
10409    cx.set_state(indoc!(
10410        "fn a() {
10411         ˇ    dog();
10412             cat();
10413        }"
10414    ));
10415    cx.update_editor(|editor, window, cx| {
10416        editor.toggle_comments(toggle_comments, window, cx);
10417    });
10418    cx.assert_editor_state(indoc!(
10419        "fn a() {
10420             // dog();
10421         ˇ    cat();
10422        }"
10423    ));
10424}
10425
10426#[gpui::test]
10427async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10428    init_test(cx, |_| {});
10429
10430    let mut cx = EditorTestContext::new(cx).await;
10431
10432    let html_language = Arc::new(
10433        Language::new(
10434            LanguageConfig {
10435                name: "HTML".into(),
10436                block_comment: Some(("<!-- ".into(), " -->".into())),
10437                ..Default::default()
10438            },
10439            Some(tree_sitter_html::LANGUAGE.into()),
10440        )
10441        .with_injection_query(
10442            r#"
10443            (script_element
10444                (raw_text) @injection.content
10445                (#set! injection.language "javascript"))
10446            "#,
10447        )
10448        .unwrap(),
10449    );
10450
10451    let javascript_language = Arc::new(Language::new(
10452        LanguageConfig {
10453            name: "JavaScript".into(),
10454            line_comments: vec!["// ".into()],
10455            ..Default::default()
10456        },
10457        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10458    ));
10459
10460    cx.language_registry().add(html_language.clone());
10461    cx.language_registry().add(javascript_language.clone());
10462    cx.update_buffer(|buffer, cx| {
10463        buffer.set_language(Some(html_language), cx);
10464    });
10465
10466    // Toggle comments for empty selections
10467    cx.set_state(
10468        &r#"
10469            <p>A</p>ˇ
10470            <p>B</p>ˇ
10471            <p>C</p>ˇ
10472        "#
10473        .unindent(),
10474    );
10475    cx.update_editor(|editor, window, cx| {
10476        editor.toggle_comments(&ToggleComments::default(), window, cx)
10477    });
10478    cx.assert_editor_state(
10479        &r#"
10480            <!-- <p>A</p>ˇ -->
10481            <!-- <p>B</p>ˇ -->
10482            <!-- <p>C</p>ˇ -->
10483        "#
10484        .unindent(),
10485    );
10486    cx.update_editor(|editor, window, cx| {
10487        editor.toggle_comments(&ToggleComments::default(), window, cx)
10488    });
10489    cx.assert_editor_state(
10490        &r#"
10491            <p>A</p>ˇ
10492            <p>B</p>ˇ
10493            <p>C</p>ˇ
10494        "#
10495        .unindent(),
10496    );
10497
10498    // Toggle comments for mixture of empty and non-empty selections, where
10499    // multiple selections occupy a given line.
10500    cx.set_state(
10501        &r#"
10502            <p>A«</p>
10503            <p>ˇ»B</p>ˇ
10504            <p>C«</p>
10505            <p>ˇ»D</p>ˇ
10506        "#
10507        .unindent(),
10508    );
10509
10510    cx.update_editor(|editor, window, cx| {
10511        editor.toggle_comments(&ToggleComments::default(), window, cx)
10512    });
10513    cx.assert_editor_state(
10514        &r#"
10515            <!-- <p>A«</p>
10516            <p>ˇ»B</p>ˇ -->
10517            <!-- <p>C«</p>
10518            <p>ˇ»D</p>ˇ -->
10519        "#
10520        .unindent(),
10521    );
10522    cx.update_editor(|editor, window, cx| {
10523        editor.toggle_comments(&ToggleComments::default(), window, cx)
10524    });
10525    cx.assert_editor_state(
10526        &r#"
10527            <p>A«</p>
10528            <p>ˇ»B</p>ˇ
10529            <p>C«</p>
10530            <p>ˇ»D</p>ˇ
10531        "#
10532        .unindent(),
10533    );
10534
10535    // Toggle comments when different languages are active for different
10536    // selections.
10537    cx.set_state(
10538        &r#"
10539            ˇ<script>
10540                ˇvar x = new Y();
10541            ˇ</script>
10542        "#
10543        .unindent(),
10544    );
10545    cx.executor().run_until_parked();
10546    cx.update_editor(|editor, window, cx| {
10547        editor.toggle_comments(&ToggleComments::default(), window, cx)
10548    });
10549    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10550    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10551    cx.assert_editor_state(
10552        &r#"
10553            <!-- ˇ<script> -->
10554                // ˇvar x = new Y();
10555            <!-- ˇ</script> -->
10556        "#
10557        .unindent(),
10558    );
10559}
10560
10561#[gpui::test]
10562fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10563    init_test(cx, |_| {});
10564
10565    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10566    let multibuffer = cx.new(|cx| {
10567        let mut multibuffer = MultiBuffer::new(ReadWrite);
10568        multibuffer.push_excerpts(
10569            buffer.clone(),
10570            [
10571                ExcerptRange {
10572                    context: Point::new(0, 0)..Point::new(0, 4),
10573                    primary: None,
10574                },
10575                ExcerptRange {
10576                    context: Point::new(1, 0)..Point::new(1, 4),
10577                    primary: None,
10578                },
10579            ],
10580            cx,
10581        );
10582        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10583        multibuffer
10584    });
10585
10586    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10587    editor.update_in(cx, |editor, window, cx| {
10588        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10589        editor.change_selections(None, window, cx, |s| {
10590            s.select_ranges([
10591                Point::new(0, 0)..Point::new(0, 0),
10592                Point::new(1, 0)..Point::new(1, 0),
10593            ])
10594        });
10595
10596        editor.handle_input("X", window, cx);
10597        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10598        assert_eq!(
10599            editor.selections.ranges(cx),
10600            [
10601                Point::new(0, 1)..Point::new(0, 1),
10602                Point::new(1, 1)..Point::new(1, 1),
10603            ]
10604        );
10605
10606        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10607        editor.change_selections(None, window, cx, |s| {
10608            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10609        });
10610        editor.backspace(&Default::default(), window, cx);
10611        assert_eq!(editor.text(cx), "Xa\nbbb");
10612        assert_eq!(
10613            editor.selections.ranges(cx),
10614            [Point::new(1, 0)..Point::new(1, 0)]
10615        );
10616
10617        editor.change_selections(None, window, cx, |s| {
10618            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10619        });
10620        editor.backspace(&Default::default(), window, cx);
10621        assert_eq!(editor.text(cx), "X\nbb");
10622        assert_eq!(
10623            editor.selections.ranges(cx),
10624            [Point::new(0, 1)..Point::new(0, 1)]
10625        );
10626    });
10627}
10628
10629#[gpui::test]
10630fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10631    init_test(cx, |_| {});
10632
10633    let markers = vec![('[', ']').into(), ('(', ')').into()];
10634    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10635        indoc! {"
10636            [aaaa
10637            (bbbb]
10638            cccc)",
10639        },
10640        markers.clone(),
10641    );
10642    let excerpt_ranges = markers.into_iter().map(|marker| {
10643        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10644        ExcerptRange {
10645            context,
10646            primary: None,
10647        }
10648    });
10649    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10650    let multibuffer = cx.new(|cx| {
10651        let mut multibuffer = MultiBuffer::new(ReadWrite);
10652        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10653        multibuffer
10654    });
10655
10656    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10657    editor.update_in(cx, |editor, window, cx| {
10658        let (expected_text, selection_ranges) = marked_text_ranges(
10659            indoc! {"
10660                aaaa
10661                bˇbbb
10662                bˇbbˇb
10663                cccc"
10664            },
10665            true,
10666        );
10667        assert_eq!(editor.text(cx), expected_text);
10668        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10669
10670        editor.handle_input("X", window, cx);
10671
10672        let (expected_text, expected_selections) = marked_text_ranges(
10673            indoc! {"
10674                aaaa
10675                bXˇbbXb
10676                bXˇbbXˇb
10677                cccc"
10678            },
10679            false,
10680        );
10681        assert_eq!(editor.text(cx), expected_text);
10682        assert_eq!(editor.selections.ranges(cx), expected_selections);
10683
10684        editor.newline(&Newline, window, cx);
10685        let (expected_text, expected_selections) = marked_text_ranges(
10686            indoc! {"
10687                aaaa
10688                bX
10689                ˇbbX
10690                b
10691                bX
10692                ˇbbX
10693                ˇb
10694                cccc"
10695            },
10696            false,
10697        );
10698        assert_eq!(editor.text(cx), expected_text);
10699        assert_eq!(editor.selections.ranges(cx), expected_selections);
10700    });
10701}
10702
10703#[gpui::test]
10704fn test_refresh_selections(cx: &mut TestAppContext) {
10705    init_test(cx, |_| {});
10706
10707    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10708    let mut excerpt1_id = None;
10709    let multibuffer = cx.new(|cx| {
10710        let mut multibuffer = MultiBuffer::new(ReadWrite);
10711        excerpt1_id = multibuffer
10712            .push_excerpts(
10713                buffer.clone(),
10714                [
10715                    ExcerptRange {
10716                        context: Point::new(0, 0)..Point::new(1, 4),
10717                        primary: None,
10718                    },
10719                    ExcerptRange {
10720                        context: Point::new(1, 0)..Point::new(2, 4),
10721                        primary: None,
10722                    },
10723                ],
10724                cx,
10725            )
10726            .into_iter()
10727            .next();
10728        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10729        multibuffer
10730    });
10731
10732    let editor = cx.add_window(|window, cx| {
10733        let mut editor = build_editor(multibuffer.clone(), window, cx);
10734        let snapshot = editor.snapshot(window, cx);
10735        editor.change_selections(None, window, cx, |s| {
10736            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10737        });
10738        editor.begin_selection(
10739            Point::new(2, 1).to_display_point(&snapshot),
10740            true,
10741            1,
10742            window,
10743            cx,
10744        );
10745        assert_eq!(
10746            editor.selections.ranges(cx),
10747            [
10748                Point::new(1, 3)..Point::new(1, 3),
10749                Point::new(2, 1)..Point::new(2, 1),
10750            ]
10751        );
10752        editor
10753    });
10754
10755    // Refreshing selections is a no-op when excerpts haven't changed.
10756    _ = editor.update(cx, |editor, window, cx| {
10757        editor.change_selections(None, window, cx, |s| s.refresh());
10758        assert_eq!(
10759            editor.selections.ranges(cx),
10760            [
10761                Point::new(1, 3)..Point::new(1, 3),
10762                Point::new(2, 1)..Point::new(2, 1),
10763            ]
10764        );
10765    });
10766
10767    multibuffer.update(cx, |multibuffer, cx| {
10768        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10769    });
10770    _ = editor.update(cx, |editor, window, cx| {
10771        // Removing an excerpt causes the first selection to become degenerate.
10772        assert_eq!(
10773            editor.selections.ranges(cx),
10774            [
10775                Point::new(0, 0)..Point::new(0, 0),
10776                Point::new(0, 1)..Point::new(0, 1)
10777            ]
10778        );
10779
10780        // Refreshing selections will relocate the first selection to the original buffer
10781        // location.
10782        editor.change_selections(None, window, cx, |s| s.refresh());
10783        assert_eq!(
10784            editor.selections.ranges(cx),
10785            [
10786                Point::new(0, 1)..Point::new(0, 1),
10787                Point::new(0, 3)..Point::new(0, 3)
10788            ]
10789        );
10790        assert!(editor.selections.pending_anchor().is_some());
10791    });
10792}
10793
10794#[gpui::test]
10795fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10796    init_test(cx, |_| {});
10797
10798    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10799    let mut excerpt1_id = None;
10800    let multibuffer = cx.new(|cx| {
10801        let mut multibuffer = MultiBuffer::new(ReadWrite);
10802        excerpt1_id = multibuffer
10803            .push_excerpts(
10804                buffer.clone(),
10805                [
10806                    ExcerptRange {
10807                        context: Point::new(0, 0)..Point::new(1, 4),
10808                        primary: None,
10809                    },
10810                    ExcerptRange {
10811                        context: Point::new(1, 0)..Point::new(2, 4),
10812                        primary: None,
10813                    },
10814                ],
10815                cx,
10816            )
10817            .into_iter()
10818            .next();
10819        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10820        multibuffer
10821    });
10822
10823    let editor = cx.add_window(|window, cx| {
10824        let mut editor = build_editor(multibuffer.clone(), window, cx);
10825        let snapshot = editor.snapshot(window, cx);
10826        editor.begin_selection(
10827            Point::new(1, 3).to_display_point(&snapshot),
10828            false,
10829            1,
10830            window,
10831            cx,
10832        );
10833        assert_eq!(
10834            editor.selections.ranges(cx),
10835            [Point::new(1, 3)..Point::new(1, 3)]
10836        );
10837        editor
10838    });
10839
10840    multibuffer.update(cx, |multibuffer, cx| {
10841        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10842    });
10843    _ = editor.update(cx, |editor, window, cx| {
10844        assert_eq!(
10845            editor.selections.ranges(cx),
10846            [Point::new(0, 0)..Point::new(0, 0)]
10847        );
10848
10849        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10850        editor.change_selections(None, window, cx, |s| s.refresh());
10851        assert_eq!(
10852            editor.selections.ranges(cx),
10853            [Point::new(0, 3)..Point::new(0, 3)]
10854        );
10855        assert!(editor.selections.pending_anchor().is_some());
10856    });
10857}
10858
10859#[gpui::test]
10860async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10861    init_test(cx, |_| {});
10862
10863    let language = Arc::new(
10864        Language::new(
10865            LanguageConfig {
10866                brackets: BracketPairConfig {
10867                    pairs: vec![
10868                        BracketPair {
10869                            start: "{".to_string(),
10870                            end: "}".to_string(),
10871                            close: true,
10872                            surround: true,
10873                            newline: true,
10874                        },
10875                        BracketPair {
10876                            start: "/* ".to_string(),
10877                            end: " */".to_string(),
10878                            close: true,
10879                            surround: true,
10880                            newline: true,
10881                        },
10882                    ],
10883                    ..Default::default()
10884                },
10885                ..Default::default()
10886            },
10887            Some(tree_sitter_rust::LANGUAGE.into()),
10888        )
10889        .with_indents_query("")
10890        .unwrap(),
10891    );
10892
10893    let text = concat!(
10894        "{   }\n",     //
10895        "  x\n",       //
10896        "  /*   */\n", //
10897        "x\n",         //
10898        "{{} }\n",     //
10899    );
10900
10901    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10902    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10903    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10904    editor
10905        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10906        .await;
10907
10908    editor.update_in(cx, |editor, window, cx| {
10909        editor.change_selections(None, window, cx, |s| {
10910            s.select_display_ranges([
10911                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10912                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10913                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10914            ])
10915        });
10916        editor.newline(&Newline, window, cx);
10917
10918        assert_eq!(
10919            editor.buffer().read(cx).read(cx).text(),
10920            concat!(
10921                "{ \n",    // Suppress rustfmt
10922                "\n",      //
10923                "}\n",     //
10924                "  x\n",   //
10925                "  /* \n", //
10926                "  \n",    //
10927                "  */\n",  //
10928                "x\n",     //
10929                "{{} \n",  //
10930                "}\n",     //
10931            )
10932        );
10933    });
10934}
10935
10936#[gpui::test]
10937fn test_highlighted_ranges(cx: &mut TestAppContext) {
10938    init_test(cx, |_| {});
10939
10940    let editor = cx.add_window(|window, cx| {
10941        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10942        build_editor(buffer.clone(), window, cx)
10943    });
10944
10945    _ = editor.update(cx, |editor, window, cx| {
10946        struct Type1;
10947        struct Type2;
10948
10949        let buffer = editor.buffer.read(cx).snapshot(cx);
10950
10951        let anchor_range =
10952            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10953
10954        editor.highlight_background::<Type1>(
10955            &[
10956                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10957                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10958                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10959                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10960            ],
10961            |_| Hsla::red(),
10962            cx,
10963        );
10964        editor.highlight_background::<Type2>(
10965            &[
10966                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10967                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10968                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10969                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10970            ],
10971            |_| Hsla::green(),
10972            cx,
10973        );
10974
10975        let snapshot = editor.snapshot(window, cx);
10976        let mut highlighted_ranges = editor.background_highlights_in_range(
10977            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10978            &snapshot,
10979            cx.theme().colors(),
10980        );
10981        // Enforce a consistent ordering based on color without relying on the ordering of the
10982        // highlight's `TypeId` which is non-executor.
10983        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10984        assert_eq!(
10985            highlighted_ranges,
10986            &[
10987                (
10988                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10989                    Hsla::red(),
10990                ),
10991                (
10992                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10993                    Hsla::red(),
10994                ),
10995                (
10996                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10997                    Hsla::green(),
10998                ),
10999                (
11000                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11001                    Hsla::green(),
11002                ),
11003            ]
11004        );
11005        assert_eq!(
11006            editor.background_highlights_in_range(
11007                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11008                &snapshot,
11009                cx.theme().colors(),
11010            ),
11011            &[(
11012                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11013                Hsla::red(),
11014            )]
11015        );
11016    });
11017}
11018
11019#[gpui::test]
11020async fn test_following(cx: &mut TestAppContext) {
11021    init_test(cx, |_| {});
11022
11023    let fs = FakeFs::new(cx.executor());
11024    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11025
11026    let buffer = project.update(cx, |project, cx| {
11027        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11028        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11029    });
11030    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11031    let follower = cx.update(|cx| {
11032        cx.open_window(
11033            WindowOptions {
11034                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11035                    gpui::Point::new(px(0.), px(0.)),
11036                    gpui::Point::new(px(10.), px(80.)),
11037                ))),
11038                ..Default::default()
11039            },
11040            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11041        )
11042        .unwrap()
11043    });
11044
11045    let is_still_following = Rc::new(RefCell::new(true));
11046    let follower_edit_event_count = Rc::new(RefCell::new(0));
11047    let pending_update = Rc::new(RefCell::new(None));
11048    let leader_entity = leader.root(cx).unwrap();
11049    let follower_entity = follower.root(cx).unwrap();
11050    _ = follower.update(cx, {
11051        let update = pending_update.clone();
11052        let is_still_following = is_still_following.clone();
11053        let follower_edit_event_count = follower_edit_event_count.clone();
11054        |_, window, cx| {
11055            cx.subscribe_in(
11056                &leader_entity,
11057                window,
11058                move |_, leader, event, window, cx| {
11059                    leader.read(cx).add_event_to_update_proto(
11060                        event,
11061                        &mut update.borrow_mut(),
11062                        window,
11063                        cx,
11064                    );
11065                },
11066            )
11067            .detach();
11068
11069            cx.subscribe_in(
11070                &follower_entity,
11071                window,
11072                move |_, _, event: &EditorEvent, _window, _cx| {
11073                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11074                        *is_still_following.borrow_mut() = false;
11075                    }
11076
11077                    if let EditorEvent::BufferEdited = event {
11078                        *follower_edit_event_count.borrow_mut() += 1;
11079                    }
11080                },
11081            )
11082            .detach();
11083        }
11084    });
11085
11086    // Update the selections only
11087    _ = leader.update(cx, |leader, window, cx| {
11088        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11089    });
11090    follower
11091        .update(cx, |follower, window, cx| {
11092            follower.apply_update_proto(
11093                &project,
11094                pending_update.borrow_mut().take().unwrap(),
11095                window,
11096                cx,
11097            )
11098        })
11099        .unwrap()
11100        .await
11101        .unwrap();
11102    _ = follower.update(cx, |follower, _, cx| {
11103        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11104    });
11105    assert!(*is_still_following.borrow());
11106    assert_eq!(*follower_edit_event_count.borrow(), 0);
11107
11108    // Update the scroll position only
11109    _ = leader.update(cx, |leader, window, cx| {
11110        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11111    });
11112    follower
11113        .update(cx, |follower, window, cx| {
11114            follower.apply_update_proto(
11115                &project,
11116                pending_update.borrow_mut().take().unwrap(),
11117                window,
11118                cx,
11119            )
11120        })
11121        .unwrap()
11122        .await
11123        .unwrap();
11124    assert_eq!(
11125        follower
11126            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11127            .unwrap(),
11128        gpui::Point::new(1.5, 3.5)
11129    );
11130    assert!(*is_still_following.borrow());
11131    assert_eq!(*follower_edit_event_count.borrow(), 0);
11132
11133    // Update the selections and scroll position. The follower's scroll position is updated
11134    // via autoscroll, not via the leader's exact scroll position.
11135    _ = leader.update(cx, |leader, window, cx| {
11136        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11137        leader.request_autoscroll(Autoscroll::newest(), cx);
11138        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11139    });
11140    follower
11141        .update(cx, |follower, window, cx| {
11142            follower.apply_update_proto(
11143                &project,
11144                pending_update.borrow_mut().take().unwrap(),
11145                window,
11146                cx,
11147            )
11148        })
11149        .unwrap()
11150        .await
11151        .unwrap();
11152    _ = follower.update(cx, |follower, _, cx| {
11153        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11154        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11155    });
11156    assert!(*is_still_following.borrow());
11157
11158    // Creating a pending selection that precedes another selection
11159    _ = leader.update(cx, |leader, window, cx| {
11160        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11161        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11162    });
11163    follower
11164        .update(cx, |follower, window, cx| {
11165            follower.apply_update_proto(
11166                &project,
11167                pending_update.borrow_mut().take().unwrap(),
11168                window,
11169                cx,
11170            )
11171        })
11172        .unwrap()
11173        .await
11174        .unwrap();
11175    _ = follower.update(cx, |follower, _, cx| {
11176        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11177    });
11178    assert!(*is_still_following.borrow());
11179
11180    // Extend the pending selection so that it surrounds another selection
11181    _ = leader.update(cx, |leader, window, cx| {
11182        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11183    });
11184    follower
11185        .update(cx, |follower, window, cx| {
11186            follower.apply_update_proto(
11187                &project,
11188                pending_update.borrow_mut().take().unwrap(),
11189                window,
11190                cx,
11191            )
11192        })
11193        .unwrap()
11194        .await
11195        .unwrap();
11196    _ = follower.update(cx, |follower, _, cx| {
11197        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11198    });
11199
11200    // Scrolling locally breaks the follow
11201    _ = follower.update(cx, |follower, window, cx| {
11202        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11203        follower.set_scroll_anchor(
11204            ScrollAnchor {
11205                anchor: top_anchor,
11206                offset: gpui::Point::new(0.0, 0.5),
11207            },
11208            window,
11209            cx,
11210        );
11211    });
11212    assert!(!(*is_still_following.borrow()));
11213}
11214
11215#[gpui::test]
11216async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11217    init_test(cx, |_| {});
11218
11219    let fs = FakeFs::new(cx.executor());
11220    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11221    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11222    let pane = workspace
11223        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11224        .unwrap();
11225
11226    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11227
11228    let leader = pane.update_in(cx, |_, window, cx| {
11229        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11230        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11231    });
11232
11233    // Start following the editor when it has no excerpts.
11234    let mut state_message =
11235        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11236    let workspace_entity = workspace.root(cx).unwrap();
11237    let follower_1 = cx
11238        .update_window(*workspace.deref(), |_, window, cx| {
11239            Editor::from_state_proto(
11240                workspace_entity,
11241                ViewId {
11242                    creator: Default::default(),
11243                    id: 0,
11244                },
11245                &mut state_message,
11246                window,
11247                cx,
11248            )
11249        })
11250        .unwrap()
11251        .unwrap()
11252        .await
11253        .unwrap();
11254
11255    let update_message = Rc::new(RefCell::new(None));
11256    follower_1.update_in(cx, {
11257        let update = update_message.clone();
11258        |_, window, cx| {
11259            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11260                leader.read(cx).add_event_to_update_proto(
11261                    event,
11262                    &mut update.borrow_mut(),
11263                    window,
11264                    cx,
11265                );
11266            })
11267            .detach();
11268        }
11269    });
11270
11271    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11272        (
11273            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11274            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11275        )
11276    });
11277
11278    // Insert some excerpts.
11279    leader.update(cx, |leader, cx| {
11280        leader.buffer.update(cx, |multibuffer, cx| {
11281            let excerpt_ids = multibuffer.push_excerpts(
11282                buffer_1.clone(),
11283                [
11284                    ExcerptRange {
11285                        context: 1..6,
11286                        primary: None,
11287                    },
11288                    ExcerptRange {
11289                        context: 12..15,
11290                        primary: None,
11291                    },
11292                    ExcerptRange {
11293                        context: 0..3,
11294                        primary: None,
11295                    },
11296                ],
11297                cx,
11298            );
11299            multibuffer.insert_excerpts_after(
11300                excerpt_ids[0],
11301                buffer_2.clone(),
11302                [
11303                    ExcerptRange {
11304                        context: 8..12,
11305                        primary: None,
11306                    },
11307                    ExcerptRange {
11308                        context: 0..6,
11309                        primary: None,
11310                    },
11311                ],
11312                cx,
11313            );
11314        });
11315    });
11316
11317    // Apply the update of adding the excerpts.
11318    follower_1
11319        .update_in(cx, |follower, window, cx| {
11320            follower.apply_update_proto(
11321                &project,
11322                update_message.borrow().clone().unwrap(),
11323                window,
11324                cx,
11325            )
11326        })
11327        .await
11328        .unwrap();
11329    assert_eq!(
11330        follower_1.update(cx, |editor, cx| editor.text(cx)),
11331        leader.update(cx, |editor, cx| editor.text(cx))
11332    );
11333    update_message.borrow_mut().take();
11334
11335    // Start following separately after it already has excerpts.
11336    let mut state_message =
11337        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11338    let workspace_entity = workspace.root(cx).unwrap();
11339    let follower_2 = cx
11340        .update_window(*workspace.deref(), |_, window, cx| {
11341            Editor::from_state_proto(
11342                workspace_entity,
11343                ViewId {
11344                    creator: Default::default(),
11345                    id: 0,
11346                },
11347                &mut state_message,
11348                window,
11349                cx,
11350            )
11351        })
11352        .unwrap()
11353        .unwrap()
11354        .await
11355        .unwrap();
11356    assert_eq!(
11357        follower_2.update(cx, |editor, cx| editor.text(cx)),
11358        leader.update(cx, |editor, cx| editor.text(cx))
11359    );
11360
11361    // Remove some excerpts.
11362    leader.update(cx, |leader, cx| {
11363        leader.buffer.update(cx, |multibuffer, cx| {
11364            let excerpt_ids = multibuffer.excerpt_ids();
11365            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11366            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11367        });
11368    });
11369
11370    // Apply the update of removing the excerpts.
11371    follower_1
11372        .update_in(cx, |follower, window, cx| {
11373            follower.apply_update_proto(
11374                &project,
11375                update_message.borrow().clone().unwrap(),
11376                window,
11377                cx,
11378            )
11379        })
11380        .await
11381        .unwrap();
11382    follower_2
11383        .update_in(cx, |follower, window, cx| {
11384            follower.apply_update_proto(
11385                &project,
11386                update_message.borrow().clone().unwrap(),
11387                window,
11388                cx,
11389            )
11390        })
11391        .await
11392        .unwrap();
11393    update_message.borrow_mut().take();
11394    assert_eq!(
11395        follower_1.update(cx, |editor, cx| editor.text(cx)),
11396        leader.update(cx, |editor, cx| editor.text(cx))
11397    );
11398}
11399
11400#[gpui::test]
11401async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11402    init_test(cx, |_| {});
11403
11404    let mut cx = EditorTestContext::new(cx).await;
11405    let lsp_store =
11406        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11407
11408    cx.set_state(indoc! {"
11409        ˇfn func(abc def: i32) -> u32 {
11410        }
11411    "});
11412
11413    cx.update(|_, cx| {
11414        lsp_store.update(cx, |lsp_store, cx| {
11415            lsp_store
11416                .update_diagnostics(
11417                    LanguageServerId(0),
11418                    lsp::PublishDiagnosticsParams {
11419                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11420                        version: None,
11421                        diagnostics: vec![
11422                            lsp::Diagnostic {
11423                                range: lsp::Range::new(
11424                                    lsp::Position::new(0, 11),
11425                                    lsp::Position::new(0, 12),
11426                                ),
11427                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11428                                ..Default::default()
11429                            },
11430                            lsp::Diagnostic {
11431                                range: lsp::Range::new(
11432                                    lsp::Position::new(0, 12),
11433                                    lsp::Position::new(0, 15),
11434                                ),
11435                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11436                                ..Default::default()
11437                            },
11438                            lsp::Diagnostic {
11439                                range: lsp::Range::new(
11440                                    lsp::Position::new(0, 25),
11441                                    lsp::Position::new(0, 28),
11442                                ),
11443                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11444                                ..Default::default()
11445                            },
11446                        ],
11447                    },
11448                    &[],
11449                    cx,
11450                )
11451                .unwrap()
11452        });
11453    });
11454
11455    executor.run_until_parked();
11456
11457    cx.update_editor(|editor, window, cx| {
11458        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11459    });
11460
11461    cx.assert_editor_state(indoc! {"
11462        fn func(abc def: i32) -> ˇu32 {
11463        }
11464    "});
11465
11466    cx.update_editor(|editor, window, cx| {
11467        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11468    });
11469
11470    cx.assert_editor_state(indoc! {"
11471        fn func(abc ˇdef: i32) -> u32 {
11472        }
11473    "});
11474
11475    cx.update_editor(|editor, window, cx| {
11476        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11477    });
11478
11479    cx.assert_editor_state(indoc! {"
11480        fn func(abcˇ def: i32) -> u32 {
11481        }
11482    "});
11483
11484    cx.update_editor(|editor, window, cx| {
11485        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11486    });
11487
11488    cx.assert_editor_state(indoc! {"
11489        fn func(abc def: i32) -> ˇu32 {
11490        }
11491    "});
11492}
11493
11494#[gpui::test]
11495async fn cycle_through_same_place_diagnostics(
11496    executor: BackgroundExecutor,
11497    cx: &mut TestAppContext,
11498) {
11499    init_test(cx, |_| {});
11500
11501    let mut cx = EditorTestContext::new(cx).await;
11502    let lsp_store =
11503        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11504
11505    cx.set_state(indoc! {"
11506        ˇfn func(abc def: i32) -> u32 {
11507        }
11508    "});
11509
11510    cx.update(|_, cx| {
11511        lsp_store.update(cx, |lsp_store, cx| {
11512            lsp_store
11513                .update_diagnostics(
11514                    LanguageServerId(0),
11515                    lsp::PublishDiagnosticsParams {
11516                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11517                        version: None,
11518                        diagnostics: vec![
11519                            lsp::Diagnostic {
11520                                range: lsp::Range::new(
11521                                    lsp::Position::new(0, 11),
11522                                    lsp::Position::new(0, 12),
11523                                ),
11524                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11525                                ..Default::default()
11526                            },
11527                            lsp::Diagnostic {
11528                                range: lsp::Range::new(
11529                                    lsp::Position::new(0, 12),
11530                                    lsp::Position::new(0, 15),
11531                                ),
11532                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11533                                ..Default::default()
11534                            },
11535                            lsp::Diagnostic {
11536                                range: lsp::Range::new(
11537                                    lsp::Position::new(0, 12),
11538                                    lsp::Position::new(0, 15),
11539                                ),
11540                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11541                                ..Default::default()
11542                            },
11543                            lsp::Diagnostic {
11544                                range: lsp::Range::new(
11545                                    lsp::Position::new(0, 25),
11546                                    lsp::Position::new(0, 28),
11547                                ),
11548                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11549                                ..Default::default()
11550                            },
11551                        ],
11552                    },
11553                    &[],
11554                    cx,
11555                )
11556                .unwrap()
11557        });
11558    });
11559    executor.run_until_parked();
11560
11561    //// Backward
11562
11563    // Fourth diagnostic
11564    cx.update_editor(|editor, window, cx| {
11565        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11566    });
11567    cx.assert_editor_state(indoc! {"
11568        fn func(abc def: i32) -> ˇu32 {
11569        }
11570    "});
11571
11572    // Third diagnostic
11573    cx.update_editor(|editor, window, cx| {
11574        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11575    });
11576    cx.assert_editor_state(indoc! {"
11577        fn func(abc ˇdef: i32) -> u32 {
11578        }
11579    "});
11580
11581    // Second diagnostic, same place
11582    cx.update_editor(|editor, window, cx| {
11583        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11584    });
11585    cx.assert_editor_state(indoc! {"
11586        fn func(abc ˇdef: i32) -> u32 {
11587        }
11588    "});
11589
11590    // First diagnostic
11591    cx.update_editor(|editor, window, cx| {
11592        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11593    });
11594    cx.assert_editor_state(indoc! {"
11595        fn func(abcˇ def: i32) -> u32 {
11596        }
11597    "});
11598
11599    // Wrapped over, fourth diagnostic
11600    cx.update_editor(|editor, window, cx| {
11601        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11602    });
11603    cx.assert_editor_state(indoc! {"
11604        fn func(abc def: i32) -> ˇu32 {
11605        }
11606    "});
11607
11608    cx.update_editor(|editor, window, cx| {
11609        editor.move_to_beginning(&MoveToBeginning, window, cx);
11610    });
11611    cx.assert_editor_state(indoc! {"
11612        ˇfn func(abc def: i32) -> u32 {
11613        }
11614    "});
11615
11616    //// Forward
11617
11618    // First diagnostic
11619    cx.update_editor(|editor, window, cx| {
11620        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11621    });
11622    cx.assert_editor_state(indoc! {"
11623        fn func(abcˇ def: i32) -> u32 {
11624        }
11625    "});
11626
11627    // Second diagnostic
11628    cx.update_editor(|editor, window, cx| {
11629        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11630    });
11631    cx.assert_editor_state(indoc! {"
11632        fn func(abc ˇdef: i32) -> u32 {
11633        }
11634    "});
11635
11636    // Third diagnostic, same place
11637    cx.update_editor(|editor, window, cx| {
11638        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11639    });
11640    cx.assert_editor_state(indoc! {"
11641        fn func(abc ˇdef: i32) -> u32 {
11642        }
11643    "});
11644
11645    // Fourth diagnostic
11646    cx.update_editor(|editor, window, cx| {
11647        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11648    });
11649    cx.assert_editor_state(indoc! {"
11650        fn func(abc def: i32) -> ˇu32 {
11651        }
11652    "});
11653
11654    // Wrapped around, first diagnostic
11655    cx.update_editor(|editor, window, cx| {
11656        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11657    });
11658    cx.assert_editor_state(indoc! {"
11659        fn func(abcˇ def: i32) -> u32 {
11660        }
11661    "});
11662}
11663
11664#[gpui::test]
11665async fn active_diagnostics_dismiss_after_invalidation(
11666    executor: BackgroundExecutor,
11667    cx: &mut TestAppContext,
11668) {
11669    init_test(cx, |_| {});
11670
11671    let mut cx = EditorTestContext::new(cx).await;
11672    let lsp_store =
11673        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11674
11675    cx.set_state(indoc! {"
11676        ˇfn func(abc def: i32) -> u32 {
11677        }
11678    "});
11679
11680    let message = "Something's wrong!";
11681    cx.update(|_, cx| {
11682        lsp_store.update(cx, |lsp_store, cx| {
11683            lsp_store
11684                .update_diagnostics(
11685                    LanguageServerId(0),
11686                    lsp::PublishDiagnosticsParams {
11687                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11688                        version: None,
11689                        diagnostics: vec![lsp::Diagnostic {
11690                            range: lsp::Range::new(
11691                                lsp::Position::new(0, 11),
11692                                lsp::Position::new(0, 12),
11693                            ),
11694                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11695                            message: message.to_string(),
11696                            ..Default::default()
11697                        }],
11698                    },
11699                    &[],
11700                    cx,
11701                )
11702                .unwrap()
11703        });
11704    });
11705    executor.run_until_parked();
11706
11707    cx.update_editor(|editor, window, cx| {
11708        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11709        assert_eq!(
11710            editor
11711                .active_diagnostics
11712                .as_ref()
11713                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11714            Some(message),
11715            "Should have a diagnostics group activated"
11716        );
11717    });
11718    cx.assert_editor_state(indoc! {"
11719        fn func(abcˇ def: i32) -> u32 {
11720        }
11721    "});
11722
11723    cx.update(|_, cx| {
11724        lsp_store.update(cx, |lsp_store, cx| {
11725            lsp_store
11726                .update_diagnostics(
11727                    LanguageServerId(0),
11728                    lsp::PublishDiagnosticsParams {
11729                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11730                        version: None,
11731                        diagnostics: Vec::new(),
11732                    },
11733                    &[],
11734                    cx,
11735                )
11736                .unwrap()
11737        });
11738    });
11739    executor.run_until_parked();
11740    cx.update_editor(|editor, _, _| {
11741        assert_eq!(
11742            editor.active_diagnostics, None,
11743            "After no diagnostics set to the editor, no diagnostics should be active"
11744        );
11745    });
11746    cx.assert_editor_state(indoc! {"
11747        fn func(abcˇ def: i32) -> u32 {
11748        }
11749    "});
11750
11751    cx.update_editor(|editor, window, cx| {
11752        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11753        assert_eq!(
11754            editor.active_diagnostics, None,
11755            "Should be no diagnostics to go to and activate"
11756        );
11757    });
11758    cx.assert_editor_state(indoc! {"
11759        fn func(abcˇ def: i32) -> u32 {
11760        }
11761    "});
11762}
11763
11764#[gpui::test]
11765async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11766    init_test(cx, |_| {});
11767
11768    let mut cx = EditorTestContext::new(cx).await;
11769
11770    cx.set_state(indoc! {"
11771        fn func(abˇc def: i32) -> u32 {
11772        }
11773    "});
11774    let lsp_store =
11775        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11776
11777    cx.update(|_, cx| {
11778        lsp_store.update(cx, |lsp_store, cx| {
11779            lsp_store.update_diagnostics(
11780                LanguageServerId(0),
11781                lsp::PublishDiagnosticsParams {
11782                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11783                    version: None,
11784                    diagnostics: vec![lsp::Diagnostic {
11785                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11786                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11787                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11788                        ..Default::default()
11789                    }],
11790                },
11791                &[],
11792                cx,
11793            )
11794        })
11795    }).unwrap();
11796    cx.run_until_parked();
11797    cx.update_editor(|editor, window, cx| {
11798        hover_popover::hover(editor, &Default::default(), window, cx)
11799    });
11800    cx.run_until_parked();
11801    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11802}
11803
11804#[gpui::test]
11805async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11806    init_test(cx, |_| {});
11807
11808    let mut cx = EditorTestContext::new(cx).await;
11809
11810    let diff_base = r#"
11811        use some::mod;
11812
11813        const A: u32 = 42;
11814
11815        fn main() {
11816            println!("hello");
11817
11818            println!("world");
11819        }
11820        "#
11821    .unindent();
11822
11823    // Edits are modified, removed, modified, added
11824    cx.set_state(
11825        &r#"
11826        use some::modified;
11827
11828        ˇ
11829        fn main() {
11830            println!("hello there");
11831
11832            println!("around the");
11833            println!("world");
11834        }
11835        "#
11836        .unindent(),
11837    );
11838
11839    cx.set_head_text(&diff_base);
11840    executor.run_until_parked();
11841
11842    cx.update_editor(|editor, window, cx| {
11843        //Wrap around the bottom of the buffer
11844        for _ in 0..3 {
11845            editor.go_to_next_hunk(&GoToHunk, window, cx);
11846        }
11847    });
11848
11849    cx.assert_editor_state(
11850        &r#"
11851        ˇuse some::modified;
11852
11853
11854        fn main() {
11855            println!("hello there");
11856
11857            println!("around the");
11858            println!("world");
11859        }
11860        "#
11861        .unindent(),
11862    );
11863
11864    cx.update_editor(|editor, window, cx| {
11865        //Wrap around the top of the buffer
11866        for _ in 0..2 {
11867            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11868        }
11869    });
11870
11871    cx.assert_editor_state(
11872        &r#"
11873        use some::modified;
11874
11875
11876        fn main() {
11877        ˇ    println!("hello there");
11878
11879            println!("around the");
11880            println!("world");
11881        }
11882        "#
11883        .unindent(),
11884    );
11885
11886    cx.update_editor(|editor, window, cx| {
11887        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11888    });
11889
11890    cx.assert_editor_state(
11891        &r#"
11892        use some::modified;
11893
11894        ˇ
11895        fn main() {
11896            println!("hello there");
11897
11898            println!("around the");
11899            println!("world");
11900        }
11901        "#
11902        .unindent(),
11903    );
11904
11905    cx.update_editor(|editor, window, cx| {
11906        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11907    });
11908
11909    cx.assert_editor_state(
11910        &r#"
11911        ˇuse some::modified;
11912
11913
11914        fn main() {
11915            println!("hello there");
11916
11917            println!("around the");
11918            println!("world");
11919        }
11920        "#
11921        .unindent(),
11922    );
11923
11924    cx.update_editor(|editor, window, cx| {
11925        for _ in 0..2 {
11926            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11927        }
11928    });
11929
11930    cx.assert_editor_state(
11931        &r#"
11932        use some::modified;
11933
11934
11935        fn main() {
11936        ˇ    println!("hello there");
11937
11938            println!("around the");
11939            println!("world");
11940        }
11941        "#
11942        .unindent(),
11943    );
11944
11945    cx.update_editor(|editor, window, cx| {
11946        editor.fold(&Fold, window, cx);
11947    });
11948
11949    cx.update_editor(|editor, window, cx| {
11950        editor.go_to_next_hunk(&GoToHunk, window, cx);
11951    });
11952
11953    cx.assert_editor_state(
11954        &r#"
11955        ˇuse some::modified;
11956
11957
11958        fn main() {
11959            println!("hello there");
11960
11961            println!("around the");
11962            println!("world");
11963        }
11964        "#
11965        .unindent(),
11966    );
11967}
11968
11969#[test]
11970fn test_split_words() {
11971    fn split(text: &str) -> Vec<&str> {
11972        split_words(text).collect()
11973    }
11974
11975    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11976    assert_eq!(split("hello_world"), &["hello_", "world"]);
11977    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11978    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11979    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11980    assert_eq!(split("helloworld"), &["helloworld"]);
11981
11982    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11983}
11984
11985#[gpui::test]
11986async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11987    init_test(cx, |_| {});
11988
11989    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11990    let mut assert = |before, after| {
11991        let _state_context = cx.set_state(before);
11992        cx.run_until_parked();
11993        cx.update_editor(|editor, window, cx| {
11994            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11995        });
11996        cx.run_until_parked();
11997        cx.assert_editor_state(after);
11998    };
11999
12000    // Outside bracket jumps to outside of matching bracket
12001    assert("console.logˇ(var);", "console.log(var)ˇ;");
12002    assert("console.log(var)ˇ;", "console.logˇ(var);");
12003
12004    // Inside bracket jumps to inside of matching bracket
12005    assert("console.log(ˇvar);", "console.log(varˇ);");
12006    assert("console.log(varˇ);", "console.log(ˇvar);");
12007
12008    // When outside a bracket and inside, favor jumping to the inside bracket
12009    assert(
12010        "console.log('foo', [1, 2, 3]ˇ);",
12011        "console.log(ˇ'foo', [1, 2, 3]);",
12012    );
12013    assert(
12014        "console.log(ˇ'foo', [1, 2, 3]);",
12015        "console.log('foo', [1, 2, 3]ˇ);",
12016    );
12017
12018    // Bias forward if two options are equally likely
12019    assert(
12020        "let result = curried_fun()ˇ();",
12021        "let result = curried_fun()()ˇ;",
12022    );
12023
12024    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12025    assert(
12026        indoc! {"
12027            function test() {
12028                console.log('test')ˇ
12029            }"},
12030        indoc! {"
12031            function test() {
12032                console.logˇ('test')
12033            }"},
12034    );
12035}
12036
12037#[gpui::test]
12038async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12039    init_test(cx, |_| {});
12040
12041    let fs = FakeFs::new(cx.executor());
12042    fs.insert_tree(
12043        path!("/a"),
12044        json!({
12045            "main.rs": "fn main() { let a = 5; }",
12046            "other.rs": "// Test file",
12047        }),
12048    )
12049    .await;
12050    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12051
12052    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12053    language_registry.add(Arc::new(Language::new(
12054        LanguageConfig {
12055            name: "Rust".into(),
12056            matcher: LanguageMatcher {
12057                path_suffixes: vec!["rs".to_string()],
12058                ..Default::default()
12059            },
12060            brackets: BracketPairConfig {
12061                pairs: vec![BracketPair {
12062                    start: "{".to_string(),
12063                    end: "}".to_string(),
12064                    close: true,
12065                    surround: true,
12066                    newline: true,
12067                }],
12068                disabled_scopes_by_bracket_ix: Vec::new(),
12069            },
12070            ..Default::default()
12071        },
12072        Some(tree_sitter_rust::LANGUAGE.into()),
12073    )));
12074    let mut fake_servers = language_registry.register_fake_lsp(
12075        "Rust",
12076        FakeLspAdapter {
12077            capabilities: lsp::ServerCapabilities {
12078                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12079                    first_trigger_character: "{".to_string(),
12080                    more_trigger_character: None,
12081                }),
12082                ..Default::default()
12083            },
12084            ..Default::default()
12085        },
12086    );
12087
12088    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12089
12090    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12091
12092    let worktree_id = workspace
12093        .update(cx, |workspace, _, cx| {
12094            workspace.project().update(cx, |project, cx| {
12095                project.worktrees(cx).next().unwrap().read(cx).id()
12096            })
12097        })
12098        .unwrap();
12099
12100    let buffer = project
12101        .update(cx, |project, cx| {
12102            project.open_local_buffer(path!("/a/main.rs"), cx)
12103        })
12104        .await
12105        .unwrap();
12106    let editor_handle = workspace
12107        .update(cx, |workspace, window, cx| {
12108            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12109        })
12110        .unwrap()
12111        .await
12112        .unwrap()
12113        .downcast::<Editor>()
12114        .unwrap();
12115
12116    cx.executor().start_waiting();
12117    let fake_server = fake_servers.next().await.unwrap();
12118
12119    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
12120        assert_eq!(
12121            params.text_document_position.text_document.uri,
12122            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12123        );
12124        assert_eq!(
12125            params.text_document_position.position,
12126            lsp::Position::new(0, 21),
12127        );
12128
12129        Ok(Some(vec![lsp::TextEdit {
12130            new_text: "]".to_string(),
12131            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12132        }]))
12133    });
12134
12135    editor_handle.update_in(cx, |editor, window, cx| {
12136        window.focus(&editor.focus_handle(cx));
12137        editor.change_selections(None, window, cx, |s| {
12138            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12139        });
12140        editor.handle_input("{", window, cx);
12141    });
12142
12143    cx.executor().run_until_parked();
12144
12145    buffer.update(cx, |buffer, _| {
12146        assert_eq!(
12147            buffer.text(),
12148            "fn main() { let a = {5}; }",
12149            "No extra braces from on type formatting should appear in the buffer"
12150        )
12151    });
12152}
12153
12154#[gpui::test]
12155async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12156    init_test(cx, |_| {});
12157
12158    let fs = FakeFs::new(cx.executor());
12159    fs.insert_tree(
12160        path!("/a"),
12161        json!({
12162            "main.rs": "fn main() { let a = 5; }",
12163            "other.rs": "// Test file",
12164        }),
12165    )
12166    .await;
12167
12168    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12169
12170    let server_restarts = Arc::new(AtomicUsize::new(0));
12171    let closure_restarts = Arc::clone(&server_restarts);
12172    let language_server_name = "test language server";
12173    let language_name: LanguageName = "Rust".into();
12174
12175    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12176    language_registry.add(Arc::new(Language::new(
12177        LanguageConfig {
12178            name: language_name.clone(),
12179            matcher: LanguageMatcher {
12180                path_suffixes: vec!["rs".to_string()],
12181                ..Default::default()
12182            },
12183            ..Default::default()
12184        },
12185        Some(tree_sitter_rust::LANGUAGE.into()),
12186    )));
12187    let mut fake_servers = language_registry.register_fake_lsp(
12188        "Rust",
12189        FakeLspAdapter {
12190            name: language_server_name,
12191            initialization_options: Some(json!({
12192                "testOptionValue": true
12193            })),
12194            initializer: Some(Box::new(move |fake_server| {
12195                let task_restarts = Arc::clone(&closure_restarts);
12196                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12197                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12198                    futures::future::ready(Ok(()))
12199                });
12200            })),
12201            ..Default::default()
12202        },
12203    );
12204
12205    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12206    let _buffer = project
12207        .update(cx, |project, cx| {
12208            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12209        })
12210        .await
12211        .unwrap();
12212    let _fake_server = fake_servers.next().await.unwrap();
12213    update_test_language_settings(cx, |language_settings| {
12214        language_settings.languages.insert(
12215            language_name.clone(),
12216            LanguageSettingsContent {
12217                tab_size: NonZeroU32::new(8),
12218                ..Default::default()
12219            },
12220        );
12221    });
12222    cx.executor().run_until_parked();
12223    assert_eq!(
12224        server_restarts.load(atomic::Ordering::Acquire),
12225        0,
12226        "Should not restart LSP server on an unrelated change"
12227    );
12228
12229    update_test_project_settings(cx, |project_settings| {
12230        project_settings.lsp.insert(
12231            "Some other server name".into(),
12232            LspSettings {
12233                binary: None,
12234                settings: None,
12235                initialization_options: Some(json!({
12236                    "some other init value": false
12237                })),
12238            },
12239        );
12240    });
12241    cx.executor().run_until_parked();
12242    assert_eq!(
12243        server_restarts.load(atomic::Ordering::Acquire),
12244        0,
12245        "Should not restart LSP server on an unrelated LSP settings change"
12246    );
12247
12248    update_test_project_settings(cx, |project_settings| {
12249        project_settings.lsp.insert(
12250            language_server_name.into(),
12251            LspSettings {
12252                binary: None,
12253                settings: None,
12254                initialization_options: Some(json!({
12255                    "anotherInitValue": false
12256                })),
12257            },
12258        );
12259    });
12260    cx.executor().run_until_parked();
12261    assert_eq!(
12262        server_restarts.load(atomic::Ordering::Acquire),
12263        1,
12264        "Should restart LSP server on a related LSP settings change"
12265    );
12266
12267    update_test_project_settings(cx, |project_settings| {
12268        project_settings.lsp.insert(
12269            language_server_name.into(),
12270            LspSettings {
12271                binary: None,
12272                settings: None,
12273                initialization_options: Some(json!({
12274                    "anotherInitValue": false
12275                })),
12276            },
12277        );
12278    });
12279    cx.executor().run_until_parked();
12280    assert_eq!(
12281        server_restarts.load(atomic::Ordering::Acquire),
12282        1,
12283        "Should not restart LSP server on a related LSP settings change that is the same"
12284    );
12285
12286    update_test_project_settings(cx, |project_settings| {
12287        project_settings.lsp.insert(
12288            language_server_name.into(),
12289            LspSettings {
12290                binary: None,
12291                settings: None,
12292                initialization_options: None,
12293            },
12294        );
12295    });
12296    cx.executor().run_until_parked();
12297    assert_eq!(
12298        server_restarts.load(atomic::Ordering::Acquire),
12299        2,
12300        "Should restart LSP server on another related LSP settings change"
12301    );
12302}
12303
12304#[gpui::test]
12305async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12306    init_test(cx, |_| {});
12307
12308    let mut cx = EditorLspTestContext::new_rust(
12309        lsp::ServerCapabilities {
12310            completion_provider: Some(lsp::CompletionOptions {
12311                trigger_characters: Some(vec![".".to_string()]),
12312                resolve_provider: Some(true),
12313                ..Default::default()
12314            }),
12315            ..Default::default()
12316        },
12317        cx,
12318    )
12319    .await;
12320
12321    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12322    cx.simulate_keystroke(".");
12323    let completion_item = lsp::CompletionItem {
12324        label: "some".into(),
12325        kind: Some(lsp::CompletionItemKind::SNIPPET),
12326        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12327        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12328            kind: lsp::MarkupKind::Markdown,
12329            value: "```rust\nSome(2)\n```".to_string(),
12330        })),
12331        deprecated: Some(false),
12332        sort_text: Some("fffffff2".to_string()),
12333        filter_text: Some("some".to_string()),
12334        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12335        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12336            range: lsp::Range {
12337                start: lsp::Position {
12338                    line: 0,
12339                    character: 22,
12340                },
12341                end: lsp::Position {
12342                    line: 0,
12343                    character: 22,
12344                },
12345            },
12346            new_text: "Some(2)".to_string(),
12347        })),
12348        additional_text_edits: Some(vec![lsp::TextEdit {
12349            range: lsp::Range {
12350                start: lsp::Position {
12351                    line: 0,
12352                    character: 20,
12353                },
12354                end: lsp::Position {
12355                    line: 0,
12356                    character: 22,
12357                },
12358            },
12359            new_text: "".to_string(),
12360        }]),
12361        ..Default::default()
12362    };
12363
12364    let closure_completion_item = completion_item.clone();
12365    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12366        let task_completion_item = closure_completion_item.clone();
12367        async move {
12368            Ok(Some(lsp::CompletionResponse::Array(vec![
12369                task_completion_item,
12370            ])))
12371        }
12372    });
12373
12374    request.next().await;
12375
12376    cx.condition(|editor, _| editor.context_menu_visible())
12377        .await;
12378    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12379        editor
12380            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12381            .unwrap()
12382    });
12383    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12384
12385    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12386        let task_completion_item = completion_item.clone();
12387        async move { Ok(task_completion_item) }
12388    })
12389    .next()
12390    .await
12391    .unwrap();
12392    apply_additional_edits.await.unwrap();
12393    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12394}
12395
12396#[gpui::test]
12397async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12398    init_test(cx, |_| {});
12399
12400    let mut cx = EditorLspTestContext::new_rust(
12401        lsp::ServerCapabilities {
12402            completion_provider: Some(lsp::CompletionOptions {
12403                trigger_characters: Some(vec![".".to_string()]),
12404                resolve_provider: Some(true),
12405                ..Default::default()
12406            }),
12407            ..Default::default()
12408        },
12409        cx,
12410    )
12411    .await;
12412
12413    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12414    cx.simulate_keystroke(".");
12415
12416    let item1 = lsp::CompletionItem {
12417        label: "method id()".to_string(),
12418        filter_text: Some("id".to_string()),
12419        detail: None,
12420        documentation: None,
12421        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12422            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12423            new_text: ".id".to_string(),
12424        })),
12425        ..lsp::CompletionItem::default()
12426    };
12427
12428    let item2 = lsp::CompletionItem {
12429        label: "other".to_string(),
12430        filter_text: Some("other".to_string()),
12431        detail: None,
12432        documentation: None,
12433        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12434            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12435            new_text: ".other".to_string(),
12436        })),
12437        ..lsp::CompletionItem::default()
12438    };
12439
12440    let item1 = item1.clone();
12441    cx.handle_request::<lsp::request::Completion, _, _>({
12442        let item1 = item1.clone();
12443        move |_, _, _| {
12444            let item1 = item1.clone();
12445            let item2 = item2.clone();
12446            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12447        }
12448    })
12449    .next()
12450    .await;
12451
12452    cx.condition(|editor, _| editor.context_menu_visible())
12453        .await;
12454    cx.update_editor(|editor, _, _| {
12455        let context_menu = editor.context_menu.borrow_mut();
12456        let context_menu = context_menu
12457            .as_ref()
12458            .expect("Should have the context menu deployed");
12459        match context_menu {
12460            CodeContextMenu::Completions(completions_menu) => {
12461                let completions = completions_menu.completions.borrow_mut();
12462                assert_eq!(
12463                    completions
12464                        .iter()
12465                        .map(|completion| &completion.label.text)
12466                        .collect::<Vec<_>>(),
12467                    vec!["method id()", "other"]
12468                )
12469            }
12470            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12471        }
12472    });
12473
12474    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12475        let item1 = item1.clone();
12476        move |_, item_to_resolve, _| {
12477            let item1 = item1.clone();
12478            async move {
12479                if item1 == item_to_resolve {
12480                    Ok(lsp::CompletionItem {
12481                        label: "method id()".to_string(),
12482                        filter_text: Some("id".to_string()),
12483                        detail: Some("Now resolved!".to_string()),
12484                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12485                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12486                            range: lsp::Range::new(
12487                                lsp::Position::new(0, 22),
12488                                lsp::Position::new(0, 22),
12489                            ),
12490                            new_text: ".id".to_string(),
12491                        })),
12492                        ..lsp::CompletionItem::default()
12493                    })
12494                } else {
12495                    Ok(item_to_resolve)
12496                }
12497            }
12498        }
12499    })
12500    .next()
12501    .await
12502    .unwrap();
12503    cx.run_until_parked();
12504
12505    cx.update_editor(|editor, window, cx| {
12506        editor.context_menu_next(&Default::default(), window, cx);
12507    });
12508
12509    cx.update_editor(|editor, _, _| {
12510        let context_menu = editor.context_menu.borrow_mut();
12511        let context_menu = context_menu
12512            .as_ref()
12513            .expect("Should have the context menu deployed");
12514        match context_menu {
12515            CodeContextMenu::Completions(completions_menu) => {
12516                let completions = completions_menu.completions.borrow_mut();
12517                assert_eq!(
12518                    completions
12519                        .iter()
12520                        .map(|completion| &completion.label.text)
12521                        .collect::<Vec<_>>(),
12522                    vec!["method id() Now resolved!", "other"],
12523                    "Should update first completion label, but not second as the filter text did not match."
12524                );
12525            }
12526            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12527        }
12528    });
12529}
12530
12531#[gpui::test]
12532async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12533    init_test(cx, |_| {});
12534
12535    let mut cx = EditorLspTestContext::new_rust(
12536        lsp::ServerCapabilities {
12537            completion_provider: Some(lsp::CompletionOptions {
12538                trigger_characters: Some(vec![".".to_string()]),
12539                resolve_provider: Some(true),
12540                ..Default::default()
12541            }),
12542            ..Default::default()
12543        },
12544        cx,
12545    )
12546    .await;
12547
12548    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12549    cx.simulate_keystroke(".");
12550
12551    let unresolved_item_1 = lsp::CompletionItem {
12552        label: "id".to_string(),
12553        filter_text: Some("id".to_string()),
12554        detail: None,
12555        documentation: None,
12556        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12557            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12558            new_text: ".id".to_string(),
12559        })),
12560        ..lsp::CompletionItem::default()
12561    };
12562    let resolved_item_1 = lsp::CompletionItem {
12563        additional_text_edits: Some(vec![lsp::TextEdit {
12564            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12565            new_text: "!!".to_string(),
12566        }]),
12567        ..unresolved_item_1.clone()
12568    };
12569    let unresolved_item_2 = lsp::CompletionItem {
12570        label: "other".to_string(),
12571        filter_text: Some("other".to_string()),
12572        detail: None,
12573        documentation: None,
12574        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12575            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12576            new_text: ".other".to_string(),
12577        })),
12578        ..lsp::CompletionItem::default()
12579    };
12580    let resolved_item_2 = lsp::CompletionItem {
12581        additional_text_edits: Some(vec![lsp::TextEdit {
12582            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12583            new_text: "??".to_string(),
12584        }]),
12585        ..unresolved_item_2.clone()
12586    };
12587
12588    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12589    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12590    cx.lsp
12591        .server
12592        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12593            let unresolved_item_1 = unresolved_item_1.clone();
12594            let resolved_item_1 = resolved_item_1.clone();
12595            let unresolved_item_2 = unresolved_item_2.clone();
12596            let resolved_item_2 = resolved_item_2.clone();
12597            let resolve_requests_1 = resolve_requests_1.clone();
12598            let resolve_requests_2 = resolve_requests_2.clone();
12599            move |unresolved_request, _| {
12600                let unresolved_item_1 = unresolved_item_1.clone();
12601                let resolved_item_1 = resolved_item_1.clone();
12602                let unresolved_item_2 = unresolved_item_2.clone();
12603                let resolved_item_2 = resolved_item_2.clone();
12604                let resolve_requests_1 = resolve_requests_1.clone();
12605                let resolve_requests_2 = resolve_requests_2.clone();
12606                async move {
12607                    if unresolved_request == unresolved_item_1 {
12608                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12609                        Ok(resolved_item_1.clone())
12610                    } else if unresolved_request == unresolved_item_2 {
12611                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12612                        Ok(resolved_item_2.clone())
12613                    } else {
12614                        panic!("Unexpected completion item {unresolved_request:?}")
12615                    }
12616                }
12617            }
12618        })
12619        .detach();
12620
12621    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12622        let unresolved_item_1 = unresolved_item_1.clone();
12623        let unresolved_item_2 = unresolved_item_2.clone();
12624        async move {
12625            Ok(Some(lsp::CompletionResponse::Array(vec![
12626                unresolved_item_1,
12627                unresolved_item_2,
12628            ])))
12629        }
12630    })
12631    .next()
12632    .await;
12633
12634    cx.condition(|editor, _| editor.context_menu_visible())
12635        .await;
12636    cx.update_editor(|editor, _, _| {
12637        let context_menu = editor.context_menu.borrow_mut();
12638        let context_menu = context_menu
12639            .as_ref()
12640            .expect("Should have the context menu deployed");
12641        match context_menu {
12642            CodeContextMenu::Completions(completions_menu) => {
12643                let completions = completions_menu.completions.borrow_mut();
12644                assert_eq!(
12645                    completions
12646                        .iter()
12647                        .map(|completion| &completion.label.text)
12648                        .collect::<Vec<_>>(),
12649                    vec!["id", "other"]
12650                )
12651            }
12652            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12653        }
12654    });
12655    cx.run_until_parked();
12656
12657    cx.update_editor(|editor, window, cx| {
12658        editor.context_menu_next(&ContextMenuNext, window, cx);
12659    });
12660    cx.run_until_parked();
12661    cx.update_editor(|editor, window, cx| {
12662        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12663    });
12664    cx.run_until_parked();
12665    cx.update_editor(|editor, window, cx| {
12666        editor.context_menu_next(&ContextMenuNext, window, cx);
12667    });
12668    cx.run_until_parked();
12669    cx.update_editor(|editor, window, cx| {
12670        editor
12671            .compose_completion(&ComposeCompletion::default(), window, cx)
12672            .expect("No task returned")
12673    })
12674    .await
12675    .expect("Completion failed");
12676    cx.run_until_parked();
12677
12678    cx.update_editor(|editor, _, cx| {
12679        assert_eq!(
12680            resolve_requests_1.load(atomic::Ordering::Acquire),
12681            1,
12682            "Should always resolve once despite multiple selections"
12683        );
12684        assert_eq!(
12685            resolve_requests_2.load(atomic::Ordering::Acquire),
12686            1,
12687            "Should always resolve once after multiple selections and applying the completion"
12688        );
12689        assert_eq!(
12690            editor.text(cx),
12691            "fn main() { let a = ??.other; }",
12692            "Should use resolved data when applying the completion"
12693        );
12694    });
12695}
12696
12697#[gpui::test]
12698async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12699    init_test(cx, |_| {});
12700
12701    let item_0 = lsp::CompletionItem {
12702        label: "abs".into(),
12703        insert_text: Some("abs".into()),
12704        data: Some(json!({ "very": "special"})),
12705        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12706        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12707            lsp::InsertReplaceEdit {
12708                new_text: "abs".to_string(),
12709                insert: lsp::Range::default(),
12710                replace: lsp::Range::default(),
12711            },
12712        )),
12713        ..lsp::CompletionItem::default()
12714    };
12715    let items = iter::once(item_0.clone())
12716        .chain((11..51).map(|i| lsp::CompletionItem {
12717            label: format!("item_{}", i),
12718            insert_text: Some(format!("item_{}", i)),
12719            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12720            ..lsp::CompletionItem::default()
12721        }))
12722        .collect::<Vec<_>>();
12723
12724    let default_commit_characters = vec!["?".to_string()];
12725    let default_data = json!({ "default": "data"});
12726    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12727    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12728    let default_edit_range = lsp::Range {
12729        start: lsp::Position {
12730            line: 0,
12731            character: 5,
12732        },
12733        end: lsp::Position {
12734            line: 0,
12735            character: 5,
12736        },
12737    };
12738
12739    let mut cx = EditorLspTestContext::new_rust(
12740        lsp::ServerCapabilities {
12741            completion_provider: Some(lsp::CompletionOptions {
12742                trigger_characters: Some(vec![".".to_string()]),
12743                resolve_provider: Some(true),
12744                ..Default::default()
12745            }),
12746            ..Default::default()
12747        },
12748        cx,
12749    )
12750    .await;
12751
12752    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12753    cx.simulate_keystroke(".");
12754
12755    let completion_data = default_data.clone();
12756    let completion_characters = default_commit_characters.clone();
12757    let completion_items = items.clone();
12758    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12759        let default_data = completion_data.clone();
12760        let default_commit_characters = completion_characters.clone();
12761        let items = completion_items.clone();
12762        async move {
12763            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12764                items,
12765                item_defaults: Some(lsp::CompletionListItemDefaults {
12766                    data: Some(default_data.clone()),
12767                    commit_characters: Some(default_commit_characters.clone()),
12768                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12769                        default_edit_range,
12770                    )),
12771                    insert_text_format: Some(default_insert_text_format),
12772                    insert_text_mode: Some(default_insert_text_mode),
12773                }),
12774                ..lsp::CompletionList::default()
12775            })))
12776        }
12777    })
12778    .next()
12779    .await;
12780
12781    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12782    cx.lsp
12783        .server
12784        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12785            let closure_resolved_items = resolved_items.clone();
12786            move |item_to_resolve, _| {
12787                let closure_resolved_items = closure_resolved_items.clone();
12788                async move {
12789                    closure_resolved_items.lock().push(item_to_resolve.clone());
12790                    Ok(item_to_resolve)
12791                }
12792            }
12793        })
12794        .detach();
12795
12796    cx.condition(|editor, _| editor.context_menu_visible())
12797        .await;
12798    cx.run_until_parked();
12799    cx.update_editor(|editor, _, _| {
12800        let menu = editor.context_menu.borrow_mut();
12801        match menu.as_ref().expect("should have the completions menu") {
12802            CodeContextMenu::Completions(completions_menu) => {
12803                assert_eq!(
12804                    completions_menu
12805                        .entries
12806                        .borrow()
12807                        .iter()
12808                        .map(|mat| mat.string.clone())
12809                        .collect::<Vec<String>>(),
12810                    items
12811                        .iter()
12812                        .map(|completion| completion.label.clone())
12813                        .collect::<Vec<String>>()
12814                );
12815            }
12816            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12817        }
12818    });
12819    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12820    // with 4 from the end.
12821    assert_eq!(
12822        *resolved_items.lock(),
12823        [&items[0..16], &items[items.len() - 4..items.len()]]
12824            .concat()
12825            .iter()
12826            .cloned()
12827            .map(|mut item| {
12828                if item.data.is_none() {
12829                    item.data = Some(default_data.clone());
12830                }
12831                item
12832            })
12833            .collect::<Vec<lsp::CompletionItem>>(),
12834        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12835    );
12836    resolved_items.lock().clear();
12837
12838    cx.update_editor(|editor, window, cx| {
12839        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12840    });
12841    cx.run_until_parked();
12842    // Completions that have already been resolved are skipped.
12843    assert_eq!(
12844        *resolved_items.lock(),
12845        items[items.len() - 16..items.len() - 4]
12846            .iter()
12847            .cloned()
12848            .map(|mut item| {
12849                if item.data.is_none() {
12850                    item.data = Some(default_data.clone());
12851                }
12852                item
12853            })
12854            .collect::<Vec<lsp::CompletionItem>>()
12855    );
12856    resolved_items.lock().clear();
12857}
12858
12859#[gpui::test]
12860async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12861    init_test(cx, |_| {});
12862
12863    let mut cx = EditorLspTestContext::new(
12864        Language::new(
12865            LanguageConfig {
12866                matcher: LanguageMatcher {
12867                    path_suffixes: vec!["jsx".into()],
12868                    ..Default::default()
12869                },
12870                overrides: [(
12871                    "element".into(),
12872                    LanguageConfigOverride {
12873                        word_characters: Override::Set(['-'].into_iter().collect()),
12874                        ..Default::default()
12875                    },
12876                )]
12877                .into_iter()
12878                .collect(),
12879                ..Default::default()
12880            },
12881            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12882        )
12883        .with_override_query("(jsx_self_closing_element) @element")
12884        .unwrap(),
12885        lsp::ServerCapabilities {
12886            completion_provider: Some(lsp::CompletionOptions {
12887                trigger_characters: Some(vec![":".to_string()]),
12888                ..Default::default()
12889            }),
12890            ..Default::default()
12891        },
12892        cx,
12893    )
12894    .await;
12895
12896    cx.lsp
12897        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12898            Ok(Some(lsp::CompletionResponse::Array(vec![
12899                lsp::CompletionItem {
12900                    label: "bg-blue".into(),
12901                    ..Default::default()
12902                },
12903                lsp::CompletionItem {
12904                    label: "bg-red".into(),
12905                    ..Default::default()
12906                },
12907                lsp::CompletionItem {
12908                    label: "bg-yellow".into(),
12909                    ..Default::default()
12910                },
12911            ])))
12912        });
12913
12914    cx.set_state(r#"<p class="bgˇ" />"#);
12915
12916    // Trigger completion when typing a dash, because the dash is an extra
12917    // word character in the 'element' scope, which contains the cursor.
12918    cx.simulate_keystroke("-");
12919    cx.executor().run_until_parked();
12920    cx.update_editor(|editor, _, _| {
12921        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12922        {
12923            assert_eq!(
12924                completion_menu_entries(&menu),
12925                &["bg-red", "bg-blue", "bg-yellow"]
12926            );
12927        } else {
12928            panic!("expected completion menu to be open");
12929        }
12930    });
12931
12932    cx.simulate_keystroke("l");
12933    cx.executor().run_until_parked();
12934    cx.update_editor(|editor, _, _| {
12935        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12936        {
12937            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12938        } else {
12939            panic!("expected completion menu to be open");
12940        }
12941    });
12942
12943    // When filtering completions, consider the character after the '-' to
12944    // be the start of a subword.
12945    cx.set_state(r#"<p class="yelˇ" />"#);
12946    cx.simulate_keystroke("l");
12947    cx.executor().run_until_parked();
12948    cx.update_editor(|editor, _, _| {
12949        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12950        {
12951            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12952        } else {
12953            panic!("expected completion menu to be open");
12954        }
12955    });
12956}
12957
12958fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12959    let entries = menu.entries.borrow();
12960    entries.iter().map(|mat| mat.string.clone()).collect()
12961}
12962
12963#[gpui::test]
12964async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12965    init_test(cx, |settings| {
12966        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12967            FormatterList(vec![Formatter::Prettier].into()),
12968        ))
12969    });
12970
12971    let fs = FakeFs::new(cx.executor());
12972    fs.insert_file(path!("/file.ts"), Default::default()).await;
12973
12974    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12975    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12976
12977    language_registry.add(Arc::new(Language::new(
12978        LanguageConfig {
12979            name: "TypeScript".into(),
12980            matcher: LanguageMatcher {
12981                path_suffixes: vec!["ts".to_string()],
12982                ..Default::default()
12983            },
12984            ..Default::default()
12985        },
12986        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12987    )));
12988    update_test_language_settings(cx, |settings| {
12989        settings.defaults.prettier = Some(PrettierSettings {
12990            allowed: true,
12991            ..PrettierSettings::default()
12992        });
12993    });
12994
12995    let test_plugin = "test_plugin";
12996    let _ = language_registry.register_fake_lsp(
12997        "TypeScript",
12998        FakeLspAdapter {
12999            prettier_plugins: vec![test_plugin],
13000            ..Default::default()
13001        },
13002    );
13003
13004    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13005    let buffer = project
13006        .update(cx, |project, cx| {
13007            project.open_local_buffer(path!("/file.ts"), cx)
13008        })
13009        .await
13010        .unwrap();
13011
13012    let buffer_text = "one\ntwo\nthree\n";
13013    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13014    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13015    editor.update_in(cx, |editor, window, cx| {
13016        editor.set_text(buffer_text, window, cx)
13017    });
13018
13019    editor
13020        .update_in(cx, |editor, window, cx| {
13021            editor.perform_format(
13022                project.clone(),
13023                FormatTrigger::Manual,
13024                FormatTarget::Buffers,
13025                window,
13026                cx,
13027            )
13028        })
13029        .unwrap()
13030        .await;
13031    assert_eq!(
13032        editor.update(cx, |editor, cx| editor.text(cx)),
13033        buffer_text.to_string() + prettier_format_suffix,
13034        "Test prettier formatting was not applied to the original buffer text",
13035    );
13036
13037    update_test_language_settings(cx, |settings| {
13038        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13039    });
13040    let format = editor.update_in(cx, |editor, window, cx| {
13041        editor.perform_format(
13042            project.clone(),
13043            FormatTrigger::Manual,
13044            FormatTarget::Buffers,
13045            window,
13046            cx,
13047        )
13048    });
13049    format.await.unwrap();
13050    assert_eq!(
13051        editor.update(cx, |editor, cx| editor.text(cx)),
13052        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13053        "Autoformatting (via test prettier) was not applied to the original buffer text",
13054    );
13055}
13056
13057#[gpui::test]
13058async fn test_addition_reverts(cx: &mut TestAppContext) {
13059    init_test(cx, |_| {});
13060    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13061    let base_text = indoc! {r#"
13062        struct Row;
13063        struct Row1;
13064        struct Row2;
13065
13066        struct Row4;
13067        struct Row5;
13068        struct Row6;
13069
13070        struct Row8;
13071        struct Row9;
13072        struct Row10;"#};
13073
13074    // When addition hunks are not adjacent to carets, no hunk revert is performed
13075    assert_hunk_revert(
13076        indoc! {r#"struct Row;
13077                   struct Row1;
13078                   struct Row1.1;
13079                   struct Row1.2;
13080                   struct Row2;ˇ
13081
13082                   struct Row4;
13083                   struct Row5;
13084                   struct Row6;
13085
13086                   struct Row8;
13087                   ˇstruct Row9;
13088                   struct Row9.1;
13089                   struct Row9.2;
13090                   struct Row9.3;
13091                   struct Row10;"#},
13092        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13093        indoc! {r#"struct Row;
13094                   struct Row1;
13095                   struct Row1.1;
13096                   struct Row1.2;
13097                   struct Row2;ˇ
13098
13099                   struct Row4;
13100                   struct Row5;
13101                   struct Row6;
13102
13103                   struct Row8;
13104                   ˇstruct Row9;
13105                   struct Row9.1;
13106                   struct Row9.2;
13107                   struct Row9.3;
13108                   struct Row10;"#},
13109        base_text,
13110        &mut cx,
13111    );
13112    // Same for selections
13113    assert_hunk_revert(
13114        indoc! {r#"struct Row;
13115                   struct Row1;
13116                   struct Row2;
13117                   struct Row2.1;
13118                   struct Row2.2;
13119                   «ˇ
13120                   struct Row4;
13121                   struct» Row5;
13122                   «struct Row6;
13123                   ˇ»
13124                   struct Row9.1;
13125                   struct Row9.2;
13126                   struct Row9.3;
13127                   struct Row8;
13128                   struct Row9;
13129                   struct Row10;"#},
13130        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13131        indoc! {r#"struct Row;
13132                   struct Row1;
13133                   struct Row2;
13134                   struct Row2.1;
13135                   struct Row2.2;
13136                   «ˇ
13137                   struct Row4;
13138                   struct» Row5;
13139                   «struct Row6;
13140                   ˇ»
13141                   struct Row9.1;
13142                   struct Row9.2;
13143                   struct Row9.3;
13144                   struct Row8;
13145                   struct Row9;
13146                   struct Row10;"#},
13147        base_text,
13148        &mut cx,
13149    );
13150
13151    // When carets and selections intersect the addition hunks, those are reverted.
13152    // Adjacent carets got merged.
13153    assert_hunk_revert(
13154        indoc! {r#"struct Row;
13155                   ˇ// something on the top
13156                   struct Row1;
13157                   struct Row2;
13158                   struct Roˇw3.1;
13159                   struct Row2.2;
13160                   struct Row2.3;ˇ
13161
13162                   struct Row4;
13163                   struct ˇRow5.1;
13164                   struct Row5.2;
13165                   struct «Rowˇ»5.3;
13166                   struct Row5;
13167                   struct Row6;
13168                   ˇ
13169                   struct Row9.1;
13170                   struct «Rowˇ»9.2;
13171                   struct «ˇRow»9.3;
13172                   struct Row8;
13173                   struct Row9;
13174                   «ˇ// something on bottom»
13175                   struct Row10;"#},
13176        vec![
13177            DiffHunkStatusKind::Added,
13178            DiffHunkStatusKind::Added,
13179            DiffHunkStatusKind::Added,
13180            DiffHunkStatusKind::Added,
13181            DiffHunkStatusKind::Added,
13182        ],
13183        indoc! {r#"struct Row;
13184                   ˇstruct Row1;
13185                   struct Row2;
13186                   ˇ
13187                   struct Row4;
13188                   ˇstruct Row5;
13189                   struct Row6;
13190                   ˇ
13191                   ˇstruct Row8;
13192                   struct Row9;
13193                   ˇstruct Row10;"#},
13194        base_text,
13195        &mut cx,
13196    );
13197}
13198
13199#[gpui::test]
13200async fn test_modification_reverts(cx: &mut TestAppContext) {
13201    init_test(cx, |_| {});
13202    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13203    let base_text = indoc! {r#"
13204        struct Row;
13205        struct Row1;
13206        struct Row2;
13207
13208        struct Row4;
13209        struct Row5;
13210        struct Row6;
13211
13212        struct Row8;
13213        struct Row9;
13214        struct Row10;"#};
13215
13216    // Modification hunks behave the same as the addition ones.
13217    assert_hunk_revert(
13218        indoc! {r#"struct Row;
13219                   struct Row1;
13220                   struct Row33;
13221                   ˇ
13222                   struct Row4;
13223                   struct Row5;
13224                   struct Row6;
13225                   ˇ
13226                   struct Row99;
13227                   struct Row9;
13228                   struct Row10;"#},
13229        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13230        indoc! {r#"struct Row;
13231                   struct Row1;
13232                   struct Row33;
13233                   ˇ
13234                   struct Row4;
13235                   struct Row5;
13236                   struct Row6;
13237                   ˇ
13238                   struct Row99;
13239                   struct Row9;
13240                   struct Row10;"#},
13241        base_text,
13242        &mut cx,
13243    );
13244    assert_hunk_revert(
13245        indoc! {r#"struct Row;
13246                   struct Row1;
13247                   struct Row33;
13248                   «ˇ
13249                   struct Row4;
13250                   struct» Row5;
13251                   «struct Row6;
13252                   ˇ»
13253                   struct Row99;
13254                   struct Row9;
13255                   struct Row10;"#},
13256        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13257        indoc! {r#"struct Row;
13258                   struct Row1;
13259                   struct Row33;
13260                   «ˇ
13261                   struct Row4;
13262                   struct» Row5;
13263                   «struct Row6;
13264                   ˇ»
13265                   struct Row99;
13266                   struct Row9;
13267                   struct Row10;"#},
13268        base_text,
13269        &mut cx,
13270    );
13271
13272    assert_hunk_revert(
13273        indoc! {r#"ˇstruct Row1.1;
13274                   struct Row1;
13275                   «ˇstr»uct Row22;
13276
13277                   struct ˇRow44;
13278                   struct Row5;
13279                   struct «Rˇ»ow66;ˇ
13280
13281                   «struˇ»ct Row88;
13282                   struct Row9;
13283                   struct Row1011;ˇ"#},
13284        vec![
13285            DiffHunkStatusKind::Modified,
13286            DiffHunkStatusKind::Modified,
13287            DiffHunkStatusKind::Modified,
13288            DiffHunkStatusKind::Modified,
13289            DiffHunkStatusKind::Modified,
13290            DiffHunkStatusKind::Modified,
13291        ],
13292        indoc! {r#"struct Row;
13293                   ˇstruct Row1;
13294                   struct Row2;
13295                   ˇ
13296                   struct Row4;
13297                   ˇstruct Row5;
13298                   struct Row6;
13299                   ˇ
13300                   struct Row8;
13301                   ˇstruct Row9;
13302                   struct Row10;ˇ"#},
13303        base_text,
13304        &mut cx,
13305    );
13306}
13307
13308#[gpui::test]
13309async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13310    init_test(cx, |_| {});
13311    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13312    let base_text = indoc! {r#"
13313        one
13314
13315        two
13316        three
13317        "#};
13318
13319    cx.set_head_text(base_text);
13320    cx.set_state("\nˇ\n");
13321    cx.executor().run_until_parked();
13322    cx.update_editor(|editor, _window, cx| {
13323        editor.expand_selected_diff_hunks(cx);
13324    });
13325    cx.executor().run_until_parked();
13326    cx.update_editor(|editor, window, cx| {
13327        editor.backspace(&Default::default(), window, cx);
13328    });
13329    cx.run_until_parked();
13330    cx.assert_state_with_diff(
13331        indoc! {r#"
13332
13333        - two
13334        - threeˇ
13335        +
13336        "#}
13337        .to_string(),
13338    );
13339}
13340
13341#[gpui::test]
13342async fn test_deletion_reverts(cx: &mut TestAppContext) {
13343    init_test(cx, |_| {});
13344    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13345    let base_text = indoc! {r#"struct Row;
13346struct Row1;
13347struct Row2;
13348
13349struct Row4;
13350struct Row5;
13351struct Row6;
13352
13353struct Row8;
13354struct Row9;
13355struct Row10;"#};
13356
13357    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13358    assert_hunk_revert(
13359        indoc! {r#"struct Row;
13360                   struct Row2;
13361
13362                   ˇstruct Row4;
13363                   struct Row5;
13364                   struct Row6;
13365                   ˇ
13366                   struct Row8;
13367                   struct Row10;"#},
13368        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13369        indoc! {r#"struct Row;
13370                   struct Row2;
13371
13372                   ˇstruct Row4;
13373                   struct Row5;
13374                   struct Row6;
13375                   ˇ
13376                   struct Row8;
13377                   struct Row10;"#},
13378        base_text,
13379        &mut cx,
13380    );
13381    assert_hunk_revert(
13382        indoc! {r#"struct Row;
13383                   struct Row2;
13384
13385                   «ˇstruct Row4;
13386                   struct» Row5;
13387                   «struct Row6;
13388                   ˇ»
13389                   struct Row8;
13390                   struct Row10;"#},
13391        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13392        indoc! {r#"struct Row;
13393                   struct Row2;
13394
13395                   «ˇstruct Row4;
13396                   struct» Row5;
13397                   «struct Row6;
13398                   ˇ»
13399                   struct Row8;
13400                   struct Row10;"#},
13401        base_text,
13402        &mut cx,
13403    );
13404
13405    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13406    assert_hunk_revert(
13407        indoc! {r#"struct Row;
13408                   ˇstruct Row2;
13409
13410                   struct Row4;
13411                   struct Row5;
13412                   struct Row6;
13413
13414                   struct Row8;ˇ
13415                   struct Row10;"#},
13416        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13417        indoc! {r#"struct Row;
13418                   struct Row1;
13419                   ˇstruct Row2;
13420
13421                   struct Row4;
13422                   struct Row5;
13423                   struct Row6;
13424
13425                   struct Row8;ˇ
13426                   struct Row9;
13427                   struct Row10;"#},
13428        base_text,
13429        &mut cx,
13430    );
13431    assert_hunk_revert(
13432        indoc! {r#"struct Row;
13433                   struct Row2«ˇ;
13434                   struct Row4;
13435                   struct» Row5;
13436                   «struct Row6;
13437
13438                   struct Row8;ˇ»
13439                   struct Row10;"#},
13440        vec![
13441            DiffHunkStatusKind::Deleted,
13442            DiffHunkStatusKind::Deleted,
13443            DiffHunkStatusKind::Deleted,
13444        ],
13445        indoc! {r#"struct Row;
13446                   struct Row1;
13447                   struct Row2«ˇ;
13448
13449                   struct Row4;
13450                   struct» Row5;
13451                   «struct Row6;
13452
13453                   struct Row8;ˇ»
13454                   struct Row9;
13455                   struct Row10;"#},
13456        base_text,
13457        &mut cx,
13458    );
13459}
13460
13461#[gpui::test]
13462async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13463    init_test(cx, |_| {});
13464
13465    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13466    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13467    let base_text_3 =
13468        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13469
13470    let text_1 = edit_first_char_of_every_line(base_text_1);
13471    let text_2 = edit_first_char_of_every_line(base_text_2);
13472    let text_3 = edit_first_char_of_every_line(base_text_3);
13473
13474    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13475    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13476    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13477
13478    let multibuffer = cx.new(|cx| {
13479        let mut multibuffer = MultiBuffer::new(ReadWrite);
13480        multibuffer.push_excerpts(
13481            buffer_1.clone(),
13482            [
13483                ExcerptRange {
13484                    context: Point::new(0, 0)..Point::new(3, 0),
13485                    primary: None,
13486                },
13487                ExcerptRange {
13488                    context: Point::new(5, 0)..Point::new(7, 0),
13489                    primary: None,
13490                },
13491                ExcerptRange {
13492                    context: Point::new(9, 0)..Point::new(10, 4),
13493                    primary: None,
13494                },
13495            ],
13496            cx,
13497        );
13498        multibuffer.push_excerpts(
13499            buffer_2.clone(),
13500            [
13501                ExcerptRange {
13502                    context: Point::new(0, 0)..Point::new(3, 0),
13503                    primary: None,
13504                },
13505                ExcerptRange {
13506                    context: Point::new(5, 0)..Point::new(7, 0),
13507                    primary: None,
13508                },
13509                ExcerptRange {
13510                    context: Point::new(9, 0)..Point::new(10, 4),
13511                    primary: None,
13512                },
13513            ],
13514            cx,
13515        );
13516        multibuffer.push_excerpts(
13517            buffer_3.clone(),
13518            [
13519                ExcerptRange {
13520                    context: Point::new(0, 0)..Point::new(3, 0),
13521                    primary: None,
13522                },
13523                ExcerptRange {
13524                    context: Point::new(5, 0)..Point::new(7, 0),
13525                    primary: None,
13526                },
13527                ExcerptRange {
13528                    context: Point::new(9, 0)..Point::new(10, 4),
13529                    primary: None,
13530                },
13531            ],
13532            cx,
13533        );
13534        multibuffer
13535    });
13536
13537    let fs = FakeFs::new(cx.executor());
13538    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13539    let (editor, cx) = cx
13540        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13541    editor.update_in(cx, |editor, _window, cx| {
13542        for (buffer, diff_base) in [
13543            (buffer_1.clone(), base_text_1),
13544            (buffer_2.clone(), base_text_2),
13545            (buffer_3.clone(), base_text_3),
13546        ] {
13547            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13548            editor
13549                .buffer
13550                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13551        }
13552    });
13553    cx.executor().run_until_parked();
13554
13555    editor.update_in(cx, |editor, window, cx| {
13556        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}");
13557        editor.select_all(&SelectAll, window, cx);
13558        editor.git_restore(&Default::default(), window, cx);
13559    });
13560    cx.executor().run_until_parked();
13561
13562    // When all ranges are selected, all buffer hunks are reverted.
13563    editor.update(cx, |editor, cx| {
13564        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");
13565    });
13566    buffer_1.update(cx, |buffer, _| {
13567        assert_eq!(buffer.text(), base_text_1);
13568    });
13569    buffer_2.update(cx, |buffer, _| {
13570        assert_eq!(buffer.text(), base_text_2);
13571    });
13572    buffer_3.update(cx, |buffer, _| {
13573        assert_eq!(buffer.text(), base_text_3);
13574    });
13575
13576    editor.update_in(cx, |editor, window, cx| {
13577        editor.undo(&Default::default(), window, cx);
13578    });
13579
13580    editor.update_in(cx, |editor, window, cx| {
13581        editor.change_selections(None, window, cx, |s| {
13582            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13583        });
13584        editor.git_restore(&Default::default(), window, cx);
13585    });
13586
13587    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13588    // but not affect buffer_2 and its related excerpts.
13589    editor.update(cx, |editor, cx| {
13590        assert_eq!(
13591            editor.text(cx),
13592            "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}"
13593        );
13594    });
13595    buffer_1.update(cx, |buffer, _| {
13596        assert_eq!(buffer.text(), base_text_1);
13597    });
13598    buffer_2.update(cx, |buffer, _| {
13599        assert_eq!(
13600            buffer.text(),
13601            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13602        );
13603    });
13604    buffer_3.update(cx, |buffer, _| {
13605        assert_eq!(
13606            buffer.text(),
13607            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13608        );
13609    });
13610
13611    fn edit_first_char_of_every_line(text: &str) -> String {
13612        text.split('\n')
13613            .map(|line| format!("X{}", &line[1..]))
13614            .collect::<Vec<_>>()
13615            .join("\n")
13616    }
13617}
13618
13619#[gpui::test]
13620async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13621    init_test(cx, |_| {});
13622
13623    let cols = 4;
13624    let rows = 10;
13625    let sample_text_1 = sample_text(rows, cols, 'a');
13626    assert_eq!(
13627        sample_text_1,
13628        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13629    );
13630    let sample_text_2 = sample_text(rows, cols, 'l');
13631    assert_eq!(
13632        sample_text_2,
13633        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13634    );
13635    let sample_text_3 = sample_text(rows, cols, 'v');
13636    assert_eq!(
13637        sample_text_3,
13638        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13639    );
13640
13641    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13642    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13643    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13644
13645    let multi_buffer = cx.new(|cx| {
13646        let mut multibuffer = MultiBuffer::new(ReadWrite);
13647        multibuffer.push_excerpts(
13648            buffer_1.clone(),
13649            [
13650                ExcerptRange {
13651                    context: Point::new(0, 0)..Point::new(3, 0),
13652                    primary: None,
13653                },
13654                ExcerptRange {
13655                    context: Point::new(5, 0)..Point::new(7, 0),
13656                    primary: None,
13657                },
13658                ExcerptRange {
13659                    context: Point::new(9, 0)..Point::new(10, 4),
13660                    primary: None,
13661                },
13662            ],
13663            cx,
13664        );
13665        multibuffer.push_excerpts(
13666            buffer_2.clone(),
13667            [
13668                ExcerptRange {
13669                    context: Point::new(0, 0)..Point::new(3, 0),
13670                    primary: None,
13671                },
13672                ExcerptRange {
13673                    context: Point::new(5, 0)..Point::new(7, 0),
13674                    primary: None,
13675                },
13676                ExcerptRange {
13677                    context: Point::new(9, 0)..Point::new(10, 4),
13678                    primary: None,
13679                },
13680            ],
13681            cx,
13682        );
13683        multibuffer.push_excerpts(
13684            buffer_3.clone(),
13685            [
13686                ExcerptRange {
13687                    context: Point::new(0, 0)..Point::new(3, 0),
13688                    primary: None,
13689                },
13690                ExcerptRange {
13691                    context: Point::new(5, 0)..Point::new(7, 0),
13692                    primary: None,
13693                },
13694                ExcerptRange {
13695                    context: Point::new(9, 0)..Point::new(10, 4),
13696                    primary: None,
13697                },
13698            ],
13699            cx,
13700        );
13701        multibuffer
13702    });
13703
13704    let fs = FakeFs::new(cx.executor());
13705    fs.insert_tree(
13706        "/a",
13707        json!({
13708            "main.rs": sample_text_1,
13709            "other.rs": sample_text_2,
13710            "lib.rs": sample_text_3,
13711        }),
13712    )
13713    .await;
13714    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13715    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13716    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13717    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13718        Editor::new(
13719            EditorMode::Full,
13720            multi_buffer,
13721            Some(project.clone()),
13722            window,
13723            cx,
13724        )
13725    });
13726    let multibuffer_item_id = workspace
13727        .update(cx, |workspace, window, cx| {
13728            assert!(
13729                workspace.active_item(cx).is_none(),
13730                "active item should be None before the first item is added"
13731            );
13732            workspace.add_item_to_active_pane(
13733                Box::new(multi_buffer_editor.clone()),
13734                None,
13735                true,
13736                window,
13737                cx,
13738            );
13739            let active_item = workspace
13740                .active_item(cx)
13741                .expect("should have an active item after adding the multi buffer");
13742            assert!(
13743                !active_item.is_singleton(cx),
13744                "A multi buffer was expected to active after adding"
13745            );
13746            active_item.item_id()
13747        })
13748        .unwrap();
13749    cx.executor().run_until_parked();
13750
13751    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13752        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13753            s.select_ranges(Some(1..2))
13754        });
13755        editor.open_excerpts(&OpenExcerpts, window, cx);
13756    });
13757    cx.executor().run_until_parked();
13758    let first_item_id = workspace
13759        .update(cx, |workspace, window, cx| {
13760            let active_item = workspace
13761                .active_item(cx)
13762                .expect("should have an active item after navigating into the 1st buffer");
13763            let first_item_id = active_item.item_id();
13764            assert_ne!(
13765                first_item_id, multibuffer_item_id,
13766                "Should navigate into the 1st buffer and activate it"
13767            );
13768            assert!(
13769                active_item.is_singleton(cx),
13770                "New active item should be a singleton buffer"
13771            );
13772            assert_eq!(
13773                active_item
13774                    .act_as::<Editor>(cx)
13775                    .expect("should have navigated into an editor for the 1st buffer")
13776                    .read(cx)
13777                    .text(cx),
13778                sample_text_1
13779            );
13780
13781            workspace
13782                .go_back(workspace.active_pane().downgrade(), window, cx)
13783                .detach_and_log_err(cx);
13784
13785            first_item_id
13786        })
13787        .unwrap();
13788    cx.executor().run_until_parked();
13789    workspace
13790        .update(cx, |workspace, _, cx| {
13791            let active_item = workspace
13792                .active_item(cx)
13793                .expect("should have an active item after navigating back");
13794            assert_eq!(
13795                active_item.item_id(),
13796                multibuffer_item_id,
13797                "Should navigate back to the multi buffer"
13798            );
13799            assert!(!active_item.is_singleton(cx));
13800        })
13801        .unwrap();
13802
13803    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13804        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13805            s.select_ranges(Some(39..40))
13806        });
13807        editor.open_excerpts(&OpenExcerpts, window, cx);
13808    });
13809    cx.executor().run_until_parked();
13810    let second_item_id = workspace
13811        .update(cx, |workspace, window, cx| {
13812            let active_item = workspace
13813                .active_item(cx)
13814                .expect("should have an active item after navigating into the 2nd buffer");
13815            let second_item_id = active_item.item_id();
13816            assert_ne!(
13817                second_item_id, multibuffer_item_id,
13818                "Should navigate away from the multibuffer"
13819            );
13820            assert_ne!(
13821                second_item_id, first_item_id,
13822                "Should navigate into the 2nd buffer and activate it"
13823            );
13824            assert!(
13825                active_item.is_singleton(cx),
13826                "New active item should be a singleton buffer"
13827            );
13828            assert_eq!(
13829                active_item
13830                    .act_as::<Editor>(cx)
13831                    .expect("should have navigated into an editor")
13832                    .read(cx)
13833                    .text(cx),
13834                sample_text_2
13835            );
13836
13837            workspace
13838                .go_back(workspace.active_pane().downgrade(), window, cx)
13839                .detach_and_log_err(cx);
13840
13841            second_item_id
13842        })
13843        .unwrap();
13844    cx.executor().run_until_parked();
13845    workspace
13846        .update(cx, |workspace, _, cx| {
13847            let active_item = workspace
13848                .active_item(cx)
13849                .expect("should have an active item after navigating back from the 2nd buffer");
13850            assert_eq!(
13851                active_item.item_id(),
13852                multibuffer_item_id,
13853                "Should navigate back from the 2nd buffer to the multi buffer"
13854            );
13855            assert!(!active_item.is_singleton(cx));
13856        })
13857        .unwrap();
13858
13859    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13860        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13861            s.select_ranges(Some(70..70))
13862        });
13863        editor.open_excerpts(&OpenExcerpts, window, cx);
13864    });
13865    cx.executor().run_until_parked();
13866    workspace
13867        .update(cx, |workspace, window, cx| {
13868            let active_item = workspace
13869                .active_item(cx)
13870                .expect("should have an active item after navigating into the 3rd buffer");
13871            let third_item_id = active_item.item_id();
13872            assert_ne!(
13873                third_item_id, multibuffer_item_id,
13874                "Should navigate into the 3rd buffer and activate it"
13875            );
13876            assert_ne!(third_item_id, first_item_id);
13877            assert_ne!(third_item_id, second_item_id);
13878            assert!(
13879                active_item.is_singleton(cx),
13880                "New active item should be a singleton buffer"
13881            );
13882            assert_eq!(
13883                active_item
13884                    .act_as::<Editor>(cx)
13885                    .expect("should have navigated into an editor")
13886                    .read(cx)
13887                    .text(cx),
13888                sample_text_3
13889            );
13890
13891            workspace
13892                .go_back(workspace.active_pane().downgrade(), window, cx)
13893                .detach_and_log_err(cx);
13894        })
13895        .unwrap();
13896    cx.executor().run_until_parked();
13897    workspace
13898        .update(cx, |workspace, _, cx| {
13899            let active_item = workspace
13900                .active_item(cx)
13901                .expect("should have an active item after navigating back from the 3rd buffer");
13902            assert_eq!(
13903                active_item.item_id(),
13904                multibuffer_item_id,
13905                "Should navigate back from the 3rd buffer to the multi buffer"
13906            );
13907            assert!(!active_item.is_singleton(cx));
13908        })
13909        .unwrap();
13910}
13911
13912#[gpui::test]
13913async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13914    init_test(cx, |_| {});
13915
13916    let mut cx = EditorTestContext::new(cx).await;
13917
13918    let diff_base = r#"
13919        use some::mod;
13920
13921        const A: u32 = 42;
13922
13923        fn main() {
13924            println!("hello");
13925
13926            println!("world");
13927        }
13928        "#
13929    .unindent();
13930
13931    cx.set_state(
13932        &r#"
13933        use some::modified;
13934
13935        ˇ
13936        fn main() {
13937            println!("hello there");
13938
13939            println!("around the");
13940            println!("world");
13941        }
13942        "#
13943        .unindent(),
13944    );
13945
13946    cx.set_head_text(&diff_base);
13947    executor.run_until_parked();
13948
13949    cx.update_editor(|editor, window, cx| {
13950        editor.go_to_next_hunk(&GoToHunk, window, cx);
13951        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13952    });
13953    executor.run_until_parked();
13954    cx.assert_state_with_diff(
13955        r#"
13956          use some::modified;
13957
13958
13959          fn main() {
13960        -     println!("hello");
13961        + ˇ    println!("hello there");
13962
13963              println!("around the");
13964              println!("world");
13965          }
13966        "#
13967        .unindent(),
13968    );
13969
13970    cx.update_editor(|editor, window, cx| {
13971        for _ in 0..2 {
13972            editor.go_to_next_hunk(&GoToHunk, window, cx);
13973            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13974        }
13975    });
13976    executor.run_until_parked();
13977    cx.assert_state_with_diff(
13978        r#"
13979        - use some::mod;
13980        + ˇuse some::modified;
13981
13982
13983          fn main() {
13984        -     println!("hello");
13985        +     println!("hello there");
13986
13987        +     println!("around the");
13988              println!("world");
13989          }
13990        "#
13991        .unindent(),
13992    );
13993
13994    cx.update_editor(|editor, window, cx| {
13995        editor.go_to_next_hunk(&GoToHunk, window, cx);
13996        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13997    });
13998    executor.run_until_parked();
13999    cx.assert_state_with_diff(
14000        r#"
14001        - use some::mod;
14002        + use some::modified;
14003
14004        - const A: u32 = 42;
14005          ˇ
14006          fn main() {
14007        -     println!("hello");
14008        +     println!("hello there");
14009
14010        +     println!("around the");
14011              println!("world");
14012          }
14013        "#
14014        .unindent(),
14015    );
14016
14017    cx.update_editor(|editor, window, cx| {
14018        editor.cancel(&Cancel, window, cx);
14019    });
14020
14021    cx.assert_state_with_diff(
14022        r#"
14023          use some::modified;
14024
14025          ˇ
14026          fn main() {
14027              println!("hello there");
14028
14029              println!("around the");
14030              println!("world");
14031          }
14032        "#
14033        .unindent(),
14034    );
14035}
14036
14037#[gpui::test]
14038async fn test_diff_base_change_with_expanded_diff_hunks(
14039    executor: BackgroundExecutor,
14040    cx: &mut TestAppContext,
14041) {
14042    init_test(cx, |_| {});
14043
14044    let mut cx = EditorTestContext::new(cx).await;
14045
14046    let diff_base = r#"
14047        use some::mod1;
14048        use some::mod2;
14049
14050        const A: u32 = 42;
14051        const B: u32 = 42;
14052        const C: u32 = 42;
14053
14054        fn main() {
14055            println!("hello");
14056
14057            println!("world");
14058        }
14059        "#
14060    .unindent();
14061
14062    cx.set_state(
14063        &r#"
14064        use some::mod2;
14065
14066        const A: u32 = 42;
14067        const C: u32 = 42;
14068
14069        fn main(ˇ) {
14070            //println!("hello");
14071
14072            println!("world");
14073            //
14074            //
14075        }
14076        "#
14077        .unindent(),
14078    );
14079
14080    cx.set_head_text(&diff_base);
14081    executor.run_until_parked();
14082
14083    cx.update_editor(|editor, window, cx| {
14084        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14085    });
14086    executor.run_until_parked();
14087    cx.assert_state_with_diff(
14088        r#"
14089        - use some::mod1;
14090          use some::mod2;
14091
14092          const A: u32 = 42;
14093        - const B: u32 = 42;
14094          const C: u32 = 42;
14095
14096          fn main(ˇ) {
14097        -     println!("hello");
14098        +     //println!("hello");
14099
14100              println!("world");
14101        +     //
14102        +     //
14103          }
14104        "#
14105        .unindent(),
14106    );
14107
14108    cx.set_head_text("new diff base!");
14109    executor.run_until_parked();
14110    cx.assert_state_with_diff(
14111        r#"
14112        - new diff base!
14113        + use some::mod2;
14114        +
14115        + const A: u32 = 42;
14116        + const C: u32 = 42;
14117        +
14118        + fn main(ˇ) {
14119        +     //println!("hello");
14120        +
14121        +     println!("world");
14122        +     //
14123        +     //
14124        + }
14125        "#
14126        .unindent(),
14127    );
14128}
14129
14130#[gpui::test]
14131async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14132    init_test(cx, |_| {});
14133
14134    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14135    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14136    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14137    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14138    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14139    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14140
14141    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14142    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14143    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14144
14145    let multi_buffer = cx.new(|cx| {
14146        let mut multibuffer = MultiBuffer::new(ReadWrite);
14147        multibuffer.push_excerpts(
14148            buffer_1.clone(),
14149            [
14150                ExcerptRange {
14151                    context: Point::new(0, 0)..Point::new(3, 0),
14152                    primary: None,
14153                },
14154                ExcerptRange {
14155                    context: Point::new(5, 0)..Point::new(7, 0),
14156                    primary: None,
14157                },
14158                ExcerptRange {
14159                    context: Point::new(9, 0)..Point::new(10, 3),
14160                    primary: None,
14161                },
14162            ],
14163            cx,
14164        );
14165        multibuffer.push_excerpts(
14166            buffer_2.clone(),
14167            [
14168                ExcerptRange {
14169                    context: Point::new(0, 0)..Point::new(3, 0),
14170                    primary: None,
14171                },
14172                ExcerptRange {
14173                    context: Point::new(5, 0)..Point::new(7, 0),
14174                    primary: None,
14175                },
14176                ExcerptRange {
14177                    context: Point::new(9, 0)..Point::new(10, 3),
14178                    primary: None,
14179                },
14180            ],
14181            cx,
14182        );
14183        multibuffer.push_excerpts(
14184            buffer_3.clone(),
14185            [
14186                ExcerptRange {
14187                    context: Point::new(0, 0)..Point::new(3, 0),
14188                    primary: None,
14189                },
14190                ExcerptRange {
14191                    context: Point::new(5, 0)..Point::new(7, 0),
14192                    primary: None,
14193                },
14194                ExcerptRange {
14195                    context: Point::new(9, 0)..Point::new(10, 3),
14196                    primary: None,
14197                },
14198            ],
14199            cx,
14200        );
14201        multibuffer
14202    });
14203
14204    let editor =
14205        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14206    editor
14207        .update(cx, |editor, _window, cx| {
14208            for (buffer, diff_base) in [
14209                (buffer_1.clone(), file_1_old),
14210                (buffer_2.clone(), file_2_old),
14211                (buffer_3.clone(), file_3_old),
14212            ] {
14213                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14214                editor
14215                    .buffer
14216                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14217            }
14218        })
14219        .unwrap();
14220
14221    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14222    cx.run_until_parked();
14223
14224    cx.assert_editor_state(
14225        &"
14226            ˇaaa
14227            ccc
14228            ddd
14229
14230            ggg
14231            hhh
14232
14233
14234            lll
14235            mmm
14236            NNN
14237
14238            qqq
14239            rrr
14240
14241            uuu
14242            111
14243            222
14244            333
14245
14246            666
14247            777
14248
14249            000
14250            !!!"
14251        .unindent(),
14252    );
14253
14254    cx.update_editor(|editor, window, cx| {
14255        editor.select_all(&SelectAll, window, cx);
14256        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14257    });
14258    cx.executor().run_until_parked();
14259
14260    cx.assert_state_with_diff(
14261        "
14262            «aaa
14263          - bbb
14264            ccc
14265            ddd
14266
14267            ggg
14268            hhh
14269
14270
14271            lll
14272            mmm
14273          - nnn
14274          + NNN
14275
14276            qqq
14277            rrr
14278
14279            uuu
14280            111
14281            222
14282            333
14283
14284          + 666
14285            777
14286
14287            000
14288            !!!ˇ»"
14289            .unindent(),
14290    );
14291}
14292
14293#[gpui::test]
14294async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14295    init_test(cx, |_| {});
14296
14297    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14298    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14299
14300    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14301    let multi_buffer = cx.new(|cx| {
14302        let mut multibuffer = MultiBuffer::new(ReadWrite);
14303        multibuffer.push_excerpts(
14304            buffer.clone(),
14305            [
14306                ExcerptRange {
14307                    context: Point::new(0, 0)..Point::new(2, 0),
14308                    primary: None,
14309                },
14310                ExcerptRange {
14311                    context: Point::new(4, 0)..Point::new(7, 0),
14312                    primary: None,
14313                },
14314                ExcerptRange {
14315                    context: Point::new(9, 0)..Point::new(10, 0),
14316                    primary: None,
14317                },
14318            ],
14319            cx,
14320        );
14321        multibuffer
14322    });
14323
14324    let editor =
14325        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14326    editor
14327        .update(cx, |editor, _window, cx| {
14328            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14329            editor
14330                .buffer
14331                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14332        })
14333        .unwrap();
14334
14335    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14336    cx.run_until_parked();
14337
14338    cx.update_editor(|editor, window, cx| {
14339        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14340    });
14341    cx.executor().run_until_parked();
14342
14343    // When the start of a hunk coincides with the start of its excerpt,
14344    // the hunk is expanded. When the start of a a hunk is earlier than
14345    // the start of its excerpt, the hunk is not expanded.
14346    cx.assert_state_with_diff(
14347        "
14348            ˇaaa
14349          - bbb
14350          + BBB
14351
14352          - ddd
14353          - eee
14354          + DDD
14355          + EEE
14356            fff
14357
14358            iii
14359        "
14360        .unindent(),
14361    );
14362}
14363
14364#[gpui::test]
14365async fn test_edits_around_expanded_insertion_hunks(
14366    executor: BackgroundExecutor,
14367    cx: &mut TestAppContext,
14368) {
14369    init_test(cx, |_| {});
14370
14371    let mut cx = EditorTestContext::new(cx).await;
14372
14373    let diff_base = r#"
14374        use some::mod1;
14375        use some::mod2;
14376
14377        const A: u32 = 42;
14378
14379        fn main() {
14380            println!("hello");
14381
14382            println!("world");
14383        }
14384        "#
14385    .unindent();
14386    executor.run_until_parked();
14387    cx.set_state(
14388        &r#"
14389        use some::mod1;
14390        use some::mod2;
14391
14392        const A: u32 = 42;
14393        const B: u32 = 42;
14394        const C: u32 = 42;
14395        ˇ
14396
14397        fn main() {
14398            println!("hello");
14399
14400            println!("world");
14401        }
14402        "#
14403        .unindent(),
14404    );
14405
14406    cx.set_head_text(&diff_base);
14407    executor.run_until_parked();
14408
14409    cx.update_editor(|editor, window, cx| {
14410        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14411    });
14412    executor.run_until_parked();
14413
14414    cx.assert_state_with_diff(
14415        r#"
14416        use some::mod1;
14417        use some::mod2;
14418
14419        const A: u32 = 42;
14420      + const B: u32 = 42;
14421      + const C: u32 = 42;
14422      + ˇ
14423
14424        fn main() {
14425            println!("hello");
14426
14427            println!("world");
14428        }
14429      "#
14430        .unindent(),
14431    );
14432
14433    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14434    executor.run_until_parked();
14435
14436    cx.assert_state_with_diff(
14437        r#"
14438        use some::mod1;
14439        use some::mod2;
14440
14441        const A: u32 = 42;
14442      + const B: u32 = 42;
14443      + const C: u32 = 42;
14444      + const D: u32 = 42;
14445      + ˇ
14446
14447        fn main() {
14448            println!("hello");
14449
14450            println!("world");
14451        }
14452      "#
14453        .unindent(),
14454    );
14455
14456    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14457    executor.run_until_parked();
14458
14459    cx.assert_state_with_diff(
14460        r#"
14461        use some::mod1;
14462        use some::mod2;
14463
14464        const A: u32 = 42;
14465      + const B: u32 = 42;
14466      + const C: u32 = 42;
14467      + const D: u32 = 42;
14468      + const E: u32 = 42;
14469      + ˇ
14470
14471        fn main() {
14472            println!("hello");
14473
14474            println!("world");
14475        }
14476      "#
14477        .unindent(),
14478    );
14479
14480    cx.update_editor(|editor, window, cx| {
14481        editor.delete_line(&DeleteLine, window, cx);
14482    });
14483    executor.run_until_parked();
14484
14485    cx.assert_state_with_diff(
14486        r#"
14487        use some::mod1;
14488        use some::mod2;
14489
14490        const A: u32 = 42;
14491      + const B: u32 = 42;
14492      + const C: u32 = 42;
14493      + const D: u32 = 42;
14494      + const E: u32 = 42;
14495        ˇ
14496        fn main() {
14497            println!("hello");
14498
14499            println!("world");
14500        }
14501      "#
14502        .unindent(),
14503    );
14504
14505    cx.update_editor(|editor, window, cx| {
14506        editor.move_up(&MoveUp, window, cx);
14507        editor.delete_line(&DeleteLine, window, cx);
14508        editor.move_up(&MoveUp, window, cx);
14509        editor.delete_line(&DeleteLine, window, cx);
14510        editor.move_up(&MoveUp, window, cx);
14511        editor.delete_line(&DeleteLine, window, cx);
14512    });
14513    executor.run_until_parked();
14514    cx.assert_state_with_diff(
14515        r#"
14516        use some::mod1;
14517        use some::mod2;
14518
14519        const A: u32 = 42;
14520      + const B: u32 = 42;
14521        ˇ
14522        fn main() {
14523            println!("hello");
14524
14525            println!("world");
14526        }
14527      "#
14528        .unindent(),
14529    );
14530
14531    cx.update_editor(|editor, window, cx| {
14532        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14533        editor.delete_line(&DeleteLine, window, cx);
14534    });
14535    executor.run_until_parked();
14536    cx.assert_state_with_diff(
14537        r#"
14538        ˇ
14539        fn main() {
14540            println!("hello");
14541
14542            println!("world");
14543        }
14544      "#
14545        .unindent(),
14546    );
14547}
14548
14549#[gpui::test]
14550async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14551    init_test(cx, |_| {});
14552
14553    let mut cx = EditorTestContext::new(cx).await;
14554    cx.set_head_text(indoc! { "
14555        one
14556        two
14557        three
14558        four
14559        five
14560        "
14561    });
14562    cx.set_state(indoc! { "
14563        one
14564        ˇthree
14565        five
14566    "});
14567    cx.run_until_parked();
14568    cx.update_editor(|editor, window, cx| {
14569        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14570    });
14571    cx.assert_state_with_diff(
14572        indoc! { "
14573        one
14574      - two
14575        ˇthree
14576      - four
14577        five
14578    "}
14579        .to_string(),
14580    );
14581    cx.update_editor(|editor, window, cx| {
14582        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14583    });
14584
14585    cx.assert_state_with_diff(
14586        indoc! { "
14587        one
14588        ˇthree
14589        five
14590    "}
14591        .to_string(),
14592    );
14593
14594    cx.set_state(indoc! { "
14595        one
14596        ˇTWO
14597        three
14598        four
14599        five
14600    "});
14601    cx.run_until_parked();
14602    cx.update_editor(|editor, window, cx| {
14603        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14604    });
14605
14606    cx.assert_state_with_diff(
14607        indoc! { "
14608            one
14609          - two
14610          + ˇTWO
14611            three
14612            four
14613            five
14614        "}
14615        .to_string(),
14616    );
14617    cx.update_editor(|editor, window, cx| {
14618        editor.move_up(&Default::default(), window, cx);
14619        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14620    });
14621    cx.assert_state_with_diff(
14622        indoc! { "
14623            one
14624            ˇTWO
14625            three
14626            four
14627            five
14628        "}
14629        .to_string(),
14630    );
14631}
14632
14633#[gpui::test]
14634async fn test_edits_around_expanded_deletion_hunks(
14635    executor: BackgroundExecutor,
14636    cx: &mut TestAppContext,
14637) {
14638    init_test(cx, |_| {});
14639
14640    let mut cx = EditorTestContext::new(cx).await;
14641
14642    let diff_base = r#"
14643        use some::mod1;
14644        use some::mod2;
14645
14646        const A: u32 = 42;
14647        const B: u32 = 42;
14648        const C: u32 = 42;
14649
14650
14651        fn main() {
14652            println!("hello");
14653
14654            println!("world");
14655        }
14656    "#
14657    .unindent();
14658    executor.run_until_parked();
14659    cx.set_state(
14660        &r#"
14661        use some::mod1;
14662        use some::mod2;
14663
14664        ˇconst B: u32 = 42;
14665        const C: u32 = 42;
14666
14667
14668        fn main() {
14669            println!("hello");
14670
14671            println!("world");
14672        }
14673        "#
14674        .unindent(),
14675    );
14676
14677    cx.set_head_text(&diff_base);
14678    executor.run_until_parked();
14679
14680    cx.update_editor(|editor, window, cx| {
14681        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14682    });
14683    executor.run_until_parked();
14684
14685    cx.assert_state_with_diff(
14686        r#"
14687        use some::mod1;
14688        use some::mod2;
14689
14690      - const A: u32 = 42;
14691        ˇconst B: u32 = 42;
14692        const C: u32 = 42;
14693
14694
14695        fn main() {
14696            println!("hello");
14697
14698            println!("world");
14699        }
14700      "#
14701        .unindent(),
14702    );
14703
14704    cx.update_editor(|editor, window, cx| {
14705        editor.delete_line(&DeleteLine, window, cx);
14706    });
14707    executor.run_until_parked();
14708    cx.assert_state_with_diff(
14709        r#"
14710        use some::mod1;
14711        use some::mod2;
14712
14713      - const A: u32 = 42;
14714      - const B: u32 = 42;
14715        ˇconst C: u32 = 42;
14716
14717
14718        fn main() {
14719            println!("hello");
14720
14721            println!("world");
14722        }
14723      "#
14724        .unindent(),
14725    );
14726
14727    cx.update_editor(|editor, window, cx| {
14728        editor.delete_line(&DeleteLine, window, cx);
14729    });
14730    executor.run_until_parked();
14731    cx.assert_state_with_diff(
14732        r#"
14733        use some::mod1;
14734        use some::mod2;
14735
14736      - const A: u32 = 42;
14737      - const B: u32 = 42;
14738      - const C: u32 = 42;
14739        ˇ
14740
14741        fn main() {
14742            println!("hello");
14743
14744            println!("world");
14745        }
14746      "#
14747        .unindent(),
14748    );
14749
14750    cx.update_editor(|editor, window, cx| {
14751        editor.handle_input("replacement", window, cx);
14752    });
14753    executor.run_until_parked();
14754    cx.assert_state_with_diff(
14755        r#"
14756        use some::mod1;
14757        use some::mod2;
14758
14759      - const A: u32 = 42;
14760      - const B: u32 = 42;
14761      - const C: u32 = 42;
14762      -
14763      + replacementˇ
14764
14765        fn main() {
14766            println!("hello");
14767
14768            println!("world");
14769        }
14770      "#
14771        .unindent(),
14772    );
14773}
14774
14775#[gpui::test]
14776async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14777    init_test(cx, |_| {});
14778
14779    let mut cx = EditorTestContext::new(cx).await;
14780
14781    let base_text = r#"
14782        one
14783        two
14784        three
14785        four
14786        five
14787    "#
14788    .unindent();
14789    executor.run_until_parked();
14790    cx.set_state(
14791        &r#"
14792        one
14793        two
14794        fˇour
14795        five
14796        "#
14797        .unindent(),
14798    );
14799
14800    cx.set_head_text(&base_text);
14801    executor.run_until_parked();
14802
14803    cx.update_editor(|editor, window, cx| {
14804        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14805    });
14806    executor.run_until_parked();
14807
14808    cx.assert_state_with_diff(
14809        r#"
14810          one
14811          two
14812        - three
14813          fˇour
14814          five
14815        "#
14816        .unindent(),
14817    );
14818
14819    cx.update_editor(|editor, window, cx| {
14820        editor.backspace(&Backspace, window, cx);
14821        editor.backspace(&Backspace, window, cx);
14822    });
14823    executor.run_until_parked();
14824    cx.assert_state_with_diff(
14825        r#"
14826          one
14827          two
14828        - threeˇ
14829        - four
14830        + our
14831          five
14832        "#
14833        .unindent(),
14834    );
14835}
14836
14837#[gpui::test]
14838async fn test_edit_after_expanded_modification_hunk(
14839    executor: BackgroundExecutor,
14840    cx: &mut TestAppContext,
14841) {
14842    init_test(cx, |_| {});
14843
14844    let mut cx = EditorTestContext::new(cx).await;
14845
14846    let diff_base = r#"
14847        use some::mod1;
14848        use some::mod2;
14849
14850        const A: u32 = 42;
14851        const B: u32 = 42;
14852        const C: u32 = 42;
14853        const D: u32 = 42;
14854
14855
14856        fn main() {
14857            println!("hello");
14858
14859            println!("world");
14860        }"#
14861    .unindent();
14862
14863    cx.set_state(
14864        &r#"
14865        use some::mod1;
14866        use some::mod2;
14867
14868        const A: u32 = 42;
14869        const B: u32 = 42;
14870        const C: u32 = 43ˇ
14871        const D: u32 = 42;
14872
14873
14874        fn main() {
14875            println!("hello");
14876
14877            println!("world");
14878        }"#
14879        .unindent(),
14880    );
14881
14882    cx.set_head_text(&diff_base);
14883    executor.run_until_parked();
14884    cx.update_editor(|editor, window, cx| {
14885        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14886    });
14887    executor.run_until_parked();
14888
14889    cx.assert_state_with_diff(
14890        r#"
14891        use some::mod1;
14892        use some::mod2;
14893
14894        const A: u32 = 42;
14895        const B: u32 = 42;
14896      - const C: u32 = 42;
14897      + const C: u32 = 43ˇ
14898        const D: u32 = 42;
14899
14900
14901        fn main() {
14902            println!("hello");
14903
14904            println!("world");
14905        }"#
14906        .unindent(),
14907    );
14908
14909    cx.update_editor(|editor, window, cx| {
14910        editor.handle_input("\nnew_line\n", window, cx);
14911    });
14912    executor.run_until_parked();
14913
14914    cx.assert_state_with_diff(
14915        r#"
14916        use some::mod1;
14917        use some::mod2;
14918
14919        const A: u32 = 42;
14920        const B: u32 = 42;
14921      - const C: u32 = 42;
14922      + const C: u32 = 43
14923      + new_line
14924      + ˇ
14925        const D: u32 = 42;
14926
14927
14928        fn main() {
14929            println!("hello");
14930
14931            println!("world");
14932        }"#
14933        .unindent(),
14934    );
14935}
14936
14937#[gpui::test]
14938async fn test_stage_and_unstage_added_file_hunk(
14939    executor: BackgroundExecutor,
14940    cx: &mut TestAppContext,
14941) {
14942    init_test(cx, |_| {});
14943
14944    let mut cx = EditorTestContext::new(cx).await;
14945    cx.update_editor(|editor, _, cx| {
14946        editor.set_expand_all_diff_hunks(cx);
14947    });
14948
14949    let working_copy = r#"
14950            ˇfn main() {
14951                println!("hello, world!");
14952            }
14953        "#
14954    .unindent();
14955
14956    cx.set_state(&working_copy);
14957    executor.run_until_parked();
14958
14959    cx.assert_state_with_diff(
14960        r#"
14961            + ˇfn main() {
14962            +     println!("hello, world!");
14963            + }
14964        "#
14965        .unindent(),
14966    );
14967    cx.assert_index_text(None);
14968
14969    cx.update_editor(|editor, window, cx| {
14970        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14971    });
14972    executor.run_until_parked();
14973    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14974    cx.assert_state_with_diff(
14975        r#"
14976            + ˇfn main() {
14977            +     println!("hello, world!");
14978            + }
14979        "#
14980        .unindent(),
14981    );
14982
14983    cx.update_editor(|editor, window, cx| {
14984        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14985    });
14986    executor.run_until_parked();
14987    cx.assert_index_text(None);
14988}
14989
14990async fn setup_indent_guides_editor(
14991    text: &str,
14992    cx: &mut TestAppContext,
14993) -> (BufferId, EditorTestContext) {
14994    init_test(cx, |_| {});
14995
14996    let mut cx = EditorTestContext::new(cx).await;
14997
14998    let buffer_id = cx.update_editor(|editor, window, cx| {
14999        editor.set_text(text, window, cx);
15000        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15001
15002        buffer_ids[0]
15003    });
15004
15005    (buffer_id, cx)
15006}
15007
15008fn assert_indent_guides(
15009    range: Range<u32>,
15010    expected: Vec<IndentGuide>,
15011    active_indices: Option<Vec<usize>>,
15012    cx: &mut EditorTestContext,
15013) {
15014    let indent_guides = cx.update_editor(|editor, window, cx| {
15015        let snapshot = editor.snapshot(window, cx).display_snapshot;
15016        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15017            editor,
15018            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15019            true,
15020            &snapshot,
15021            cx,
15022        );
15023
15024        indent_guides.sort_by(|a, b| {
15025            a.depth.cmp(&b.depth).then(
15026                a.start_row
15027                    .cmp(&b.start_row)
15028                    .then(a.end_row.cmp(&b.end_row)),
15029            )
15030        });
15031        indent_guides
15032    });
15033
15034    if let Some(expected) = active_indices {
15035        let active_indices = cx.update_editor(|editor, window, cx| {
15036            let snapshot = editor.snapshot(window, cx).display_snapshot;
15037            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15038        });
15039
15040        assert_eq!(
15041            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15042            expected,
15043            "Active indent guide indices do not match"
15044        );
15045    }
15046
15047    assert_eq!(indent_guides, expected, "Indent guides do not match");
15048}
15049
15050fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15051    IndentGuide {
15052        buffer_id,
15053        start_row: MultiBufferRow(start_row),
15054        end_row: MultiBufferRow(end_row),
15055        depth,
15056        tab_size: 4,
15057        settings: IndentGuideSettings {
15058            enabled: true,
15059            line_width: 1,
15060            active_line_width: 1,
15061            ..Default::default()
15062        },
15063    }
15064}
15065
15066#[gpui::test]
15067async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15068    let (buffer_id, mut cx) = setup_indent_guides_editor(
15069        &"
15070    fn main() {
15071        let a = 1;
15072    }"
15073        .unindent(),
15074        cx,
15075    )
15076    .await;
15077
15078    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15079}
15080
15081#[gpui::test]
15082async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15083    let (buffer_id, mut cx) = setup_indent_guides_editor(
15084        &"
15085    fn main() {
15086        let a = 1;
15087        let b = 2;
15088    }"
15089        .unindent(),
15090        cx,
15091    )
15092    .await;
15093
15094    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15095}
15096
15097#[gpui::test]
15098async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15099    let (buffer_id, mut cx) = setup_indent_guides_editor(
15100        &"
15101    fn main() {
15102        let a = 1;
15103        if a == 3 {
15104            let b = 2;
15105        } else {
15106            let c = 3;
15107        }
15108    }"
15109        .unindent(),
15110        cx,
15111    )
15112    .await;
15113
15114    assert_indent_guides(
15115        0..8,
15116        vec![
15117            indent_guide(buffer_id, 1, 6, 0),
15118            indent_guide(buffer_id, 3, 3, 1),
15119            indent_guide(buffer_id, 5, 5, 1),
15120        ],
15121        None,
15122        &mut cx,
15123    );
15124}
15125
15126#[gpui::test]
15127async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15128    let (buffer_id, mut cx) = setup_indent_guides_editor(
15129        &"
15130    fn main() {
15131        let a = 1;
15132            let b = 2;
15133        let c = 3;
15134    }"
15135        .unindent(),
15136        cx,
15137    )
15138    .await;
15139
15140    assert_indent_guides(
15141        0..5,
15142        vec![
15143            indent_guide(buffer_id, 1, 3, 0),
15144            indent_guide(buffer_id, 2, 2, 1),
15145        ],
15146        None,
15147        &mut cx,
15148    );
15149}
15150
15151#[gpui::test]
15152async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15153    let (buffer_id, mut cx) = setup_indent_guides_editor(
15154        &"
15155        fn main() {
15156            let a = 1;
15157
15158            let c = 3;
15159        }"
15160        .unindent(),
15161        cx,
15162    )
15163    .await;
15164
15165    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15166}
15167
15168#[gpui::test]
15169async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15170    let (buffer_id, mut cx) = setup_indent_guides_editor(
15171        &"
15172        fn main() {
15173            let a = 1;
15174
15175            let c = 3;
15176
15177            if a == 3 {
15178                let b = 2;
15179            } else {
15180                let c = 3;
15181            }
15182        }"
15183        .unindent(),
15184        cx,
15185    )
15186    .await;
15187
15188    assert_indent_guides(
15189        0..11,
15190        vec![
15191            indent_guide(buffer_id, 1, 9, 0),
15192            indent_guide(buffer_id, 6, 6, 1),
15193            indent_guide(buffer_id, 8, 8, 1),
15194        ],
15195        None,
15196        &mut cx,
15197    );
15198}
15199
15200#[gpui::test]
15201async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15202    let (buffer_id, mut cx) = setup_indent_guides_editor(
15203        &"
15204        fn main() {
15205            let a = 1;
15206
15207            let c = 3;
15208
15209            if a == 3 {
15210                let b = 2;
15211            } else {
15212                let c = 3;
15213            }
15214        }"
15215        .unindent(),
15216        cx,
15217    )
15218    .await;
15219
15220    assert_indent_guides(
15221        1..11,
15222        vec![
15223            indent_guide(buffer_id, 1, 9, 0),
15224            indent_guide(buffer_id, 6, 6, 1),
15225            indent_guide(buffer_id, 8, 8, 1),
15226        ],
15227        None,
15228        &mut cx,
15229    );
15230}
15231
15232#[gpui::test]
15233async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15234    let (buffer_id, mut cx) = setup_indent_guides_editor(
15235        &"
15236        fn main() {
15237            let a = 1;
15238
15239            let c = 3;
15240
15241            if a == 3 {
15242                let b = 2;
15243            } else {
15244                let c = 3;
15245            }
15246        }"
15247        .unindent(),
15248        cx,
15249    )
15250    .await;
15251
15252    assert_indent_guides(
15253        1..10,
15254        vec![
15255            indent_guide(buffer_id, 1, 9, 0),
15256            indent_guide(buffer_id, 6, 6, 1),
15257            indent_guide(buffer_id, 8, 8, 1),
15258        ],
15259        None,
15260        &mut cx,
15261    );
15262}
15263
15264#[gpui::test]
15265async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15266    let (buffer_id, mut cx) = setup_indent_guides_editor(
15267        &"
15268        block1
15269            block2
15270                block3
15271                    block4
15272            block2
15273        block1
15274        block1"
15275            .unindent(),
15276        cx,
15277    )
15278    .await;
15279
15280    assert_indent_guides(
15281        1..10,
15282        vec![
15283            indent_guide(buffer_id, 1, 4, 0),
15284            indent_guide(buffer_id, 2, 3, 1),
15285            indent_guide(buffer_id, 3, 3, 2),
15286        ],
15287        None,
15288        &mut cx,
15289    );
15290}
15291
15292#[gpui::test]
15293async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15294    let (buffer_id, mut cx) = setup_indent_guides_editor(
15295        &"
15296        block1
15297            block2
15298                block3
15299
15300        block1
15301        block1"
15302            .unindent(),
15303        cx,
15304    )
15305    .await;
15306
15307    assert_indent_guides(
15308        0..6,
15309        vec![
15310            indent_guide(buffer_id, 1, 2, 0),
15311            indent_guide(buffer_id, 2, 2, 1),
15312        ],
15313        None,
15314        &mut cx,
15315    );
15316}
15317
15318#[gpui::test]
15319async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15320    let (buffer_id, mut cx) = setup_indent_guides_editor(
15321        &"
15322        block1
15323
15324
15325
15326            block2
15327        "
15328        .unindent(),
15329        cx,
15330    )
15331    .await;
15332
15333    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15334}
15335
15336#[gpui::test]
15337async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15338    let (buffer_id, mut cx) = setup_indent_guides_editor(
15339        &"
15340        def a:
15341        \tb = 3
15342        \tif True:
15343        \t\tc = 4
15344        \t\td = 5
15345        \tprint(b)
15346        "
15347        .unindent(),
15348        cx,
15349    )
15350    .await;
15351
15352    assert_indent_guides(
15353        0..6,
15354        vec![
15355            indent_guide(buffer_id, 1, 6, 0),
15356            indent_guide(buffer_id, 3, 4, 1),
15357        ],
15358        None,
15359        &mut cx,
15360    );
15361}
15362
15363#[gpui::test]
15364async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15365    let (buffer_id, mut cx) = setup_indent_guides_editor(
15366        &"
15367    fn main() {
15368        let a = 1;
15369    }"
15370        .unindent(),
15371        cx,
15372    )
15373    .await;
15374
15375    cx.update_editor(|editor, window, cx| {
15376        editor.change_selections(None, window, cx, |s| {
15377            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15378        });
15379    });
15380
15381    assert_indent_guides(
15382        0..3,
15383        vec![indent_guide(buffer_id, 1, 1, 0)],
15384        Some(vec![0]),
15385        &mut cx,
15386    );
15387}
15388
15389#[gpui::test]
15390async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15391    let (buffer_id, mut cx) = setup_indent_guides_editor(
15392        &"
15393    fn main() {
15394        if 1 == 2 {
15395            let a = 1;
15396        }
15397    }"
15398        .unindent(),
15399        cx,
15400    )
15401    .await;
15402
15403    cx.update_editor(|editor, window, cx| {
15404        editor.change_selections(None, window, cx, |s| {
15405            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15406        });
15407    });
15408
15409    assert_indent_guides(
15410        0..4,
15411        vec![
15412            indent_guide(buffer_id, 1, 3, 0),
15413            indent_guide(buffer_id, 2, 2, 1),
15414        ],
15415        Some(vec![1]),
15416        &mut cx,
15417    );
15418
15419    cx.update_editor(|editor, window, cx| {
15420        editor.change_selections(None, window, cx, |s| {
15421            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15422        });
15423    });
15424
15425    assert_indent_guides(
15426        0..4,
15427        vec![
15428            indent_guide(buffer_id, 1, 3, 0),
15429            indent_guide(buffer_id, 2, 2, 1),
15430        ],
15431        Some(vec![1]),
15432        &mut cx,
15433    );
15434
15435    cx.update_editor(|editor, window, cx| {
15436        editor.change_selections(None, window, cx, |s| {
15437            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15438        });
15439    });
15440
15441    assert_indent_guides(
15442        0..4,
15443        vec![
15444            indent_guide(buffer_id, 1, 3, 0),
15445            indent_guide(buffer_id, 2, 2, 1),
15446        ],
15447        Some(vec![0]),
15448        &mut cx,
15449    );
15450}
15451
15452#[gpui::test]
15453async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15454    let (buffer_id, mut cx) = setup_indent_guides_editor(
15455        &"
15456    fn main() {
15457        let a = 1;
15458
15459        let b = 2;
15460    }"
15461        .unindent(),
15462        cx,
15463    )
15464    .await;
15465
15466    cx.update_editor(|editor, window, cx| {
15467        editor.change_selections(None, window, cx, |s| {
15468            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15469        });
15470    });
15471
15472    assert_indent_guides(
15473        0..5,
15474        vec![indent_guide(buffer_id, 1, 3, 0)],
15475        Some(vec![0]),
15476        &mut cx,
15477    );
15478}
15479
15480#[gpui::test]
15481async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15482    let (buffer_id, mut cx) = setup_indent_guides_editor(
15483        &"
15484    def m:
15485        a = 1
15486        pass"
15487            .unindent(),
15488        cx,
15489    )
15490    .await;
15491
15492    cx.update_editor(|editor, window, cx| {
15493        editor.change_selections(None, window, cx, |s| {
15494            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15495        });
15496    });
15497
15498    assert_indent_guides(
15499        0..3,
15500        vec![indent_guide(buffer_id, 1, 2, 0)],
15501        Some(vec![0]),
15502        &mut cx,
15503    );
15504}
15505
15506#[gpui::test]
15507async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15508    init_test(cx, |_| {});
15509    let mut cx = EditorTestContext::new(cx).await;
15510    let text = indoc! {
15511        "
15512        impl A {
15513            fn b() {
15514                0;
15515                3;
15516                5;
15517                6;
15518                7;
15519            }
15520        }
15521        "
15522    };
15523    let base_text = indoc! {
15524        "
15525        impl A {
15526            fn b() {
15527                0;
15528                1;
15529                2;
15530                3;
15531                4;
15532            }
15533            fn c() {
15534                5;
15535                6;
15536                7;
15537            }
15538        }
15539        "
15540    };
15541
15542    cx.update_editor(|editor, window, cx| {
15543        editor.set_text(text, window, cx);
15544
15545        editor.buffer().update(cx, |multibuffer, cx| {
15546            let buffer = multibuffer.as_singleton().unwrap();
15547            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15548
15549            multibuffer.set_all_diff_hunks_expanded(cx);
15550            multibuffer.add_diff(diff, cx);
15551
15552            buffer.read(cx).remote_id()
15553        })
15554    });
15555    cx.run_until_parked();
15556
15557    cx.assert_state_with_diff(
15558        indoc! { "
15559          impl A {
15560              fn b() {
15561                  0;
15562        -         1;
15563        -         2;
15564                  3;
15565        -         4;
15566        -     }
15567        -     fn c() {
15568                  5;
15569                  6;
15570                  7;
15571              }
15572          }
15573          ˇ"
15574        }
15575        .to_string(),
15576    );
15577
15578    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15579        editor
15580            .snapshot(window, cx)
15581            .buffer_snapshot
15582            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15583            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15584            .collect::<Vec<_>>()
15585    });
15586    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15587    assert_eq!(
15588        actual_guides,
15589        vec![
15590            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15591            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15592            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15593        ]
15594    );
15595}
15596
15597#[gpui::test]
15598async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15599    init_test(cx, |_| {});
15600    let mut cx = EditorTestContext::new(cx).await;
15601
15602    let diff_base = r#"
15603        a
15604        b
15605        c
15606        "#
15607    .unindent();
15608
15609    cx.set_state(
15610        &r#"
15611        ˇA
15612        b
15613        C
15614        "#
15615        .unindent(),
15616    );
15617    cx.set_head_text(&diff_base);
15618    cx.update_editor(|editor, window, cx| {
15619        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15620    });
15621    executor.run_until_parked();
15622
15623    let both_hunks_expanded = r#"
15624        - a
15625        + ˇA
15626          b
15627        - c
15628        + C
15629        "#
15630    .unindent();
15631
15632    cx.assert_state_with_diff(both_hunks_expanded.clone());
15633
15634    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15635        let snapshot = editor.snapshot(window, cx);
15636        let hunks = editor
15637            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15638            .collect::<Vec<_>>();
15639        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15640        let buffer_id = hunks[0].buffer_id;
15641        hunks
15642            .into_iter()
15643            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15644            .collect::<Vec<_>>()
15645    });
15646    assert_eq!(hunk_ranges.len(), 2);
15647
15648    cx.update_editor(|editor, _, cx| {
15649        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15650    });
15651    executor.run_until_parked();
15652
15653    let second_hunk_expanded = r#"
15654          ˇA
15655          b
15656        - c
15657        + C
15658        "#
15659    .unindent();
15660
15661    cx.assert_state_with_diff(second_hunk_expanded);
15662
15663    cx.update_editor(|editor, _, cx| {
15664        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15665    });
15666    executor.run_until_parked();
15667
15668    cx.assert_state_with_diff(both_hunks_expanded.clone());
15669
15670    cx.update_editor(|editor, _, cx| {
15671        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15672    });
15673    executor.run_until_parked();
15674
15675    let first_hunk_expanded = r#"
15676        - a
15677        + ˇA
15678          b
15679          C
15680        "#
15681    .unindent();
15682
15683    cx.assert_state_with_diff(first_hunk_expanded);
15684
15685    cx.update_editor(|editor, _, cx| {
15686        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15687    });
15688    executor.run_until_parked();
15689
15690    cx.assert_state_with_diff(both_hunks_expanded);
15691
15692    cx.set_state(
15693        &r#"
15694        ˇA
15695        b
15696        "#
15697        .unindent(),
15698    );
15699    cx.run_until_parked();
15700
15701    // TODO this cursor position seems bad
15702    cx.assert_state_with_diff(
15703        r#"
15704        - ˇa
15705        + A
15706          b
15707        "#
15708        .unindent(),
15709    );
15710
15711    cx.update_editor(|editor, window, cx| {
15712        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15713    });
15714
15715    cx.assert_state_with_diff(
15716        r#"
15717            - ˇa
15718            + A
15719              b
15720            - c
15721            "#
15722        .unindent(),
15723    );
15724
15725    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15726        let snapshot = editor.snapshot(window, cx);
15727        let hunks = editor
15728            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15729            .collect::<Vec<_>>();
15730        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15731        let buffer_id = hunks[0].buffer_id;
15732        hunks
15733            .into_iter()
15734            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15735            .collect::<Vec<_>>()
15736    });
15737    assert_eq!(hunk_ranges.len(), 2);
15738
15739    cx.update_editor(|editor, _, cx| {
15740        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15741    });
15742    executor.run_until_parked();
15743
15744    cx.assert_state_with_diff(
15745        r#"
15746        - ˇa
15747        + A
15748          b
15749        "#
15750        .unindent(),
15751    );
15752}
15753
15754#[gpui::test]
15755async fn test_toggle_deletion_hunk_at_start_of_file(
15756    executor: BackgroundExecutor,
15757    cx: &mut TestAppContext,
15758) {
15759    init_test(cx, |_| {});
15760    let mut cx = EditorTestContext::new(cx).await;
15761
15762    let diff_base = r#"
15763        a
15764        b
15765        c
15766        "#
15767    .unindent();
15768
15769    cx.set_state(
15770        &r#"
15771        ˇb
15772        c
15773        "#
15774        .unindent(),
15775    );
15776    cx.set_head_text(&diff_base);
15777    cx.update_editor(|editor, window, cx| {
15778        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15779    });
15780    executor.run_until_parked();
15781
15782    let hunk_expanded = r#"
15783        - a
15784          ˇb
15785          c
15786        "#
15787    .unindent();
15788
15789    cx.assert_state_with_diff(hunk_expanded.clone());
15790
15791    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15792        let snapshot = editor.snapshot(window, cx);
15793        let hunks = editor
15794            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15795            .collect::<Vec<_>>();
15796        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15797        let buffer_id = hunks[0].buffer_id;
15798        hunks
15799            .into_iter()
15800            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15801            .collect::<Vec<_>>()
15802    });
15803    assert_eq!(hunk_ranges.len(), 1);
15804
15805    cx.update_editor(|editor, _, cx| {
15806        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15807    });
15808    executor.run_until_parked();
15809
15810    let hunk_collapsed = r#"
15811          ˇb
15812          c
15813        "#
15814    .unindent();
15815
15816    cx.assert_state_with_diff(hunk_collapsed);
15817
15818    cx.update_editor(|editor, _, cx| {
15819        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15820    });
15821    executor.run_until_parked();
15822
15823    cx.assert_state_with_diff(hunk_expanded.clone());
15824}
15825
15826#[gpui::test]
15827async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15828    init_test(cx, |_| {});
15829
15830    let fs = FakeFs::new(cx.executor());
15831    fs.insert_tree(
15832        path!("/test"),
15833        json!({
15834            ".git": {},
15835            "file-1": "ONE\n",
15836            "file-2": "TWO\n",
15837            "file-3": "THREE\n",
15838        }),
15839    )
15840    .await;
15841
15842    fs.set_head_for_repo(
15843        path!("/test/.git").as_ref(),
15844        &[
15845            ("file-1".into(), "one\n".into()),
15846            ("file-2".into(), "two\n".into()),
15847            ("file-3".into(), "three\n".into()),
15848        ],
15849    );
15850
15851    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15852    let mut buffers = vec![];
15853    for i in 1..=3 {
15854        let buffer = project
15855            .update(cx, |project, cx| {
15856                let path = format!(path!("/test/file-{}"), i);
15857                project.open_local_buffer(path, cx)
15858            })
15859            .await
15860            .unwrap();
15861        buffers.push(buffer);
15862    }
15863
15864    let multibuffer = cx.new(|cx| {
15865        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15866        multibuffer.set_all_diff_hunks_expanded(cx);
15867        for buffer in &buffers {
15868            let snapshot = buffer.read(cx).snapshot();
15869            multibuffer.set_excerpts_for_path(
15870                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15871                buffer.clone(),
15872                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15873                DEFAULT_MULTIBUFFER_CONTEXT,
15874                cx,
15875            );
15876        }
15877        multibuffer
15878    });
15879
15880    let editor = cx.add_window(|window, cx| {
15881        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15882    });
15883    cx.run_until_parked();
15884
15885    let snapshot = editor
15886        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15887        .unwrap();
15888    let hunks = snapshot
15889        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15890        .map(|hunk| match hunk {
15891            DisplayDiffHunk::Unfolded {
15892                display_row_range, ..
15893            } => display_row_range,
15894            DisplayDiffHunk::Folded { .. } => unreachable!(),
15895        })
15896        .collect::<Vec<_>>();
15897    assert_eq!(
15898        hunks,
15899        [
15900            DisplayRow(2)..DisplayRow(4),
15901            DisplayRow(7)..DisplayRow(9),
15902            DisplayRow(12)..DisplayRow(14),
15903        ]
15904    );
15905}
15906
15907#[gpui::test]
15908async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15909    init_test(cx, |_| {});
15910
15911    let mut cx = EditorTestContext::new(cx).await;
15912    cx.set_head_text(indoc! { "
15913        one
15914        two
15915        three
15916        four
15917        five
15918        "
15919    });
15920    cx.set_index_text(indoc! { "
15921        one
15922        two
15923        three
15924        four
15925        five
15926        "
15927    });
15928    cx.set_state(indoc! {"
15929        one
15930        TWO
15931        ˇTHREE
15932        FOUR
15933        five
15934    "});
15935    cx.run_until_parked();
15936    cx.update_editor(|editor, window, cx| {
15937        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15938    });
15939    cx.run_until_parked();
15940    cx.assert_index_text(Some(indoc! {"
15941        one
15942        TWO
15943        THREE
15944        FOUR
15945        five
15946    "}));
15947    cx.set_state(indoc! { "
15948        one
15949        TWO
15950        ˇTHREE-HUNDRED
15951        FOUR
15952        five
15953    "});
15954    cx.run_until_parked();
15955    cx.update_editor(|editor, window, cx| {
15956        let snapshot = editor.snapshot(window, cx);
15957        let hunks = editor
15958            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15959            .collect::<Vec<_>>();
15960        assert_eq!(hunks.len(), 1);
15961        assert_eq!(
15962            hunks[0].status(),
15963            DiffHunkStatus {
15964                kind: DiffHunkStatusKind::Modified,
15965                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15966            }
15967        );
15968
15969        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15970    });
15971    cx.run_until_parked();
15972    cx.assert_index_text(Some(indoc! {"
15973        one
15974        TWO
15975        THREE-HUNDRED
15976        FOUR
15977        five
15978    "}));
15979}
15980
15981#[gpui::test]
15982fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15983    init_test(cx, |_| {});
15984
15985    let editor = cx.add_window(|window, cx| {
15986        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15987        build_editor(buffer, window, cx)
15988    });
15989
15990    let render_args = Arc::new(Mutex::new(None));
15991    let snapshot = editor
15992        .update(cx, |editor, window, cx| {
15993            let snapshot = editor.buffer().read(cx).snapshot(cx);
15994            let range =
15995                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15996
15997            struct RenderArgs {
15998                row: MultiBufferRow,
15999                folded: bool,
16000                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16001            }
16002
16003            let crease = Crease::inline(
16004                range,
16005                FoldPlaceholder::test(),
16006                {
16007                    let toggle_callback = render_args.clone();
16008                    move |row, folded, callback, _window, _cx| {
16009                        *toggle_callback.lock() = Some(RenderArgs {
16010                            row,
16011                            folded,
16012                            callback,
16013                        });
16014                        div()
16015                    }
16016                },
16017                |_row, _folded, _window, _cx| div(),
16018            );
16019
16020            editor.insert_creases(Some(crease), cx);
16021            let snapshot = editor.snapshot(window, cx);
16022            let _div = snapshot.render_crease_toggle(
16023                MultiBufferRow(1),
16024                false,
16025                cx.entity().clone(),
16026                window,
16027                cx,
16028            );
16029            snapshot
16030        })
16031        .unwrap();
16032
16033    let render_args = render_args.lock().take().unwrap();
16034    assert_eq!(render_args.row, MultiBufferRow(1));
16035    assert!(!render_args.folded);
16036    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16037
16038    cx.update_window(*editor, |_, window, cx| {
16039        (render_args.callback)(true, window, cx)
16040    })
16041    .unwrap();
16042    let snapshot = editor
16043        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16044        .unwrap();
16045    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16046
16047    cx.update_window(*editor, |_, window, cx| {
16048        (render_args.callback)(false, window, cx)
16049    })
16050    .unwrap();
16051    let snapshot = editor
16052        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16053        .unwrap();
16054    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16055}
16056
16057#[gpui::test]
16058async fn test_input_text(cx: &mut TestAppContext) {
16059    init_test(cx, |_| {});
16060    let mut cx = EditorTestContext::new(cx).await;
16061
16062    cx.set_state(
16063        &r#"ˇone
16064        two
16065
16066        three
16067        fourˇ
16068        five
16069
16070        siˇx"#
16071            .unindent(),
16072    );
16073
16074    cx.dispatch_action(HandleInput(String::new()));
16075    cx.assert_editor_state(
16076        &r#"ˇone
16077        two
16078
16079        three
16080        fourˇ
16081        five
16082
16083        siˇx"#
16084            .unindent(),
16085    );
16086
16087    cx.dispatch_action(HandleInput("AAAA".to_string()));
16088    cx.assert_editor_state(
16089        &r#"AAAAˇone
16090        two
16091
16092        three
16093        fourAAAAˇ
16094        five
16095
16096        siAAAAˇx"#
16097            .unindent(),
16098    );
16099}
16100
16101#[gpui::test]
16102async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16103    init_test(cx, |_| {});
16104
16105    let mut cx = EditorTestContext::new(cx).await;
16106    cx.set_state(
16107        r#"let foo = 1;
16108let foo = 2;
16109let foo = 3;
16110let fooˇ = 4;
16111let foo = 5;
16112let foo = 6;
16113let foo = 7;
16114let foo = 8;
16115let foo = 9;
16116let foo = 10;
16117let foo = 11;
16118let foo = 12;
16119let foo = 13;
16120let foo = 14;
16121let foo = 15;"#,
16122    );
16123
16124    cx.update_editor(|e, window, cx| {
16125        assert_eq!(
16126            e.next_scroll_position,
16127            NextScrollCursorCenterTopBottom::Center,
16128            "Default next scroll direction is center",
16129        );
16130
16131        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16132        assert_eq!(
16133            e.next_scroll_position,
16134            NextScrollCursorCenterTopBottom::Top,
16135            "After center, next scroll direction should be top",
16136        );
16137
16138        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16139        assert_eq!(
16140            e.next_scroll_position,
16141            NextScrollCursorCenterTopBottom::Bottom,
16142            "After top, next scroll direction should be bottom",
16143        );
16144
16145        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16146        assert_eq!(
16147            e.next_scroll_position,
16148            NextScrollCursorCenterTopBottom::Center,
16149            "After bottom, scrolling should start over",
16150        );
16151
16152        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16153        assert_eq!(
16154            e.next_scroll_position,
16155            NextScrollCursorCenterTopBottom::Top,
16156            "Scrolling continues if retriggered fast enough"
16157        );
16158    });
16159
16160    cx.executor()
16161        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16162    cx.executor().run_until_parked();
16163    cx.update_editor(|e, _, _| {
16164        assert_eq!(
16165            e.next_scroll_position,
16166            NextScrollCursorCenterTopBottom::Center,
16167            "If scrolling is not triggered fast enough, it should reset"
16168        );
16169    });
16170}
16171
16172#[gpui::test]
16173async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16174    init_test(cx, |_| {});
16175    let mut cx = EditorLspTestContext::new_rust(
16176        lsp::ServerCapabilities {
16177            definition_provider: Some(lsp::OneOf::Left(true)),
16178            references_provider: Some(lsp::OneOf::Left(true)),
16179            ..lsp::ServerCapabilities::default()
16180        },
16181        cx,
16182    )
16183    .await;
16184
16185    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16186        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16187            move |params, _| async move {
16188                if empty_go_to_definition {
16189                    Ok(None)
16190                } else {
16191                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16192                        uri: params.text_document_position_params.text_document.uri,
16193                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16194                    })))
16195                }
16196            },
16197        );
16198        let references =
16199            cx.lsp
16200                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16201                    Ok(Some(vec![lsp::Location {
16202                        uri: params.text_document_position.text_document.uri,
16203                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16204                    }]))
16205                });
16206        (go_to_definition, references)
16207    };
16208
16209    cx.set_state(
16210        &r#"fn one() {
16211            let mut a = ˇtwo();
16212        }
16213
16214        fn two() {}"#
16215            .unindent(),
16216    );
16217    set_up_lsp_handlers(false, &mut cx);
16218    let navigated = cx
16219        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16220        .await
16221        .expect("Failed to navigate to definition");
16222    assert_eq!(
16223        navigated,
16224        Navigated::Yes,
16225        "Should have navigated to definition from the GetDefinition response"
16226    );
16227    cx.assert_editor_state(
16228        &r#"fn one() {
16229            let mut a = two();
16230        }
16231
16232        fn «twoˇ»() {}"#
16233            .unindent(),
16234    );
16235
16236    let editors = cx.update_workspace(|workspace, _, cx| {
16237        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16238    });
16239    cx.update_editor(|_, _, test_editor_cx| {
16240        assert_eq!(
16241            editors.len(),
16242            1,
16243            "Initially, only one, test, editor should be open in the workspace"
16244        );
16245        assert_eq!(
16246            test_editor_cx.entity(),
16247            editors.last().expect("Asserted len is 1").clone()
16248        );
16249    });
16250
16251    set_up_lsp_handlers(true, &mut cx);
16252    let navigated = cx
16253        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16254        .await
16255        .expect("Failed to navigate to lookup references");
16256    assert_eq!(
16257        navigated,
16258        Navigated::Yes,
16259        "Should have navigated to references as a fallback after empty GoToDefinition response"
16260    );
16261    // We should not change the selections in the existing file,
16262    // if opening another milti buffer with the references
16263    cx.assert_editor_state(
16264        &r#"fn one() {
16265            let mut a = two();
16266        }
16267
16268        fn «twoˇ»() {}"#
16269            .unindent(),
16270    );
16271    let editors = cx.update_workspace(|workspace, _, cx| {
16272        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16273    });
16274    cx.update_editor(|_, _, test_editor_cx| {
16275        assert_eq!(
16276            editors.len(),
16277            2,
16278            "After falling back to references search, we open a new editor with the results"
16279        );
16280        let references_fallback_text = editors
16281            .into_iter()
16282            .find(|new_editor| *new_editor != test_editor_cx.entity())
16283            .expect("Should have one non-test editor now")
16284            .read(test_editor_cx)
16285            .text(test_editor_cx);
16286        assert_eq!(
16287            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16288            "Should use the range from the references response and not the GoToDefinition one"
16289        );
16290    });
16291}
16292
16293#[gpui::test]
16294async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16295    init_test(cx, |_| {});
16296
16297    let language = Arc::new(Language::new(
16298        LanguageConfig::default(),
16299        Some(tree_sitter_rust::LANGUAGE.into()),
16300    ));
16301
16302    let text = r#"
16303        #[cfg(test)]
16304        mod tests() {
16305            #[test]
16306            fn runnable_1() {
16307                let a = 1;
16308            }
16309
16310            #[test]
16311            fn runnable_2() {
16312                let a = 1;
16313                let b = 2;
16314            }
16315        }
16316    "#
16317    .unindent();
16318
16319    let fs = FakeFs::new(cx.executor());
16320    fs.insert_file("/file.rs", Default::default()).await;
16321
16322    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16323    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16324    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16325    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16326    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16327
16328    let editor = cx.new_window_entity(|window, cx| {
16329        Editor::new(
16330            EditorMode::Full,
16331            multi_buffer,
16332            Some(project.clone()),
16333            window,
16334            cx,
16335        )
16336    });
16337
16338    editor.update_in(cx, |editor, window, cx| {
16339        let snapshot = editor.buffer().read(cx).snapshot(cx);
16340        editor.tasks.insert(
16341            (buffer.read(cx).remote_id(), 3),
16342            RunnableTasks {
16343                templates: vec![],
16344                offset: snapshot.anchor_before(43),
16345                column: 0,
16346                extra_variables: HashMap::default(),
16347                context_range: BufferOffset(43)..BufferOffset(85),
16348            },
16349        );
16350        editor.tasks.insert(
16351            (buffer.read(cx).remote_id(), 8),
16352            RunnableTasks {
16353                templates: vec![],
16354                offset: snapshot.anchor_before(86),
16355                column: 0,
16356                extra_variables: HashMap::default(),
16357                context_range: BufferOffset(86)..BufferOffset(191),
16358            },
16359        );
16360
16361        // Test finding task when cursor is inside function body
16362        editor.change_selections(None, window, cx, |s| {
16363            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16364        });
16365        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16366        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16367
16368        // Test finding task when cursor is on function name
16369        editor.change_selections(None, window, cx, |s| {
16370            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16371        });
16372        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16373        assert_eq!(row, 8, "Should find task when cursor is on function name");
16374    });
16375}
16376
16377#[gpui::test]
16378async fn test_folding_buffers(cx: &mut TestAppContext) {
16379    init_test(cx, |_| {});
16380
16381    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16382    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16383    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16384
16385    let fs = FakeFs::new(cx.executor());
16386    fs.insert_tree(
16387        path!("/a"),
16388        json!({
16389            "first.rs": sample_text_1,
16390            "second.rs": sample_text_2,
16391            "third.rs": sample_text_3,
16392        }),
16393    )
16394    .await;
16395    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16396    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16397    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16398    let worktree = project.update(cx, |project, cx| {
16399        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16400        assert_eq!(worktrees.len(), 1);
16401        worktrees.pop().unwrap()
16402    });
16403    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16404
16405    let buffer_1 = project
16406        .update(cx, |project, cx| {
16407            project.open_buffer((worktree_id, "first.rs"), cx)
16408        })
16409        .await
16410        .unwrap();
16411    let buffer_2 = project
16412        .update(cx, |project, cx| {
16413            project.open_buffer((worktree_id, "second.rs"), cx)
16414        })
16415        .await
16416        .unwrap();
16417    let buffer_3 = project
16418        .update(cx, |project, cx| {
16419            project.open_buffer((worktree_id, "third.rs"), cx)
16420        })
16421        .await
16422        .unwrap();
16423
16424    let multi_buffer = cx.new(|cx| {
16425        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16426        multi_buffer.push_excerpts(
16427            buffer_1.clone(),
16428            [
16429                ExcerptRange {
16430                    context: Point::new(0, 0)..Point::new(3, 0),
16431                    primary: None,
16432                },
16433                ExcerptRange {
16434                    context: Point::new(5, 0)..Point::new(7, 0),
16435                    primary: None,
16436                },
16437                ExcerptRange {
16438                    context: Point::new(9, 0)..Point::new(10, 4),
16439                    primary: None,
16440                },
16441            ],
16442            cx,
16443        );
16444        multi_buffer.push_excerpts(
16445            buffer_2.clone(),
16446            [
16447                ExcerptRange {
16448                    context: Point::new(0, 0)..Point::new(3, 0),
16449                    primary: None,
16450                },
16451                ExcerptRange {
16452                    context: Point::new(5, 0)..Point::new(7, 0),
16453                    primary: None,
16454                },
16455                ExcerptRange {
16456                    context: Point::new(9, 0)..Point::new(10, 4),
16457                    primary: None,
16458                },
16459            ],
16460            cx,
16461        );
16462        multi_buffer.push_excerpts(
16463            buffer_3.clone(),
16464            [
16465                ExcerptRange {
16466                    context: Point::new(0, 0)..Point::new(3, 0),
16467                    primary: None,
16468                },
16469                ExcerptRange {
16470                    context: Point::new(5, 0)..Point::new(7, 0),
16471                    primary: None,
16472                },
16473                ExcerptRange {
16474                    context: Point::new(9, 0)..Point::new(10, 4),
16475                    primary: None,
16476                },
16477            ],
16478            cx,
16479        );
16480        multi_buffer
16481    });
16482    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16483        Editor::new(
16484            EditorMode::Full,
16485            multi_buffer.clone(),
16486            Some(project.clone()),
16487            window,
16488            cx,
16489        )
16490    });
16491
16492    assert_eq!(
16493        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16494        "\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",
16495    );
16496
16497    multi_buffer_editor.update(cx, |editor, cx| {
16498        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16499    });
16500    assert_eq!(
16501        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16502        "\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",
16503        "After folding the first buffer, its text should not be displayed"
16504    );
16505
16506    multi_buffer_editor.update(cx, |editor, cx| {
16507        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16508    });
16509    assert_eq!(
16510        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16511        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16512        "After folding the second buffer, its text should not be displayed"
16513    );
16514
16515    multi_buffer_editor.update(cx, |editor, cx| {
16516        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16517    });
16518    assert_eq!(
16519        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16520        "\n\n\n\n\n",
16521        "After folding the third buffer, its text should not be displayed"
16522    );
16523
16524    // Emulate selection inside the fold logic, that should work
16525    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16526        editor
16527            .snapshot(window, cx)
16528            .next_line_boundary(Point::new(0, 4));
16529    });
16530
16531    multi_buffer_editor.update(cx, |editor, cx| {
16532        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16533    });
16534    assert_eq!(
16535        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16536        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16537        "After unfolding the second buffer, its text should be displayed"
16538    );
16539
16540    // Typing inside of buffer 1 causes that buffer to be unfolded.
16541    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16542        assert_eq!(
16543            multi_buffer
16544                .read(cx)
16545                .snapshot(cx)
16546                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16547                .collect::<String>(),
16548            "bbbb"
16549        );
16550        editor.change_selections(None, window, cx, |selections| {
16551            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16552        });
16553        editor.handle_input("B", window, cx);
16554    });
16555
16556    assert_eq!(
16557        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16558        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16559        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16560    );
16561
16562    multi_buffer_editor.update(cx, |editor, cx| {
16563        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16564    });
16565    assert_eq!(
16566        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16567        "\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",
16568        "After unfolding the all buffers, all original text should be displayed"
16569    );
16570}
16571
16572#[gpui::test]
16573async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16574    init_test(cx, |_| {});
16575
16576    let sample_text_1 = "1111\n2222\n3333".to_string();
16577    let sample_text_2 = "4444\n5555\n6666".to_string();
16578    let sample_text_3 = "7777\n8888\n9999".to_string();
16579
16580    let fs = FakeFs::new(cx.executor());
16581    fs.insert_tree(
16582        path!("/a"),
16583        json!({
16584            "first.rs": sample_text_1,
16585            "second.rs": sample_text_2,
16586            "third.rs": sample_text_3,
16587        }),
16588    )
16589    .await;
16590    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16591    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16592    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16593    let worktree = project.update(cx, |project, cx| {
16594        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16595        assert_eq!(worktrees.len(), 1);
16596        worktrees.pop().unwrap()
16597    });
16598    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16599
16600    let buffer_1 = project
16601        .update(cx, |project, cx| {
16602            project.open_buffer((worktree_id, "first.rs"), cx)
16603        })
16604        .await
16605        .unwrap();
16606    let buffer_2 = project
16607        .update(cx, |project, cx| {
16608            project.open_buffer((worktree_id, "second.rs"), cx)
16609        })
16610        .await
16611        .unwrap();
16612    let buffer_3 = project
16613        .update(cx, |project, cx| {
16614            project.open_buffer((worktree_id, "third.rs"), cx)
16615        })
16616        .await
16617        .unwrap();
16618
16619    let multi_buffer = cx.new(|cx| {
16620        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16621        multi_buffer.push_excerpts(
16622            buffer_1.clone(),
16623            [ExcerptRange {
16624                context: Point::new(0, 0)..Point::new(3, 0),
16625                primary: None,
16626            }],
16627            cx,
16628        );
16629        multi_buffer.push_excerpts(
16630            buffer_2.clone(),
16631            [ExcerptRange {
16632                context: Point::new(0, 0)..Point::new(3, 0),
16633                primary: None,
16634            }],
16635            cx,
16636        );
16637        multi_buffer.push_excerpts(
16638            buffer_3.clone(),
16639            [ExcerptRange {
16640                context: Point::new(0, 0)..Point::new(3, 0),
16641                primary: None,
16642            }],
16643            cx,
16644        );
16645        multi_buffer
16646    });
16647
16648    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16649        Editor::new(
16650            EditorMode::Full,
16651            multi_buffer,
16652            Some(project.clone()),
16653            window,
16654            cx,
16655        )
16656    });
16657
16658    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16659    assert_eq!(
16660        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16661        full_text,
16662    );
16663
16664    multi_buffer_editor.update(cx, |editor, cx| {
16665        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16666    });
16667    assert_eq!(
16668        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16669        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16670        "After folding the first buffer, its text should not be displayed"
16671    );
16672
16673    multi_buffer_editor.update(cx, |editor, cx| {
16674        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16675    });
16676
16677    assert_eq!(
16678        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16679        "\n\n\n\n\n\n7777\n8888\n9999",
16680        "After folding the second buffer, its text should not be displayed"
16681    );
16682
16683    multi_buffer_editor.update(cx, |editor, cx| {
16684        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16685    });
16686    assert_eq!(
16687        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16688        "\n\n\n\n\n",
16689        "After folding the third buffer, its text should not be displayed"
16690    );
16691
16692    multi_buffer_editor.update(cx, |editor, cx| {
16693        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16694    });
16695    assert_eq!(
16696        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16697        "\n\n\n\n4444\n5555\n6666\n\n",
16698        "After unfolding the second buffer, its text should be displayed"
16699    );
16700
16701    multi_buffer_editor.update(cx, |editor, cx| {
16702        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16703    });
16704    assert_eq!(
16705        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16706        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16707        "After unfolding the first buffer, its text should be displayed"
16708    );
16709
16710    multi_buffer_editor.update(cx, |editor, cx| {
16711        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16712    });
16713    assert_eq!(
16714        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16715        full_text,
16716        "After unfolding all buffers, all original text should be displayed"
16717    );
16718}
16719
16720#[gpui::test]
16721async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16722    init_test(cx, |_| {});
16723
16724    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16725
16726    let fs = FakeFs::new(cx.executor());
16727    fs.insert_tree(
16728        path!("/a"),
16729        json!({
16730            "main.rs": sample_text,
16731        }),
16732    )
16733    .await;
16734    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16735    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16736    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16737    let worktree = project.update(cx, |project, cx| {
16738        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16739        assert_eq!(worktrees.len(), 1);
16740        worktrees.pop().unwrap()
16741    });
16742    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16743
16744    let buffer_1 = project
16745        .update(cx, |project, cx| {
16746            project.open_buffer((worktree_id, "main.rs"), cx)
16747        })
16748        .await
16749        .unwrap();
16750
16751    let multi_buffer = cx.new(|cx| {
16752        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16753        multi_buffer.push_excerpts(
16754            buffer_1.clone(),
16755            [ExcerptRange {
16756                context: Point::new(0, 0)
16757                    ..Point::new(
16758                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16759                        0,
16760                    ),
16761                primary: None,
16762            }],
16763            cx,
16764        );
16765        multi_buffer
16766    });
16767    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16768        Editor::new(
16769            EditorMode::Full,
16770            multi_buffer,
16771            Some(project.clone()),
16772            window,
16773            cx,
16774        )
16775    });
16776
16777    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16778    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16779        enum TestHighlight {}
16780        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16781        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16782        editor.highlight_text::<TestHighlight>(
16783            vec![highlight_range.clone()],
16784            HighlightStyle::color(Hsla::green()),
16785            cx,
16786        );
16787        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16788    });
16789
16790    let full_text = format!("\n\n{sample_text}");
16791    assert_eq!(
16792        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16793        full_text,
16794    );
16795}
16796
16797#[gpui::test]
16798async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16799    init_test(cx, |_| {});
16800    cx.update(|cx| {
16801        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16802            "keymaps/default-linux.json",
16803            cx,
16804        )
16805        .unwrap();
16806        cx.bind_keys(default_key_bindings);
16807    });
16808
16809    let (editor, cx) = cx.add_window_view(|window, cx| {
16810        let multi_buffer = MultiBuffer::build_multi(
16811            [
16812                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16813                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16814                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16815                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16816            ],
16817            cx,
16818        );
16819        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16820
16821        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16822        // fold all but the second buffer, so that we test navigating between two
16823        // adjacent folded buffers, as well as folded buffers at the start and
16824        // end the multibuffer
16825        editor.fold_buffer(buffer_ids[0], cx);
16826        editor.fold_buffer(buffer_ids[2], cx);
16827        editor.fold_buffer(buffer_ids[3], cx);
16828
16829        editor
16830    });
16831    cx.simulate_resize(size(px(1000.), px(1000.)));
16832
16833    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16834    cx.assert_excerpts_with_selections(indoc! {"
16835        [EXCERPT]
16836        ˇ[FOLDED]
16837        [EXCERPT]
16838        a1
16839        b1
16840        [EXCERPT]
16841        [FOLDED]
16842        [EXCERPT]
16843        [FOLDED]
16844        "
16845    });
16846    cx.simulate_keystroke("down");
16847    cx.assert_excerpts_with_selections(indoc! {"
16848        [EXCERPT]
16849        [FOLDED]
16850        [EXCERPT]
16851        ˇa1
16852        b1
16853        [EXCERPT]
16854        [FOLDED]
16855        [EXCERPT]
16856        [FOLDED]
16857        "
16858    });
16859    cx.simulate_keystroke("down");
16860    cx.assert_excerpts_with_selections(indoc! {"
16861        [EXCERPT]
16862        [FOLDED]
16863        [EXCERPT]
16864        a1
16865        ˇb1
16866        [EXCERPT]
16867        [FOLDED]
16868        [EXCERPT]
16869        [FOLDED]
16870        "
16871    });
16872    cx.simulate_keystroke("down");
16873    cx.assert_excerpts_with_selections(indoc! {"
16874        [EXCERPT]
16875        [FOLDED]
16876        [EXCERPT]
16877        a1
16878        b1
16879        ˇ[EXCERPT]
16880        [FOLDED]
16881        [EXCERPT]
16882        [FOLDED]
16883        "
16884    });
16885    cx.simulate_keystroke("down");
16886    cx.assert_excerpts_with_selections(indoc! {"
16887        [EXCERPT]
16888        [FOLDED]
16889        [EXCERPT]
16890        a1
16891        b1
16892        [EXCERPT]
16893        ˇ[FOLDED]
16894        [EXCERPT]
16895        [FOLDED]
16896        "
16897    });
16898    for _ in 0..5 {
16899        cx.simulate_keystroke("down");
16900        cx.assert_excerpts_with_selections(indoc! {"
16901            [EXCERPT]
16902            [FOLDED]
16903            [EXCERPT]
16904            a1
16905            b1
16906            [EXCERPT]
16907            [FOLDED]
16908            [EXCERPT]
16909            ˇ[FOLDED]
16910            "
16911        });
16912    }
16913
16914    cx.simulate_keystroke("up");
16915    cx.assert_excerpts_with_selections(indoc! {"
16916        [EXCERPT]
16917        [FOLDED]
16918        [EXCERPT]
16919        a1
16920        b1
16921        [EXCERPT]
16922        ˇ[FOLDED]
16923        [EXCERPT]
16924        [FOLDED]
16925        "
16926    });
16927    cx.simulate_keystroke("up");
16928    cx.assert_excerpts_with_selections(indoc! {"
16929        [EXCERPT]
16930        [FOLDED]
16931        [EXCERPT]
16932        a1
16933        b1
16934        ˇ[EXCERPT]
16935        [FOLDED]
16936        [EXCERPT]
16937        [FOLDED]
16938        "
16939    });
16940    cx.simulate_keystroke("up");
16941    cx.assert_excerpts_with_selections(indoc! {"
16942        [EXCERPT]
16943        [FOLDED]
16944        [EXCERPT]
16945        a1
16946        ˇb1
16947        [EXCERPT]
16948        [FOLDED]
16949        [EXCERPT]
16950        [FOLDED]
16951        "
16952    });
16953    cx.simulate_keystroke("up");
16954    cx.assert_excerpts_with_selections(indoc! {"
16955        [EXCERPT]
16956        [FOLDED]
16957        [EXCERPT]
16958        ˇa1
16959        b1
16960        [EXCERPT]
16961        [FOLDED]
16962        [EXCERPT]
16963        [FOLDED]
16964        "
16965    });
16966    for _ in 0..5 {
16967        cx.simulate_keystroke("up");
16968        cx.assert_excerpts_with_selections(indoc! {"
16969            [EXCERPT]
16970            ˇ[FOLDED]
16971            [EXCERPT]
16972            a1
16973            b1
16974            [EXCERPT]
16975            [FOLDED]
16976            [EXCERPT]
16977            [FOLDED]
16978            "
16979        });
16980    }
16981}
16982
16983#[gpui::test]
16984async fn test_inline_completion_text(cx: &mut TestAppContext) {
16985    init_test(cx, |_| {});
16986
16987    // Simple insertion
16988    assert_highlighted_edits(
16989        "Hello, world!",
16990        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16991        true,
16992        cx,
16993        |highlighted_edits, cx| {
16994            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16995            assert_eq!(highlighted_edits.highlights.len(), 1);
16996            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16997            assert_eq!(
16998                highlighted_edits.highlights[0].1.background_color,
16999                Some(cx.theme().status().created_background)
17000            );
17001        },
17002    )
17003    .await;
17004
17005    // Replacement
17006    assert_highlighted_edits(
17007        "This is a test.",
17008        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17009        false,
17010        cx,
17011        |highlighted_edits, cx| {
17012            assert_eq!(highlighted_edits.text, "That is a test.");
17013            assert_eq!(highlighted_edits.highlights.len(), 1);
17014            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17015            assert_eq!(
17016                highlighted_edits.highlights[0].1.background_color,
17017                Some(cx.theme().status().created_background)
17018            );
17019        },
17020    )
17021    .await;
17022
17023    // Multiple edits
17024    assert_highlighted_edits(
17025        "Hello, world!",
17026        vec![
17027            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17028            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17029        ],
17030        false,
17031        cx,
17032        |highlighted_edits, cx| {
17033            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17034            assert_eq!(highlighted_edits.highlights.len(), 2);
17035            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17036            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17037            assert_eq!(
17038                highlighted_edits.highlights[0].1.background_color,
17039                Some(cx.theme().status().created_background)
17040            );
17041            assert_eq!(
17042                highlighted_edits.highlights[1].1.background_color,
17043                Some(cx.theme().status().created_background)
17044            );
17045        },
17046    )
17047    .await;
17048
17049    // Multiple lines with edits
17050    assert_highlighted_edits(
17051        "First line\nSecond line\nThird line\nFourth line",
17052        vec![
17053            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17054            (
17055                Point::new(2, 0)..Point::new(2, 10),
17056                "New third line".to_string(),
17057            ),
17058            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17059        ],
17060        false,
17061        cx,
17062        |highlighted_edits, cx| {
17063            assert_eq!(
17064                highlighted_edits.text,
17065                "Second modified\nNew third line\nFourth updated line"
17066            );
17067            assert_eq!(highlighted_edits.highlights.len(), 3);
17068            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17069            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17070            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17071            for highlight in &highlighted_edits.highlights {
17072                assert_eq!(
17073                    highlight.1.background_color,
17074                    Some(cx.theme().status().created_background)
17075                );
17076            }
17077        },
17078    )
17079    .await;
17080}
17081
17082#[gpui::test]
17083async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17084    init_test(cx, |_| {});
17085
17086    // Deletion
17087    assert_highlighted_edits(
17088        "Hello, world!",
17089        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17090        true,
17091        cx,
17092        |highlighted_edits, cx| {
17093            assert_eq!(highlighted_edits.text, "Hello, world!");
17094            assert_eq!(highlighted_edits.highlights.len(), 1);
17095            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17096            assert_eq!(
17097                highlighted_edits.highlights[0].1.background_color,
17098                Some(cx.theme().status().deleted_background)
17099            );
17100        },
17101    )
17102    .await;
17103
17104    // Insertion
17105    assert_highlighted_edits(
17106        "Hello, world!",
17107        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17108        true,
17109        cx,
17110        |highlighted_edits, cx| {
17111            assert_eq!(highlighted_edits.highlights.len(), 1);
17112            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17113            assert_eq!(
17114                highlighted_edits.highlights[0].1.background_color,
17115                Some(cx.theme().status().created_background)
17116            );
17117        },
17118    )
17119    .await;
17120}
17121
17122async fn assert_highlighted_edits(
17123    text: &str,
17124    edits: Vec<(Range<Point>, String)>,
17125    include_deletions: bool,
17126    cx: &mut TestAppContext,
17127    assertion_fn: impl Fn(HighlightedText, &App),
17128) {
17129    let window = cx.add_window(|window, cx| {
17130        let buffer = MultiBuffer::build_simple(text, cx);
17131        Editor::new(EditorMode::Full, buffer, None, window, cx)
17132    });
17133    let cx = &mut VisualTestContext::from_window(*window, cx);
17134
17135    let (buffer, snapshot) = window
17136        .update(cx, |editor, _window, cx| {
17137            (
17138                editor.buffer().clone(),
17139                editor.buffer().read(cx).snapshot(cx),
17140            )
17141        })
17142        .unwrap();
17143
17144    let edits = edits
17145        .into_iter()
17146        .map(|(range, edit)| {
17147            (
17148                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17149                edit,
17150            )
17151        })
17152        .collect::<Vec<_>>();
17153
17154    let text_anchor_edits = edits
17155        .clone()
17156        .into_iter()
17157        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17158        .collect::<Vec<_>>();
17159
17160    let edit_preview = window
17161        .update(cx, |_, _window, cx| {
17162            buffer
17163                .read(cx)
17164                .as_singleton()
17165                .unwrap()
17166                .read(cx)
17167                .preview_edits(text_anchor_edits.into(), cx)
17168        })
17169        .unwrap()
17170        .await;
17171
17172    cx.update(|_window, cx| {
17173        let highlighted_edits = inline_completion_edit_text(
17174            &snapshot.as_singleton().unwrap().2,
17175            &edits,
17176            &edit_preview,
17177            include_deletions,
17178            cx,
17179        );
17180        assertion_fn(highlighted_edits, cx)
17181    });
17182}
17183
17184#[track_caller]
17185fn assert_breakpoint(
17186    breakpoints: &BTreeMap<Arc<Path>, Vec<SerializedBreakpoint>>,
17187    path: &Arc<Path>,
17188    expected: Vec<(u32, BreakpointKind)>,
17189) {
17190    if expected.len() == 0usize {
17191        assert!(!breakpoints.contains_key(path));
17192    } else {
17193        let mut breakpoint = breakpoints
17194            .get(path)
17195            .unwrap()
17196            .into_iter()
17197            .map(|breakpoint| (breakpoint.position, breakpoint.kind.clone()))
17198            .collect::<Vec<_>>();
17199
17200        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17201
17202        assert_eq!(expected, breakpoint);
17203    }
17204}
17205
17206fn add_log_breakpoint_at_cursor(
17207    editor: &mut Editor,
17208    log_message: &str,
17209    window: &mut Window,
17210    cx: &mut Context<Editor>,
17211) {
17212    let (anchor, bp) = editor
17213        .breakpoint_at_cursor_head(window, cx)
17214        .unwrap_or_else(|| {
17215            let cursor_position: Point = editor.selections.newest(cx).head();
17216
17217            let breakpoint_position = editor
17218                .snapshot(window, cx)
17219                .display_snapshot
17220                .buffer_snapshot
17221                .anchor_before(Point::new(cursor_position.row, 0));
17222
17223            let kind = BreakpointKind::Log(Arc::from(log_message));
17224
17225            (breakpoint_position, Breakpoint { kind })
17226        });
17227
17228    editor.edit_breakpoint_at_anchor(
17229        anchor,
17230        bp.kind,
17231        BreakpointEditAction::EditLogMessage(log_message.into()),
17232        cx,
17233    );
17234}
17235
17236#[gpui::test]
17237async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17238    init_test(cx, |_| {});
17239
17240    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17241    let fs = FakeFs::new(cx.executor());
17242    fs.insert_tree(
17243        path!("/a"),
17244        json!({
17245            "main.rs": sample_text,
17246        }),
17247    )
17248    .await;
17249    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17250    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17251    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17252
17253    let fs = FakeFs::new(cx.executor());
17254    fs.insert_tree(
17255        path!("/a"),
17256        json!({
17257            "main.rs": sample_text,
17258        }),
17259    )
17260    .await;
17261    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17262    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17263    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17264    let worktree_id = workspace
17265        .update(cx, |workspace, _window, cx| {
17266            workspace.project().update(cx, |project, cx| {
17267                project.worktrees(cx).next().unwrap().read(cx).id()
17268            })
17269        })
17270        .unwrap();
17271
17272    let buffer = project
17273        .update(cx, |project, cx| {
17274            project.open_buffer((worktree_id, "main.rs"), cx)
17275        })
17276        .await
17277        .unwrap();
17278
17279    let (editor, cx) = cx.add_window_view(|window, cx| {
17280        Editor::new(
17281            EditorMode::Full,
17282            MultiBuffer::build_from_buffer(buffer, cx),
17283            Some(project.clone()),
17284            window,
17285            cx,
17286        )
17287    });
17288
17289    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17290    let abs_path = project.read_with(cx, |project, cx| {
17291        project
17292            .absolute_path(&project_path, cx)
17293            .map(|path_buf| Arc::from(path_buf.to_owned()))
17294            .unwrap()
17295    });
17296
17297    // assert we can add breakpoint on the first line
17298    editor.update_in(cx, |editor, window, cx| {
17299        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17300        editor.move_to_end(&MoveToEnd, window, cx);
17301        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17302    });
17303
17304    let breakpoints = editor.update(cx, |editor, cx| {
17305        editor
17306            .breakpoint_store()
17307            .as_ref()
17308            .unwrap()
17309            .read(cx)
17310            .all_breakpoints(cx)
17311            .clone()
17312    });
17313
17314    assert_eq!(1, breakpoints.len());
17315    assert_breakpoint(
17316        &breakpoints,
17317        &abs_path,
17318        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17319    );
17320
17321    editor.update_in(cx, |editor, window, cx| {
17322        editor.move_to_beginning(&MoveToBeginning, window, cx);
17323        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17324    });
17325
17326    let breakpoints = editor.update(cx, |editor, cx| {
17327        editor
17328            .breakpoint_store()
17329            .as_ref()
17330            .unwrap()
17331            .read(cx)
17332            .all_breakpoints(cx)
17333            .clone()
17334    });
17335
17336    assert_eq!(1, breakpoints.len());
17337    assert_breakpoint(&breakpoints, &abs_path, vec![(3, BreakpointKind::Standard)]);
17338
17339    editor.update_in(cx, |editor, window, cx| {
17340        editor.move_to_end(&MoveToEnd, window, cx);
17341        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17342    });
17343
17344    let breakpoints = editor.update(cx, |editor, cx| {
17345        editor
17346            .breakpoint_store()
17347            .as_ref()
17348            .unwrap()
17349            .read(cx)
17350            .all_breakpoints(cx)
17351            .clone()
17352    });
17353
17354    assert_eq!(0, breakpoints.len());
17355    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17356}
17357
17358#[gpui::test]
17359async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17360    init_test(cx, |_| {});
17361
17362    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17363
17364    let fs = FakeFs::new(cx.executor());
17365    fs.insert_tree(
17366        path!("/a"),
17367        json!({
17368            "main.rs": sample_text,
17369        }),
17370    )
17371    .await;
17372    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17373    let (workspace, cx) =
17374        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17375
17376    let worktree_id = workspace.update(cx, |workspace, cx| {
17377        workspace.project().update(cx, |project, cx| {
17378            project.worktrees(cx).next().unwrap().read(cx).id()
17379        })
17380    });
17381
17382    let buffer = project
17383        .update(cx, |project, cx| {
17384            project.open_buffer((worktree_id, "main.rs"), cx)
17385        })
17386        .await
17387        .unwrap();
17388
17389    let (editor, cx) = cx.add_window_view(|window, cx| {
17390        Editor::new(
17391            EditorMode::Full,
17392            MultiBuffer::build_from_buffer(buffer, cx),
17393            Some(project.clone()),
17394            window,
17395            cx,
17396        )
17397    });
17398
17399    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17400    let abs_path = project.read_with(cx, |project, cx| {
17401        project
17402            .absolute_path(&project_path, cx)
17403            .map(|path_buf| Arc::from(path_buf.to_owned()))
17404            .unwrap()
17405    });
17406
17407    editor.update_in(cx, |editor, window, cx| {
17408        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17409    });
17410
17411    let breakpoints = editor.update(cx, |editor, cx| {
17412        editor
17413            .breakpoint_store()
17414            .as_ref()
17415            .unwrap()
17416            .read(cx)
17417            .all_breakpoints(cx)
17418            .clone()
17419    });
17420
17421    assert_breakpoint(
17422        &breakpoints,
17423        &abs_path,
17424        vec![(0, BreakpointKind::Log("hello world".into()))],
17425    );
17426
17427    // Removing a log message from a log breakpoint should remove it
17428    editor.update_in(cx, |editor, window, cx| {
17429        add_log_breakpoint_at_cursor(editor, "", window, cx);
17430    });
17431
17432    let breakpoints = editor.update(cx, |editor, cx| {
17433        editor
17434            .breakpoint_store()
17435            .as_ref()
17436            .unwrap()
17437            .read(cx)
17438            .all_breakpoints(cx)
17439            .clone()
17440    });
17441
17442    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17443
17444    editor.update_in(cx, |editor, window, cx| {
17445        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17446        editor.move_to_end(&MoveToEnd, window, cx);
17447        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17448        // Not adding a log message to a standard breakpoint shouldn't remove it
17449        add_log_breakpoint_at_cursor(editor, "", window, cx);
17450    });
17451
17452    let breakpoints = editor.update(cx, |editor, cx| {
17453        editor
17454            .breakpoint_store()
17455            .as_ref()
17456            .unwrap()
17457            .read(cx)
17458            .all_breakpoints(cx)
17459            .clone()
17460    });
17461
17462    assert_breakpoint(
17463        &breakpoints,
17464        &abs_path,
17465        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17466    );
17467
17468    editor.update_in(cx, |editor, window, cx| {
17469        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17470    });
17471
17472    let breakpoints = editor.update(cx, |editor, cx| {
17473        editor
17474            .breakpoint_store()
17475            .as_ref()
17476            .unwrap()
17477            .read(cx)
17478            .all_breakpoints(cx)
17479            .clone()
17480    });
17481
17482    assert_breakpoint(
17483        &breakpoints,
17484        &abs_path,
17485        vec![
17486            (0, BreakpointKind::Standard),
17487            (3, BreakpointKind::Log("hello world".into())),
17488        ],
17489    );
17490
17491    editor.update_in(cx, |editor, window, cx| {
17492        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17493    });
17494
17495    let breakpoints = editor.update(cx, |editor, cx| {
17496        editor
17497            .breakpoint_store()
17498            .as_ref()
17499            .unwrap()
17500            .read(cx)
17501            .all_breakpoints(cx)
17502            .clone()
17503    });
17504
17505    assert_breakpoint(
17506        &breakpoints,
17507        &abs_path,
17508        vec![
17509            (0, BreakpointKind::Standard),
17510            (3, BreakpointKind::Log("hello Earth !!".into())),
17511        ],
17512    );
17513}
17514
17515#[gpui::test]
17516async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17517    init_test(cx, |_| {});
17518    let capabilities = lsp::ServerCapabilities {
17519        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17520            prepare_provider: Some(true),
17521            work_done_progress_options: Default::default(),
17522        })),
17523        ..Default::default()
17524    };
17525    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17526
17527    cx.set_state(indoc! {"
17528        struct Fˇoo {}
17529    "});
17530
17531    cx.update_editor(|editor, _, cx| {
17532        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17533        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17534        editor.highlight_background::<DocumentHighlightRead>(
17535            &[highlight_range],
17536            |c| c.editor_document_highlight_read_background,
17537            cx,
17538        );
17539    });
17540
17541    let mut prepare_rename_handler =
17542        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17543            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17544                start: lsp::Position {
17545                    line: 0,
17546                    character: 7,
17547                },
17548                end: lsp::Position {
17549                    line: 0,
17550                    character: 10,
17551                },
17552            })))
17553        });
17554    let prepare_rename_task = cx
17555        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17556        .expect("Prepare rename was not started");
17557    prepare_rename_handler.next().await.unwrap();
17558    prepare_rename_task.await.expect("Prepare rename failed");
17559
17560    let mut rename_handler =
17561        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17562            let edit = lsp::TextEdit {
17563                range: lsp::Range {
17564                    start: lsp::Position {
17565                        line: 0,
17566                        character: 7,
17567                    },
17568                    end: lsp::Position {
17569                        line: 0,
17570                        character: 10,
17571                    },
17572                },
17573                new_text: "FooRenamed".to_string(),
17574            };
17575            Ok(Some(lsp::WorkspaceEdit::new(
17576                // Specify the same edit twice
17577                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17578            )))
17579        });
17580    let rename_task = cx
17581        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17582        .expect("Confirm rename was not started");
17583    rename_handler.next().await.unwrap();
17584    rename_task.await.expect("Confirm rename failed");
17585    cx.run_until_parked();
17586
17587    // Despite two edits, only one is actually applied as those are identical
17588    cx.assert_editor_state(indoc! {"
17589        struct FooRenamedˇ {}
17590    "});
17591}
17592
17593#[gpui::test]
17594async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17595    init_test(cx, |_| {});
17596    // These capabilities indicate that the server does not support prepare rename.
17597    let capabilities = lsp::ServerCapabilities {
17598        rename_provider: Some(lsp::OneOf::Left(true)),
17599        ..Default::default()
17600    };
17601    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17602
17603    cx.set_state(indoc! {"
17604        struct Fˇoo {}
17605    "});
17606
17607    cx.update_editor(|editor, _window, cx| {
17608        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17609        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17610        editor.highlight_background::<DocumentHighlightRead>(
17611            &[highlight_range],
17612            |c| c.editor_document_highlight_read_background,
17613            cx,
17614        );
17615    });
17616
17617    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17618        .expect("Prepare rename was not started")
17619        .await
17620        .expect("Prepare rename failed");
17621
17622    let mut rename_handler =
17623        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17624            let edit = lsp::TextEdit {
17625                range: lsp::Range {
17626                    start: lsp::Position {
17627                        line: 0,
17628                        character: 7,
17629                    },
17630                    end: lsp::Position {
17631                        line: 0,
17632                        character: 10,
17633                    },
17634                },
17635                new_text: "FooRenamed".to_string(),
17636            };
17637            Ok(Some(lsp::WorkspaceEdit::new(
17638                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17639            )))
17640        });
17641    let rename_task = cx
17642        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17643        .expect("Confirm rename was not started");
17644    rename_handler.next().await.unwrap();
17645    rename_task.await.expect("Confirm rename failed");
17646    cx.run_until_parked();
17647
17648    // Correct range is renamed, as `surrounding_word` is used to find it.
17649    cx.assert_editor_state(indoc! {"
17650        struct FooRenamedˇ {}
17651    "});
17652}
17653
17654#[gpui::test]
17655async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17656    init_test(cx, |_| {});
17657    let mut cx = EditorTestContext::new(cx).await;
17658
17659    let language = Arc::new(
17660        Language::new(
17661            LanguageConfig::default(),
17662            Some(tree_sitter_html::LANGUAGE.into()),
17663        )
17664        .with_brackets_query(
17665            r#"
17666            ("<" @open "/>" @close)
17667            ("</" @open ">" @close)
17668            ("<" @open ">" @close)
17669            ("\"" @open "\"" @close)
17670            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17671        "#,
17672        )
17673        .unwrap(),
17674    );
17675    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17676
17677    cx.set_state(indoc! {"
17678        <span>ˇ</span>
17679    "});
17680    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17681    cx.assert_editor_state(indoc! {"
17682        <span>
17683        ˇ
17684        </span>
17685    "});
17686
17687    cx.set_state(indoc! {"
17688        <span><span></span>ˇ</span>
17689    "});
17690    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17691    cx.assert_editor_state(indoc! {"
17692        <span><span></span>
17693        ˇ</span>
17694    "});
17695
17696    cx.set_state(indoc! {"
17697        <span>ˇ
17698        </span>
17699    "});
17700    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17701    cx.assert_editor_state(indoc! {"
17702        <span>
17703        ˇ
17704        </span>
17705    "});
17706}
17707
17708#[gpui::test(iterations = 10)]
17709async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17710    init_test(cx, |_| {});
17711
17712    let fs = FakeFs::new(cx.executor());
17713    fs.insert_tree(
17714        path!("/dir"),
17715        json!({
17716            "a.ts": "a",
17717        }),
17718    )
17719    .await;
17720
17721    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17722    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17723    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17724
17725    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17726    language_registry.add(Arc::new(Language::new(
17727        LanguageConfig {
17728            name: "TypeScript".into(),
17729            matcher: LanguageMatcher {
17730                path_suffixes: vec!["ts".to_string()],
17731                ..Default::default()
17732            },
17733            ..Default::default()
17734        },
17735        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17736    )));
17737    let mut fake_language_servers = language_registry.register_fake_lsp(
17738        "TypeScript",
17739        FakeLspAdapter {
17740            capabilities: lsp::ServerCapabilities {
17741                code_lens_provider: Some(lsp::CodeLensOptions {
17742                    resolve_provider: Some(true),
17743                }),
17744                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17745                    commands: vec!["_the/command".to_string()],
17746                    ..lsp::ExecuteCommandOptions::default()
17747                }),
17748                ..lsp::ServerCapabilities::default()
17749            },
17750            ..FakeLspAdapter::default()
17751        },
17752    );
17753
17754    let (buffer, _handle) = project
17755        .update(cx, |p, cx| {
17756            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17757        })
17758        .await
17759        .unwrap();
17760    cx.executor().run_until_parked();
17761
17762    let fake_server = fake_language_servers.next().await.unwrap();
17763
17764    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17765    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17766    drop(buffer_snapshot);
17767    let actions = cx
17768        .update_window(*workspace, |_, window, cx| {
17769            project.code_actions(&buffer, anchor..anchor, window, cx)
17770        })
17771        .unwrap();
17772
17773    fake_server
17774        .handle_request::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17775            Ok(Some(vec![
17776                lsp::CodeLens {
17777                    range: lsp::Range::default(),
17778                    command: Some(lsp::Command {
17779                        title: "Code lens command".to_owned(),
17780                        command: "_the/command".to_owned(),
17781                        arguments: None,
17782                    }),
17783                    data: None,
17784                },
17785                lsp::CodeLens {
17786                    range: lsp::Range::default(),
17787                    command: Some(lsp::Command {
17788                        title: "Command not in capabilities".to_owned(),
17789                        command: "not in capabilities".to_owned(),
17790                        arguments: None,
17791                    }),
17792                    data: None,
17793                },
17794                lsp::CodeLens {
17795                    range: lsp::Range {
17796                        start: lsp::Position {
17797                            line: 1,
17798                            character: 1,
17799                        },
17800                        end: lsp::Position {
17801                            line: 1,
17802                            character: 1,
17803                        },
17804                    },
17805                    command: Some(lsp::Command {
17806                        title: "Command not in range".to_owned(),
17807                        command: "_the/command".to_owned(),
17808                        arguments: None,
17809                    }),
17810                    data: None,
17811                },
17812            ]))
17813        })
17814        .next()
17815        .await;
17816
17817    let actions = actions.await.unwrap();
17818    assert_eq!(
17819        actions.len(),
17820        1,
17821        "Should have only one valid action for the 0..0 range"
17822    );
17823    let action = actions[0].clone();
17824    let apply = project.update(cx, |project, cx| {
17825        project.apply_code_action(buffer.clone(), action, true, cx)
17826    });
17827
17828    // Resolving the code action does not populate its edits. In absence of
17829    // edits, we must execute the given command.
17830    fake_server.handle_request::<lsp::request::CodeLensResolve, _, _>(|mut lens, _| async move {
17831        let lens_command = lens.command.as_mut().expect("should have a command");
17832        assert_eq!(lens_command.title, "Code lens command");
17833        lens_command.arguments = Some(vec![json!("the-argument")]);
17834        Ok(lens)
17835    });
17836
17837    // While executing the command, the language server sends the editor
17838    // a `workspaceEdit` request.
17839    fake_server
17840        .handle_request::<lsp::request::ExecuteCommand, _, _>({
17841            let fake = fake_server.clone();
17842            move |params, _| {
17843                assert_eq!(params.command, "_the/command");
17844                let fake = fake.clone();
17845                async move {
17846                    fake.server
17847                        .request::<lsp::request::ApplyWorkspaceEdit>(
17848                            lsp::ApplyWorkspaceEditParams {
17849                                label: None,
17850                                edit: lsp::WorkspaceEdit {
17851                                    changes: Some(
17852                                        [(
17853                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
17854                                            vec![lsp::TextEdit {
17855                                                range: lsp::Range::new(
17856                                                    lsp::Position::new(0, 0),
17857                                                    lsp::Position::new(0, 0),
17858                                                ),
17859                                                new_text: "X".into(),
17860                                            }],
17861                                        )]
17862                                        .into_iter()
17863                                        .collect(),
17864                                    ),
17865                                    ..Default::default()
17866                                },
17867                            },
17868                        )
17869                        .await
17870                        .unwrap();
17871                    Ok(Some(json!(null)))
17872                }
17873            }
17874        })
17875        .next()
17876        .await;
17877
17878    // Applying the code lens command returns a project transaction containing the edits
17879    // sent by the language server in its `workspaceEdit` request.
17880    let transaction = apply.await.unwrap();
17881    assert!(transaction.0.contains_key(&buffer));
17882    buffer.update(cx, |buffer, cx| {
17883        assert_eq!(buffer.text(), "Xa");
17884        buffer.undo(cx);
17885        assert_eq!(buffer.text(), "a");
17886    });
17887}
17888
17889mod autoclose_tags {
17890    use super::*;
17891    use language::language_settings::JsxTagAutoCloseSettings;
17892    use languages::language;
17893
17894    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17895        init_test(cx, |settings| {
17896            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17897        });
17898
17899        let mut cx = EditorTestContext::new(cx).await;
17900        cx.update_buffer(|buffer, cx| {
17901            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17902
17903            buffer.set_language(Some(language), cx)
17904        });
17905
17906        cx
17907    }
17908
17909    macro_rules! check {
17910        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17911            #[gpui::test]
17912            async fn $name(cx: &mut TestAppContext) {
17913                let mut cx = test_setup(cx).await;
17914                cx.set_state($initial);
17915                cx.run_until_parked();
17916
17917                cx.update_editor(|editor, window, cx| {
17918                    editor.handle_input($input, window, cx);
17919                });
17920                cx.run_until_parked();
17921                cx.assert_editor_state($expected);
17922            }
17923        };
17924    }
17925
17926    check!(
17927        test_basic,
17928        "<divˇ" + ">" => "<div>ˇ</div>"
17929    );
17930
17931    check!(
17932        test_basic_nested,
17933        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17934    );
17935
17936    check!(
17937        test_basic_ignore_already_closed,
17938        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17939    );
17940
17941    check!(
17942        test_doesnt_autoclose_closing_tag,
17943        "</divˇ" + ">" => "</div>ˇ"
17944    );
17945
17946    check!(
17947        test_jsx_attr,
17948        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17949    );
17950
17951    check!(
17952        test_ignores_closing_tags_in_expr_block,
17953        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17954    );
17955
17956    check!(
17957        test_doesnt_autoclose_on_gt_in_expr,
17958        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17959    );
17960
17961    check!(
17962        test_ignores_closing_tags_with_different_tag_names,
17963        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17964    );
17965
17966    check!(
17967        test_autocloses_in_jsx_expression,
17968        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17969    );
17970
17971    check!(
17972        test_doesnt_autoclose_already_closed_in_jsx_expression,
17973        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17974    );
17975
17976    check!(
17977        test_autocloses_fragment,
17978        "" + ">" => "<>ˇ</>"
17979    );
17980
17981    check!(
17982        test_does_not_include_type_argument_in_autoclose_tag_name,
17983        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17984    );
17985
17986    check!(
17987        test_does_not_autoclose_doctype,
17988        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17989    );
17990
17991    check!(
17992        test_does_not_autoclose_comment,
17993        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17994    );
17995
17996    check!(
17997        test_multi_cursor_autoclose_same_tag,
17998        r#"
17999        <divˇ
18000        <divˇ
18001        "#
18002        + ">" =>
18003        r#"
18004        <div>ˇ</div>
18005        <div>ˇ</div>
18006        "#
18007    );
18008
18009    check!(
18010        test_multi_cursor_autoclose_different_tags,
18011        r#"
18012        <divˇ
18013        <spanˇ
18014        "#
18015        + ">" =>
18016        r#"
18017        <div>ˇ</div>
18018        <span>ˇ</span>
18019        "#
18020    );
18021
18022    check!(
18023        test_multi_cursor_autoclose_some_dont_autoclose_others,
18024        r#"
18025        <divˇ
18026        <div /ˇ
18027        <spanˇ</span>
18028        <!DOCTYPE htmlˇ
18029        </headˇ
18030        <Component<T>ˇ
18031        ˇ
18032        "#
18033        + ">" =>
18034        r#"
18035        <div>ˇ</div>
18036        <div />ˇ
18037        <span>ˇ</span>
18038        <!DOCTYPE html>ˇ
18039        </head>ˇ
18040        <Component<T>>ˇ</Component>
1804118042        "#
18043    );
18044
18045    check!(
18046        test_doesnt_mess_up_trailing_text,
18047        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
18048    );
18049
18050    #[gpui::test]
18051    async fn test_multibuffer(cx: &mut TestAppContext) {
18052        init_test(cx, |settings| {
18053            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
18054        });
18055
18056        let buffer_a = cx.new(|cx| {
18057            let mut buf = language::Buffer::local("<div", cx);
18058            buf.set_language(
18059                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
18060                cx,
18061            );
18062            buf
18063        });
18064        let buffer_b = cx.new(|cx| {
18065            let mut buf = language::Buffer::local("<pre", cx);
18066            buf.set_language(
18067                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
18068                cx,
18069            );
18070            buf
18071        });
18072        let buffer_c = cx.new(|cx| {
18073            let buf = language::Buffer::local("<span", cx);
18074            buf
18075        });
18076        let buffer = cx.new(|cx| {
18077            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
18078            buf.push_excerpts(
18079                buffer_a,
18080                [ExcerptRange {
18081                    context: text::Anchor::MIN..text::Anchor::MAX,
18082                    primary: None,
18083                }],
18084                cx,
18085            );
18086            buf.push_excerpts(
18087                buffer_b,
18088                [ExcerptRange {
18089                    context: text::Anchor::MIN..text::Anchor::MAX,
18090                    primary: None,
18091                }],
18092                cx,
18093            );
18094            buf.push_excerpts(
18095                buffer_c,
18096                [ExcerptRange {
18097                    context: text::Anchor::MIN..text::Anchor::MAX,
18098                    primary: None,
18099                }],
18100                cx,
18101            );
18102            buf
18103        });
18104        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
18105
18106        let mut cx = EditorTestContext::for_editor(editor, cx).await;
18107
18108        cx.update_editor(|editor, window, cx| {
18109            editor.change_selections(None, window, cx, |selections| {
18110                selections.select(vec![
18111                    Selection::from_offset(4),
18112                    Selection::from_offset(9),
18113                    Selection::from_offset(15),
18114                ])
18115            })
18116        });
18117        cx.run_until_parked();
18118
18119        cx.update_editor(|editor, window, cx| {
18120            editor.handle_input(">", window, cx);
18121        });
18122        cx.run_until_parked();
18123
18124        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
18125    }
18126}
18127
18128fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18129    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18130    point..point
18131}
18132
18133fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18134    let (text, ranges) = marked_text_ranges(marked_text, true);
18135    assert_eq!(editor.text(cx), text);
18136    assert_eq!(
18137        editor.selections.ranges(cx),
18138        ranges,
18139        "Assert selections are {}",
18140        marked_text
18141    );
18142}
18143
18144pub fn handle_signature_help_request(
18145    cx: &mut EditorLspTestContext,
18146    mocked_response: lsp::SignatureHelp,
18147) -> impl Future<Output = ()> {
18148    let mut request =
18149        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18150            let mocked_response = mocked_response.clone();
18151            async move { Ok(Some(mocked_response)) }
18152        });
18153
18154    async move {
18155        request.next().await;
18156    }
18157}
18158
18159/// Handle completion request passing a marked string specifying where the completion
18160/// should be triggered from using '|' character, what range should be replaced, and what completions
18161/// should be returned using '<' and '>' to delimit the range
18162pub fn handle_completion_request(
18163    cx: &mut EditorLspTestContext,
18164    marked_string: &str,
18165    completions: Vec<&'static str>,
18166    counter: Arc<AtomicUsize>,
18167) -> impl Future<Output = ()> {
18168    let complete_from_marker: TextRangeMarker = '|'.into();
18169    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18170    let (_, mut marked_ranges) = marked_text_ranges_by(
18171        marked_string,
18172        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18173    );
18174
18175    let complete_from_position =
18176        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18177    let replace_range =
18178        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18179
18180    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
18181        let completions = completions.clone();
18182        counter.fetch_add(1, atomic::Ordering::Release);
18183        async move {
18184            assert_eq!(params.text_document_position.text_document.uri, url.clone());
18185            assert_eq!(
18186                params.text_document_position.position,
18187                complete_from_position
18188            );
18189            Ok(Some(lsp::CompletionResponse::Array(
18190                completions
18191                    .iter()
18192                    .map(|completion_text| lsp::CompletionItem {
18193                        label: completion_text.to_string(),
18194                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18195                            range: replace_range,
18196                            new_text: completion_text.to_string(),
18197                        })),
18198                        ..Default::default()
18199                    })
18200                    .collect(),
18201            )))
18202        }
18203    });
18204
18205    async move {
18206        request.next().await;
18207    }
18208}
18209
18210fn handle_resolve_completion_request(
18211    cx: &mut EditorLspTestContext,
18212    edits: Option<Vec<(&'static str, &'static str)>>,
18213) -> impl Future<Output = ()> {
18214    let edits = edits.map(|edits| {
18215        edits
18216            .iter()
18217            .map(|(marked_string, new_text)| {
18218                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18219                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18220                lsp::TextEdit::new(replace_range, new_text.to_string())
18221            })
18222            .collect::<Vec<_>>()
18223    });
18224
18225    let mut request =
18226        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18227            let edits = edits.clone();
18228            async move {
18229                Ok(lsp::CompletionItem {
18230                    additional_text_edits: edits,
18231                    ..Default::default()
18232                })
18233            }
18234        });
18235
18236    async move {
18237        request.next().await;
18238    }
18239}
18240
18241pub(crate) fn update_test_language_settings(
18242    cx: &mut TestAppContext,
18243    f: impl Fn(&mut AllLanguageSettingsContent),
18244) {
18245    cx.update(|cx| {
18246        SettingsStore::update_global(cx, |store, cx| {
18247            store.update_user_settings::<AllLanguageSettings>(cx, f);
18248        });
18249    });
18250}
18251
18252pub(crate) fn update_test_project_settings(
18253    cx: &mut TestAppContext,
18254    f: impl Fn(&mut ProjectSettings),
18255) {
18256    cx.update(|cx| {
18257        SettingsStore::update_global(cx, |store, cx| {
18258            store.update_user_settings::<ProjectSettings>(cx, f);
18259        });
18260    });
18261}
18262
18263pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18264    cx.update(|cx| {
18265        assets::Assets.load_test_fonts(cx);
18266        let store = SettingsStore::test(cx);
18267        cx.set_global(store);
18268        theme::init(theme::LoadThemes::JustBase, cx);
18269        release_channel::init(SemanticVersion::default(), cx);
18270        client::init_settings(cx);
18271        language::init(cx);
18272        Project::init_settings(cx);
18273        workspace::init_settings(cx);
18274        crate::init(cx);
18275    });
18276
18277    update_test_language_settings(cx, f);
18278}
18279
18280#[track_caller]
18281fn assert_hunk_revert(
18282    not_reverted_text_with_selections: &str,
18283    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18284    expected_reverted_text_with_selections: &str,
18285    base_text: &str,
18286    cx: &mut EditorLspTestContext,
18287) {
18288    cx.set_state(not_reverted_text_with_selections);
18289    cx.set_head_text(base_text);
18290    cx.executor().run_until_parked();
18291
18292    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18293        let snapshot = editor.snapshot(window, cx);
18294        let reverted_hunk_statuses = snapshot
18295            .buffer_snapshot
18296            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18297            .map(|hunk| hunk.status().kind)
18298            .collect::<Vec<_>>();
18299
18300        editor.git_restore(&Default::default(), window, cx);
18301        reverted_hunk_statuses
18302    });
18303    cx.executor().run_until_parked();
18304    cx.assert_editor_state(expected_reverted_text_with_selections);
18305    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18306}