editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::IndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45};
   46use workspace::{
   47    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   48    NavigationEntry, ViewId,
   49};
   50
   51#[gpui::test]
   52fn test_edit_events(cx: &mut TestAppContext) {
   53    init_test(cx, |_| {});
   54
   55    let buffer = cx.new_model(|cx| {
   56        let mut buffer = language::Buffer::local("123456", cx);
   57        buffer.set_group_interval(Duration::from_secs(1));
   58        buffer
   59    });
   60
   61    let events = Rc::new(RefCell::new(Vec::new()));
   62    let editor1 = cx.add_window({
   63        let events = events.clone();
   64        |cx| {
   65            let view = cx.view().clone();
   66            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| match event {
   67                EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   68                EditorEvent::BufferEdited => events.borrow_mut().push(("editor1", "buffer edited")),
   69                _ => {}
   70            })
   71            .detach();
   72            Editor::for_buffer(buffer.clone(), None, cx)
   73        }
   74    });
   75
   76    let editor2 = cx.add_window({
   77        let events = events.clone();
   78        |cx| {
   79            cx.subscribe(
   80                &cx.view().clone(),
   81                move |_, _, event: &EditorEvent, _| match event {
   82                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   83                    EditorEvent::BufferEdited => {
   84                        events.borrow_mut().push(("editor2", "buffer edited"))
   85                    }
   86                    _ => {}
   87                },
   88            )
   89            .detach();
   90            Editor::for_buffer(buffer.clone(), None, cx)
   91        }
   92    });
   93
   94    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
   95
   96    // Mutating editor 1 will emit an `Edited` event only for that editor.
   97    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
   98    assert_eq!(
   99        mem::take(&mut *events.borrow_mut()),
  100        [
  101            ("editor1", "edited"),
  102            ("editor1", "buffer edited"),
  103            ("editor2", "buffer edited"),
  104        ]
  105    );
  106
  107    // Mutating editor 2 will emit an `Edited` event only for that editor.
  108    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  109    assert_eq!(
  110        mem::take(&mut *events.borrow_mut()),
  111        [
  112            ("editor2", "edited"),
  113            ("editor1", "buffer edited"),
  114            ("editor2", "buffer edited"),
  115        ]
  116    );
  117
  118    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  119    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  120    assert_eq!(
  121        mem::take(&mut *events.borrow_mut()),
  122        [
  123            ("editor1", "edited"),
  124            ("editor1", "buffer edited"),
  125            ("editor2", "buffer edited"),
  126        ]
  127    );
  128
  129    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  130    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
  131    assert_eq!(
  132        mem::take(&mut *events.borrow_mut()),
  133        [
  134            ("editor1", "edited"),
  135            ("editor1", "buffer edited"),
  136            ("editor2", "buffer edited"),
  137        ]
  138    );
  139
  140    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  141    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
  142    assert_eq!(
  143        mem::take(&mut *events.borrow_mut()),
  144        [
  145            ("editor2", "edited"),
  146            ("editor1", "buffer edited"),
  147            ("editor2", "buffer edited"),
  148        ]
  149    );
  150
  151    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  152    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
  153    assert_eq!(
  154        mem::take(&mut *events.borrow_mut()),
  155        [
  156            ("editor2", "edited"),
  157            ("editor1", "buffer edited"),
  158            ("editor2", "buffer edited"),
  159        ]
  160    );
  161
  162    // No event is emitted when the mutation is a no-op.
  163    _ = editor2.update(cx, |editor, cx| {
  164        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
  165
  166        editor.backspace(&Backspace, cx);
  167    });
  168    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  169}
  170
  171#[gpui::test]
  172fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  173    init_test(cx, |_| {});
  174
  175    let mut now = Instant::now();
  176    let group_interval = Duration::from_millis(1);
  177    let buffer = cx.new_model(|cx| {
  178        let mut buf = language::Buffer::local("123456", cx);
  179        buf.set_group_interval(group_interval);
  180        buf
  181    });
  182    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  183    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
  184
  185    _ = editor.update(cx, |editor, cx| {
  186        editor.start_transaction_at(now, cx);
  187        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
  188
  189        editor.insert("cd", cx);
  190        editor.end_transaction_at(now, cx);
  191        assert_eq!(editor.text(cx), "12cd56");
  192        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  193
  194        editor.start_transaction_at(now, cx);
  195        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
  196        editor.insert("e", cx);
  197        editor.end_transaction_at(now, cx);
  198        assert_eq!(editor.text(cx), "12cde6");
  199        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  200
  201        now += group_interval + Duration::from_millis(1);
  202        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
  203
  204        // Simulate an edit in another editor
  205        buffer.update(cx, |buffer, cx| {
  206            buffer.start_transaction_at(now, cx);
  207            buffer.edit([(0..1, "a")], None, cx);
  208            buffer.edit([(1..1, "b")], None, cx);
  209            buffer.end_transaction_at(now, cx);
  210        });
  211
  212        assert_eq!(editor.text(cx), "ab2cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  214
  215        // Last transaction happened past the group interval in a different editor.
  216        // Undo it individually and don't restore selections.
  217        editor.undo(&Undo, cx);
  218        assert_eq!(editor.text(cx), "12cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  220
  221        // First two transactions happened within the group interval in this editor.
  222        // Undo them together and restore selections.
  223        editor.undo(&Undo, cx);
  224        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
  225        assert_eq!(editor.text(cx), "123456");
  226        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  227
  228        // Redo the first two transactions together.
  229        editor.redo(&Redo, cx);
  230        assert_eq!(editor.text(cx), "12cde6");
  231        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  232
  233        // Redo the last transaction on its own.
  234        editor.redo(&Redo, cx);
  235        assert_eq!(editor.text(cx), "ab2cde6");
  236        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  237
  238        // Test empty transactions.
  239        editor.start_transaction_at(now, cx);
  240        editor.end_transaction_at(now, cx);
  241        editor.undo(&Undo, cx);
  242        assert_eq!(editor.text(cx), "12cde6");
  243    });
  244}
  245
  246#[gpui::test]
  247fn test_ime_composition(cx: &mut TestAppContext) {
  248    init_test(cx, |_| {});
  249
  250    let buffer = cx.new_model(|cx| {
  251        let mut buffer = language::Buffer::local("abcde", cx);
  252        // Ensure automatic grouping doesn't occur.
  253        buffer.set_group_interval(Duration::ZERO);
  254        buffer
  255    });
  256
  257    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
  258    cx.add_window(|cx| {
  259        let mut editor = build_editor(buffer.clone(), cx);
  260
  261        // Start a new IME composition.
  262        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  263        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
  264        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
  265        assert_eq!(editor.text(cx), "äbcde");
  266        assert_eq!(
  267            editor.marked_text_ranges(cx),
  268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  269        );
  270
  271        // Finalize IME composition.
  272        editor.replace_text_in_range(None, "ā", cx);
  273        assert_eq!(editor.text(cx), "ābcde");
  274        assert_eq!(editor.marked_text_ranges(cx), None);
  275
  276        // IME composition edits are grouped and are undone/redone at once.
  277        editor.undo(&Default::default(), cx);
  278        assert_eq!(editor.text(cx), "abcde");
  279        assert_eq!(editor.marked_text_ranges(cx), None);
  280        editor.redo(&Default::default(), cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // Start a new IME composition.
  285        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
  286        assert_eq!(
  287            editor.marked_text_ranges(cx),
  288            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  289        );
  290
  291        // Undoing during an IME composition cancels it.
  292        editor.undo(&Default::default(), cx);
  293        assert_eq!(editor.text(cx), "ābcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295
  296        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  297        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
  298        assert_eq!(editor.text(cx), "ābcdè");
  299        assert_eq!(
  300            editor.marked_text_ranges(cx),
  301            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  302        );
  303
  304        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  305        editor.replace_text_in_range(Some(4..999), "ę", cx);
  306        assert_eq!(editor.text(cx), "ābcdę");
  307        assert_eq!(editor.marked_text_ranges(cx), None);
  308
  309        // Start a new IME composition with multiple cursors.
  310        editor.change_selections(None, cx, |s| {
  311            s.select_ranges([
  312                OffsetUtf16(1)..OffsetUtf16(1),
  313                OffsetUtf16(3)..OffsetUtf16(3),
  314                OffsetUtf16(5)..OffsetUtf16(5),
  315            ])
  316        });
  317        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
  318        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  319        assert_eq!(
  320            editor.marked_text_ranges(cx),
  321            Some(vec![
  322                OffsetUtf16(0)..OffsetUtf16(3),
  323                OffsetUtf16(4)..OffsetUtf16(7),
  324                OffsetUtf16(8)..OffsetUtf16(11)
  325            ])
  326        );
  327
  328        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  329        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
  330        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  331        assert_eq!(
  332            editor.marked_text_ranges(cx),
  333            Some(vec![
  334                OffsetUtf16(1)..OffsetUtf16(2),
  335                OffsetUtf16(5)..OffsetUtf16(6),
  336                OffsetUtf16(9)..OffsetUtf16(10)
  337            ])
  338        );
  339
  340        // Finalize IME composition with multiple cursors.
  341        editor.replace_text_in_range(Some(9..10), "2", cx);
  342        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  343        assert_eq!(editor.marked_text_ranges(cx), None);
  344
  345        editor
  346    });
  347}
  348
  349#[gpui::test]
  350fn test_selection_with_mouse(cx: &mut TestAppContext) {
  351    init_test(cx, |_| {});
  352
  353    let editor = cx.add_window(|cx| {
  354        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  355        build_editor(buffer, cx)
  356    });
  357
  358    _ = editor.update(cx, |view, cx| {
  359        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  360    });
  361    assert_eq!(
  362        editor
  363            .update(cx, |view, cx| view.selections.display_ranges(cx))
  364            .unwrap(),
  365        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  366    );
  367
  368    _ = editor.update(cx, |view, cx| {
  369        view.update_selection(
  370            DisplayPoint::new(DisplayRow(3), 3),
  371            0,
  372            gpui::Point::<f32>::default(),
  373            cx,
  374        );
  375    });
  376
  377    assert_eq!(
  378        editor
  379            .update(cx, |view, cx| view.selections.display_ranges(cx))
  380            .unwrap(),
  381        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  382    );
  383
  384    _ = editor.update(cx, |view, cx| {
  385        view.update_selection(
  386            DisplayPoint::new(DisplayRow(1), 1),
  387            0,
  388            gpui::Point::<f32>::default(),
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |view, cx| view.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  398    );
  399
  400    _ = editor.update(cx, |view, cx| {
  401        view.end_selection(cx);
  402        view.update_selection(
  403            DisplayPoint::new(DisplayRow(3), 3),
  404            0,
  405            gpui::Point::<f32>::default(),
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |view, cx| view.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |view, cx| {
  418        view.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, cx);
  419        view.update_selection(
  420            DisplayPoint::new(DisplayRow(0), 0),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |view, cx| view.selections.display_ranges(cx))
  430            .unwrap(),
  431        [
  432            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  433            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  434        ]
  435    );
  436
  437    _ = editor.update(cx, |view, cx| {
  438        view.end_selection(cx);
  439    });
  440
  441    assert_eq!(
  442        editor
  443            .update(cx, |view, cx| view.selections.display_ranges(cx))
  444            .unwrap(),
  445        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  446    );
  447}
  448
  449#[gpui::test]
  450fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  451    init_test(cx, |_| {});
  452
  453    let editor = cx.add_window(|cx| {
  454        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  455        build_editor(buffer, cx)
  456    });
  457
  458    _ = editor.update(cx, |view, cx| {
  459        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
  460    });
  461
  462    _ = editor.update(cx, |view, cx| {
  463        view.end_selection(cx);
  464    });
  465
  466    _ = editor.update(cx, |view, cx| {
  467        view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
  468    });
  469
  470    _ = editor.update(cx, |view, cx| {
  471        view.end_selection(cx);
  472    });
  473
  474    assert_eq!(
  475        editor
  476            .update(cx, |view, cx| view.selections.display_ranges(cx))
  477            .unwrap(),
  478        [
  479            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  480            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  481        ]
  482    );
  483
  484    _ = editor.update(cx, |view, cx| {
  485        view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
  486    });
  487
  488    _ = editor.update(cx, |view, cx| {
  489        view.end_selection(cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |view, cx| view.selections.display_ranges(cx))
  495            .unwrap(),
  496        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  497    );
  498}
  499
  500#[gpui::test]
  501fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  502    init_test(cx, |_| {});
  503
  504    let view = cx.add_window(|cx| {
  505        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  506        build_editor(buffer, cx)
  507    });
  508
  509    _ = view.update(cx, |view, cx| {
  510        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  511        assert_eq!(
  512            view.selections.display_ranges(cx),
  513            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  514        );
  515    });
  516
  517    _ = view.update(cx, |view, cx| {
  518        view.update_selection(
  519            DisplayPoint::new(DisplayRow(3), 3),
  520            0,
  521            gpui::Point::<f32>::default(),
  522            cx,
  523        );
  524        assert_eq!(
  525            view.selections.display_ranges(cx),
  526            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  527        );
  528    });
  529
  530    _ = view.update(cx, |view, cx| {
  531        view.cancel(&Cancel, cx);
  532        view.update_selection(
  533            DisplayPoint::new(DisplayRow(1), 1),
  534            0,
  535            gpui::Point::<f32>::default(),
  536            cx,
  537        );
  538        assert_eq!(
  539            view.selections.display_ranges(cx),
  540            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  541        );
  542    });
  543}
  544
  545#[gpui::test]
  546fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  547    init_test(cx, |_| {});
  548
  549    let view = cx.add_window(|cx| {
  550        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  551        build_editor(buffer, cx)
  552    });
  553
  554    _ = view.update(cx, |view, cx| {
  555        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  556        assert_eq!(
  557            view.selections.display_ranges(cx),
  558            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  559        );
  560
  561        view.move_down(&Default::default(), cx);
  562        assert_eq!(
  563            view.selections.display_ranges(cx),
  564            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  565        );
  566
  567        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
  568        assert_eq!(
  569            view.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        view.move_up(&Default::default(), cx);
  574        assert_eq!(
  575            view.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  577        );
  578    });
  579}
  580
  581#[gpui::test]
  582fn test_clone(cx: &mut TestAppContext) {
  583    init_test(cx, |_| {});
  584
  585    let (text, selection_ranges) = marked_text_ranges(
  586        indoc! {"
  587            one
  588            two
  589            threeˇ
  590            four
  591            fiveˇ
  592        "},
  593        true,
  594    );
  595
  596    let editor = cx.add_window(|cx| {
  597        let buffer = MultiBuffer::build_simple(&text, cx);
  598        build_editor(buffer, cx)
  599    });
  600
  601    _ = editor.update(cx, |editor, cx| {
  602        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
  603        editor.fold_creases(
  604            vec![
  605                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  606                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  607            ],
  608            true,
  609            cx,
  610        );
  611    });
  612
  613    let cloned_editor = editor
  614        .update(cx, |editor, cx| {
  615            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
  616        })
  617        .unwrap()
  618        .unwrap();
  619
  620    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  621    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
  622
  623    assert_eq!(
  624        cloned_editor
  625            .update(cx, |e, cx| e.display_text(cx))
  626            .unwrap(),
  627        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
  628    );
  629    assert_eq!(
  630        cloned_snapshot
  631            .folds_in_range(0..text.len())
  632            .collect::<Vec<_>>(),
  633        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  634    );
  635    assert_set_eq!(
  636        cloned_editor
  637            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
  638            .unwrap(),
  639        editor
  640            .update(cx, |editor, cx| editor.selections.ranges(cx))
  641            .unwrap()
  642    );
  643    assert_set_eq!(
  644        cloned_editor
  645            .update(cx, |e, cx| e.selections.display_ranges(cx))
  646            .unwrap(),
  647        editor
  648            .update(cx, |e, cx| e.selections.display_ranges(cx))
  649            .unwrap()
  650    );
  651}
  652
  653#[gpui::test]
  654async fn test_navigation_history(cx: &mut TestAppContext) {
  655    init_test(cx, |_| {});
  656
  657    use workspace::item::Item;
  658
  659    let fs = FakeFs::new(cx.executor());
  660    let project = Project::test(fs, [], cx).await;
  661    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
  662    let pane = workspace
  663        .update(cx, |workspace, _| workspace.active_pane().clone())
  664        .unwrap();
  665
  666    _ = workspace.update(cx, |_v, cx| {
  667        cx.new_view(|cx| {
  668            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  669            let mut editor = build_editor(buffer.clone(), cx);
  670            let handle = cx.view();
  671            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(handle)));
  672
  673            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
  674                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  675            }
  676
  677            // Move the cursor a small distance.
  678            // Nothing is added to the navigation history.
  679            editor.change_selections(None, cx, |s| {
  680                s.select_display_ranges([
  681                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  682                ])
  683            });
  684            editor.change_selections(None, cx, |s| {
  685                s.select_display_ranges([
  686                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  687                ])
  688            });
  689            assert!(pop_history(&mut editor, cx).is_none());
  690
  691            // Move the cursor a large distance.
  692            // The history can jump back to the previous position.
  693            editor.change_selections(None, cx, |s| {
  694                s.select_display_ranges([
  695                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  696                ])
  697            });
  698            let nav_entry = pop_history(&mut editor, cx).unwrap();
  699            editor.navigate(nav_entry.data.unwrap(), cx);
  700            assert_eq!(nav_entry.item.id(), cx.entity_id());
  701            assert_eq!(
  702                editor.selections.display_ranges(cx),
  703                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  704            );
  705            assert!(pop_history(&mut editor, cx).is_none());
  706
  707            // Move the cursor a small distance via the mouse.
  708            // Nothing is added to the navigation history.
  709            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, cx);
  710            editor.end_selection(cx);
  711            assert_eq!(
  712                editor.selections.display_ranges(cx),
  713                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  714            );
  715            assert!(pop_history(&mut editor, cx).is_none());
  716
  717            // Move the cursor a large distance via the mouse.
  718            // The history can jump back to the previous position.
  719            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, cx);
  720            editor.end_selection(cx);
  721            assert_eq!(
  722                editor.selections.display_ranges(cx),
  723                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  724            );
  725            let nav_entry = pop_history(&mut editor, cx).unwrap();
  726            editor.navigate(nav_entry.data.unwrap(), cx);
  727            assert_eq!(nav_entry.item.id(), cx.entity_id());
  728            assert_eq!(
  729                editor.selections.display_ranges(cx),
  730                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  731            );
  732            assert!(pop_history(&mut editor, cx).is_none());
  733
  734            // Set scroll position to check later
  735            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
  736            let original_scroll_position = editor.scroll_manager.anchor();
  737
  738            // Jump to the end of the document and adjust scroll
  739            editor.move_to_end(&MoveToEnd, cx);
  740            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
  741            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  742
  743            let nav_entry = pop_history(&mut editor, cx).unwrap();
  744            editor.navigate(nav_entry.data.unwrap(), cx);
  745            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  746
  747            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  748            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  749            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  750            let invalid_point = Point::new(9999, 0);
  751            editor.navigate(
  752                Box::new(NavigationData {
  753                    cursor_anchor: invalid_anchor,
  754                    cursor_position: invalid_point,
  755                    scroll_anchor: ScrollAnchor {
  756                        anchor: invalid_anchor,
  757                        offset: Default::default(),
  758                    },
  759                    scroll_top_row: invalid_point.row,
  760                }),
  761                cx,
  762            );
  763            assert_eq!(
  764                editor.selections.display_ranges(cx),
  765                &[editor.max_point(cx)..editor.max_point(cx)]
  766            );
  767            assert_eq!(
  768                editor.scroll_position(cx),
  769                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  770            );
  771
  772            editor
  773        })
  774    });
  775}
  776
  777#[gpui::test]
  778fn test_cancel(cx: &mut TestAppContext) {
  779    init_test(cx, |_| {});
  780
  781    let view = cx.add_window(|cx| {
  782        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  783        build_editor(buffer, cx)
  784    });
  785
  786    _ = view.update(cx, |view, cx| {
  787        view.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, cx);
  788        view.update_selection(
  789            DisplayPoint::new(DisplayRow(1), 1),
  790            0,
  791            gpui::Point::<f32>::default(),
  792            cx,
  793        );
  794        view.end_selection(cx);
  795
  796        view.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, cx);
  797        view.update_selection(
  798            DisplayPoint::new(DisplayRow(0), 3),
  799            0,
  800            gpui::Point::<f32>::default(),
  801            cx,
  802        );
  803        view.end_selection(cx);
  804        assert_eq!(
  805            view.selections.display_ranges(cx),
  806            [
  807                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  808                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  809            ]
  810        );
  811    });
  812
  813    _ = view.update(cx, |view, cx| {
  814        view.cancel(&Cancel, cx);
  815        assert_eq!(
  816            view.selections.display_ranges(cx),
  817            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  818        );
  819    });
  820
  821    _ = view.update(cx, |view, cx| {
  822        view.cancel(&Cancel, cx);
  823        assert_eq!(
  824            view.selections.display_ranges(cx),
  825            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  826        );
  827    });
  828}
  829
  830#[gpui::test]
  831fn test_fold_action(cx: &mut TestAppContext) {
  832    init_test(cx, |_| {});
  833
  834    let view = cx.add_window(|cx| {
  835        let buffer = MultiBuffer::build_simple(
  836            &"
  837                impl Foo {
  838                    // Hello!
  839
  840                    fn a() {
  841                        1
  842                    }
  843
  844                    fn b() {
  845                        2
  846                    }
  847
  848                    fn c() {
  849                        3
  850                    }
  851                }
  852            "
  853            .unindent(),
  854            cx,
  855        );
  856        build_editor(buffer.clone(), cx)
  857    });
  858
  859    _ = view.update(cx, |view, cx| {
  860        view.change_selections(None, cx, |s| {
  861            s.select_display_ranges([
  862                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  863            ]);
  864        });
  865        view.fold(&Fold, cx);
  866        assert_eq!(
  867            view.display_text(cx),
  868            "
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {⋯
  877                    }
  878
  879                    fn c() {⋯
  880                    }
  881                }
  882            "
  883            .unindent(),
  884        );
  885
  886        view.fold(&Fold, cx);
  887        assert_eq!(
  888            view.display_text(cx),
  889            "
  890                impl Foo {⋯
  891                }
  892            "
  893            .unindent(),
  894        );
  895
  896        view.unfold_lines(&UnfoldLines, cx);
  897        assert_eq!(
  898            view.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        view.unfold_lines(&UnfoldLines, cx);
  918        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  919    });
  920}
  921
  922#[gpui::test]
  923fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  924    init_test(cx, |_| {});
  925
  926    let view = cx.add_window(|cx| {
  927        let buffer = MultiBuffer::build_simple(
  928            &"
  929                class Foo:
  930                    # Hello!
  931
  932                    def a():
  933                        print(1)
  934
  935                    def b():
  936                        print(2)
  937
  938                    def c():
  939                        print(3)
  940            "
  941            .unindent(),
  942            cx,
  943        );
  944        build_editor(buffer.clone(), cx)
  945    });
  946
  947    _ = view.update(cx, |view, cx| {
  948        view.change_selections(None, cx, |s| {
  949            s.select_display_ranges([
  950                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  951            ]);
  952        });
  953        view.fold(&Fold, cx);
  954        assert_eq!(
  955            view.display_text(cx),
  956            "
  957                class Foo:
  958                    # Hello!
  959
  960                    def a():
  961                        print(1)
  962
  963                    def b():⋯
  964
  965                    def c():⋯
  966            "
  967            .unindent(),
  968        );
  969
  970        view.fold(&Fold, cx);
  971        assert_eq!(
  972            view.display_text(cx),
  973            "
  974                class Foo:⋯
  975            "
  976            .unindent(),
  977        );
  978
  979        view.unfold_lines(&UnfoldLines, cx);
  980        assert_eq!(
  981            view.display_text(cx),
  982            "
  983                class Foo:
  984                    # Hello!
  985
  986                    def a():
  987                        print(1)
  988
  989                    def b():⋯
  990
  991                    def c():⋯
  992            "
  993            .unindent(),
  994        );
  995
  996        view.unfold_lines(&UnfoldLines, cx);
  997        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
  998    });
  999}
 1000
 1001#[gpui::test]
 1002fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1003    init_test(cx, |_| {});
 1004
 1005    let view = cx.add_window(|cx| {
 1006        let buffer = MultiBuffer::build_simple(
 1007            &"
 1008                class Foo:
 1009                    # Hello!
 1010
 1011                    def a():
 1012                        print(1)
 1013
 1014                    def b():
 1015                        print(2)
 1016
 1017
 1018                    def c():
 1019                        print(3)
 1020
 1021
 1022            "
 1023            .unindent(),
 1024            cx,
 1025        );
 1026        build_editor(buffer.clone(), cx)
 1027    });
 1028
 1029    _ = view.update(cx, |view, cx| {
 1030        view.change_selections(None, cx, |s| {
 1031            s.select_display_ranges([
 1032                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1033            ]);
 1034        });
 1035        view.fold(&Fold, cx);
 1036        assert_eq!(
 1037            view.display_text(cx),
 1038            "
 1039                class Foo:
 1040                    # Hello!
 1041
 1042                    def a():
 1043                        print(1)
 1044
 1045                    def b():⋯
 1046
 1047
 1048                    def c():⋯
 1049
 1050
 1051            "
 1052            .unindent(),
 1053        );
 1054
 1055        view.fold(&Fold, cx);
 1056        assert_eq!(
 1057            view.display_text(cx),
 1058            "
 1059                class Foo:⋯
 1060
 1061
 1062            "
 1063            .unindent(),
 1064        );
 1065
 1066        view.unfold_lines(&UnfoldLines, cx);
 1067        assert_eq!(
 1068            view.display_text(cx),
 1069            "
 1070                class Foo:
 1071                    # Hello!
 1072
 1073                    def a():
 1074                        print(1)
 1075
 1076                    def b():⋯
 1077
 1078
 1079                    def c():⋯
 1080
 1081
 1082            "
 1083            .unindent(),
 1084        );
 1085
 1086        view.unfold_lines(&UnfoldLines, cx);
 1087        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1088    });
 1089}
 1090
 1091#[gpui::test]
 1092fn test_fold_at_level(cx: &mut TestAppContext) {
 1093    init_test(cx, |_| {});
 1094
 1095    let view = cx.add_window(|cx| {
 1096        let buffer = MultiBuffer::build_simple(
 1097            &"
 1098                class Foo:
 1099                    # Hello!
 1100
 1101                    def a():
 1102                        print(1)
 1103
 1104                    def b():
 1105                        print(2)
 1106
 1107
 1108                class Bar:
 1109                    # World!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():
 1115                        print(2)
 1116
 1117
 1118            "
 1119            .unindent(),
 1120            cx,
 1121        );
 1122        build_editor(buffer.clone(), cx)
 1123    });
 1124
 1125    _ = view.update(cx, |view, cx| {
 1126        view.fold_at_level(&FoldAtLevel { level: 2 }, cx);
 1127        assert_eq!(
 1128            view.display_text(cx),
 1129            "
 1130                class Foo:
 1131                    # Hello!
 1132
 1133                    def a():⋯
 1134
 1135                    def b():⋯
 1136
 1137
 1138                class Bar:
 1139                    # World!
 1140
 1141                    def a():⋯
 1142
 1143                    def b():⋯
 1144
 1145
 1146            "
 1147            .unindent(),
 1148        );
 1149
 1150        view.fold_at_level(&FoldAtLevel { level: 1 }, cx);
 1151        assert_eq!(
 1152            view.display_text(cx),
 1153            "
 1154                class Foo:⋯
 1155
 1156
 1157                class Bar:⋯
 1158
 1159
 1160            "
 1161            .unindent(),
 1162        );
 1163
 1164        view.unfold_all(&UnfoldAll, cx);
 1165        view.fold_at_level(&FoldAtLevel { level: 0 }, cx);
 1166        assert_eq!(
 1167            view.display_text(cx),
 1168            "
 1169                class Foo:
 1170                    # Hello!
 1171
 1172                    def a():
 1173                        print(1)
 1174
 1175                    def b():
 1176                        print(2)
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():
 1183                        print(1)
 1184
 1185                    def b():
 1186                        print(2)
 1187
 1188
 1189            "
 1190            .unindent(),
 1191        );
 1192
 1193        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 1194    });
 1195}
 1196
 1197#[gpui::test]
 1198fn test_move_cursor(cx: &mut TestAppContext) {
 1199    init_test(cx, |_| {});
 1200
 1201    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1202    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 1203
 1204    buffer.update(cx, |buffer, cx| {
 1205        buffer.edit(
 1206            vec![
 1207                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1208                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1209            ],
 1210            None,
 1211            cx,
 1212        );
 1213    });
 1214    _ = view.update(cx, |view, cx| {
 1215        assert_eq!(
 1216            view.selections.display_ranges(cx),
 1217            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1218        );
 1219
 1220        view.move_down(&MoveDown, cx);
 1221        assert_eq!(
 1222            view.selections.display_ranges(cx),
 1223            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1224        );
 1225
 1226        view.move_right(&MoveRight, cx);
 1227        assert_eq!(
 1228            view.selections.display_ranges(cx),
 1229            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1230        );
 1231
 1232        view.move_left(&MoveLeft, cx);
 1233        assert_eq!(
 1234            view.selections.display_ranges(cx),
 1235            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1236        );
 1237
 1238        view.move_up(&MoveUp, cx);
 1239        assert_eq!(
 1240            view.selections.display_ranges(cx),
 1241            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1242        );
 1243
 1244        view.move_to_end(&MoveToEnd, cx);
 1245        assert_eq!(
 1246            view.selections.display_ranges(cx),
 1247            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1248        );
 1249
 1250        view.move_to_beginning(&MoveToBeginning, cx);
 1251        assert_eq!(
 1252            view.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        view.change_selections(None, cx, |s| {
 1257            s.select_display_ranges([
 1258                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1259            ]);
 1260        });
 1261        view.select_to_beginning(&SelectToBeginning, cx);
 1262        assert_eq!(
 1263            view.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1265        );
 1266
 1267        view.select_to_end(&SelectToEnd, cx);
 1268        assert_eq!(
 1269            view.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1271        );
 1272    });
 1273}
 1274
 1275// TODO: Re-enable this test
 1276#[cfg(target_os = "macos")]
 1277#[gpui::test]
 1278fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1279    init_test(cx, |_| {});
 1280
 1281    let view = cx.add_window(|cx| {
 1282        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 1283        build_editor(buffer.clone(), cx)
 1284    });
 1285
 1286    assert_eq!('ⓐ'.len_utf8(), 3);
 1287    assert_eq!('α'.len_utf8(), 2);
 1288
 1289    _ = view.update(cx, |view, cx| {
 1290        view.fold_creases(
 1291            vec![
 1292                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
 1293                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1294                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1295            ],
 1296            true,
 1297            cx,
 1298        );
 1299        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 1300
 1301        view.move_right(&MoveRight, cx);
 1302        assert_eq!(
 1303            view.selections.display_ranges(cx),
 1304            &[empty_range(0, "".len())]
 1305        );
 1306        view.move_right(&MoveRight, cx);
 1307        assert_eq!(
 1308            view.selections.display_ranges(cx),
 1309            &[empty_range(0, "ⓐⓑ".len())]
 1310        );
 1311        view.move_right(&MoveRight, cx);
 1312        assert_eq!(
 1313            view.selections.display_ranges(cx),
 1314            &[empty_range(0, "ⓐⓑ⋯".len())]
 1315        );
 1316
 1317        view.move_down(&MoveDown, cx);
 1318        assert_eq!(
 1319            view.selections.display_ranges(cx),
 1320            &[empty_range(1, "ab⋯e".len())]
 1321        );
 1322        view.move_left(&MoveLeft, cx);
 1323        assert_eq!(
 1324            view.selections.display_ranges(cx),
 1325            &[empty_range(1, "ab⋯".len())]
 1326        );
 1327        view.move_left(&MoveLeft, cx);
 1328        assert_eq!(
 1329            view.selections.display_ranges(cx),
 1330            &[empty_range(1, "ab".len())]
 1331        );
 1332        view.move_left(&MoveLeft, cx);
 1333        assert_eq!(
 1334            view.selections.display_ranges(cx),
 1335            &[empty_range(1, "a".len())]
 1336        );
 1337
 1338        view.move_down(&MoveDown, cx);
 1339        assert_eq!(
 1340            view.selections.display_ranges(cx),
 1341            &[empty_range(2, "α".len())]
 1342        );
 1343        view.move_right(&MoveRight, cx);
 1344        assert_eq!(
 1345            view.selections.display_ranges(cx),
 1346            &[empty_range(2, "αβ".len())]
 1347        );
 1348        view.move_right(&MoveRight, cx);
 1349        assert_eq!(
 1350            view.selections.display_ranges(cx),
 1351            &[empty_range(2, "αβ⋯".len())]
 1352        );
 1353        view.move_right(&MoveRight, cx);
 1354        assert_eq!(
 1355            view.selections.display_ranges(cx),
 1356            &[empty_range(2, "αβ⋯ε".len())]
 1357        );
 1358
 1359        view.move_up(&MoveUp, cx);
 1360        assert_eq!(
 1361            view.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯e".len())]
 1363        );
 1364        view.move_down(&MoveDown, cx);
 1365        assert_eq!(
 1366            view.selections.display_ranges(cx),
 1367            &[empty_range(2, "αβ⋯ε".len())]
 1368        );
 1369        view.move_up(&MoveUp, cx);
 1370        assert_eq!(
 1371            view.selections.display_ranges(cx),
 1372            &[empty_range(1, "ab⋯e".len())]
 1373        );
 1374
 1375        view.move_up(&MoveUp, cx);
 1376        assert_eq!(
 1377            view.selections.display_ranges(cx),
 1378            &[empty_range(0, "ⓐⓑ".len())]
 1379        );
 1380        view.move_left(&MoveLeft, cx);
 1381        assert_eq!(
 1382            view.selections.display_ranges(cx),
 1383            &[empty_range(0, "".len())]
 1384        );
 1385        view.move_left(&MoveLeft, cx);
 1386        assert_eq!(
 1387            view.selections.display_ranges(cx),
 1388            &[empty_range(0, "".len())]
 1389        );
 1390    });
 1391}
 1392
 1393#[gpui::test]
 1394fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1395    init_test(cx, |_| {});
 1396
 1397    let view = cx.add_window(|cx| {
 1398        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1399        build_editor(buffer.clone(), cx)
 1400    });
 1401    _ = view.update(cx, |view, cx| {
 1402        view.change_selections(None, cx, |s| {
 1403            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1404        });
 1405
 1406        // moving above start of document should move selection to start of document,
 1407        // but the next move down should still be at the original goal_x
 1408        view.move_up(&MoveUp, cx);
 1409        assert_eq!(
 1410            view.selections.display_ranges(cx),
 1411            &[empty_range(0, "".len())]
 1412        );
 1413
 1414        view.move_down(&MoveDown, cx);
 1415        assert_eq!(
 1416            view.selections.display_ranges(cx),
 1417            &[empty_range(1, "abcd".len())]
 1418        );
 1419
 1420        view.move_down(&MoveDown, cx);
 1421        assert_eq!(
 1422            view.selections.display_ranges(cx),
 1423            &[empty_range(2, "αβγ".len())]
 1424        );
 1425
 1426        view.move_down(&MoveDown, cx);
 1427        assert_eq!(
 1428            view.selections.display_ranges(cx),
 1429            &[empty_range(3, "abcd".len())]
 1430        );
 1431
 1432        view.move_down(&MoveDown, cx);
 1433        assert_eq!(
 1434            view.selections.display_ranges(cx),
 1435            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1436        );
 1437
 1438        // moving past end of document should not change goal_x
 1439        view.move_down(&MoveDown, cx);
 1440        assert_eq!(
 1441            view.selections.display_ranges(cx),
 1442            &[empty_range(5, "".len())]
 1443        );
 1444
 1445        view.move_down(&MoveDown, cx);
 1446        assert_eq!(
 1447            view.selections.display_ranges(cx),
 1448            &[empty_range(5, "".len())]
 1449        );
 1450
 1451        view.move_up(&MoveUp, cx);
 1452        assert_eq!(
 1453            view.selections.display_ranges(cx),
 1454            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1455        );
 1456
 1457        view.move_up(&MoveUp, cx);
 1458        assert_eq!(
 1459            view.selections.display_ranges(cx),
 1460            &[empty_range(3, "abcd".len())]
 1461        );
 1462
 1463        view.move_up(&MoveUp, cx);
 1464        assert_eq!(
 1465            view.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468    });
 1469}
 1470
 1471#[gpui::test]
 1472fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1473    init_test(cx, |_| {});
 1474    let move_to_beg = MoveToBeginningOfLine {
 1475        stop_at_soft_wraps: true,
 1476    };
 1477
 1478    let move_to_end = MoveToEndOfLine {
 1479        stop_at_soft_wraps: true,
 1480    };
 1481
 1482    let view = cx.add_window(|cx| {
 1483        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1484        build_editor(buffer, cx)
 1485    });
 1486    _ = view.update(cx, |view, cx| {
 1487        view.change_selections(None, cx, |s| {
 1488            s.select_display_ranges([
 1489                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1490                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1491            ]);
 1492        });
 1493    });
 1494
 1495    _ = view.update(cx, |view, cx| {
 1496        view.move_to_beginning_of_line(&move_to_beg, cx);
 1497        assert_eq!(
 1498            view.selections.display_ranges(cx),
 1499            &[
 1500                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1501                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1502            ]
 1503        );
 1504    });
 1505
 1506    _ = view.update(cx, |view, cx| {
 1507        view.move_to_beginning_of_line(&move_to_beg, cx);
 1508        assert_eq!(
 1509            view.selections.display_ranges(cx),
 1510            &[
 1511                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1512                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1513            ]
 1514        );
 1515    });
 1516
 1517    _ = view.update(cx, |view, cx| {
 1518        view.move_to_beginning_of_line(&move_to_beg, cx);
 1519        assert_eq!(
 1520            view.selections.display_ranges(cx),
 1521            &[
 1522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1523                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1524            ]
 1525        );
 1526    });
 1527
 1528    _ = view.update(cx, |view, cx| {
 1529        view.move_to_end_of_line(&move_to_end, cx);
 1530        assert_eq!(
 1531            view.selections.display_ranges(cx),
 1532            &[
 1533                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1534                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1535            ]
 1536        );
 1537    });
 1538
 1539    // Moving to the end of line again is a no-op.
 1540    _ = view.update(cx, |view, cx| {
 1541        view.move_to_end_of_line(&move_to_end, cx);
 1542        assert_eq!(
 1543            view.selections.display_ranges(cx),
 1544            &[
 1545                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1546                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1547            ]
 1548        );
 1549    });
 1550
 1551    _ = view.update(cx, |view, cx| {
 1552        view.move_left(&MoveLeft, cx);
 1553        view.select_to_beginning_of_line(
 1554            &SelectToBeginningOfLine {
 1555                stop_at_soft_wraps: true,
 1556            },
 1557            cx,
 1558        );
 1559        assert_eq!(
 1560            view.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = view.update(cx, |view, cx| {
 1569        view.select_to_beginning_of_line(
 1570            &SelectToBeginningOfLine {
 1571                stop_at_soft_wraps: true,
 1572            },
 1573            cx,
 1574        );
 1575        assert_eq!(
 1576            view.selections.display_ranges(cx),
 1577            &[
 1578                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1579                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1580            ]
 1581        );
 1582    });
 1583
 1584    _ = view.update(cx, |view, cx| {
 1585        view.select_to_beginning_of_line(
 1586            &SelectToBeginningOfLine {
 1587                stop_at_soft_wraps: true,
 1588            },
 1589            cx,
 1590        );
 1591        assert_eq!(
 1592            view.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1595                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = view.update(cx, |view, cx| {
 1601        view.select_to_end_of_line(
 1602            &SelectToEndOfLine {
 1603                stop_at_soft_wraps: true,
 1604            },
 1605            cx,
 1606        );
 1607        assert_eq!(
 1608            view.selections.display_ranges(cx),
 1609            &[
 1610                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1611                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1612            ]
 1613        );
 1614    });
 1615
 1616    _ = view.update(cx, |view, cx| {
 1617        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
 1618        assert_eq!(view.display_text(cx), "ab\n  de");
 1619        assert_eq!(
 1620            view.selections.display_ranges(cx),
 1621            &[
 1622                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1623                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1624            ]
 1625        );
 1626    });
 1627
 1628    _ = view.update(cx, |view, cx| {
 1629        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 1630        assert_eq!(view.display_text(cx), "\n");
 1631        assert_eq!(
 1632            view.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1636            ]
 1637        );
 1638    });
 1639}
 1640
 1641#[gpui::test]
 1642fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1643    init_test(cx, |_| {});
 1644    let move_to_beg = MoveToBeginningOfLine {
 1645        stop_at_soft_wraps: false,
 1646    };
 1647
 1648    let move_to_end = MoveToEndOfLine {
 1649        stop_at_soft_wraps: false,
 1650    };
 1651
 1652    let view = cx.add_window(|cx| {
 1653        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1654        build_editor(buffer, cx)
 1655    });
 1656
 1657    _ = view.update(cx, |view, cx| {
 1658        view.set_wrap_width(Some(140.0.into()), cx);
 1659
 1660        // We expect the following lines after wrapping
 1661        // ```
 1662        // thequickbrownfox
 1663        // jumpedoverthelazydo
 1664        // gs
 1665        // ```
 1666        // The final `gs` was soft-wrapped onto a new line.
 1667        assert_eq!(
 1668            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1669            view.display_text(cx),
 1670        );
 1671
 1672        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1673        // Start the cursor at the `k` on the first line
 1674        view.change_selections(None, cx, |s| {
 1675            s.select_display_ranges([
 1676                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1677            ]);
 1678        });
 1679
 1680        // Moving to the beginning of the line should put us at the beginning of the line.
 1681        view.move_to_beginning_of_line(&move_to_beg, cx);
 1682        assert_eq!(
 1683            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1684            view.selections.display_ranges(cx)
 1685        );
 1686
 1687        // Moving to the end of the line should put us at the end of the line.
 1688        view.move_to_end_of_line(&move_to_end, cx);
 1689        assert_eq!(
 1690            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1691            view.selections.display_ranges(cx)
 1692        );
 1693
 1694        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1695        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1696        view.change_selections(None, cx, |s| {
 1697            s.select_display_ranges([
 1698                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1699            ]);
 1700        });
 1701
 1702        // Moving to the beginning of the line should put us at the start of the second line of
 1703        // display text, i.e., the `j`.
 1704        view.move_to_beginning_of_line(&move_to_beg, cx);
 1705        assert_eq!(
 1706            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1707            view.selections.display_ranges(cx)
 1708        );
 1709
 1710        // Moving to the beginning of the line again should be a no-op.
 1711        view.move_to_beginning_of_line(&move_to_beg, cx);
 1712        assert_eq!(
 1713            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1714            view.selections.display_ranges(cx)
 1715        );
 1716
 1717        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1718        // next display line.
 1719        view.move_to_end_of_line(&move_to_end, cx);
 1720        assert_eq!(
 1721            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1722            view.selections.display_ranges(cx)
 1723        );
 1724
 1725        // Moving to the end of the line again should be a no-op.
 1726        view.move_to_end_of_line(&move_to_end, cx);
 1727        assert_eq!(
 1728            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1729            view.selections.display_ranges(cx)
 1730        );
 1731    });
 1732}
 1733
 1734#[gpui::test]
 1735fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1736    init_test(cx, |_| {});
 1737
 1738    let view = cx.add_window(|cx| {
 1739        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1740        build_editor(buffer, cx)
 1741    });
 1742    _ = view.update(cx, |view, cx| {
 1743        view.change_selections(None, cx, |s| {
 1744            s.select_display_ranges([
 1745                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1746                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1747            ])
 1748        });
 1749
 1750        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1751        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1752
 1753        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1754        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
 1755
 1756        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1757        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
 1758
 1759        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1760        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1761
 1762        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1763        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
 1764
 1765        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1766        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
 1767
 1768        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1769        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
 1770
 1771        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1772        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
 1773
 1774        view.move_right(&MoveRight, cx);
 1775        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1776        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1777
 1778        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
 1779        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
 1780
 1781        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
 1782        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
 1783    });
 1784}
 1785
 1786#[gpui::test]
 1787fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1788    init_test(cx, |_| {});
 1789
 1790    let view = cx.add_window(|cx| {
 1791        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1792        build_editor(buffer, cx)
 1793    });
 1794
 1795    _ = view.update(cx, |view, cx| {
 1796        view.set_wrap_width(Some(140.0.into()), cx);
 1797        assert_eq!(
 1798            view.display_text(cx),
 1799            "use one::{\n    two::three::\n    four::five\n};"
 1800        );
 1801
 1802        view.change_selections(None, cx, |s| {
 1803            s.select_display_ranges([
 1804                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1805            ]);
 1806        });
 1807
 1808        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1809        assert_eq!(
 1810            view.selections.display_ranges(cx),
 1811            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1812        );
 1813
 1814        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1815        assert_eq!(
 1816            view.selections.display_ranges(cx),
 1817            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1818        );
 1819
 1820        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1821        assert_eq!(
 1822            view.selections.display_ranges(cx),
 1823            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1824        );
 1825
 1826        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
 1827        assert_eq!(
 1828            view.selections.display_ranges(cx),
 1829            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1830        );
 1831
 1832        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1833        assert_eq!(
 1834            view.selections.display_ranges(cx),
 1835            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1836        );
 1837
 1838        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
 1839        assert_eq!(
 1840            view.selections.display_ranges(cx),
 1841            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1842        );
 1843    });
 1844}
 1845
 1846#[gpui::test]
 1847async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1848    init_test(cx, |_| {});
 1849    let mut cx = EditorTestContext::new(cx).await;
 1850
 1851    let line_height = cx.editor(|editor, cx| {
 1852        editor
 1853            .style()
 1854            .unwrap()
 1855            .text
 1856            .line_height_in_pixels(cx.rem_size())
 1857    });
 1858    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1859
 1860    cx.set_state(
 1861        &r#"ˇone
 1862        two
 1863
 1864        three
 1865        fourˇ
 1866        five
 1867
 1868        six"#
 1869            .unindent(),
 1870    );
 1871
 1872    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1873    cx.assert_editor_state(
 1874        &r#"one
 1875        two
 1876        ˇ
 1877        three
 1878        four
 1879        five
 1880        ˇ
 1881        six"#
 1882            .unindent(),
 1883    );
 1884
 1885    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1886    cx.assert_editor_state(
 1887        &r#"one
 1888        two
 1889
 1890        three
 1891        four
 1892        five
 1893        ˇ
 1894        sixˇ"#
 1895            .unindent(),
 1896    );
 1897
 1898    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
 1899    cx.assert_editor_state(
 1900        &r#"one
 1901        two
 1902
 1903        three
 1904        four
 1905        five
 1906
 1907        sixˇ"#
 1908            .unindent(),
 1909    );
 1910
 1911    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1912    cx.assert_editor_state(
 1913        &r#"one
 1914        two
 1915
 1916        three
 1917        four
 1918        five
 1919        ˇ
 1920        six"#
 1921            .unindent(),
 1922    );
 1923
 1924    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1925    cx.assert_editor_state(
 1926        &r#"one
 1927        two
 1928        ˇ
 1929        three
 1930        four
 1931        five
 1932
 1933        six"#
 1934            .unindent(),
 1935    );
 1936
 1937    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
 1938    cx.assert_editor_state(
 1939        &r#"ˇone
 1940        two
 1941
 1942        three
 1943        four
 1944        five
 1945
 1946        six"#
 1947            .unindent(),
 1948    );
 1949}
 1950
 1951#[gpui::test]
 1952async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 1953    init_test(cx, |_| {});
 1954    let mut cx = EditorTestContext::new(cx).await;
 1955    let line_height = cx.editor(|editor, cx| {
 1956        editor
 1957            .style()
 1958            .unwrap()
 1959            .text
 1960            .line_height_in_pixels(cx.rem_size())
 1961    });
 1962    let window = cx.window;
 1963    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 1964
 1965    cx.set_state(
 1966        r#"ˇone
 1967        two
 1968        three
 1969        four
 1970        five
 1971        six
 1972        seven
 1973        eight
 1974        nine
 1975        ten
 1976        "#,
 1977    );
 1978
 1979    cx.update_editor(|editor, cx| {
 1980        assert_eq!(
 1981            editor.snapshot(cx).scroll_position(),
 1982            gpui::Point::new(0., 0.)
 1983        );
 1984        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1985        assert_eq!(
 1986            editor.snapshot(cx).scroll_position(),
 1987            gpui::Point::new(0., 3.)
 1988        );
 1989        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
 1990        assert_eq!(
 1991            editor.snapshot(cx).scroll_position(),
 1992            gpui::Point::new(0., 6.)
 1993        );
 1994        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
 1995        assert_eq!(
 1996            editor.snapshot(cx).scroll_position(),
 1997            gpui::Point::new(0., 3.)
 1998        );
 1999
 2000        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
 2001        assert_eq!(
 2002            editor.snapshot(cx).scroll_position(),
 2003            gpui::Point::new(0., 1.)
 2004        );
 2005        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
 2006        assert_eq!(
 2007            editor.snapshot(cx).scroll_position(),
 2008            gpui::Point::new(0., 3.)
 2009        );
 2010    });
 2011}
 2012
 2013#[gpui::test]
 2014async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2015    init_test(cx, |_| {});
 2016    let mut cx = EditorTestContext::new(cx).await;
 2017
 2018    let line_height = cx.update_editor(|editor, cx| {
 2019        editor.set_vertical_scroll_margin(2, cx);
 2020        editor
 2021            .style()
 2022            .unwrap()
 2023            .text
 2024            .line_height_in_pixels(cx.rem_size())
 2025    });
 2026    let window = cx.window;
 2027    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2028
 2029    cx.set_state(
 2030        r#"ˇone
 2031            two
 2032            three
 2033            four
 2034            five
 2035            six
 2036            seven
 2037            eight
 2038            nine
 2039            ten
 2040        "#,
 2041    );
 2042    cx.update_editor(|editor, cx| {
 2043        assert_eq!(
 2044            editor.snapshot(cx).scroll_position(),
 2045            gpui::Point::new(0., 0.0)
 2046        );
 2047    });
 2048
 2049    // Add a cursor below the visible area. Since both cursors cannot fit
 2050    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2051    // allows the vertical scroll margin below that cursor.
 2052    cx.update_editor(|editor, cx| {
 2053        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2054            selections.select_ranges([
 2055                Point::new(0, 0)..Point::new(0, 0),
 2056                Point::new(6, 0)..Point::new(6, 0),
 2057            ]);
 2058        })
 2059    });
 2060    cx.update_editor(|editor, cx| {
 2061        assert_eq!(
 2062            editor.snapshot(cx).scroll_position(),
 2063            gpui::Point::new(0., 3.0)
 2064        );
 2065    });
 2066
 2067    // Move down. The editor cursor scrolls down to track the newest cursor.
 2068    cx.update_editor(|editor, cx| {
 2069        editor.move_down(&Default::default(), cx);
 2070    });
 2071    cx.update_editor(|editor, cx| {
 2072        assert_eq!(
 2073            editor.snapshot(cx).scroll_position(),
 2074            gpui::Point::new(0., 4.0)
 2075        );
 2076    });
 2077
 2078    // Add a cursor above the visible area. Since both cursors fit on screen,
 2079    // the editor scrolls to show both.
 2080    cx.update_editor(|editor, cx| {
 2081        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
 2082            selections.select_ranges([
 2083                Point::new(1, 0)..Point::new(1, 0),
 2084                Point::new(6, 0)..Point::new(6, 0),
 2085            ]);
 2086        })
 2087    });
 2088    cx.update_editor(|editor, cx| {
 2089        assert_eq!(
 2090            editor.snapshot(cx).scroll_position(),
 2091            gpui::Point::new(0., 1.0)
 2092        );
 2093    });
 2094}
 2095
 2096#[gpui::test]
 2097async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2098    init_test(cx, |_| {});
 2099    let mut cx = EditorTestContext::new(cx).await;
 2100
 2101    let line_height = cx.editor(|editor, cx| {
 2102        editor
 2103            .style()
 2104            .unwrap()
 2105            .text
 2106            .line_height_in_pixels(cx.rem_size())
 2107    });
 2108    let window = cx.window;
 2109    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2110    cx.set_state(
 2111        &r#"
 2112        ˇone
 2113        two
 2114        threeˇ
 2115        four
 2116        five
 2117        six
 2118        seven
 2119        eight
 2120        nine
 2121        ten
 2122        "#
 2123        .unindent(),
 2124    );
 2125
 2126    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2127    cx.assert_editor_state(
 2128        &r#"
 2129        one
 2130        two
 2131        three
 2132        ˇfour
 2133        five
 2134        sixˇ
 2135        seven
 2136        eight
 2137        nine
 2138        ten
 2139        "#
 2140        .unindent(),
 2141    );
 2142
 2143    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
 2144    cx.assert_editor_state(
 2145        &r#"
 2146        one
 2147        two
 2148        three
 2149        four
 2150        five
 2151        six
 2152        ˇseven
 2153        eight
 2154        nineˇ
 2155        ten
 2156        "#
 2157        .unindent(),
 2158    );
 2159
 2160    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2161    cx.assert_editor_state(
 2162        &r#"
 2163        one
 2164        two
 2165        three
 2166        ˇfour
 2167        five
 2168        sixˇ
 2169        seven
 2170        eight
 2171        nine
 2172        ten
 2173        "#
 2174        .unindent(),
 2175    );
 2176
 2177    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
 2178    cx.assert_editor_state(
 2179        &r#"
 2180        ˇone
 2181        two
 2182        threeˇ
 2183        four
 2184        five
 2185        six
 2186        seven
 2187        eight
 2188        nine
 2189        ten
 2190        "#
 2191        .unindent(),
 2192    );
 2193
 2194    // Test select collapsing
 2195    cx.update_editor(|editor, cx| {
 2196        editor.move_page_down(&MovePageDown::default(), cx);
 2197        editor.move_page_down(&MovePageDown::default(), cx);
 2198        editor.move_page_down(&MovePageDown::default(), cx);
 2199    });
 2200    cx.assert_editor_state(
 2201        &r#"
 2202        one
 2203        two
 2204        three
 2205        four
 2206        five
 2207        six
 2208        seven
 2209        eight
 2210        nine
 2211        ˇten
 2212        ˇ"#
 2213        .unindent(),
 2214    );
 2215}
 2216
 2217#[gpui::test]
 2218async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2219    init_test(cx, |_| {});
 2220    let mut cx = EditorTestContext::new(cx).await;
 2221    cx.set_state("one «two threeˇ» four");
 2222    cx.update_editor(|editor, cx| {
 2223        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
 2224        assert_eq!(editor.text(cx), " four");
 2225    });
 2226}
 2227
 2228#[gpui::test]
 2229fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2230    init_test(cx, |_| {});
 2231
 2232    let view = cx.add_window(|cx| {
 2233        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2234        build_editor(buffer.clone(), cx)
 2235    });
 2236
 2237    _ = view.update(cx, |view, cx| {
 2238        view.change_selections(None, cx, |s| {
 2239            s.select_display_ranges([
 2240                // an empty selection - the preceding word fragment is deleted
 2241                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2242                // characters selected - they are deleted
 2243                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2244            ])
 2245        });
 2246        view.delete_to_previous_word_start(
 2247            &DeleteToPreviousWordStart {
 2248                ignore_newlines: false,
 2249            },
 2250            cx,
 2251        );
 2252        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
 2253    });
 2254
 2255    _ = view.update(cx, |view, cx| {
 2256        view.change_selections(None, cx, |s| {
 2257            s.select_display_ranges([
 2258                // an empty selection - the following word fragment is deleted
 2259                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2260                // characters selected - they are deleted
 2261                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2262            ])
 2263        });
 2264        view.delete_to_next_word_end(
 2265            &DeleteToNextWordEnd {
 2266                ignore_newlines: false,
 2267            },
 2268            cx,
 2269        );
 2270        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
 2271    });
 2272}
 2273
 2274#[gpui::test]
 2275fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2276    init_test(cx, |_| {});
 2277
 2278    let view = cx.add_window(|cx| {
 2279        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2280        build_editor(buffer.clone(), cx)
 2281    });
 2282    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2283        ignore_newlines: false,
 2284    };
 2285    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2286        ignore_newlines: true,
 2287    };
 2288
 2289    _ = view.update(cx, |view, cx| {
 2290        view.change_selections(None, cx, |s| {
 2291            s.select_display_ranges([
 2292                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2293            ])
 2294        });
 2295        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2296        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2297        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2298        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2299        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2300        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2\n");
 2301        view.delete_to_previous_word_start(&del_to_prev_word_start, cx);
 2302        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n2");
 2303        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2304        assert_eq!(view.buffer.read(cx).read(cx).text(), "one\n");
 2305        view.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, cx);
 2306        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2307    });
 2308}
 2309
 2310#[gpui::test]
 2311fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2312    init_test(cx, |_| {});
 2313
 2314    let view = cx.add_window(|cx| {
 2315        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2316        build_editor(buffer.clone(), cx)
 2317    });
 2318    let del_to_next_word_end = DeleteToNextWordEnd {
 2319        ignore_newlines: false,
 2320    };
 2321    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2322        ignore_newlines: true,
 2323    };
 2324
 2325    _ = view.update(cx, |view, cx| {
 2326        view.change_selections(None, cx, |s| {
 2327            s.select_display_ranges([
 2328                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2329            ])
 2330        });
 2331        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2332        assert_eq!(
 2333            view.buffer.read(cx).read(cx).text(),
 2334            "one\n   two\nthree\n   four"
 2335        );
 2336        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2337        assert_eq!(
 2338            view.buffer.read(cx).read(cx).text(),
 2339            "\n   two\nthree\n   four"
 2340        );
 2341        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2342        assert_eq!(view.buffer.read(cx).read(cx).text(), "two\nthree\n   four");
 2343        view.delete_to_next_word_end(&del_to_next_word_end, cx);
 2344        assert_eq!(view.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2345        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2346        assert_eq!(view.buffer.read(cx).read(cx).text(), "\n   four");
 2347        view.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, cx);
 2348        assert_eq!(view.buffer.read(cx).read(cx).text(), "");
 2349    });
 2350}
 2351
 2352#[gpui::test]
 2353fn test_newline(cx: &mut TestAppContext) {
 2354    init_test(cx, |_| {});
 2355
 2356    let view = cx.add_window(|cx| {
 2357        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2358        build_editor(buffer.clone(), cx)
 2359    });
 2360
 2361    _ = view.update(cx, |view, cx| {
 2362        view.change_selections(None, cx, |s| {
 2363            s.select_display_ranges([
 2364                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2365                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2366                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2367            ])
 2368        });
 2369
 2370        view.newline(&Newline, cx);
 2371        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2372    });
 2373}
 2374
 2375#[gpui::test]
 2376fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2377    init_test(cx, |_| {});
 2378
 2379    let editor = cx.add_window(|cx| {
 2380        let buffer = MultiBuffer::build_simple(
 2381            "
 2382                a
 2383                b(
 2384                    X
 2385                )
 2386                c(
 2387                    X
 2388                )
 2389            "
 2390            .unindent()
 2391            .as_str(),
 2392            cx,
 2393        );
 2394        let mut editor = build_editor(buffer.clone(), cx);
 2395        editor.change_selections(None, cx, |s| {
 2396            s.select_ranges([
 2397                Point::new(2, 4)..Point::new(2, 5),
 2398                Point::new(5, 4)..Point::new(5, 5),
 2399            ])
 2400        });
 2401        editor
 2402    });
 2403
 2404    _ = editor.update(cx, |editor, cx| {
 2405        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2406        editor.buffer.update(cx, |buffer, cx| {
 2407            buffer.edit(
 2408                [
 2409                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2410                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2411                ],
 2412                None,
 2413                cx,
 2414            );
 2415            assert_eq!(
 2416                buffer.read(cx).text(),
 2417                "
 2418                    a
 2419                    b()
 2420                    c()
 2421                "
 2422                .unindent()
 2423            );
 2424        });
 2425        assert_eq!(
 2426            editor.selections.ranges(cx),
 2427            &[
 2428                Point::new(1, 2)..Point::new(1, 2),
 2429                Point::new(2, 2)..Point::new(2, 2),
 2430            ],
 2431        );
 2432
 2433        editor.newline(&Newline, cx);
 2434        assert_eq!(
 2435            editor.text(cx),
 2436            "
 2437                a
 2438                b(
 2439                )
 2440                c(
 2441                )
 2442            "
 2443            .unindent()
 2444        );
 2445
 2446        // The selections are moved after the inserted newlines
 2447        assert_eq!(
 2448            editor.selections.ranges(cx),
 2449            &[
 2450                Point::new(2, 0)..Point::new(2, 0),
 2451                Point::new(4, 0)..Point::new(4, 0),
 2452            ],
 2453        );
 2454    });
 2455}
 2456
 2457#[gpui::test]
 2458async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2459    init_test(cx, |settings| {
 2460        settings.defaults.tab_size = NonZeroU32::new(4)
 2461    });
 2462
 2463    let language = Arc::new(
 2464        Language::new(
 2465            LanguageConfig::default(),
 2466            Some(tree_sitter_rust::LANGUAGE.into()),
 2467        )
 2468        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2469        .unwrap(),
 2470    );
 2471
 2472    let mut cx = EditorTestContext::new(cx).await;
 2473    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2474    cx.set_state(indoc! {"
 2475        const a: ˇA = (
 2476 2477                «const_functionˇ»(ˇ),
 2478                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2479 2480        ˇ);ˇ
 2481    "});
 2482
 2483    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
 2484    cx.assert_editor_state(indoc! {"
 2485        ˇ
 2486        const a: A = (
 2487            ˇ
 2488            (
 2489                ˇ
 2490                ˇ
 2491                const_function(),
 2492                ˇ
 2493                ˇ
 2494                ˇ
 2495                ˇ
 2496                something_else,
 2497                ˇ
 2498            )
 2499            ˇ
 2500            ˇ
 2501        );
 2502    "});
 2503}
 2504
 2505#[gpui::test]
 2506async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2507    init_test(cx, |settings| {
 2508        settings.defaults.tab_size = NonZeroU32::new(4)
 2509    });
 2510
 2511    let language = Arc::new(
 2512        Language::new(
 2513            LanguageConfig::default(),
 2514            Some(tree_sitter_rust::LANGUAGE.into()),
 2515        )
 2516        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2517        .unwrap(),
 2518    );
 2519
 2520    let mut cx = EditorTestContext::new(cx).await;
 2521    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2522    cx.set_state(indoc! {"
 2523        const a: ˇA = (
 2524 2525                «const_functionˇ»(ˇ),
 2526                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2527 2528        ˇ);ˇ
 2529    "});
 2530
 2531    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
 2532    cx.assert_editor_state(indoc! {"
 2533        const a: A = (
 2534            ˇ
 2535            (
 2536                ˇ
 2537                const_function(),
 2538                ˇ
 2539                ˇ
 2540                something_else,
 2541                ˇ
 2542                ˇ
 2543                ˇ
 2544                ˇ
 2545            )
 2546            ˇ
 2547        );
 2548        ˇ
 2549        ˇ
 2550    "});
 2551}
 2552
 2553#[gpui::test]
 2554async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2555    init_test(cx, |settings| {
 2556        settings.defaults.tab_size = NonZeroU32::new(4)
 2557    });
 2558
 2559    let language = Arc::new(Language::new(
 2560        LanguageConfig {
 2561            line_comments: vec!["//".into()],
 2562            ..LanguageConfig::default()
 2563        },
 2564        None,
 2565    ));
 2566    {
 2567        let mut cx = EditorTestContext::new(cx).await;
 2568        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2569        cx.set_state(indoc! {"
 2570        // Fooˇ
 2571    "});
 2572
 2573        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2574        cx.assert_editor_state(indoc! {"
 2575        // Foo
 2576        //ˇ
 2577    "});
 2578        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2579        cx.set_state(indoc! {"
 2580        ˇ// Foo
 2581    "});
 2582        cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2583        cx.assert_editor_state(indoc! {"
 2584
 2585        ˇ// Foo
 2586    "});
 2587    }
 2588    // Ensure that comment continuations can be disabled.
 2589    update_test_language_settings(cx, |settings| {
 2590        settings.defaults.extend_comment_on_newline = Some(false);
 2591    });
 2592    let mut cx = EditorTestContext::new(cx).await;
 2593    cx.set_state(indoc! {"
 2594        // Fooˇ
 2595    "});
 2596    cx.update_editor(|e, cx| e.newline(&Newline, cx));
 2597    cx.assert_editor_state(indoc! {"
 2598        // Foo
 2599        ˇ
 2600    "});
 2601}
 2602
 2603#[gpui::test]
 2604fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2605    init_test(cx, |_| {});
 2606
 2607    let editor = cx.add_window(|cx| {
 2608        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2609        let mut editor = build_editor(buffer.clone(), cx);
 2610        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
 2611        editor
 2612    });
 2613
 2614    _ = editor.update(cx, |editor, cx| {
 2615        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2616        editor.buffer.update(cx, |buffer, cx| {
 2617            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2618            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2619        });
 2620        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2621
 2622        editor.insert("Z", cx);
 2623        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2624
 2625        // The selections are moved after the inserted characters
 2626        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2627    });
 2628}
 2629
 2630#[gpui::test]
 2631async fn test_tab(cx: &mut gpui::TestAppContext) {
 2632    init_test(cx, |settings| {
 2633        settings.defaults.tab_size = NonZeroU32::new(3)
 2634    });
 2635
 2636    let mut cx = EditorTestContext::new(cx).await;
 2637    cx.set_state(indoc! {"
 2638        ˇabˇc
 2639        ˇ🏀ˇ🏀ˇefg
 2640 2641    "});
 2642    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2643    cx.assert_editor_state(indoc! {"
 2644           ˇab ˇc
 2645           ˇ🏀  ˇ🏀  ˇefg
 2646        d  ˇ
 2647    "});
 2648
 2649    cx.set_state(indoc! {"
 2650        a
 2651        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2652    "});
 2653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2654    cx.assert_editor_state(indoc! {"
 2655        a
 2656           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2657    "});
 2658}
 2659
 2660#[gpui::test]
 2661async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2662    init_test(cx, |_| {});
 2663
 2664    let mut cx = EditorTestContext::new(cx).await;
 2665    let language = Arc::new(
 2666        Language::new(
 2667            LanguageConfig::default(),
 2668            Some(tree_sitter_rust::LANGUAGE.into()),
 2669        )
 2670        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2671        .unwrap(),
 2672    );
 2673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2674
 2675    // cursors that are already at the suggested indent level insert
 2676    // a soft tab. cursors that are to the left of the suggested indent
 2677    // auto-indent their line.
 2678    cx.set_state(indoc! {"
 2679        ˇ
 2680        const a: B = (
 2681            c(
 2682                d(
 2683        ˇ
 2684                )
 2685        ˇ
 2686        ˇ    )
 2687        );
 2688    "});
 2689    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2690    cx.assert_editor_state(indoc! {"
 2691            ˇ
 2692        const a: B = (
 2693            c(
 2694                d(
 2695                    ˇ
 2696                )
 2697                ˇ
 2698            ˇ)
 2699        );
 2700    "});
 2701
 2702    // handle auto-indent when there are multiple cursors on the same line
 2703    cx.set_state(indoc! {"
 2704        const a: B = (
 2705            c(
 2706        ˇ    ˇ
 2707        ˇ    )
 2708        );
 2709    "});
 2710    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2711    cx.assert_editor_state(indoc! {"
 2712        const a: B = (
 2713            c(
 2714                ˇ
 2715            ˇ)
 2716        );
 2717    "});
 2718}
 2719
 2720#[gpui::test]
 2721async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2722    init_test(cx, |settings| {
 2723        settings.defaults.tab_size = NonZeroU32::new(4)
 2724    });
 2725
 2726    let language = Arc::new(
 2727        Language::new(
 2728            LanguageConfig::default(),
 2729            Some(tree_sitter_rust::LANGUAGE.into()),
 2730        )
 2731        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2732        .unwrap(),
 2733    );
 2734
 2735    let mut cx = EditorTestContext::new(cx).await;
 2736    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2737    cx.set_state(indoc! {"
 2738        fn a() {
 2739            if b {
 2740        \t ˇc
 2741            }
 2742        }
 2743    "});
 2744
 2745    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2746    cx.assert_editor_state(indoc! {"
 2747        fn a() {
 2748            if b {
 2749                ˇc
 2750            }
 2751        }
 2752    "});
 2753}
 2754
 2755#[gpui::test]
 2756async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2757    init_test(cx, |settings| {
 2758        settings.defaults.tab_size = NonZeroU32::new(4);
 2759    });
 2760
 2761    let mut cx = EditorTestContext::new(cx).await;
 2762
 2763    cx.set_state(indoc! {"
 2764          «oneˇ» «twoˇ»
 2765        three
 2766         four
 2767    "});
 2768    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770            «oneˇ» «twoˇ»
 2771        three
 2772         four
 2773    "});
 2774
 2775    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2776    cx.assert_editor_state(indoc! {"
 2777        «oneˇ» «twoˇ»
 2778        three
 2779         four
 2780    "});
 2781
 2782    // select across line ending
 2783    cx.set_state(indoc! {"
 2784        one two
 2785        t«hree
 2786        ˇ» four
 2787    "});
 2788    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        one two
 2791            t«hree
 2792        ˇ» four
 2793    "});
 2794
 2795    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2796    cx.assert_editor_state(indoc! {"
 2797        one two
 2798        t«hree
 2799        ˇ» four
 2800    "});
 2801
 2802    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2803    cx.set_state(indoc! {"
 2804        one two
 2805        ˇthree
 2806            four
 2807    "});
 2808    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2809    cx.assert_editor_state(indoc! {"
 2810        one two
 2811            ˇthree
 2812            four
 2813    "});
 2814
 2815    cx.set_state(indoc! {"
 2816        one two
 2817        ˇ    three
 2818            four
 2819    "});
 2820    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2821    cx.assert_editor_state(indoc! {"
 2822        one two
 2823        ˇthree
 2824            four
 2825    "});
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.hard_tabs = Some(true);
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835
 2836    // select two ranges on one line
 2837    cx.set_state(indoc! {"
 2838        «oneˇ» «twoˇ»
 2839        three
 2840        four
 2841    "});
 2842    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844        \t«oneˇ» «twoˇ»
 2845        three
 2846        four
 2847    "});
 2848    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2849    cx.assert_editor_state(indoc! {"
 2850        \t\t«oneˇ» «twoˇ»
 2851        three
 2852        four
 2853    "});
 2854    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2855    cx.assert_editor_state(indoc! {"
 2856        \t«oneˇ» «twoˇ»
 2857        three
 2858        four
 2859    "});
 2860    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2861    cx.assert_editor_state(indoc! {"
 2862        «oneˇ» «twoˇ»
 2863        three
 2864        four
 2865    "});
 2866
 2867    // select across a line ending
 2868    cx.set_state(indoc! {"
 2869        one two
 2870        t«hree
 2871        ˇ»four
 2872    "});
 2873    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2874    cx.assert_editor_state(indoc! {"
 2875        one two
 2876        \tt«hree
 2877        ˇ»four
 2878    "});
 2879    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2880    cx.assert_editor_state(indoc! {"
 2881        one two
 2882        \t\tt«hree
 2883        ˇ»four
 2884    "});
 2885    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2886    cx.assert_editor_state(indoc! {"
 2887        one two
 2888        \tt«hree
 2889        ˇ»four
 2890    "});
 2891    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2892    cx.assert_editor_state(indoc! {"
 2893        one two
 2894        t«hree
 2895        ˇ»four
 2896    "});
 2897
 2898    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2899    cx.set_state(indoc! {"
 2900        one two
 2901        ˇthree
 2902        four
 2903    "});
 2904    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        one two
 2907        ˇthree
 2908        four
 2909    "});
 2910    cx.update_editor(|e, cx| e.tab(&Tab, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        one two
 2913        \tˇthree
 2914        four
 2915    "});
 2916    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
 2917    cx.assert_editor_state(indoc! {"
 2918        one two
 2919        ˇthree
 2920        four
 2921    "});
 2922}
 2923
 2924#[gpui::test]
 2925fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 2926    init_test(cx, |settings| {
 2927        settings.languages.extend([
 2928            (
 2929                "TOML".into(),
 2930                LanguageSettingsContent {
 2931                    tab_size: NonZeroU32::new(2),
 2932                    ..Default::default()
 2933                },
 2934            ),
 2935            (
 2936                "Rust".into(),
 2937                LanguageSettingsContent {
 2938                    tab_size: NonZeroU32::new(4),
 2939                    ..Default::default()
 2940                },
 2941            ),
 2942        ]);
 2943    });
 2944
 2945    let toml_language = Arc::new(Language::new(
 2946        LanguageConfig {
 2947            name: "TOML".into(),
 2948            ..Default::default()
 2949        },
 2950        None,
 2951    ));
 2952    let rust_language = Arc::new(Language::new(
 2953        LanguageConfig {
 2954            name: "Rust".into(),
 2955            ..Default::default()
 2956        },
 2957        None,
 2958    ));
 2959
 2960    let toml_buffer =
 2961        cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 2962    let rust_buffer = cx.new_model(|cx| {
 2963        Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
 2964    });
 2965    let multibuffer = cx.new_model(|cx| {
 2966        let mut multibuffer = MultiBuffer::new(ReadWrite);
 2967        multibuffer.push_excerpts(
 2968            toml_buffer.clone(),
 2969            [ExcerptRange {
 2970                context: Point::new(0, 0)..Point::new(2, 0),
 2971                primary: None,
 2972            }],
 2973            cx,
 2974        );
 2975        multibuffer.push_excerpts(
 2976            rust_buffer.clone(),
 2977            [ExcerptRange {
 2978                context: Point::new(0, 0)..Point::new(1, 0),
 2979                primary: None,
 2980            }],
 2981            cx,
 2982        );
 2983        multibuffer
 2984    });
 2985
 2986    cx.add_window(|cx| {
 2987        let mut editor = build_editor(multibuffer, cx);
 2988
 2989        assert_eq!(
 2990            editor.text(cx),
 2991            indoc! {"
 2992                a = 1
 2993                b = 2
 2994
 2995                const c: usize = 3;
 2996            "}
 2997        );
 2998
 2999        select_ranges(
 3000            &mut editor,
 3001            indoc! {"
 3002                «aˇ» = 1
 3003                b = 2
 3004
 3005                «const c:ˇ» usize = 3;
 3006            "},
 3007            cx,
 3008        );
 3009
 3010        editor.tab(&Tab, cx);
 3011        assert_text_with_selections(
 3012            &mut editor,
 3013            indoc! {"
 3014                  «aˇ» = 1
 3015                b = 2
 3016
 3017                    «const c:ˇ» usize = 3;
 3018            "},
 3019            cx,
 3020        );
 3021        editor.tab_prev(&TabPrev, cx);
 3022        assert_text_with_selections(
 3023            &mut editor,
 3024            indoc! {"
 3025                «aˇ» = 1
 3026                b = 2
 3027
 3028                «const c:ˇ» usize = 3;
 3029            "},
 3030            cx,
 3031        );
 3032
 3033        editor
 3034    });
 3035}
 3036
 3037#[gpui::test]
 3038async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3039    init_test(cx, |_| {});
 3040
 3041    let mut cx = EditorTestContext::new(cx).await;
 3042
 3043    // Basic backspace
 3044    cx.set_state(indoc! {"
 3045        onˇe two three
 3046        fou«rˇ» five six
 3047        seven «ˇeight nine
 3048        »ten
 3049    "});
 3050    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        oˇe two three
 3053        fouˇ five six
 3054        seven ˇten
 3055    "});
 3056
 3057    // Test backspace inside and around indents
 3058    cx.set_state(indoc! {"
 3059        zero
 3060            ˇone
 3061                ˇtwo
 3062            ˇ ˇ ˇ  three
 3063        ˇ  ˇ  four
 3064    "});
 3065    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        zero
 3068        ˇone
 3069            ˇtwo
 3070        ˇ  threeˇ  four
 3071    "});
 3072
 3073    // Test backspace with line_mode set to true
 3074    cx.update_editor(|e, _| e.selections.line_mode = true);
 3075    cx.set_state(indoc! {"
 3076        The ˇquick ˇbrown
 3077        fox jumps over
 3078        the lazy dog
 3079        ˇThe qu«ick bˇ»rown"});
 3080    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3081    cx.assert_editor_state(indoc! {"
 3082        ˇfox jumps over
 3083        the lazy dogˇ"});
 3084}
 3085
 3086#[gpui::test]
 3087async fn test_delete(cx: &mut gpui::TestAppContext) {
 3088    init_test(cx, |_| {});
 3089
 3090    let mut cx = EditorTestContext::new(cx).await;
 3091    cx.set_state(indoc! {"
 3092        onˇe two three
 3093        fou«rˇ» five six
 3094        seven «ˇeight nine
 3095        »ten
 3096    "});
 3097    cx.update_editor(|e, cx| e.delete(&Delete, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        onˇ two three
 3100        fouˇ five six
 3101        seven ˇten
 3102    "});
 3103
 3104    // Test backspace with line_mode set to true
 3105    cx.update_editor(|e, _| e.selections.line_mode = true);
 3106    cx.set_state(indoc! {"
 3107        The ˇquick ˇbrown
 3108        fox «ˇjum»ps over
 3109        the lazy dog
 3110        ˇThe qu«ick bˇ»rown"});
 3111    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
 3112    cx.assert_editor_state("ˇthe lazy dogˇ");
 3113}
 3114
 3115#[gpui::test]
 3116fn test_delete_line(cx: &mut TestAppContext) {
 3117    init_test(cx, |_| {});
 3118
 3119    let view = cx.add_window(|cx| {
 3120        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3121        build_editor(buffer, cx)
 3122    });
 3123    _ = view.update(cx, |view, cx| {
 3124        view.change_selections(None, cx, |s| {
 3125            s.select_display_ranges([
 3126                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3127                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3128                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3129            ])
 3130        });
 3131        view.delete_line(&DeleteLine, cx);
 3132        assert_eq!(view.display_text(cx), "ghi");
 3133        assert_eq!(
 3134            view.selections.display_ranges(cx),
 3135            vec![
 3136                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3137                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3138            ]
 3139        );
 3140    });
 3141
 3142    let view = cx.add_window(|cx| {
 3143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3144        build_editor(buffer, cx)
 3145    });
 3146    _ = view.update(cx, |view, cx| {
 3147        view.change_selections(None, cx, |s| {
 3148            s.select_display_ranges([
 3149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3150            ])
 3151        });
 3152        view.delete_line(&DeleteLine, cx);
 3153        assert_eq!(view.display_text(cx), "ghi\n");
 3154        assert_eq!(
 3155            view.selections.display_ranges(cx),
 3156            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3157        );
 3158    });
 3159}
 3160
 3161#[gpui::test]
 3162fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3163    init_test(cx, |_| {});
 3164
 3165    cx.add_window(|cx| {
 3166        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3167        let mut editor = build_editor(buffer.clone(), cx);
 3168        let buffer = buffer.read(cx).as_singleton().unwrap();
 3169
 3170        assert_eq!(
 3171            editor.selections.ranges::<Point>(cx),
 3172            &[Point::new(0, 0)..Point::new(0, 0)]
 3173        );
 3174
 3175        // When on single line, replace newline at end by space
 3176        editor.join_lines(&JoinLines, cx);
 3177        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3178        assert_eq!(
 3179            editor.selections.ranges::<Point>(cx),
 3180            &[Point::new(0, 3)..Point::new(0, 3)]
 3181        );
 3182
 3183        // When multiple lines are selected, remove newlines that are spanned by the selection
 3184        editor.change_selections(None, cx, |s| {
 3185            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3186        });
 3187        editor.join_lines(&JoinLines, cx);
 3188        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3189        assert_eq!(
 3190            editor.selections.ranges::<Point>(cx),
 3191            &[Point::new(0, 11)..Point::new(0, 11)]
 3192        );
 3193
 3194        // Undo should be transactional
 3195        editor.undo(&Undo, cx);
 3196        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3197        assert_eq!(
 3198            editor.selections.ranges::<Point>(cx),
 3199            &[Point::new(0, 5)..Point::new(2, 2)]
 3200        );
 3201
 3202        // When joining an empty line don't insert a space
 3203        editor.change_selections(None, cx, |s| {
 3204            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3205        });
 3206        editor.join_lines(&JoinLines, cx);
 3207        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3208        assert_eq!(
 3209            editor.selections.ranges::<Point>(cx),
 3210            [Point::new(2, 3)..Point::new(2, 3)]
 3211        );
 3212
 3213        // We can remove trailing newlines
 3214        editor.join_lines(&JoinLines, cx);
 3215        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3216        assert_eq!(
 3217            editor.selections.ranges::<Point>(cx),
 3218            [Point::new(2, 3)..Point::new(2, 3)]
 3219        );
 3220
 3221        // We don't blow up on the last line
 3222        editor.join_lines(&JoinLines, cx);
 3223        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3224        assert_eq!(
 3225            editor.selections.ranges::<Point>(cx),
 3226            [Point::new(2, 3)..Point::new(2, 3)]
 3227        );
 3228
 3229        // reset to test indentation
 3230        editor.buffer.update(cx, |buffer, cx| {
 3231            buffer.edit(
 3232                [
 3233                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3234                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3235                ],
 3236                None,
 3237                cx,
 3238            )
 3239        });
 3240
 3241        // We remove any leading spaces
 3242        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3243        editor.change_selections(None, cx, |s| {
 3244            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3245        });
 3246        editor.join_lines(&JoinLines, cx);
 3247        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3248
 3249        // We don't insert a space for a line containing only spaces
 3250        editor.join_lines(&JoinLines, cx);
 3251        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3252
 3253        // We ignore any leading tabs
 3254        editor.join_lines(&JoinLines, cx);
 3255        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3256
 3257        editor
 3258    });
 3259}
 3260
 3261#[gpui::test]
 3262fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3263    init_test(cx, |_| {});
 3264
 3265    cx.add_window(|cx| {
 3266        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3267        let mut editor = build_editor(buffer.clone(), cx);
 3268        let buffer = buffer.read(cx).as_singleton().unwrap();
 3269
 3270        editor.change_selections(None, cx, |s| {
 3271            s.select_ranges([
 3272                Point::new(0, 2)..Point::new(1, 1),
 3273                Point::new(1, 2)..Point::new(1, 2),
 3274                Point::new(3, 1)..Point::new(3, 2),
 3275            ])
 3276        });
 3277
 3278        editor.join_lines(&JoinLines, cx);
 3279        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3280
 3281        assert_eq!(
 3282            editor.selections.ranges::<Point>(cx),
 3283            [
 3284                Point::new(0, 7)..Point::new(0, 7),
 3285                Point::new(1, 3)..Point::new(1, 3)
 3286            ]
 3287        );
 3288        editor
 3289    });
 3290}
 3291
 3292#[gpui::test]
 3293async fn test_join_lines_with_git_diff_base(
 3294    executor: BackgroundExecutor,
 3295    cx: &mut gpui::TestAppContext,
 3296) {
 3297    init_test(cx, |_| {});
 3298
 3299    let mut cx = EditorTestContext::new(cx).await;
 3300
 3301    let diff_base = r#"
 3302        Line 0
 3303        Line 1
 3304        Line 2
 3305        Line 3
 3306        "#
 3307    .unindent();
 3308
 3309    cx.set_state(
 3310        &r#"
 3311        ˇLine 0
 3312        Line 1
 3313        Line 2
 3314        Line 3
 3315        "#
 3316        .unindent(),
 3317    );
 3318
 3319    cx.set_diff_base(&diff_base);
 3320    executor.run_until_parked();
 3321
 3322    // Join lines
 3323    cx.update_editor(|editor, cx| {
 3324        editor.join_lines(&JoinLines, cx);
 3325    });
 3326    executor.run_until_parked();
 3327
 3328    cx.assert_editor_state(
 3329        &r#"
 3330        Line 0ˇ Line 1
 3331        Line 2
 3332        Line 3
 3333        "#
 3334        .unindent(),
 3335    );
 3336    // Join again
 3337    cx.update_editor(|editor, cx| {
 3338        editor.join_lines(&JoinLines, cx);
 3339    });
 3340    executor.run_until_parked();
 3341
 3342    cx.assert_editor_state(
 3343        &r#"
 3344        Line 0 Line 1ˇ Line 2
 3345        Line 3
 3346        "#
 3347        .unindent(),
 3348    );
 3349}
 3350
 3351#[gpui::test]
 3352async fn test_custom_newlines_cause_no_false_positive_diffs(
 3353    executor: BackgroundExecutor,
 3354    cx: &mut gpui::TestAppContext,
 3355) {
 3356    init_test(cx, |_| {});
 3357    let mut cx = EditorTestContext::new(cx).await;
 3358    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3359    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3360    executor.run_until_parked();
 3361
 3362    cx.update_editor(|editor, cx| {
 3363        let snapshot = editor.snapshot(cx);
 3364        assert_eq!(
 3365            snapshot
 3366                .buffer_snapshot
 3367                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3368                .collect::<Vec<_>>(),
 3369            Vec::new(),
 3370            "Should not have any diffs for files with custom newlines"
 3371        );
 3372    });
 3373}
 3374
 3375#[gpui::test]
 3376async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3377    init_test(cx, |_| {});
 3378
 3379    let mut cx = EditorTestContext::new(cx).await;
 3380
 3381    // Test sort_lines_case_insensitive()
 3382    cx.set_state(indoc! {"
 3383        «z
 3384        y
 3385        x
 3386        Z
 3387        Y
 3388        Xˇ»
 3389    "});
 3390    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
 3391    cx.assert_editor_state(indoc! {"
 3392        «x
 3393        X
 3394        y
 3395        Y
 3396        z
 3397        Zˇ»
 3398    "});
 3399
 3400    // Test reverse_lines()
 3401    cx.set_state(indoc! {"
 3402        «5
 3403        4
 3404        3
 3405        2
 3406        1ˇ»
 3407    "});
 3408    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
 3409    cx.assert_editor_state(indoc! {"
 3410        «1
 3411        2
 3412        3
 3413        4
 3414        5ˇ»
 3415    "});
 3416
 3417    // Skip testing shuffle_line()
 3418
 3419    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3420    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3421
 3422    // Don't manipulate when cursor is on single line, but expand the selection
 3423    cx.set_state(indoc! {"
 3424        ddˇdd
 3425        ccc
 3426        bb
 3427        a
 3428    "});
 3429    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        «ddddˇ»
 3432        ccc
 3433        bb
 3434        a
 3435    "});
 3436
 3437    // Basic manipulate case
 3438    // Start selection moves to column 0
 3439    // End of selection shrinks to fit shorter line
 3440    cx.set_state(indoc! {"
 3441        dd«d
 3442        ccc
 3443        bb
 3444        aaaaaˇ»
 3445    "});
 3446    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3447    cx.assert_editor_state(indoc! {"
 3448        «aaaaa
 3449        bb
 3450        ccc
 3451        dddˇ»
 3452    "});
 3453
 3454    // Manipulate case with newlines
 3455    cx.set_state(indoc! {"
 3456        dd«d
 3457        ccc
 3458
 3459        bb
 3460        aaaaa
 3461
 3462        ˇ»
 3463    "});
 3464    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3465    cx.assert_editor_state(indoc! {"
 3466        «
 3467
 3468        aaaaa
 3469        bb
 3470        ccc
 3471        dddˇ»
 3472
 3473    "});
 3474
 3475    // Adding new line
 3476    cx.set_state(indoc! {"
 3477        aa«a
 3478        bbˇ»b
 3479    "});
 3480    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
 3481    cx.assert_editor_state(indoc! {"
 3482        «aaa
 3483        bbb
 3484        added_lineˇ»
 3485    "});
 3486
 3487    // Removing line
 3488    cx.set_state(indoc! {"
 3489        aa«a
 3490        bbbˇ»
 3491    "});
 3492    cx.update_editor(|e, cx| {
 3493        e.manipulate_lines(cx, |lines| {
 3494            lines.pop();
 3495        })
 3496    });
 3497    cx.assert_editor_state(indoc! {"
 3498        «aaaˇ»
 3499    "});
 3500
 3501    // Removing all lines
 3502    cx.set_state(indoc! {"
 3503        aa«a
 3504        bbbˇ»
 3505    "});
 3506    cx.update_editor(|e, cx| {
 3507        e.manipulate_lines(cx, |lines| {
 3508            lines.drain(..);
 3509        })
 3510    });
 3511    cx.assert_editor_state(indoc! {"
 3512        ˇ
 3513    "});
 3514}
 3515
 3516#[gpui::test]
 3517async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3518    init_test(cx, |_| {});
 3519
 3520    let mut cx = EditorTestContext::new(cx).await;
 3521
 3522    // Consider continuous selection as single selection
 3523    cx.set_state(indoc! {"
 3524        Aaa«aa
 3525        cˇ»c«c
 3526        bb
 3527        aaaˇ»aa
 3528    "});
 3529    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3530    cx.assert_editor_state(indoc! {"
 3531        «Aaaaa
 3532        ccc
 3533        bb
 3534        aaaaaˇ»
 3535    "});
 3536
 3537    cx.set_state(indoc! {"
 3538        Aaa«aa
 3539        cˇ»c«c
 3540        bb
 3541        aaaˇ»aa
 3542    "});
 3543    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3544    cx.assert_editor_state(indoc! {"
 3545        «Aaaaa
 3546        ccc
 3547        bbˇ»
 3548    "});
 3549
 3550    // Consider non continuous selection as distinct dedup operations
 3551    cx.set_state(indoc! {"
 3552        «aaaaa
 3553        bb
 3554        aaaaa
 3555        aaaaaˇ»
 3556
 3557        aaa«aaˇ»
 3558    "});
 3559    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3560    cx.assert_editor_state(indoc! {"
 3561        «aaaaa
 3562        bbˇ»
 3563
 3564        «aaaaaˇ»
 3565    "});
 3566}
 3567
 3568#[gpui::test]
 3569async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3570    init_test(cx, |_| {});
 3571
 3572    let mut cx = EditorTestContext::new(cx).await;
 3573
 3574    cx.set_state(indoc! {"
 3575        «Aaa
 3576        aAa
 3577        Aaaˇ»
 3578    "});
 3579    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «Aaa
 3582        aAaˇ»
 3583    "});
 3584
 3585    cx.set_state(indoc! {"
 3586        «Aaa
 3587        aAa
 3588        aaAˇ»
 3589    "});
 3590    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
 3591    cx.assert_editor_state(indoc! {"
 3592        «Aaaˇ»
 3593    "});
 3594}
 3595
 3596#[gpui::test]
 3597async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3598    init_test(cx, |_| {});
 3599
 3600    let mut cx = EditorTestContext::new(cx).await;
 3601
 3602    // Manipulate with multiple selections on a single line
 3603    cx.set_state(indoc! {"
 3604        dd«dd
 3605        cˇ»c«c
 3606        bb
 3607        aaaˇ»aa
 3608    "});
 3609    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3610    cx.assert_editor_state(indoc! {"
 3611        «aaaaa
 3612        bb
 3613        ccc
 3614        ddddˇ»
 3615    "});
 3616
 3617    // Manipulate with multiple disjoin selections
 3618    cx.set_state(indoc! {"
 3619 3620        4
 3621        3
 3622        2
 3623        1ˇ»
 3624
 3625        dd«dd
 3626        ccc
 3627        bb
 3628        aaaˇ»aa
 3629    "});
 3630    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
 3631    cx.assert_editor_state(indoc! {"
 3632        «1
 3633        2
 3634        3
 3635        4
 3636        5ˇ»
 3637
 3638        «aaaaa
 3639        bb
 3640        ccc
 3641        ddddˇ»
 3642    "});
 3643
 3644    // Adding lines on each selection
 3645    cx.set_state(indoc! {"
 3646 3647        1ˇ»
 3648
 3649        bb«bb
 3650        aaaˇ»aa
 3651    "});
 3652    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
 3653    cx.assert_editor_state(indoc! {"
 3654        «2
 3655        1
 3656        added lineˇ»
 3657
 3658        «bbbb
 3659        aaaaa
 3660        added lineˇ»
 3661    "});
 3662
 3663    // Removing lines on each selection
 3664    cx.set_state(indoc! {"
 3665 3666        1ˇ»
 3667
 3668        bb«bb
 3669        aaaˇ»aa
 3670    "});
 3671    cx.update_editor(|e, cx| {
 3672        e.manipulate_lines(cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «2ˇ»
 3678
 3679        «bbbbˇ»
 3680    "});
 3681}
 3682
 3683#[gpui::test]
 3684async fn test_manipulate_text(cx: &mut TestAppContext) {
 3685    init_test(cx, |_| {});
 3686
 3687    let mut cx = EditorTestContext::new(cx).await;
 3688
 3689    // Test convert_to_upper_case()
 3690    cx.set_state(indoc! {"
 3691        «hello worldˇ»
 3692    "});
 3693    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        «HELLO WORLDˇ»
 3696    "});
 3697
 3698    // Test convert_to_lower_case()
 3699    cx.set_state(indoc! {"
 3700        «HELLO WORLDˇ»
 3701    "});
 3702    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
 3703    cx.assert_editor_state(indoc! {"
 3704        «hello worldˇ»
 3705    "});
 3706
 3707    // Test multiple line, single selection case
 3708    cx.set_state(indoc! {"
 3709        «The quick brown
 3710        fox jumps over
 3711        the lazy dogˇ»
 3712    "});
 3713    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
 3714    cx.assert_editor_state(indoc! {"
 3715        «The Quick Brown
 3716        Fox Jumps Over
 3717        The Lazy Dogˇ»
 3718    "});
 3719
 3720    // Test multiple line, single selection case
 3721    cx.set_state(indoc! {"
 3722        «The quick brown
 3723        fox jumps over
 3724        the lazy dogˇ»
 3725    "});
 3726    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
 3727    cx.assert_editor_state(indoc! {"
 3728        «TheQuickBrown
 3729        FoxJumpsOver
 3730        TheLazyDogˇ»
 3731    "});
 3732
 3733    // From here on out, test more complex cases of manipulate_text()
 3734
 3735    // Test no selection case - should affect words cursors are in
 3736    // Cursor at beginning, middle, and end of word
 3737    cx.set_state(indoc! {"
 3738        ˇhello big beauˇtiful worldˇ
 3739    "});
 3740    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3741    cx.assert_editor_state(indoc! {"
 3742        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3743    "});
 3744
 3745    // Test multiple selections on a single line and across multiple lines
 3746    cx.set_state(indoc! {"
 3747        «Theˇ» quick «brown
 3748        foxˇ» jumps «overˇ»
 3749        the «lazyˇ» dog
 3750    "});
 3751    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3752    cx.assert_editor_state(indoc! {"
 3753        «THEˇ» quick «BROWN
 3754        FOXˇ» jumps «OVERˇ»
 3755        the «LAZYˇ» dog
 3756    "});
 3757
 3758    // Test case where text length grows
 3759    cx.set_state(indoc! {"
 3760        «tschüߡ»
 3761    "});
 3762    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
 3763    cx.assert_editor_state(indoc! {"
 3764        «TSCHÜSSˇ»
 3765    "});
 3766
 3767    // Test to make sure we don't crash when text shrinks
 3768    cx.set_state(indoc! {"
 3769        aaa_bbbˇ
 3770    "});
 3771    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3772    cx.assert_editor_state(indoc! {"
 3773        «aaaBbbˇ»
 3774    "});
 3775
 3776    // Test to make sure we all aware of the fact that each word can grow and shrink
 3777    // Final selections should be aware of this fact
 3778    cx.set_state(indoc! {"
 3779        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3780    "});
 3781    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
 3782    cx.assert_editor_state(indoc! {"
 3783        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3784    "});
 3785
 3786    cx.set_state(indoc! {"
 3787        «hElLo, WoRld!ˇ»
 3788    "});
 3789    cx.update_editor(|e, cx| e.convert_to_opposite_case(&ConvertToOppositeCase, cx));
 3790    cx.assert_editor_state(indoc! {"
 3791        «HeLlO, wOrLD!ˇ»
 3792    "});
 3793}
 3794
 3795#[gpui::test]
 3796fn test_duplicate_line(cx: &mut TestAppContext) {
 3797    init_test(cx, |_| {});
 3798
 3799    let view = cx.add_window(|cx| {
 3800        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3801        build_editor(buffer, cx)
 3802    });
 3803    _ = view.update(cx, |view, cx| {
 3804        view.change_selections(None, cx, |s| {
 3805            s.select_display_ranges([
 3806                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3807                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3808                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3809                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3810            ])
 3811        });
 3812        view.duplicate_line_down(&DuplicateLineDown, cx);
 3813        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3814        assert_eq!(
 3815            view.selections.display_ranges(cx),
 3816            vec![
 3817                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3818                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3819                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3820                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3821            ]
 3822        );
 3823    });
 3824
 3825    let view = cx.add_window(|cx| {
 3826        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3827        build_editor(buffer, cx)
 3828    });
 3829    _ = view.update(cx, |view, cx| {
 3830        view.change_selections(None, cx, |s| {
 3831            s.select_display_ranges([
 3832                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3834            ])
 3835        });
 3836        view.duplicate_line_down(&DuplicateLineDown, cx);
 3837        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3838        assert_eq!(
 3839            view.selections.display_ranges(cx),
 3840            vec![
 3841                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3842                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3843            ]
 3844        );
 3845    });
 3846
 3847    // With `move_upwards` the selections stay in place, except for
 3848    // the lines inserted above them
 3849    let view = cx.add_window(|cx| {
 3850        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3851        build_editor(buffer, cx)
 3852    });
 3853    _ = view.update(cx, |view, cx| {
 3854        view.change_selections(None, cx, |s| {
 3855            s.select_display_ranges([
 3856                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3857                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3858                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3859                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3860            ])
 3861        });
 3862        view.duplicate_line_up(&DuplicateLineUp, cx);
 3863        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3864        assert_eq!(
 3865            view.selections.display_ranges(cx),
 3866            vec![
 3867                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3869                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3870                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3871            ]
 3872        );
 3873    });
 3874
 3875    let view = cx.add_window(|cx| {
 3876        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3877        build_editor(buffer, cx)
 3878    });
 3879    _ = view.update(cx, |view, cx| {
 3880        view.change_selections(None, cx, |s| {
 3881            s.select_display_ranges([
 3882                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3883                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3884            ])
 3885        });
 3886        view.duplicate_line_up(&DuplicateLineUp, cx);
 3887        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3888        assert_eq!(
 3889            view.selections.display_ranges(cx),
 3890            vec![
 3891                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3892                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3893            ]
 3894        );
 3895    });
 3896
 3897    let view = cx.add_window(|cx| {
 3898        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3899        build_editor(buffer, cx)
 3900    });
 3901    _ = view.update(cx, |view, cx| {
 3902        view.change_selections(None, cx, |s| {
 3903            s.select_display_ranges([
 3904                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3905                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3906            ])
 3907        });
 3908        view.duplicate_selection(&DuplicateSelection, cx);
 3909        assert_eq!(view.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 3910        assert_eq!(
 3911            view.selections.display_ranges(cx),
 3912            vec![
 3913                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3914                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 3915            ]
 3916        );
 3917    });
 3918}
 3919
 3920#[gpui::test]
 3921fn test_move_line_up_down(cx: &mut TestAppContext) {
 3922    init_test(cx, |_| {});
 3923
 3924    let view = cx.add_window(|cx| {
 3925        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 3926        build_editor(buffer, cx)
 3927    });
 3928    _ = view.update(cx, |view, cx| {
 3929        view.fold_creases(
 3930            vec![
 3931                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 3932                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 3933                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 3934            ],
 3935            true,
 3936            cx,
 3937        );
 3938        view.change_selections(None, cx, |s| {
 3939            s.select_display_ranges([
 3940                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3941                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3942                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3943                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 3944            ])
 3945        });
 3946        assert_eq!(
 3947            view.display_text(cx),
 3948            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 3949        );
 3950
 3951        view.move_line_up(&MoveLineUp, cx);
 3952        assert_eq!(
 3953            view.display_text(cx),
 3954            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 3955        );
 3956        assert_eq!(
 3957            view.selections.display_ranges(cx),
 3958            vec![
 3959                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3960                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3961                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 3962                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 3963            ]
 3964        );
 3965    });
 3966
 3967    _ = view.update(cx, |view, cx| {
 3968        view.move_line_down(&MoveLineDown, cx);
 3969        assert_eq!(
 3970            view.display_text(cx),
 3971            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 3972        );
 3973        assert_eq!(
 3974            view.selections.display_ranges(cx),
 3975            vec![
 3976                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3977                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3978                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3979                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3980            ]
 3981        );
 3982    });
 3983
 3984    _ = view.update(cx, |view, cx| {
 3985        view.move_line_down(&MoveLineDown, cx);
 3986        assert_eq!(
 3987            view.display_text(cx),
 3988            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 3989        );
 3990        assert_eq!(
 3991            view.selections.display_ranges(cx),
 3992            vec![
 3993                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 3994                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 3995                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 3996                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 3997            ]
 3998        );
 3999    });
 4000
 4001    _ = view.update(cx, |view, cx| {
 4002        view.move_line_up(&MoveLineUp, cx);
 4003        assert_eq!(
 4004            view.display_text(cx),
 4005            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4006        );
 4007        assert_eq!(
 4008            view.selections.display_ranges(cx),
 4009            vec![
 4010                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4011                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4012                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4013                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4014            ]
 4015        );
 4016    });
 4017}
 4018
 4019#[gpui::test]
 4020fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4021    init_test(cx, |_| {});
 4022
 4023    let editor = cx.add_window(|cx| {
 4024        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4025        build_editor(buffer, cx)
 4026    });
 4027    _ = editor.update(cx, |editor, cx| {
 4028        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4029        editor.insert_blocks(
 4030            [BlockProperties {
 4031                style: BlockStyle::Fixed,
 4032                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4033                height: 1,
 4034                render: Arc::new(|_| div().into_any()),
 4035                priority: 0,
 4036            }],
 4037            Some(Autoscroll::fit()),
 4038            cx,
 4039        );
 4040        editor.change_selections(None, cx, |s| {
 4041            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4042        });
 4043        editor.move_line_down(&MoveLineDown, cx);
 4044    });
 4045}
 4046
 4047#[gpui::test]
 4048async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4049    init_test(cx, |_| {});
 4050
 4051    let mut cx = EditorTestContext::new(cx).await;
 4052    cx.set_state(
 4053        &"
 4054            ˇzero
 4055            one
 4056            two
 4057            three
 4058            four
 4059            five
 4060        "
 4061        .unindent(),
 4062    );
 4063
 4064    // Create a four-line block that replaces three lines of text.
 4065    cx.update_editor(|editor, cx| {
 4066        let snapshot = editor.snapshot(cx);
 4067        let snapshot = &snapshot.buffer_snapshot;
 4068        let placement = BlockPlacement::Replace(
 4069            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4070        );
 4071        editor.insert_blocks(
 4072            [BlockProperties {
 4073                placement,
 4074                height: 4,
 4075                style: BlockStyle::Sticky,
 4076                render: Arc::new(|_| gpui::div().into_any_element()),
 4077                priority: 0,
 4078            }],
 4079            None,
 4080            cx,
 4081        );
 4082    });
 4083
 4084    // Move down so that the cursor touches the block.
 4085    cx.update_editor(|editor, cx| {
 4086        editor.move_down(&Default::default(), cx);
 4087    });
 4088    cx.assert_editor_state(
 4089        &"
 4090            zero
 4091            «one
 4092            two
 4093            threeˇ»
 4094            four
 4095            five
 4096        "
 4097        .unindent(),
 4098    );
 4099
 4100    // Move down past the block.
 4101    cx.update_editor(|editor, cx| {
 4102        editor.move_down(&Default::default(), cx);
 4103    });
 4104    cx.assert_editor_state(
 4105        &"
 4106            zero
 4107            one
 4108            two
 4109            three
 4110            ˇfour
 4111            five
 4112        "
 4113        .unindent(),
 4114    );
 4115}
 4116
 4117#[gpui::test]
 4118fn test_transpose(cx: &mut TestAppContext) {
 4119    init_test(cx, |_| {});
 4120
 4121    _ = cx.add_window(|cx| {
 4122        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 4123        editor.set_style(EditorStyle::default(), cx);
 4124        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
 4125        editor.transpose(&Default::default(), cx);
 4126        assert_eq!(editor.text(cx), "bac");
 4127        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4128
 4129        editor.transpose(&Default::default(), cx);
 4130        assert_eq!(editor.text(cx), "bca");
 4131        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4132
 4133        editor.transpose(&Default::default(), cx);
 4134        assert_eq!(editor.text(cx), "bac");
 4135        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4136
 4137        editor
 4138    });
 4139
 4140    _ = cx.add_window(|cx| {
 4141        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4142        editor.set_style(EditorStyle::default(), cx);
 4143        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 4144        editor.transpose(&Default::default(), cx);
 4145        assert_eq!(editor.text(cx), "acb\nde");
 4146        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4147
 4148        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4149        editor.transpose(&Default::default(), cx);
 4150        assert_eq!(editor.text(cx), "acbd\ne");
 4151        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4152
 4153        editor.transpose(&Default::default(), cx);
 4154        assert_eq!(editor.text(cx), "acbde\n");
 4155        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4156
 4157        editor.transpose(&Default::default(), cx);
 4158        assert_eq!(editor.text(cx), "acbd\ne");
 4159        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4160
 4161        editor
 4162    });
 4163
 4164    _ = cx.add_window(|cx| {
 4165        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 4166        editor.set_style(EditorStyle::default(), cx);
 4167        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4168        editor.transpose(&Default::default(), cx);
 4169        assert_eq!(editor.text(cx), "bacd\ne");
 4170        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4171
 4172        editor.transpose(&Default::default(), cx);
 4173        assert_eq!(editor.text(cx), "bcade\n");
 4174        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4175
 4176        editor.transpose(&Default::default(), cx);
 4177        assert_eq!(editor.text(cx), "bcda\ne");
 4178        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4179
 4180        editor.transpose(&Default::default(), cx);
 4181        assert_eq!(editor.text(cx), "bcade\n");
 4182        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4183
 4184        editor.transpose(&Default::default(), cx);
 4185        assert_eq!(editor.text(cx), "bcaed\n");
 4186        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4187
 4188        editor
 4189    });
 4190
 4191    _ = cx.add_window(|cx| {
 4192        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 4193        editor.set_style(EditorStyle::default(), cx);
 4194        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
 4195        editor.transpose(&Default::default(), cx);
 4196        assert_eq!(editor.text(cx), "🏀🍐✋");
 4197        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4198
 4199        editor.transpose(&Default::default(), cx);
 4200        assert_eq!(editor.text(cx), "🏀✋🍐");
 4201        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4202
 4203        editor.transpose(&Default::default(), cx);
 4204        assert_eq!(editor.text(cx), "🏀🍐✋");
 4205        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4206
 4207        editor
 4208    });
 4209}
 4210
 4211#[gpui::test]
 4212async fn test_rewrap(cx: &mut TestAppContext) {
 4213    init_test(cx, |_| {});
 4214
 4215    let mut cx = EditorTestContext::new(cx).await;
 4216
 4217    let language_with_c_comments = Arc::new(Language::new(
 4218        LanguageConfig {
 4219            line_comments: vec!["// ".into()],
 4220            ..LanguageConfig::default()
 4221        },
 4222        None,
 4223    ));
 4224    let language_with_pound_comments = Arc::new(Language::new(
 4225        LanguageConfig {
 4226            line_comments: vec!["# ".into()],
 4227            ..LanguageConfig::default()
 4228        },
 4229        None,
 4230    ));
 4231    let markdown_language = Arc::new(Language::new(
 4232        LanguageConfig {
 4233            name: "Markdown".into(),
 4234            ..LanguageConfig::default()
 4235        },
 4236        None,
 4237    ));
 4238    let language_with_doc_comments = Arc::new(Language::new(
 4239        LanguageConfig {
 4240            line_comments: vec!["// ".into(), "/// ".into()],
 4241            ..LanguageConfig::default()
 4242        },
 4243        Some(tree_sitter_rust::LANGUAGE.into()),
 4244    ));
 4245
 4246    let plaintext_language = Arc::new(Language::new(
 4247        LanguageConfig {
 4248            name: "Plain Text".into(),
 4249            ..LanguageConfig::default()
 4250        },
 4251        None,
 4252    ));
 4253
 4254    assert_rewrap(
 4255        indoc! {"
 4256            // ˇ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.
 4257        "},
 4258        indoc! {"
 4259            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4260            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4261            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4262            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4263            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4264            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4265            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4266            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4267            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4268            // porttitor id. Aliquam id accumsan eros.
 4269        "},
 4270        language_with_c_comments.clone(),
 4271        &mut cx,
 4272    );
 4273
 4274    // Test that rewrapping works inside of a selection
 4275    assert_rewrap(
 4276        indoc! {"
 4277            «// 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.ˇ»
 4278        "},
 4279        indoc! {"
 4280            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4281            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4282            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4283            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4284            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4285            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4286            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4287            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4288            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4289            // porttitor id. Aliquam id accumsan eros.ˇ»
 4290        "},
 4291        language_with_c_comments.clone(),
 4292        &mut cx,
 4293    );
 4294
 4295    // Test that cursors that expand to the same region are collapsed.
 4296    assert_rewrap(
 4297        indoc! {"
 4298            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4299            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4300            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4301            // ˇ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.
 4302        "},
 4303        indoc! {"
 4304            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4305            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4306            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4307            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4308            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4309            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4310            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4311            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4312            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4313            // porttitor id. Aliquam id accumsan eros.
 4314        "},
 4315        language_with_c_comments.clone(),
 4316        &mut cx,
 4317    );
 4318
 4319    // Test that non-contiguous selections are treated separately.
 4320    assert_rewrap(
 4321        indoc! {"
 4322            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4323            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4324            //
 4325            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4326            // ˇ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.
 4327        "},
 4328        indoc! {"
 4329            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4330            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4331            // auctor, eu lacinia sapien scelerisque.
 4332            //
 4333            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4334            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4335            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4336            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4337            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4338            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4339            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4340        "},
 4341        language_with_c_comments.clone(),
 4342        &mut cx,
 4343    );
 4344
 4345    // Test that different comment prefixes are supported.
 4346    assert_rewrap(
 4347        indoc! {"
 4348            # ˇ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.
 4349        "},
 4350        indoc! {"
 4351            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4352            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4353            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4354            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4355            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4356            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4357            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4358            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4359            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4360            # accumsan eros.
 4361        "},
 4362        language_with_pound_comments.clone(),
 4363        &mut cx,
 4364    );
 4365
 4366    // Test that rewrapping is ignored outside of comments in most languages.
 4367    assert_rewrap(
 4368        indoc! {"
 4369            /// Adds two numbers.
 4370            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4371            fn add(a: u32, b: u32) -> u32 {
 4372                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ˇ
 4373            }
 4374        "},
 4375        indoc! {"
 4376            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4377            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4378            fn add(a: u32, b: u32) -> u32 {
 4379                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ˇ
 4380            }
 4381        "},
 4382        language_with_doc_comments.clone(),
 4383        &mut cx,
 4384    );
 4385
 4386    // Test that rewrapping works in Markdown and Plain Text languages.
 4387    assert_rewrap(
 4388        indoc! {"
 4389            # Hello
 4390
 4391            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.
 4392        "},
 4393        indoc! {"
 4394            # Hello
 4395
 4396            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4397            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4398            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4399            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4400            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4401            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4402            Integer sit amet scelerisque nisi.
 4403        "},
 4404        markdown_language,
 4405        &mut cx,
 4406    );
 4407
 4408    assert_rewrap(
 4409        indoc! {"
 4410            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.
 4411        "},
 4412        indoc! {"
 4413            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4414            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4415            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4416            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4417            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4418            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4419            Integer sit amet scelerisque nisi.
 4420        "},
 4421        plaintext_language,
 4422        &mut cx,
 4423    );
 4424
 4425    // Test rewrapping unaligned comments in a selection.
 4426    assert_rewrap(
 4427        indoc! {"
 4428            fn foo() {
 4429                if true {
 4430            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4431            // Praesent semper egestas tellus id dignissim.ˇ»
 4432                    do_something();
 4433                } else {
 4434                    //
 4435                }
 4436            }
 4437        "},
 4438        indoc! {"
 4439            fn foo() {
 4440                if true {
 4441            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4442                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4443                    // egestas tellus id dignissim.ˇ»
 4444                    do_something();
 4445                } else {
 4446                    //
 4447                }
 4448            }
 4449        "},
 4450        language_with_doc_comments.clone(),
 4451        &mut cx,
 4452    );
 4453
 4454    assert_rewrap(
 4455        indoc! {"
 4456            fn foo() {
 4457                if true {
 4458            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4459            // Praesent semper egestas tellus id dignissim.»
 4460                    do_something();
 4461                } else {
 4462                    //
 4463                }
 4464
 4465            }
 4466        "},
 4467        indoc! {"
 4468            fn foo() {
 4469                if true {
 4470            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4471                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4472                    // egestas tellus id dignissim.»
 4473                    do_something();
 4474                } else {
 4475                    //
 4476                }
 4477
 4478            }
 4479        "},
 4480        language_with_doc_comments.clone(),
 4481        &mut cx,
 4482    );
 4483
 4484    #[track_caller]
 4485    fn assert_rewrap(
 4486        unwrapped_text: &str,
 4487        wrapped_text: &str,
 4488        language: Arc<Language>,
 4489        cx: &mut EditorTestContext,
 4490    ) {
 4491        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4492        cx.set_state(unwrapped_text);
 4493        cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
 4494        cx.assert_editor_state(wrapped_text);
 4495    }
 4496}
 4497
 4498#[gpui::test]
 4499async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4500    init_test(cx, |_| {});
 4501
 4502    let mut cx = EditorTestContext::new(cx).await;
 4503
 4504    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4505    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4506    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4507
 4508    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4509    cx.set_state("two ˇfour ˇsix ˇ");
 4510    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4511    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4512
 4513    // Paste again but with only two cursors. Since the number of cursors doesn't
 4514    // match the number of slices in the clipboard, the entire clipboard text
 4515    // is pasted at each cursor.
 4516    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4517    cx.update_editor(|e, cx| {
 4518        e.handle_input("( ", cx);
 4519        e.paste(&Paste, cx);
 4520        e.handle_input(") ", cx);
 4521    });
 4522    cx.assert_editor_state(
 4523        &([
 4524            "( one✅ ",
 4525            "three ",
 4526            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4527            "three ",
 4528            "five ) ˇ",
 4529        ]
 4530        .join("\n")),
 4531    );
 4532
 4533    // Cut with three selections, one of which is full-line.
 4534    cx.set_state(indoc! {"
 4535        1«2ˇ»3
 4536        4ˇ567
 4537        «8ˇ»9"});
 4538    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4539    cx.assert_editor_state(indoc! {"
 4540        1ˇ3
 4541        ˇ9"});
 4542
 4543    // Paste with three selections, noticing how the copied selection that was full-line
 4544    // gets inserted before the second cursor.
 4545    cx.set_state(indoc! {"
 4546        1ˇ3
 4547 4548        «oˇ»ne"});
 4549    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4550    cx.assert_editor_state(indoc! {"
 4551        12ˇ3
 4552        4567
 4553 4554        8ˇne"});
 4555
 4556    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4557    cx.set_state(indoc! {"
 4558        The quick brown
 4559        fox juˇmps over
 4560        the lazy dog"});
 4561    cx.update_editor(|e, cx| e.copy(&Copy, cx));
 4562    assert_eq!(
 4563        cx.read_from_clipboard()
 4564            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4565        Some("fox jumps over\n".to_string())
 4566    );
 4567
 4568    // Paste with three selections, noticing how the copied full-line selection is inserted
 4569    // before the empty selections but replaces the selection that is non-empty.
 4570    cx.set_state(indoc! {"
 4571        Tˇhe quick brown
 4572        «foˇ»x jumps over
 4573        tˇhe lazy dog"});
 4574    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4575    cx.assert_editor_state(indoc! {"
 4576        fox jumps over
 4577        Tˇhe quick brown
 4578        fox jumps over
 4579        ˇx jumps over
 4580        fox jumps over
 4581        tˇhe lazy dog"});
 4582}
 4583
 4584#[gpui::test]
 4585async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4586    init_test(cx, |_| {});
 4587
 4588    let mut cx = EditorTestContext::new(cx).await;
 4589    let language = Arc::new(Language::new(
 4590        LanguageConfig::default(),
 4591        Some(tree_sitter_rust::LANGUAGE.into()),
 4592    ));
 4593    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4594
 4595    // Cut an indented block, without the leading whitespace.
 4596    cx.set_state(indoc! {"
 4597        const a: B = (
 4598            c(),
 4599            «d(
 4600                e,
 4601                f
 4602            )ˇ»
 4603        );
 4604    "});
 4605    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4606    cx.assert_editor_state(indoc! {"
 4607        const a: B = (
 4608            c(),
 4609            ˇ
 4610        );
 4611    "});
 4612
 4613    // Paste it at the same position.
 4614    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4615    cx.assert_editor_state(indoc! {"
 4616        const a: B = (
 4617            c(),
 4618            d(
 4619                e,
 4620                f
 4621 4622        );
 4623    "});
 4624
 4625    // Paste it at a line with a lower indent level.
 4626    cx.set_state(indoc! {"
 4627        ˇ
 4628        const a: B = (
 4629            c(),
 4630        );
 4631    "});
 4632    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4633    cx.assert_editor_state(indoc! {"
 4634        d(
 4635            e,
 4636            f
 4637 4638        const a: B = (
 4639            c(),
 4640        );
 4641    "});
 4642
 4643    // Cut an indented block, with the leading whitespace.
 4644    cx.set_state(indoc! {"
 4645        const a: B = (
 4646            c(),
 4647        «    d(
 4648                e,
 4649                f
 4650            )
 4651        ˇ»);
 4652    "});
 4653    cx.update_editor(|e, cx| e.cut(&Cut, cx));
 4654    cx.assert_editor_state(indoc! {"
 4655        const a: B = (
 4656            c(),
 4657        ˇ);
 4658    "});
 4659
 4660    // Paste it at the same position.
 4661    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4662    cx.assert_editor_state(indoc! {"
 4663        const a: B = (
 4664            c(),
 4665            d(
 4666                e,
 4667                f
 4668            )
 4669        ˇ);
 4670    "});
 4671
 4672    // Paste it at a line with a higher indent level.
 4673    cx.set_state(indoc! {"
 4674        const a: B = (
 4675            c(),
 4676            d(
 4677                e,
 4678 4679            )
 4680        );
 4681    "});
 4682    cx.update_editor(|e, cx| e.paste(&Paste, cx));
 4683    cx.assert_editor_state(indoc! {"
 4684        const a: B = (
 4685            c(),
 4686            d(
 4687                e,
 4688                f    d(
 4689                    e,
 4690                    f
 4691                )
 4692        ˇ
 4693            )
 4694        );
 4695    "});
 4696}
 4697
 4698#[gpui::test]
 4699fn test_select_all(cx: &mut TestAppContext) {
 4700    init_test(cx, |_| {});
 4701
 4702    let view = cx.add_window(|cx| {
 4703        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4704        build_editor(buffer, cx)
 4705    });
 4706    _ = view.update(cx, |view, cx| {
 4707        view.select_all(&SelectAll, cx);
 4708        assert_eq!(
 4709            view.selections.display_ranges(cx),
 4710            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4711        );
 4712    });
 4713}
 4714
 4715#[gpui::test]
 4716fn test_select_line(cx: &mut TestAppContext) {
 4717    init_test(cx, |_| {});
 4718
 4719    let view = cx.add_window(|cx| {
 4720        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4721        build_editor(buffer, cx)
 4722    });
 4723    _ = view.update(cx, |view, cx| {
 4724        view.change_selections(None, cx, |s| {
 4725            s.select_display_ranges([
 4726                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4727                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4728                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4729                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4730            ])
 4731        });
 4732        view.select_line(&SelectLine, cx);
 4733        assert_eq!(
 4734            view.selections.display_ranges(cx),
 4735            vec![
 4736                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4737                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4738            ]
 4739        );
 4740    });
 4741
 4742    _ = view.update(cx, |view, cx| {
 4743        view.select_line(&SelectLine, cx);
 4744        assert_eq!(
 4745            view.selections.display_ranges(cx),
 4746            vec![
 4747                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4748                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4749            ]
 4750        );
 4751    });
 4752
 4753    _ = view.update(cx, |view, cx| {
 4754        view.select_line(&SelectLine, cx);
 4755        assert_eq!(
 4756            view.selections.display_ranges(cx),
 4757            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4758        );
 4759    });
 4760}
 4761
 4762#[gpui::test]
 4763fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4764    init_test(cx, |_| {});
 4765
 4766    let view = cx.add_window(|cx| {
 4767        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4768        build_editor(buffer, cx)
 4769    });
 4770    _ = view.update(cx, |view, cx| {
 4771        view.fold_creases(
 4772            vec![
 4773                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4774                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4775                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4776            ],
 4777            true,
 4778            cx,
 4779        );
 4780        view.change_selections(None, cx, |s| {
 4781            s.select_display_ranges([
 4782                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4783                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4784                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4785                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4786            ])
 4787        });
 4788        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
 4789    });
 4790
 4791    _ = view.update(cx, |view, cx| {
 4792        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4793        assert_eq!(
 4794            view.display_text(cx),
 4795            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4796        );
 4797        assert_eq!(
 4798            view.selections.display_ranges(cx),
 4799            [
 4800                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4801                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4802                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4803                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4804            ]
 4805        );
 4806    });
 4807
 4808    _ = view.update(cx, |view, cx| {
 4809        view.change_selections(None, cx, |s| {
 4810            s.select_display_ranges([
 4811                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4812            ])
 4813        });
 4814        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
 4815        assert_eq!(
 4816            view.display_text(cx),
 4817            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4818        );
 4819        assert_eq!(
 4820            view.selections.display_ranges(cx),
 4821            [
 4822                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4823                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4824                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4825                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4826                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4827                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4828                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4829                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4830            ]
 4831        );
 4832    });
 4833}
 4834
 4835#[gpui::test]
 4836async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4837    init_test(cx, |_| {});
 4838
 4839    let mut cx = EditorTestContext::new(cx).await;
 4840
 4841    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4842    cx.set_state(indoc!(
 4843        r#"abc
 4844           defˇghi
 4845
 4846           jk
 4847           nlmo
 4848           "#
 4849    ));
 4850
 4851    cx.update_editor(|editor, cx| {
 4852        editor.add_selection_above(&Default::default(), cx);
 4853    });
 4854
 4855    cx.assert_editor_state(indoc!(
 4856        r#"abcˇ
 4857           defˇghi
 4858
 4859           jk
 4860           nlmo
 4861           "#
 4862    ));
 4863
 4864    cx.update_editor(|editor, cx| {
 4865        editor.add_selection_above(&Default::default(), cx);
 4866    });
 4867
 4868    cx.assert_editor_state(indoc!(
 4869        r#"abcˇ
 4870            defˇghi
 4871
 4872            jk
 4873            nlmo
 4874            "#
 4875    ));
 4876
 4877    cx.update_editor(|view, cx| {
 4878        view.add_selection_below(&Default::default(), cx);
 4879    });
 4880
 4881    cx.assert_editor_state(indoc!(
 4882        r#"abc
 4883           defˇghi
 4884
 4885           jk
 4886           nlmo
 4887           "#
 4888    ));
 4889
 4890    cx.update_editor(|view, cx| {
 4891        view.undo_selection(&Default::default(), cx);
 4892    });
 4893
 4894    cx.assert_editor_state(indoc!(
 4895        r#"abcˇ
 4896           defˇghi
 4897
 4898           jk
 4899           nlmo
 4900           "#
 4901    ));
 4902
 4903    cx.update_editor(|view, cx| {
 4904        view.redo_selection(&Default::default(), cx);
 4905    });
 4906
 4907    cx.assert_editor_state(indoc!(
 4908        r#"abc
 4909           defˇghi
 4910
 4911           jk
 4912           nlmo
 4913           "#
 4914    ));
 4915
 4916    cx.update_editor(|view, cx| {
 4917        view.add_selection_below(&Default::default(), cx);
 4918    });
 4919
 4920    cx.assert_editor_state(indoc!(
 4921        r#"abc
 4922           defˇghi
 4923
 4924           jk
 4925           nlmˇo
 4926           "#
 4927    ));
 4928
 4929    cx.update_editor(|view, cx| {
 4930        view.add_selection_below(&Default::default(), cx);
 4931    });
 4932
 4933    cx.assert_editor_state(indoc!(
 4934        r#"abc
 4935           defˇghi
 4936
 4937           jk
 4938           nlmˇo
 4939           "#
 4940    ));
 4941
 4942    // change selections
 4943    cx.set_state(indoc!(
 4944        r#"abc
 4945           def«ˇg»hi
 4946
 4947           jk
 4948           nlmo
 4949           "#
 4950    ));
 4951
 4952    cx.update_editor(|view, cx| {
 4953        view.add_selection_below(&Default::default(), cx);
 4954    });
 4955
 4956    cx.assert_editor_state(indoc!(
 4957        r#"abc
 4958           def«ˇg»hi
 4959
 4960           jk
 4961           nlm«ˇo»
 4962           "#
 4963    ));
 4964
 4965    cx.update_editor(|view, cx| {
 4966        view.add_selection_below(&Default::default(), cx);
 4967    });
 4968
 4969    cx.assert_editor_state(indoc!(
 4970        r#"abc
 4971           def«ˇg»hi
 4972
 4973           jk
 4974           nlm«ˇo»
 4975           "#
 4976    ));
 4977
 4978    cx.update_editor(|view, cx| {
 4979        view.add_selection_above(&Default::default(), cx);
 4980    });
 4981
 4982    cx.assert_editor_state(indoc!(
 4983        r#"abc
 4984           def«ˇg»hi
 4985
 4986           jk
 4987           nlmo
 4988           "#
 4989    ));
 4990
 4991    cx.update_editor(|view, cx| {
 4992        view.add_selection_above(&Default::default(), cx);
 4993    });
 4994
 4995    cx.assert_editor_state(indoc!(
 4996        r#"abc
 4997           def«ˇg»hi
 4998
 4999           jk
 5000           nlmo
 5001           "#
 5002    ));
 5003
 5004    // Change selections again
 5005    cx.set_state(indoc!(
 5006        r#"a«bc
 5007           defgˇ»hi
 5008
 5009           jk
 5010           nlmo
 5011           "#
 5012    ));
 5013
 5014    cx.update_editor(|view, cx| {
 5015        view.add_selection_below(&Default::default(), cx);
 5016    });
 5017
 5018    cx.assert_editor_state(indoc!(
 5019        r#"a«bcˇ»
 5020           d«efgˇ»hi
 5021
 5022           j«kˇ»
 5023           nlmo
 5024           "#
 5025    ));
 5026
 5027    cx.update_editor(|view, cx| {
 5028        view.add_selection_below(&Default::default(), cx);
 5029    });
 5030    cx.assert_editor_state(indoc!(
 5031        r#"a«bcˇ»
 5032           d«efgˇ»hi
 5033
 5034           j«kˇ»
 5035           n«lmoˇ»
 5036           "#
 5037    ));
 5038    cx.update_editor(|view, cx| {
 5039        view.add_selection_above(&Default::default(), cx);
 5040    });
 5041
 5042    cx.assert_editor_state(indoc!(
 5043        r#"a«bcˇ»
 5044           d«efgˇ»hi
 5045
 5046           j«kˇ»
 5047           nlmo
 5048           "#
 5049    ));
 5050
 5051    // Change selections again
 5052    cx.set_state(indoc!(
 5053        r#"abc
 5054           d«ˇefghi
 5055
 5056           jk
 5057           nlm»o
 5058           "#
 5059    ));
 5060
 5061    cx.update_editor(|view, cx| {
 5062        view.add_selection_above(&Default::default(), cx);
 5063    });
 5064
 5065    cx.assert_editor_state(indoc!(
 5066        r#"a«ˇbc»
 5067           d«ˇef»ghi
 5068
 5069           j«ˇk»
 5070           n«ˇlm»o
 5071           "#
 5072    ));
 5073
 5074    cx.update_editor(|view, cx| {
 5075        view.add_selection_below(&Default::default(), cx);
 5076    });
 5077
 5078    cx.assert_editor_state(indoc!(
 5079        r#"abc
 5080           d«ˇef»ghi
 5081
 5082           j«ˇk»
 5083           n«ˇlm»o
 5084           "#
 5085    ));
 5086}
 5087
 5088#[gpui::test]
 5089async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5090    init_test(cx, |_| {});
 5091
 5092    let mut cx = EditorTestContext::new(cx).await;
 5093    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5094
 5095    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5096        .unwrap();
 5097    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5098
 5099    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5100        .unwrap();
 5101    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5102
 5103    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5104    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5105
 5106    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5107    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5108
 5109    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5110        .unwrap();
 5111    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5112
 5113    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5114        .unwrap();
 5115    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5116}
 5117
 5118#[gpui::test]
 5119async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5120    init_test(cx, |_| {});
 5121
 5122    let mut cx = EditorTestContext::new(cx).await;
 5123    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5124
 5125    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
 5126        .unwrap();
 5127    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5128}
 5129
 5130#[gpui::test]
 5131async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5132    init_test(cx, |_| {});
 5133
 5134    let mut cx = EditorTestContext::new(cx).await;
 5135    cx.set_state(
 5136        r#"let foo = 2;
 5137lˇet foo = 2;
 5138let fooˇ = 2;
 5139let foo = 2;
 5140let foo = ˇ2;"#,
 5141    );
 5142
 5143    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5144        .unwrap();
 5145    cx.assert_editor_state(
 5146        r#"let foo = 2;
 5147«letˇ» foo = 2;
 5148let «fooˇ» = 2;
 5149let foo = 2;
 5150let foo = «2ˇ»;"#,
 5151    );
 5152
 5153    // noop for multiple selections with different contents
 5154    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
 5155        .unwrap();
 5156    cx.assert_editor_state(
 5157        r#"let foo = 2;
 5158«letˇ» foo = 2;
 5159let «fooˇ» = 2;
 5160let foo = 2;
 5161let foo = «2ˇ»;"#,
 5162    );
 5163}
 5164
 5165#[gpui::test]
 5166async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5167    init_test(cx, |_| {});
 5168
 5169    let mut cx =
 5170        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5171
 5172    cx.assert_editor_state(indoc! {"
 5173        ˇbbb
 5174        ccc
 5175
 5176        bbb
 5177        ccc
 5178        "});
 5179    cx.dispatch_action(SelectPrevious::default());
 5180    cx.assert_editor_state(indoc! {"
 5181                «bbbˇ»
 5182                ccc
 5183
 5184                bbb
 5185                ccc
 5186                "});
 5187    cx.dispatch_action(SelectPrevious::default());
 5188    cx.assert_editor_state(indoc! {"
 5189                «bbbˇ»
 5190                ccc
 5191
 5192                «bbbˇ»
 5193                ccc
 5194                "});
 5195}
 5196
 5197#[gpui::test]
 5198async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5199    init_test(cx, |_| {});
 5200
 5201    let mut cx = EditorTestContext::new(cx).await;
 5202    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5203
 5204    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5205        .unwrap();
 5206    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5207
 5208    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5209        .unwrap();
 5210    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5211
 5212    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5213    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5214
 5215    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5216    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5217
 5218    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5219        .unwrap();
 5220    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5221
 5222    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5223        .unwrap();
 5224    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5225
 5226    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5227        .unwrap();
 5228    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5229}
 5230
 5231#[gpui::test]
 5232async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5233    init_test(cx, |_| {});
 5234
 5235    let mut cx = EditorTestContext::new(cx).await;
 5236    cx.set_state(
 5237        r#"let foo = 2;
 5238lˇet foo = 2;
 5239let fooˇ = 2;
 5240let foo = 2;
 5241let foo = ˇ2;"#,
 5242    );
 5243
 5244    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5245        .unwrap();
 5246    cx.assert_editor_state(
 5247        r#"let foo = 2;
 5248«letˇ» foo = 2;
 5249let «fooˇ» = 2;
 5250let foo = 2;
 5251let foo = «2ˇ»;"#,
 5252    );
 5253
 5254    // noop for multiple selections with different contents
 5255    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5256        .unwrap();
 5257    cx.assert_editor_state(
 5258        r#"let foo = 2;
 5259«letˇ» foo = 2;
 5260let «fooˇ» = 2;
 5261let foo = 2;
 5262let foo = «2ˇ»;"#,
 5263    );
 5264}
 5265
 5266#[gpui::test]
 5267async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5268    init_test(cx, |_| {});
 5269
 5270    let mut cx = EditorTestContext::new(cx).await;
 5271    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5272
 5273    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5274        .unwrap();
 5275    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5276
 5277    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5278        .unwrap();
 5279    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5280
 5281    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
 5282    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5283
 5284    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
 5285    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5286
 5287    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5288        .unwrap();
 5289    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5290
 5291    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
 5292        .unwrap();
 5293    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5294}
 5295
 5296#[gpui::test]
 5297async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5298    init_test(cx, |_| {});
 5299
 5300    let language = Arc::new(Language::new(
 5301        LanguageConfig::default(),
 5302        Some(tree_sitter_rust::LANGUAGE.into()),
 5303    ));
 5304
 5305    let text = r#"
 5306        use mod1::mod2::{mod3, mod4};
 5307
 5308        fn fn_1(param1: bool, param2: &str) {
 5309            let var1 = "text";
 5310        }
 5311    "#
 5312    .unindent();
 5313
 5314    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5315    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5316    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5317
 5318    editor
 5319        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 5320        .await;
 5321
 5322    editor.update(cx, |view, cx| {
 5323        view.change_selections(None, cx, |s| {
 5324            s.select_display_ranges([
 5325                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5326                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5327                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5328            ]);
 5329        });
 5330        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5331    });
 5332    editor.update(cx, |editor, cx| {
 5333        assert_text_with_selections(
 5334            editor,
 5335            indoc! {r#"
 5336                use mod1::mod2::{mod3, «mod4ˇ»};
 5337
 5338                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5339                    let var1 = "«textˇ»";
 5340                }
 5341            "#},
 5342            cx,
 5343        );
 5344    });
 5345
 5346    editor.update(cx, |view, cx| {
 5347        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5348    });
 5349    editor.update(cx, |editor, cx| {
 5350        assert_text_with_selections(
 5351            editor,
 5352            indoc! {r#"
 5353                use mod1::mod2::«{mod3, mod4}ˇ»;
 5354
 5355                «ˇfn fn_1(param1: bool, param2: &str) {
 5356                    let var1 = "text";
 5357 5358            "#},
 5359            cx,
 5360        );
 5361    });
 5362
 5363    editor.update(cx, |view, cx| {
 5364        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5365    });
 5366    assert_eq!(
 5367        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5368        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5369    );
 5370
 5371    // Trying to expand the selected syntax node one more time has no effect.
 5372    editor.update(cx, |view, cx| {
 5373        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5374    });
 5375    assert_eq!(
 5376        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 5377        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5378    );
 5379
 5380    editor.update(cx, |view, cx| {
 5381        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5382    });
 5383    editor.update(cx, |editor, cx| {
 5384        assert_text_with_selections(
 5385            editor,
 5386            indoc! {r#"
 5387                use mod1::mod2::«{mod3, mod4}ˇ»;
 5388
 5389                «ˇfn fn_1(param1: bool, param2: &str) {
 5390                    let var1 = "text";
 5391 5392            "#},
 5393            cx,
 5394        );
 5395    });
 5396
 5397    editor.update(cx, |view, cx| {
 5398        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5399    });
 5400    editor.update(cx, |editor, cx| {
 5401        assert_text_with_selections(
 5402            editor,
 5403            indoc! {r#"
 5404                use mod1::mod2::{mod3, «mod4ˇ»};
 5405
 5406                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5407                    let var1 = "«textˇ»";
 5408                }
 5409            "#},
 5410            cx,
 5411        );
 5412    });
 5413
 5414    editor.update(cx, |view, cx| {
 5415        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5416    });
 5417    editor.update(cx, |editor, cx| {
 5418        assert_text_with_selections(
 5419            editor,
 5420            indoc! {r#"
 5421                use mod1::mod2::{mod3, mo«ˇ»d4};
 5422
 5423                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5424                    let var1 = "te«ˇ»xt";
 5425                }
 5426            "#},
 5427            cx,
 5428        );
 5429    });
 5430
 5431    // Trying to shrink the selected syntax node one more time has no effect.
 5432    editor.update(cx, |view, cx| {
 5433        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
 5434    });
 5435    editor.update(cx, |editor, cx| {
 5436        assert_text_with_selections(
 5437            editor,
 5438            indoc! {r#"
 5439                use mod1::mod2::{mod3, mo«ˇ»d4};
 5440
 5441                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5442                    let var1 = "te«ˇ»xt";
 5443                }
 5444            "#},
 5445            cx,
 5446        );
 5447    });
 5448
 5449    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5450    // a fold.
 5451    editor.update(cx, |view, cx| {
 5452        view.fold_creases(
 5453            vec![
 5454                Crease::simple(
 5455                    Point::new(0, 21)..Point::new(0, 24),
 5456                    FoldPlaceholder::test(),
 5457                ),
 5458                Crease::simple(
 5459                    Point::new(3, 20)..Point::new(3, 22),
 5460                    FoldPlaceholder::test(),
 5461                ),
 5462            ],
 5463            true,
 5464            cx,
 5465        );
 5466        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
 5467    });
 5468    editor.update(cx, |editor, cx| {
 5469        assert_text_with_selections(
 5470            editor,
 5471            indoc! {r#"
 5472                use mod1::mod2::«{mod3, mod4}ˇ»;
 5473
 5474                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5475                    «let var1 = "text";ˇ»
 5476                }
 5477            "#},
 5478            cx,
 5479        );
 5480    });
 5481}
 5482
 5483#[gpui::test]
 5484async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5485    init_test(cx, |_| {});
 5486
 5487    let base_text = r#"
 5488        impl A {
 5489            // this is an unstaged comment
 5490
 5491            fn b() {
 5492                c();
 5493            }
 5494
 5495            // this is another unstaged comment
 5496
 5497            fn d() {
 5498                // e
 5499                // f
 5500            }
 5501        }
 5502
 5503        fn g() {
 5504            // h
 5505        }
 5506    "#
 5507    .unindent();
 5508
 5509    let text = r#"
 5510        ˇimpl A {
 5511
 5512            fn b() {
 5513                c();
 5514            }
 5515
 5516            fn d() {
 5517                // e
 5518                // f
 5519            }
 5520        }
 5521
 5522        fn g() {
 5523            // h
 5524        }
 5525    "#
 5526    .unindent();
 5527
 5528    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5529    cx.set_state(&text);
 5530    cx.set_diff_base(&base_text);
 5531    cx.update_editor(|editor, cx| {
 5532        editor.expand_all_diff_hunks(&Default::default(), cx);
 5533    });
 5534
 5535    cx.assert_state_with_diff(
 5536        "
 5537        ˇimpl A {
 5538      -     // this is an unstaged comment
 5539
 5540            fn b() {
 5541                c();
 5542            }
 5543
 5544      -     // this is another unstaged comment
 5545      -
 5546            fn d() {
 5547                // e
 5548                // f
 5549            }
 5550        }
 5551
 5552        fn g() {
 5553            // h
 5554        }
 5555    "
 5556        .unindent(),
 5557    );
 5558
 5559    let expected_display_text = "
 5560        impl A {
 5561            // this is an unstaged comment
 5562
 5563            fn b() {
 5564 5565            }
 5566
 5567            // this is another unstaged comment
 5568
 5569            fn d() {
 5570 5571            }
 5572        }
 5573
 5574        fn g() {
 5575 5576        }
 5577        "
 5578    .unindent();
 5579
 5580    cx.update_editor(|editor, cx| {
 5581        editor.fold_function_bodies(&FoldFunctionBodies, cx);
 5582        assert_eq!(editor.display_text(cx), expected_display_text);
 5583    });
 5584}
 5585
 5586#[gpui::test]
 5587async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5588    init_test(cx, |_| {});
 5589
 5590    let language = Arc::new(
 5591        Language::new(
 5592            LanguageConfig {
 5593                brackets: BracketPairConfig {
 5594                    pairs: vec![
 5595                        BracketPair {
 5596                            start: "{".to_string(),
 5597                            end: "}".to_string(),
 5598                            close: false,
 5599                            surround: false,
 5600                            newline: true,
 5601                        },
 5602                        BracketPair {
 5603                            start: "(".to_string(),
 5604                            end: ")".to_string(),
 5605                            close: false,
 5606                            surround: false,
 5607                            newline: true,
 5608                        },
 5609                    ],
 5610                    ..Default::default()
 5611                },
 5612                ..Default::default()
 5613            },
 5614            Some(tree_sitter_rust::LANGUAGE.into()),
 5615        )
 5616        .with_indents_query(
 5617            r#"
 5618                (_ "(" ")" @end) @indent
 5619                (_ "{" "}" @end) @indent
 5620            "#,
 5621        )
 5622        .unwrap(),
 5623    );
 5624
 5625    let text = "fn a() {}";
 5626
 5627    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5628    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5629    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5630    editor
 5631        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5632        .await;
 5633
 5634    editor.update(cx, |editor, cx| {
 5635        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5636        editor.newline(&Newline, cx);
 5637        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5638        assert_eq!(
 5639            editor.selections.ranges(cx),
 5640            &[
 5641                Point::new(1, 4)..Point::new(1, 4),
 5642                Point::new(3, 4)..Point::new(3, 4),
 5643                Point::new(5, 0)..Point::new(5, 0)
 5644            ]
 5645        );
 5646    });
 5647}
 5648
 5649#[gpui::test]
 5650async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5651    init_test(cx, |_| {});
 5652
 5653    {
 5654        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5655        cx.set_state(indoc! {"
 5656            impl A {
 5657
 5658                fn b() {}
 5659
 5660            «fn c() {
 5661
 5662            }ˇ»
 5663            }
 5664        "});
 5665
 5666        cx.update_editor(|editor, cx| {
 5667            editor.autoindent(&Default::default(), cx);
 5668        });
 5669
 5670        cx.assert_editor_state(indoc! {"
 5671            impl A {
 5672
 5673                fn b() {}
 5674
 5675                «fn c() {
 5676
 5677                }ˇ»
 5678            }
 5679        "});
 5680    }
 5681
 5682    {
 5683        let mut cx = EditorTestContext::new_multibuffer(
 5684            cx,
 5685            [indoc! { "
 5686                impl A {
 5687                «
 5688                // a
 5689                fn b(){}
 5690                »
 5691                «
 5692                    }
 5693                    fn c(){}
 5694                »
 5695            "}],
 5696        );
 5697
 5698        let buffer = cx.update_editor(|editor, cx| {
 5699            let buffer = editor.buffer().update(cx, |buffer, _| {
 5700                buffer.all_buffers().iter().next().unwrap().clone()
 5701            });
 5702            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5703            buffer
 5704        });
 5705
 5706        cx.run_until_parked();
 5707        cx.update_editor(|editor, cx| {
 5708            editor.select_all(&Default::default(), cx);
 5709            editor.autoindent(&Default::default(), cx)
 5710        });
 5711        cx.run_until_parked();
 5712
 5713        cx.update(|cx| {
 5714            pretty_assertions::assert_eq!(
 5715                buffer.read(cx).text(),
 5716                indoc! { "
 5717                    impl A {
 5718
 5719                        // a
 5720                        fn b(){}
 5721
 5722
 5723                    }
 5724                    fn c(){}
 5725
 5726                " }
 5727            )
 5728        });
 5729    }
 5730}
 5731
 5732#[gpui::test]
 5733async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5734    init_test(cx, |_| {});
 5735
 5736    let mut cx = EditorTestContext::new(cx).await;
 5737
 5738    let language = Arc::new(Language::new(
 5739        LanguageConfig {
 5740            brackets: BracketPairConfig {
 5741                pairs: vec![
 5742                    BracketPair {
 5743                        start: "{".to_string(),
 5744                        end: "}".to_string(),
 5745                        close: true,
 5746                        surround: true,
 5747                        newline: true,
 5748                    },
 5749                    BracketPair {
 5750                        start: "(".to_string(),
 5751                        end: ")".to_string(),
 5752                        close: true,
 5753                        surround: true,
 5754                        newline: true,
 5755                    },
 5756                    BracketPair {
 5757                        start: "/*".to_string(),
 5758                        end: " */".to_string(),
 5759                        close: true,
 5760                        surround: true,
 5761                        newline: true,
 5762                    },
 5763                    BracketPair {
 5764                        start: "[".to_string(),
 5765                        end: "]".to_string(),
 5766                        close: false,
 5767                        surround: false,
 5768                        newline: true,
 5769                    },
 5770                    BracketPair {
 5771                        start: "\"".to_string(),
 5772                        end: "\"".to_string(),
 5773                        close: true,
 5774                        surround: true,
 5775                        newline: false,
 5776                    },
 5777                    BracketPair {
 5778                        start: "<".to_string(),
 5779                        end: ">".to_string(),
 5780                        close: false,
 5781                        surround: true,
 5782                        newline: true,
 5783                    },
 5784                ],
 5785                ..Default::default()
 5786            },
 5787            autoclose_before: "})]".to_string(),
 5788            ..Default::default()
 5789        },
 5790        Some(tree_sitter_rust::LANGUAGE.into()),
 5791    ));
 5792
 5793    cx.language_registry().add(language.clone());
 5794    cx.update_buffer(|buffer, cx| {
 5795        buffer.set_language(Some(language), cx);
 5796    });
 5797
 5798    cx.set_state(
 5799        &r#"
 5800            🏀ˇ
 5801            εˇ
 5802            ❤️ˇ
 5803        "#
 5804        .unindent(),
 5805    );
 5806
 5807    // autoclose multiple nested brackets at multiple cursors
 5808    cx.update_editor(|view, cx| {
 5809        view.handle_input("{", cx);
 5810        view.handle_input("{", cx);
 5811        view.handle_input("{", cx);
 5812    });
 5813    cx.assert_editor_state(
 5814        &"
 5815            🏀{{{ˇ}}}
 5816            ε{{{ˇ}}}
 5817            ❤️{{{ˇ}}}
 5818        "
 5819        .unindent(),
 5820    );
 5821
 5822    // insert a different closing bracket
 5823    cx.update_editor(|view, cx| {
 5824        view.handle_input(")", cx);
 5825    });
 5826    cx.assert_editor_state(
 5827        &"
 5828            🏀{{{)ˇ}}}
 5829            ε{{{)ˇ}}}
 5830            ❤️{{{)ˇ}}}
 5831        "
 5832        .unindent(),
 5833    );
 5834
 5835    // skip over the auto-closed brackets when typing a closing bracket
 5836    cx.update_editor(|view, cx| {
 5837        view.move_right(&MoveRight, cx);
 5838        view.handle_input("}", cx);
 5839        view.handle_input("}", cx);
 5840        view.handle_input("}", cx);
 5841    });
 5842    cx.assert_editor_state(
 5843        &"
 5844            🏀{{{)}}}}ˇ
 5845            ε{{{)}}}}ˇ
 5846            ❤️{{{)}}}}ˇ
 5847        "
 5848        .unindent(),
 5849    );
 5850
 5851    // autoclose multi-character pairs
 5852    cx.set_state(
 5853        &"
 5854            ˇ
 5855            ˇ
 5856        "
 5857        .unindent(),
 5858    );
 5859    cx.update_editor(|view, cx| {
 5860        view.handle_input("/", cx);
 5861        view.handle_input("*", cx);
 5862    });
 5863    cx.assert_editor_state(
 5864        &"
 5865            /*ˇ */
 5866            /*ˇ */
 5867        "
 5868        .unindent(),
 5869    );
 5870
 5871    // one cursor autocloses a multi-character pair, one cursor
 5872    // does not autoclose.
 5873    cx.set_state(
 5874        &"
 5875 5876            ˇ
 5877        "
 5878        .unindent(),
 5879    );
 5880    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5881    cx.assert_editor_state(
 5882        &"
 5883            /*ˇ */
 5884 5885        "
 5886        .unindent(),
 5887    );
 5888
 5889    // Don't autoclose if the next character isn't whitespace and isn't
 5890    // listed in the language's "autoclose_before" section.
 5891    cx.set_state("ˇa b");
 5892    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5893    cx.assert_editor_state("{ˇa b");
 5894
 5895    // Don't autoclose if `close` is false for the bracket pair
 5896    cx.set_state("ˇ");
 5897    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5898    cx.assert_editor_state("");
 5899
 5900    // Surround with brackets if text is selected
 5901    cx.set_state("«aˇ» b");
 5902    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5903    cx.assert_editor_state("{«aˇ»} b");
 5904
 5905    // Autclose pair where the start and end characters are the same
 5906    cx.set_state("");
 5907    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5908    cx.assert_editor_state("a\"ˇ\"");
 5909    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5910    cx.assert_editor_state("a\"\"ˇ");
 5911
 5912    // Don't autoclose pair if autoclose is disabled
 5913    cx.set_state("ˇ");
 5914    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5915    cx.assert_editor_state("");
 5916
 5917    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5918    cx.set_state("«aˇ» b");
 5919    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5920    cx.assert_editor_state("<«aˇ»> b");
 5921}
 5922
 5923#[gpui::test]
 5924async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5925    init_test(cx, |settings| {
 5926        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5927    });
 5928
 5929    let mut cx = EditorTestContext::new(cx).await;
 5930
 5931    let language = Arc::new(Language::new(
 5932        LanguageConfig {
 5933            brackets: BracketPairConfig {
 5934                pairs: vec![
 5935                    BracketPair {
 5936                        start: "{".to_string(),
 5937                        end: "}".to_string(),
 5938                        close: true,
 5939                        surround: true,
 5940                        newline: true,
 5941                    },
 5942                    BracketPair {
 5943                        start: "(".to_string(),
 5944                        end: ")".to_string(),
 5945                        close: true,
 5946                        surround: true,
 5947                        newline: true,
 5948                    },
 5949                    BracketPair {
 5950                        start: "[".to_string(),
 5951                        end: "]".to_string(),
 5952                        close: false,
 5953                        surround: false,
 5954                        newline: true,
 5955                    },
 5956                ],
 5957                ..Default::default()
 5958            },
 5959            autoclose_before: "})]".to_string(),
 5960            ..Default::default()
 5961        },
 5962        Some(tree_sitter_rust::LANGUAGE.into()),
 5963    ));
 5964
 5965    cx.language_registry().add(language.clone());
 5966    cx.update_buffer(|buffer, cx| {
 5967        buffer.set_language(Some(language), cx);
 5968    });
 5969
 5970    cx.set_state(
 5971        &"
 5972            ˇ
 5973            ˇ
 5974            ˇ
 5975        "
 5976        .unindent(),
 5977    );
 5978
 5979    // ensure only matching closing brackets are skipped over
 5980    cx.update_editor(|view, cx| {
 5981        view.handle_input("}", cx);
 5982        view.move_left(&MoveLeft, cx);
 5983        view.handle_input(")", cx);
 5984        view.move_left(&MoveLeft, cx);
 5985    });
 5986    cx.assert_editor_state(
 5987        &"
 5988            ˇ)}
 5989            ˇ)}
 5990            ˇ)}
 5991        "
 5992        .unindent(),
 5993    );
 5994
 5995    // skip-over closing brackets at multiple cursors
 5996    cx.update_editor(|view, cx| {
 5997        view.handle_input(")", cx);
 5998        view.handle_input("}", cx);
 5999    });
 6000    cx.assert_editor_state(
 6001        &"
 6002            )}ˇ
 6003            )}ˇ
 6004            )}ˇ
 6005        "
 6006        .unindent(),
 6007    );
 6008
 6009    // ignore non-close brackets
 6010    cx.update_editor(|view, cx| {
 6011        view.handle_input("]", cx);
 6012        view.move_left(&MoveLeft, cx);
 6013        view.handle_input("]", cx);
 6014    });
 6015    cx.assert_editor_state(
 6016        &"
 6017            )}]ˇ]
 6018            )}]ˇ]
 6019            )}]ˇ]
 6020        "
 6021        .unindent(),
 6022    );
 6023}
 6024
 6025#[gpui::test]
 6026async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6027    init_test(cx, |_| {});
 6028
 6029    let mut cx = EditorTestContext::new(cx).await;
 6030
 6031    let html_language = Arc::new(
 6032        Language::new(
 6033            LanguageConfig {
 6034                name: "HTML".into(),
 6035                brackets: BracketPairConfig {
 6036                    pairs: vec![
 6037                        BracketPair {
 6038                            start: "<".into(),
 6039                            end: ">".into(),
 6040                            close: true,
 6041                            ..Default::default()
 6042                        },
 6043                        BracketPair {
 6044                            start: "{".into(),
 6045                            end: "}".into(),
 6046                            close: true,
 6047                            ..Default::default()
 6048                        },
 6049                        BracketPair {
 6050                            start: "(".into(),
 6051                            end: ")".into(),
 6052                            close: true,
 6053                            ..Default::default()
 6054                        },
 6055                    ],
 6056                    ..Default::default()
 6057                },
 6058                autoclose_before: "})]>".into(),
 6059                ..Default::default()
 6060            },
 6061            Some(tree_sitter_html::language()),
 6062        )
 6063        .with_injection_query(
 6064            r#"
 6065            (script_element
 6066                (raw_text) @injection.content
 6067                (#set! injection.language "javascript"))
 6068            "#,
 6069        )
 6070        .unwrap(),
 6071    );
 6072
 6073    let javascript_language = Arc::new(Language::new(
 6074        LanguageConfig {
 6075            name: "JavaScript".into(),
 6076            brackets: BracketPairConfig {
 6077                pairs: vec![
 6078                    BracketPair {
 6079                        start: "/*".into(),
 6080                        end: " */".into(),
 6081                        close: true,
 6082                        ..Default::default()
 6083                    },
 6084                    BracketPair {
 6085                        start: "{".into(),
 6086                        end: "}".into(),
 6087                        close: true,
 6088                        ..Default::default()
 6089                    },
 6090                    BracketPair {
 6091                        start: "(".into(),
 6092                        end: ")".into(),
 6093                        close: true,
 6094                        ..Default::default()
 6095                    },
 6096                ],
 6097                ..Default::default()
 6098            },
 6099            autoclose_before: "})]>".into(),
 6100            ..Default::default()
 6101        },
 6102        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6103    ));
 6104
 6105    cx.language_registry().add(html_language.clone());
 6106    cx.language_registry().add(javascript_language.clone());
 6107
 6108    cx.update_buffer(|buffer, cx| {
 6109        buffer.set_language(Some(html_language), cx);
 6110    });
 6111
 6112    cx.set_state(
 6113        &r#"
 6114            <body>ˇ
 6115                <script>
 6116                    var x = 1;ˇ
 6117                </script>
 6118            </body>ˇ
 6119        "#
 6120        .unindent(),
 6121    );
 6122
 6123    // Precondition: different languages are active at different locations.
 6124    cx.update_editor(|editor, cx| {
 6125        let snapshot = editor.snapshot(cx);
 6126        let cursors = editor.selections.ranges::<usize>(cx);
 6127        let languages = cursors
 6128            .iter()
 6129            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6130            .collect::<Vec<_>>();
 6131        assert_eq!(
 6132            languages,
 6133            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6134        );
 6135    });
 6136
 6137    // Angle brackets autoclose in HTML, but not JavaScript.
 6138    cx.update_editor(|editor, cx| {
 6139        editor.handle_input("<", cx);
 6140        editor.handle_input("a", cx);
 6141    });
 6142    cx.assert_editor_state(
 6143        &r#"
 6144            <body><aˇ>
 6145                <script>
 6146                    var x = 1;<aˇ
 6147                </script>
 6148            </body><aˇ>
 6149        "#
 6150        .unindent(),
 6151    );
 6152
 6153    // Curly braces and parens autoclose in both HTML and JavaScript.
 6154    cx.update_editor(|editor, cx| {
 6155        editor.handle_input(" b=", cx);
 6156        editor.handle_input("{", cx);
 6157        editor.handle_input("c", cx);
 6158        editor.handle_input("(", cx);
 6159    });
 6160    cx.assert_editor_state(
 6161        &r#"
 6162            <body><a b={c(ˇ)}>
 6163                <script>
 6164                    var x = 1;<a b={c(ˇ)}
 6165                </script>
 6166            </body><a b={c(ˇ)}>
 6167        "#
 6168        .unindent(),
 6169    );
 6170
 6171    // Brackets that were already autoclosed are skipped.
 6172    cx.update_editor(|editor, cx| {
 6173        editor.handle_input(")", cx);
 6174        editor.handle_input("d", cx);
 6175        editor.handle_input("}", cx);
 6176    });
 6177    cx.assert_editor_state(
 6178        &r#"
 6179            <body><a b={c()d}ˇ>
 6180                <script>
 6181                    var x = 1;<a b={c()d}ˇ
 6182                </script>
 6183            </body><a b={c()d}ˇ>
 6184        "#
 6185        .unindent(),
 6186    );
 6187    cx.update_editor(|editor, cx| {
 6188        editor.handle_input(">", cx);
 6189    });
 6190    cx.assert_editor_state(
 6191        &r#"
 6192            <body><a b={c()d}>ˇ
 6193                <script>
 6194                    var x = 1;<a b={c()d}>ˇ
 6195                </script>
 6196            </body><a b={c()d}>ˇ
 6197        "#
 6198        .unindent(),
 6199    );
 6200
 6201    // Reset
 6202    cx.set_state(
 6203        &r#"
 6204            <body>ˇ
 6205                <script>
 6206                    var x = 1;ˇ
 6207                </script>
 6208            </body>ˇ
 6209        "#
 6210        .unindent(),
 6211    );
 6212
 6213    cx.update_editor(|editor, cx| {
 6214        editor.handle_input("<", cx);
 6215    });
 6216    cx.assert_editor_state(
 6217        &r#"
 6218            <body><ˇ>
 6219                <script>
 6220                    var x = 1;<ˇ
 6221                </script>
 6222            </body><ˇ>
 6223        "#
 6224        .unindent(),
 6225    );
 6226
 6227    // When backspacing, the closing angle brackets are removed.
 6228    cx.update_editor(|editor, cx| {
 6229        editor.backspace(&Backspace, cx);
 6230    });
 6231    cx.assert_editor_state(
 6232        &r#"
 6233            <body>ˇ
 6234                <script>
 6235                    var x = 1;ˇ
 6236                </script>
 6237            </body>ˇ
 6238        "#
 6239        .unindent(),
 6240    );
 6241
 6242    // Block comments autoclose in JavaScript, but not HTML.
 6243    cx.update_editor(|editor, cx| {
 6244        editor.handle_input("/", cx);
 6245        editor.handle_input("*", cx);
 6246    });
 6247    cx.assert_editor_state(
 6248        &r#"
 6249            <body>/*ˇ
 6250                <script>
 6251                    var x = 1;/*ˇ */
 6252                </script>
 6253            </body>/*ˇ
 6254        "#
 6255        .unindent(),
 6256    );
 6257}
 6258
 6259#[gpui::test]
 6260async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6261    init_test(cx, |_| {});
 6262
 6263    let mut cx = EditorTestContext::new(cx).await;
 6264
 6265    let rust_language = Arc::new(
 6266        Language::new(
 6267            LanguageConfig {
 6268                name: "Rust".into(),
 6269                brackets: serde_json::from_value(json!([
 6270                    { "start": "{", "end": "}", "close": true, "newline": true },
 6271                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6272                ]))
 6273                .unwrap(),
 6274                autoclose_before: "})]>".into(),
 6275                ..Default::default()
 6276            },
 6277            Some(tree_sitter_rust::LANGUAGE.into()),
 6278        )
 6279        .with_override_query("(string_literal) @string")
 6280        .unwrap(),
 6281    );
 6282
 6283    cx.language_registry().add(rust_language.clone());
 6284    cx.update_buffer(|buffer, cx| {
 6285        buffer.set_language(Some(rust_language), cx);
 6286    });
 6287
 6288    cx.set_state(
 6289        &r#"
 6290            let x = ˇ
 6291        "#
 6292        .unindent(),
 6293    );
 6294
 6295    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6296    cx.update_editor(|editor, cx| {
 6297        editor.handle_input("\"", cx);
 6298    });
 6299    cx.assert_editor_state(
 6300        &r#"
 6301            let x = "ˇ"
 6302        "#
 6303        .unindent(),
 6304    );
 6305
 6306    // Inserting another quotation mark. The cursor moves across the existing
 6307    // automatically-inserted quotation mark.
 6308    cx.update_editor(|editor, cx| {
 6309        editor.handle_input("\"", cx);
 6310    });
 6311    cx.assert_editor_state(
 6312        &r#"
 6313            let x = ""ˇ
 6314        "#
 6315        .unindent(),
 6316    );
 6317
 6318    // Reset
 6319    cx.set_state(
 6320        &r#"
 6321            let x = ˇ
 6322        "#
 6323        .unindent(),
 6324    );
 6325
 6326    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6327    cx.update_editor(|editor, cx| {
 6328        editor.handle_input("\"", cx);
 6329        editor.handle_input(" ", cx);
 6330        editor.move_left(&Default::default(), cx);
 6331        editor.handle_input("\\", cx);
 6332        editor.handle_input("\"", cx);
 6333    });
 6334    cx.assert_editor_state(
 6335        &r#"
 6336            let x = "\"ˇ "
 6337        "#
 6338        .unindent(),
 6339    );
 6340
 6341    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6342    // mark. Nothing is inserted.
 6343    cx.update_editor(|editor, cx| {
 6344        editor.move_right(&Default::default(), cx);
 6345        editor.handle_input("\"", cx);
 6346    });
 6347    cx.assert_editor_state(
 6348        &r#"
 6349            let x = "\" "ˇ
 6350        "#
 6351        .unindent(),
 6352    );
 6353}
 6354
 6355#[gpui::test]
 6356async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6357    init_test(cx, |_| {});
 6358
 6359    let language = Arc::new(Language::new(
 6360        LanguageConfig {
 6361            brackets: BracketPairConfig {
 6362                pairs: vec![
 6363                    BracketPair {
 6364                        start: "{".to_string(),
 6365                        end: "}".to_string(),
 6366                        close: true,
 6367                        surround: true,
 6368                        newline: true,
 6369                    },
 6370                    BracketPair {
 6371                        start: "/* ".to_string(),
 6372                        end: "*/".to_string(),
 6373                        close: true,
 6374                        surround: true,
 6375                        ..Default::default()
 6376                    },
 6377                ],
 6378                ..Default::default()
 6379            },
 6380            ..Default::default()
 6381        },
 6382        Some(tree_sitter_rust::LANGUAGE.into()),
 6383    ));
 6384
 6385    let text = r#"
 6386        a
 6387        b
 6388        c
 6389    "#
 6390    .unindent();
 6391
 6392    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6393    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6394    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6395    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6396        .await;
 6397
 6398    view.update(cx, |view, cx| {
 6399        view.change_selections(None, cx, |s| {
 6400            s.select_display_ranges([
 6401                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6402                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6403                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6404            ])
 6405        });
 6406
 6407        view.handle_input("{", cx);
 6408        view.handle_input("{", cx);
 6409        view.handle_input("{", cx);
 6410        assert_eq!(
 6411            view.text(cx),
 6412            "
 6413                {{{a}}}
 6414                {{{b}}}
 6415                {{{c}}}
 6416            "
 6417            .unindent()
 6418        );
 6419        assert_eq!(
 6420            view.selections.display_ranges(cx),
 6421            [
 6422                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6423                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6424                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6425            ]
 6426        );
 6427
 6428        view.undo(&Undo, cx);
 6429        view.undo(&Undo, cx);
 6430        view.undo(&Undo, cx);
 6431        assert_eq!(
 6432            view.text(cx),
 6433            "
 6434                a
 6435                b
 6436                c
 6437            "
 6438            .unindent()
 6439        );
 6440        assert_eq!(
 6441            view.selections.display_ranges(cx),
 6442            [
 6443                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6444                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6445                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6446            ]
 6447        );
 6448
 6449        // Ensure inserting the first character of a multi-byte bracket pair
 6450        // doesn't surround the selections with the bracket.
 6451        view.handle_input("/", cx);
 6452        assert_eq!(
 6453            view.text(cx),
 6454            "
 6455                /
 6456                /
 6457                /
 6458            "
 6459            .unindent()
 6460        );
 6461        assert_eq!(
 6462            view.selections.display_ranges(cx),
 6463            [
 6464                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6465                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6466                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6467            ]
 6468        );
 6469
 6470        view.undo(&Undo, cx);
 6471        assert_eq!(
 6472            view.text(cx),
 6473            "
 6474                a
 6475                b
 6476                c
 6477            "
 6478            .unindent()
 6479        );
 6480        assert_eq!(
 6481            view.selections.display_ranges(cx),
 6482            [
 6483                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6484                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6485                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6486            ]
 6487        );
 6488
 6489        // Ensure inserting the last character of a multi-byte bracket pair
 6490        // doesn't surround the selections with the bracket.
 6491        view.handle_input("*", cx);
 6492        assert_eq!(
 6493            view.text(cx),
 6494            "
 6495                *
 6496                *
 6497                *
 6498            "
 6499            .unindent()
 6500        );
 6501        assert_eq!(
 6502            view.selections.display_ranges(cx),
 6503            [
 6504                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6505                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6506                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6507            ]
 6508        );
 6509    });
 6510}
 6511
 6512#[gpui::test]
 6513async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6514    init_test(cx, |_| {});
 6515
 6516    let language = Arc::new(Language::new(
 6517        LanguageConfig {
 6518            brackets: BracketPairConfig {
 6519                pairs: vec![BracketPair {
 6520                    start: "{".to_string(),
 6521                    end: "}".to_string(),
 6522                    close: true,
 6523                    surround: true,
 6524                    newline: true,
 6525                }],
 6526                ..Default::default()
 6527            },
 6528            autoclose_before: "}".to_string(),
 6529            ..Default::default()
 6530        },
 6531        Some(tree_sitter_rust::LANGUAGE.into()),
 6532    ));
 6533
 6534    let text = r#"
 6535        a
 6536        b
 6537        c
 6538    "#
 6539    .unindent();
 6540
 6541    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6542    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6543    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6544    editor
 6545        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6546        .await;
 6547
 6548    editor.update(cx, |editor, cx| {
 6549        editor.change_selections(None, cx, |s| {
 6550            s.select_ranges([
 6551                Point::new(0, 1)..Point::new(0, 1),
 6552                Point::new(1, 1)..Point::new(1, 1),
 6553                Point::new(2, 1)..Point::new(2, 1),
 6554            ])
 6555        });
 6556
 6557        editor.handle_input("{", cx);
 6558        editor.handle_input("{", cx);
 6559        editor.handle_input("_", cx);
 6560        assert_eq!(
 6561            editor.text(cx),
 6562            "
 6563                a{{_}}
 6564                b{{_}}
 6565                c{{_}}
 6566            "
 6567            .unindent()
 6568        );
 6569        assert_eq!(
 6570            editor.selections.ranges::<Point>(cx),
 6571            [
 6572                Point::new(0, 4)..Point::new(0, 4),
 6573                Point::new(1, 4)..Point::new(1, 4),
 6574                Point::new(2, 4)..Point::new(2, 4)
 6575            ]
 6576        );
 6577
 6578        editor.backspace(&Default::default(), cx);
 6579        editor.backspace(&Default::default(), cx);
 6580        assert_eq!(
 6581            editor.text(cx),
 6582            "
 6583                a{}
 6584                b{}
 6585                c{}
 6586            "
 6587            .unindent()
 6588        );
 6589        assert_eq!(
 6590            editor.selections.ranges::<Point>(cx),
 6591            [
 6592                Point::new(0, 2)..Point::new(0, 2),
 6593                Point::new(1, 2)..Point::new(1, 2),
 6594                Point::new(2, 2)..Point::new(2, 2)
 6595            ]
 6596        );
 6597
 6598        editor.delete_to_previous_word_start(&Default::default(), cx);
 6599        assert_eq!(
 6600            editor.text(cx),
 6601            "
 6602                a
 6603                b
 6604                c
 6605            "
 6606            .unindent()
 6607        );
 6608        assert_eq!(
 6609            editor.selections.ranges::<Point>(cx),
 6610            [
 6611                Point::new(0, 1)..Point::new(0, 1),
 6612                Point::new(1, 1)..Point::new(1, 1),
 6613                Point::new(2, 1)..Point::new(2, 1)
 6614            ]
 6615        );
 6616    });
 6617}
 6618
 6619#[gpui::test]
 6620async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6621    init_test(cx, |settings| {
 6622        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6623    });
 6624
 6625    let mut cx = EditorTestContext::new(cx).await;
 6626
 6627    let language = Arc::new(Language::new(
 6628        LanguageConfig {
 6629            brackets: BracketPairConfig {
 6630                pairs: vec![
 6631                    BracketPair {
 6632                        start: "{".to_string(),
 6633                        end: "}".to_string(),
 6634                        close: true,
 6635                        surround: true,
 6636                        newline: true,
 6637                    },
 6638                    BracketPair {
 6639                        start: "(".to_string(),
 6640                        end: ")".to_string(),
 6641                        close: true,
 6642                        surround: true,
 6643                        newline: true,
 6644                    },
 6645                    BracketPair {
 6646                        start: "[".to_string(),
 6647                        end: "]".to_string(),
 6648                        close: false,
 6649                        surround: true,
 6650                        newline: true,
 6651                    },
 6652                ],
 6653                ..Default::default()
 6654            },
 6655            autoclose_before: "})]".to_string(),
 6656            ..Default::default()
 6657        },
 6658        Some(tree_sitter_rust::LANGUAGE.into()),
 6659    ));
 6660
 6661    cx.language_registry().add(language.clone());
 6662    cx.update_buffer(|buffer, cx| {
 6663        buffer.set_language(Some(language), cx);
 6664    });
 6665
 6666    cx.set_state(
 6667        &"
 6668            {(ˇ)}
 6669            [[ˇ]]
 6670            {(ˇ)}
 6671        "
 6672        .unindent(),
 6673    );
 6674
 6675    cx.update_editor(|view, cx| {
 6676        view.backspace(&Default::default(), cx);
 6677        view.backspace(&Default::default(), cx);
 6678    });
 6679
 6680    cx.assert_editor_state(
 6681        &"
 6682            ˇ
 6683            ˇ]]
 6684            ˇ
 6685        "
 6686        .unindent(),
 6687    );
 6688
 6689    cx.update_editor(|view, cx| {
 6690        view.handle_input("{", cx);
 6691        view.handle_input("{", cx);
 6692        view.move_right(&MoveRight, cx);
 6693        view.move_right(&MoveRight, cx);
 6694        view.move_left(&MoveLeft, cx);
 6695        view.move_left(&MoveLeft, cx);
 6696        view.backspace(&Default::default(), cx);
 6697    });
 6698
 6699    cx.assert_editor_state(
 6700        &"
 6701            {ˇ}
 6702            {ˇ}]]
 6703            {ˇ}
 6704        "
 6705        .unindent(),
 6706    );
 6707
 6708    cx.update_editor(|view, cx| {
 6709        view.backspace(&Default::default(), cx);
 6710    });
 6711
 6712    cx.assert_editor_state(
 6713        &"
 6714            ˇ
 6715            ˇ]]
 6716            ˇ
 6717        "
 6718        .unindent(),
 6719    );
 6720}
 6721
 6722#[gpui::test]
 6723async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6724    init_test(cx, |_| {});
 6725
 6726    let language = Arc::new(Language::new(
 6727        LanguageConfig::default(),
 6728        Some(tree_sitter_rust::LANGUAGE.into()),
 6729    ));
 6730
 6731    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6732    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6733    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6734    editor
 6735        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6736        .await;
 6737
 6738    editor.update(cx, |editor, cx| {
 6739        editor.set_auto_replace_emoji_shortcode(true);
 6740
 6741        editor.handle_input("Hello ", cx);
 6742        editor.handle_input(":wave", cx);
 6743        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6744
 6745        editor.handle_input(":", cx);
 6746        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6747
 6748        editor.handle_input(" :smile", cx);
 6749        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6750
 6751        editor.handle_input(":", cx);
 6752        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6753
 6754        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6755        editor.handle_input(":wave", cx);
 6756        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6757
 6758        editor.handle_input(":", cx);
 6759        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6760
 6761        editor.handle_input(":1", cx);
 6762        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6763
 6764        editor.handle_input(":", cx);
 6765        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6766
 6767        // Ensure shortcode does not get replaced when it is part of a word
 6768        editor.handle_input(" Test:wave", cx);
 6769        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6770
 6771        editor.handle_input(":", cx);
 6772        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6773
 6774        editor.set_auto_replace_emoji_shortcode(false);
 6775
 6776        // Ensure shortcode does not get replaced when auto replace is off
 6777        editor.handle_input(" :wave", cx);
 6778        assert_eq!(
 6779            editor.text(cx),
 6780            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6781        );
 6782
 6783        editor.handle_input(":", cx);
 6784        assert_eq!(
 6785            editor.text(cx),
 6786            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6787        );
 6788    });
 6789}
 6790
 6791#[gpui::test]
 6792async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6793    init_test(cx, |_| {});
 6794
 6795    let (text, insertion_ranges) = marked_text_ranges(
 6796        indoc! {"
 6797            ˇ
 6798        "},
 6799        false,
 6800    );
 6801
 6802    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6803    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6804
 6805    _ = editor.update(cx, |editor, cx| {
 6806        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6807
 6808        editor
 6809            .insert_snippet(&insertion_ranges, snippet, cx)
 6810            .unwrap();
 6811
 6812        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6813            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6814            assert_eq!(editor.text(cx), expected_text);
 6815            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6816        }
 6817
 6818        assert(
 6819            editor,
 6820            cx,
 6821            indoc! {"
 6822            type «» =•
 6823            "},
 6824        );
 6825
 6826        assert!(editor.context_menu_visible(), "There should be a matches");
 6827    });
 6828}
 6829
 6830#[gpui::test]
 6831async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6832    init_test(cx, |_| {});
 6833
 6834    let (text, insertion_ranges) = marked_text_ranges(
 6835        indoc! {"
 6836            a.ˇ b
 6837            a.ˇ b
 6838            a.ˇ b
 6839        "},
 6840        false,
 6841    );
 6842
 6843    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6844    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6845
 6846    editor.update(cx, |editor, cx| {
 6847        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6848
 6849        editor
 6850            .insert_snippet(&insertion_ranges, snippet, cx)
 6851            .unwrap();
 6852
 6853        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6854            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6855            assert_eq!(editor.text(cx), expected_text);
 6856            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6857        }
 6858
 6859        assert(
 6860            editor,
 6861            cx,
 6862            indoc! {"
 6863                a.f(«one», two, «three») b
 6864                a.f(«one», two, «three») b
 6865                a.f(«one», two, «three») b
 6866            "},
 6867        );
 6868
 6869        // Can't move earlier than the first tab stop
 6870        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6871        assert(
 6872            editor,
 6873            cx,
 6874            indoc! {"
 6875                a.f(«one», two, «three») b
 6876                a.f(«one», two, «three») b
 6877                a.f(«one», two, «three») b
 6878            "},
 6879        );
 6880
 6881        assert!(editor.move_to_next_snippet_tabstop(cx));
 6882        assert(
 6883            editor,
 6884            cx,
 6885            indoc! {"
 6886                a.f(one, «two», three) b
 6887                a.f(one, «two», three) b
 6888                a.f(one, «two», three) b
 6889            "},
 6890        );
 6891
 6892        editor.move_to_prev_snippet_tabstop(cx);
 6893        assert(
 6894            editor,
 6895            cx,
 6896            indoc! {"
 6897                a.f(«one», two, «three») b
 6898                a.f(«one», two, «three») b
 6899                a.f(«one», two, «three») b
 6900            "},
 6901        );
 6902
 6903        assert!(editor.move_to_next_snippet_tabstop(cx));
 6904        assert(
 6905            editor,
 6906            cx,
 6907            indoc! {"
 6908                a.f(one, «two», three) b
 6909                a.f(one, «two», three) b
 6910                a.f(one, «two», three) b
 6911            "},
 6912        );
 6913        assert!(editor.move_to_next_snippet_tabstop(cx));
 6914        assert(
 6915            editor,
 6916            cx,
 6917            indoc! {"
 6918                a.f(one, two, three)ˇ b
 6919                a.f(one, two, three)ˇ b
 6920                a.f(one, two, three)ˇ b
 6921            "},
 6922        );
 6923
 6924        // As soon as the last tab stop is reached, snippet state is gone
 6925        editor.move_to_prev_snippet_tabstop(cx);
 6926        assert(
 6927            editor,
 6928            cx,
 6929            indoc! {"
 6930                a.f(one, two, three)ˇ b
 6931                a.f(one, two, three)ˇ b
 6932                a.f(one, two, three)ˇ b
 6933            "},
 6934        );
 6935    });
 6936}
 6937
 6938#[gpui::test]
 6939async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6940    init_test(cx, |_| {});
 6941
 6942    let fs = FakeFs::new(cx.executor());
 6943    fs.insert_file("/file.rs", Default::default()).await;
 6944
 6945    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6946
 6947    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6948    language_registry.add(rust_lang());
 6949    let mut fake_servers = language_registry.register_fake_lsp(
 6950        "Rust",
 6951        FakeLspAdapter {
 6952            capabilities: lsp::ServerCapabilities {
 6953                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6954                ..Default::default()
 6955            },
 6956            ..Default::default()
 6957        },
 6958    );
 6959
 6960    let buffer = project
 6961        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6962        .await
 6963        .unwrap();
 6964
 6965    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6966    let (editor, cx) =
 6967        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 6968    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6969    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6970
 6971    cx.executor().start_waiting();
 6972    let fake_server = fake_servers.next().await.unwrap();
 6973
 6974    let save = editor
 6975        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6976        .unwrap();
 6977    fake_server
 6978        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6979            assert_eq!(
 6980                params.text_document.uri,
 6981                lsp::Url::from_file_path("/file.rs").unwrap()
 6982            );
 6983            assert_eq!(params.options.tab_size, 4);
 6984            Ok(Some(vec![lsp::TextEdit::new(
 6985                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6986                ", ".to_string(),
 6987            )]))
 6988        })
 6989        .next()
 6990        .await;
 6991    cx.executor().start_waiting();
 6992    save.await;
 6993
 6994    assert_eq!(
 6995        editor.update(cx, |editor, cx| editor.text(cx)),
 6996        "one, two\nthree\n"
 6997    );
 6998    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6999
 7000    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7001    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7002
 7003    // Ensure we can still save even if formatting hangs.
 7004    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7005        assert_eq!(
 7006            params.text_document.uri,
 7007            lsp::Url::from_file_path("/file.rs").unwrap()
 7008        );
 7009        futures::future::pending::<()>().await;
 7010        unreachable!()
 7011    });
 7012    let save = editor
 7013        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7014        .unwrap();
 7015    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7016    cx.executor().start_waiting();
 7017    save.await;
 7018    assert_eq!(
 7019        editor.update(cx, |editor, cx| editor.text(cx)),
 7020        "one\ntwo\nthree\n"
 7021    );
 7022    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7023
 7024    // For non-dirty buffer, no formatting request should be sent
 7025    let save = editor
 7026        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7027        .unwrap();
 7028    let _pending_format_request = fake_server
 7029        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7030            panic!("Should not be invoked on non-dirty buffer");
 7031        })
 7032        .next();
 7033    cx.executor().start_waiting();
 7034    save.await;
 7035
 7036    // Set rust language override and assert overridden tabsize is sent to language server
 7037    update_test_language_settings(cx, |settings| {
 7038        settings.languages.insert(
 7039            "Rust".into(),
 7040            LanguageSettingsContent {
 7041                tab_size: NonZeroU32::new(8),
 7042                ..Default::default()
 7043            },
 7044        );
 7045    });
 7046
 7047    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7048    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7049    let save = editor
 7050        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7051        .unwrap();
 7052    fake_server
 7053        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7054            assert_eq!(
 7055                params.text_document.uri,
 7056                lsp::Url::from_file_path("/file.rs").unwrap()
 7057            );
 7058            assert_eq!(params.options.tab_size, 8);
 7059            Ok(Some(vec![]))
 7060        })
 7061        .next()
 7062        .await;
 7063    cx.executor().start_waiting();
 7064    save.await;
 7065}
 7066
 7067#[gpui::test]
 7068async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7069    init_test(cx, |_| {});
 7070
 7071    let cols = 4;
 7072    let rows = 10;
 7073    let sample_text_1 = sample_text(rows, cols, 'a');
 7074    assert_eq!(
 7075        sample_text_1,
 7076        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7077    );
 7078    let sample_text_2 = sample_text(rows, cols, 'l');
 7079    assert_eq!(
 7080        sample_text_2,
 7081        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7082    );
 7083    let sample_text_3 = sample_text(rows, cols, 'v');
 7084    assert_eq!(
 7085        sample_text_3,
 7086        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7087    );
 7088
 7089    let fs = FakeFs::new(cx.executor());
 7090    fs.insert_tree(
 7091        "/a",
 7092        json!({
 7093            "main.rs": sample_text_1,
 7094            "other.rs": sample_text_2,
 7095            "lib.rs": sample_text_3,
 7096        }),
 7097    )
 7098    .await;
 7099
 7100    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 7101    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 7102    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7103
 7104    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7105    language_registry.add(rust_lang());
 7106    let mut fake_servers = language_registry.register_fake_lsp(
 7107        "Rust",
 7108        FakeLspAdapter {
 7109            capabilities: lsp::ServerCapabilities {
 7110                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7111                ..Default::default()
 7112            },
 7113            ..Default::default()
 7114        },
 7115    );
 7116
 7117    let worktree = project.update(cx, |project, cx| {
 7118        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7119        assert_eq!(worktrees.len(), 1);
 7120        worktrees.pop().unwrap()
 7121    });
 7122    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7123
 7124    let buffer_1 = project
 7125        .update(cx, |project, cx| {
 7126            project.open_buffer((worktree_id, "main.rs"), cx)
 7127        })
 7128        .await
 7129        .unwrap();
 7130    let buffer_2 = project
 7131        .update(cx, |project, cx| {
 7132            project.open_buffer((worktree_id, "other.rs"), cx)
 7133        })
 7134        .await
 7135        .unwrap();
 7136    let buffer_3 = project
 7137        .update(cx, |project, cx| {
 7138            project.open_buffer((worktree_id, "lib.rs"), cx)
 7139        })
 7140        .await
 7141        .unwrap();
 7142
 7143    let multi_buffer = cx.new_model(|cx| {
 7144        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7145        multi_buffer.push_excerpts(
 7146            buffer_1.clone(),
 7147            [
 7148                ExcerptRange {
 7149                    context: Point::new(0, 0)..Point::new(3, 0),
 7150                    primary: None,
 7151                },
 7152                ExcerptRange {
 7153                    context: Point::new(5, 0)..Point::new(7, 0),
 7154                    primary: None,
 7155                },
 7156                ExcerptRange {
 7157                    context: Point::new(9, 0)..Point::new(10, 4),
 7158                    primary: None,
 7159                },
 7160            ],
 7161            cx,
 7162        );
 7163        multi_buffer.push_excerpts(
 7164            buffer_2.clone(),
 7165            [
 7166                ExcerptRange {
 7167                    context: Point::new(0, 0)..Point::new(3, 0),
 7168                    primary: None,
 7169                },
 7170                ExcerptRange {
 7171                    context: Point::new(5, 0)..Point::new(7, 0),
 7172                    primary: None,
 7173                },
 7174                ExcerptRange {
 7175                    context: Point::new(9, 0)..Point::new(10, 4),
 7176                    primary: None,
 7177                },
 7178            ],
 7179            cx,
 7180        );
 7181        multi_buffer.push_excerpts(
 7182            buffer_3.clone(),
 7183            [
 7184                ExcerptRange {
 7185                    context: Point::new(0, 0)..Point::new(3, 0),
 7186                    primary: None,
 7187                },
 7188                ExcerptRange {
 7189                    context: Point::new(5, 0)..Point::new(7, 0),
 7190                    primary: None,
 7191                },
 7192                ExcerptRange {
 7193                    context: Point::new(9, 0)..Point::new(10, 4),
 7194                    primary: None,
 7195                },
 7196            ],
 7197            cx,
 7198        );
 7199        multi_buffer
 7200    });
 7201    let multi_buffer_editor = cx.new_view(|cx| {
 7202        Editor::new(
 7203            EditorMode::Full,
 7204            multi_buffer,
 7205            Some(project.clone()),
 7206            true,
 7207            cx,
 7208        )
 7209    });
 7210
 7211    multi_buffer_editor.update(cx, |editor, cx| {
 7212        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7213        editor.insert("|one|two|three|", cx);
 7214    });
 7215    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7216    multi_buffer_editor.update(cx, |editor, cx| {
 7217        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7218            s.select_ranges(Some(60..70))
 7219        });
 7220        editor.insert("|four|five|six|", cx);
 7221    });
 7222    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7223
 7224    // First two buffers should be edited, but not the third one.
 7225    assert_eq!(
 7226        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7227        "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}",
 7228    );
 7229    buffer_1.update(cx, |buffer, _| {
 7230        assert!(buffer.is_dirty());
 7231        assert_eq!(
 7232            buffer.text(),
 7233            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7234        )
 7235    });
 7236    buffer_2.update(cx, |buffer, _| {
 7237        assert!(buffer.is_dirty());
 7238        assert_eq!(
 7239            buffer.text(),
 7240            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7241        )
 7242    });
 7243    buffer_3.update(cx, |buffer, _| {
 7244        assert!(!buffer.is_dirty());
 7245        assert_eq!(buffer.text(), sample_text_3,)
 7246    });
 7247    cx.executor().run_until_parked();
 7248
 7249    cx.executor().start_waiting();
 7250    let save = multi_buffer_editor
 7251        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7252        .unwrap();
 7253
 7254    let fake_server = fake_servers.next().await.unwrap();
 7255    fake_server
 7256        .server
 7257        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7258            Ok(Some(vec![lsp::TextEdit::new(
 7259                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7260                format!("[{} formatted]", params.text_document.uri),
 7261            )]))
 7262        })
 7263        .detach();
 7264    save.await;
 7265
 7266    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7267    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7268    assert_eq!(
 7269        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7270        "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}",
 7271    );
 7272    buffer_1.update(cx, |buffer, _| {
 7273        assert!(!buffer.is_dirty());
 7274        assert_eq!(
 7275            buffer.text(),
 7276            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7277        )
 7278    });
 7279    buffer_2.update(cx, |buffer, _| {
 7280        assert!(!buffer.is_dirty());
 7281        assert_eq!(
 7282            buffer.text(),
 7283            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7284        )
 7285    });
 7286    buffer_3.update(cx, |buffer, _| {
 7287        assert!(!buffer.is_dirty());
 7288        assert_eq!(buffer.text(), sample_text_3,)
 7289    });
 7290}
 7291
 7292#[gpui::test]
 7293async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7294    init_test(cx, |_| {});
 7295
 7296    let fs = FakeFs::new(cx.executor());
 7297    fs.insert_file("/file.rs", Default::default()).await;
 7298
 7299    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7300
 7301    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7302    language_registry.add(rust_lang());
 7303    let mut fake_servers = language_registry.register_fake_lsp(
 7304        "Rust",
 7305        FakeLspAdapter {
 7306            capabilities: lsp::ServerCapabilities {
 7307                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7308                ..Default::default()
 7309            },
 7310            ..Default::default()
 7311        },
 7312    );
 7313
 7314    let buffer = project
 7315        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7316        .await
 7317        .unwrap();
 7318
 7319    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7320    let (editor, cx) =
 7321        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7322    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7323    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7324
 7325    cx.executor().start_waiting();
 7326    let fake_server = fake_servers.next().await.unwrap();
 7327
 7328    let save = editor
 7329        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7330        .unwrap();
 7331    fake_server
 7332        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7333            assert_eq!(
 7334                params.text_document.uri,
 7335                lsp::Url::from_file_path("/file.rs").unwrap()
 7336            );
 7337            assert_eq!(params.options.tab_size, 4);
 7338            Ok(Some(vec![lsp::TextEdit::new(
 7339                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7340                ", ".to_string(),
 7341            )]))
 7342        })
 7343        .next()
 7344        .await;
 7345    cx.executor().start_waiting();
 7346    save.await;
 7347    assert_eq!(
 7348        editor.update(cx, |editor, cx| editor.text(cx)),
 7349        "one, two\nthree\n"
 7350    );
 7351    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7352
 7353    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7354    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7355
 7356    // Ensure we can still save even if formatting hangs.
 7357    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7358        move |params, _| async move {
 7359            assert_eq!(
 7360                params.text_document.uri,
 7361                lsp::Url::from_file_path("/file.rs").unwrap()
 7362            );
 7363            futures::future::pending::<()>().await;
 7364            unreachable!()
 7365        },
 7366    );
 7367    let save = editor
 7368        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7369        .unwrap();
 7370    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7371    cx.executor().start_waiting();
 7372    save.await;
 7373    assert_eq!(
 7374        editor.update(cx, |editor, cx| editor.text(cx)),
 7375        "one\ntwo\nthree\n"
 7376    );
 7377    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7378
 7379    // For non-dirty buffer, no formatting request should be sent
 7380    let save = editor
 7381        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7382        .unwrap();
 7383    let _pending_format_request = fake_server
 7384        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7385            panic!("Should not be invoked on non-dirty buffer");
 7386        })
 7387        .next();
 7388    cx.executor().start_waiting();
 7389    save.await;
 7390
 7391    // Set Rust language override and assert overridden tabsize is sent to language server
 7392    update_test_language_settings(cx, |settings| {
 7393        settings.languages.insert(
 7394            "Rust".into(),
 7395            LanguageSettingsContent {
 7396                tab_size: NonZeroU32::new(8),
 7397                ..Default::default()
 7398            },
 7399        );
 7400    });
 7401
 7402    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7403    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7404    let save = editor
 7405        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7406        .unwrap();
 7407    fake_server
 7408        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7409            assert_eq!(
 7410                params.text_document.uri,
 7411                lsp::Url::from_file_path("/file.rs").unwrap()
 7412            );
 7413            assert_eq!(params.options.tab_size, 8);
 7414            Ok(Some(vec![]))
 7415        })
 7416        .next()
 7417        .await;
 7418    cx.executor().start_waiting();
 7419    save.await;
 7420}
 7421
 7422#[gpui::test]
 7423async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7424    init_test(cx, |settings| {
 7425        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7426            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7427        ))
 7428    });
 7429
 7430    let fs = FakeFs::new(cx.executor());
 7431    fs.insert_file("/file.rs", Default::default()).await;
 7432
 7433    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7434
 7435    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7436    language_registry.add(Arc::new(Language::new(
 7437        LanguageConfig {
 7438            name: "Rust".into(),
 7439            matcher: LanguageMatcher {
 7440                path_suffixes: vec!["rs".to_string()],
 7441                ..Default::default()
 7442            },
 7443            ..LanguageConfig::default()
 7444        },
 7445        Some(tree_sitter_rust::LANGUAGE.into()),
 7446    )));
 7447    update_test_language_settings(cx, |settings| {
 7448        // Enable Prettier formatting for the same buffer, and ensure
 7449        // LSP is called instead of Prettier.
 7450        settings.defaults.prettier = Some(PrettierSettings {
 7451            allowed: true,
 7452            ..PrettierSettings::default()
 7453        });
 7454    });
 7455    let mut fake_servers = language_registry.register_fake_lsp(
 7456        "Rust",
 7457        FakeLspAdapter {
 7458            capabilities: lsp::ServerCapabilities {
 7459                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7460                ..Default::default()
 7461            },
 7462            ..Default::default()
 7463        },
 7464    );
 7465
 7466    let buffer = project
 7467        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7468        .await
 7469        .unwrap();
 7470
 7471    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7472    let (editor, cx) =
 7473        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7474    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7475
 7476    cx.executor().start_waiting();
 7477    let fake_server = fake_servers.next().await.unwrap();
 7478
 7479    let format = editor
 7480        .update(cx, |editor, cx| {
 7481            editor.perform_format(
 7482                project.clone(),
 7483                FormatTrigger::Manual,
 7484                FormatTarget::Buffers,
 7485                cx,
 7486            )
 7487        })
 7488        .unwrap();
 7489    fake_server
 7490        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7491            assert_eq!(
 7492                params.text_document.uri,
 7493                lsp::Url::from_file_path("/file.rs").unwrap()
 7494            );
 7495            assert_eq!(params.options.tab_size, 4);
 7496            Ok(Some(vec![lsp::TextEdit::new(
 7497                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7498                ", ".to_string(),
 7499            )]))
 7500        })
 7501        .next()
 7502        .await;
 7503    cx.executor().start_waiting();
 7504    format.await;
 7505    assert_eq!(
 7506        editor.update(cx, |editor, cx| editor.text(cx)),
 7507        "one, two\nthree\n"
 7508    );
 7509
 7510    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7511    // Ensure we don't lock if formatting hangs.
 7512    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7513        assert_eq!(
 7514            params.text_document.uri,
 7515            lsp::Url::from_file_path("/file.rs").unwrap()
 7516        );
 7517        futures::future::pending::<()>().await;
 7518        unreachable!()
 7519    });
 7520    let format = editor
 7521        .update(cx, |editor, cx| {
 7522            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffers, cx)
 7523        })
 7524        .unwrap();
 7525    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7526    cx.executor().start_waiting();
 7527    format.await;
 7528    assert_eq!(
 7529        editor.update(cx, |editor, cx| editor.text(cx)),
 7530        "one\ntwo\nthree\n"
 7531    );
 7532}
 7533
 7534#[gpui::test]
 7535async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7536    init_test(cx, |_| {});
 7537
 7538    let mut cx = EditorLspTestContext::new_rust(
 7539        lsp::ServerCapabilities {
 7540            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7541            ..Default::default()
 7542        },
 7543        cx,
 7544    )
 7545    .await;
 7546
 7547    cx.set_state(indoc! {"
 7548        one.twoˇ
 7549    "});
 7550
 7551    // The format request takes a long time. When it completes, it inserts
 7552    // a newline and an indent before the `.`
 7553    cx.lsp
 7554        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7555            let executor = cx.background_executor().clone();
 7556            async move {
 7557                executor.timer(Duration::from_millis(100)).await;
 7558                Ok(Some(vec![lsp::TextEdit {
 7559                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7560                    new_text: "\n    ".into(),
 7561                }]))
 7562            }
 7563        });
 7564
 7565    // Submit a format request.
 7566    let format_1 = cx
 7567        .update_editor(|editor, cx| editor.format(&Format, cx))
 7568        .unwrap();
 7569    cx.executor().run_until_parked();
 7570
 7571    // Submit a second format request.
 7572    let format_2 = cx
 7573        .update_editor(|editor, cx| editor.format(&Format, cx))
 7574        .unwrap();
 7575    cx.executor().run_until_parked();
 7576
 7577    // Wait for both format requests to complete
 7578    cx.executor().advance_clock(Duration::from_millis(200));
 7579    cx.executor().start_waiting();
 7580    format_1.await.unwrap();
 7581    cx.executor().start_waiting();
 7582    format_2.await.unwrap();
 7583
 7584    // The formatting edits only happens once.
 7585    cx.assert_editor_state(indoc! {"
 7586        one
 7587            .twoˇ
 7588    "});
 7589}
 7590
 7591#[gpui::test]
 7592async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7593    init_test(cx, |settings| {
 7594        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7595    });
 7596
 7597    let mut cx = EditorLspTestContext::new_rust(
 7598        lsp::ServerCapabilities {
 7599            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7600            ..Default::default()
 7601        },
 7602        cx,
 7603    )
 7604    .await;
 7605
 7606    // Set up a buffer white some trailing whitespace and no trailing newline.
 7607    cx.set_state(
 7608        &[
 7609            "one ",   //
 7610            "twoˇ",   //
 7611            "three ", //
 7612            "four",   //
 7613        ]
 7614        .join("\n"),
 7615    );
 7616
 7617    // Submit a format request.
 7618    let format = cx
 7619        .update_editor(|editor, cx| editor.format(&Format, cx))
 7620        .unwrap();
 7621
 7622    // Record which buffer changes have been sent to the language server
 7623    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7624    cx.lsp
 7625        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7626            let buffer_changes = buffer_changes.clone();
 7627            move |params, _| {
 7628                buffer_changes.lock().extend(
 7629                    params
 7630                        .content_changes
 7631                        .into_iter()
 7632                        .map(|e| (e.range.unwrap(), e.text)),
 7633                );
 7634            }
 7635        });
 7636
 7637    // Handle formatting requests to the language server.
 7638    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7639        let buffer_changes = buffer_changes.clone();
 7640        move |_, _| {
 7641            // When formatting is requested, trailing whitespace has already been stripped,
 7642            // and the trailing newline has already been added.
 7643            assert_eq!(
 7644                &buffer_changes.lock()[1..],
 7645                &[
 7646                    (
 7647                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7648                        "".into()
 7649                    ),
 7650                    (
 7651                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7652                        "".into()
 7653                    ),
 7654                    (
 7655                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7656                        "\n".into()
 7657                    ),
 7658                ]
 7659            );
 7660
 7661            // Insert blank lines between each line of the buffer.
 7662            async move {
 7663                Ok(Some(vec![
 7664                    lsp::TextEdit {
 7665                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7666                        new_text: "\n".into(),
 7667                    },
 7668                    lsp::TextEdit {
 7669                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7670                        new_text: "\n".into(),
 7671                    },
 7672                ]))
 7673            }
 7674        }
 7675    });
 7676
 7677    // After formatting the buffer, the trailing whitespace is stripped,
 7678    // a newline is appended, and the edits provided by the language server
 7679    // have been applied.
 7680    format.await.unwrap();
 7681    cx.assert_editor_state(
 7682        &[
 7683            "one",   //
 7684            "",      //
 7685            "twoˇ",  //
 7686            "",      //
 7687            "three", //
 7688            "four",  //
 7689            "",      //
 7690        ]
 7691        .join("\n"),
 7692    );
 7693
 7694    // Undoing the formatting undoes the trailing whitespace removal, the
 7695    // trailing newline, and the LSP edits.
 7696    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7697    cx.assert_editor_state(
 7698        &[
 7699            "one ",   //
 7700            "twoˇ",   //
 7701            "three ", //
 7702            "four",   //
 7703        ]
 7704        .join("\n"),
 7705    );
 7706}
 7707
 7708#[gpui::test]
 7709async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7710    cx: &mut gpui::TestAppContext,
 7711) {
 7712    init_test(cx, |_| {});
 7713
 7714    cx.update(|cx| {
 7715        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7716            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7717                settings.auto_signature_help = Some(true);
 7718            });
 7719        });
 7720    });
 7721
 7722    let mut cx = EditorLspTestContext::new_rust(
 7723        lsp::ServerCapabilities {
 7724            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7725                ..Default::default()
 7726            }),
 7727            ..Default::default()
 7728        },
 7729        cx,
 7730    )
 7731    .await;
 7732
 7733    let language = Language::new(
 7734        LanguageConfig {
 7735            name: "Rust".into(),
 7736            brackets: BracketPairConfig {
 7737                pairs: vec![
 7738                    BracketPair {
 7739                        start: "{".to_string(),
 7740                        end: "}".to_string(),
 7741                        close: true,
 7742                        surround: true,
 7743                        newline: true,
 7744                    },
 7745                    BracketPair {
 7746                        start: "(".to_string(),
 7747                        end: ")".to_string(),
 7748                        close: true,
 7749                        surround: true,
 7750                        newline: true,
 7751                    },
 7752                    BracketPair {
 7753                        start: "/*".to_string(),
 7754                        end: " */".to_string(),
 7755                        close: true,
 7756                        surround: true,
 7757                        newline: true,
 7758                    },
 7759                    BracketPair {
 7760                        start: "[".to_string(),
 7761                        end: "]".to_string(),
 7762                        close: false,
 7763                        surround: false,
 7764                        newline: true,
 7765                    },
 7766                    BracketPair {
 7767                        start: "\"".to_string(),
 7768                        end: "\"".to_string(),
 7769                        close: true,
 7770                        surround: true,
 7771                        newline: false,
 7772                    },
 7773                    BracketPair {
 7774                        start: "<".to_string(),
 7775                        end: ">".to_string(),
 7776                        close: false,
 7777                        surround: true,
 7778                        newline: true,
 7779                    },
 7780                ],
 7781                ..Default::default()
 7782            },
 7783            autoclose_before: "})]".to_string(),
 7784            ..Default::default()
 7785        },
 7786        Some(tree_sitter_rust::LANGUAGE.into()),
 7787    );
 7788    let language = Arc::new(language);
 7789
 7790    cx.language_registry().add(language.clone());
 7791    cx.update_buffer(|buffer, cx| {
 7792        buffer.set_language(Some(language), cx);
 7793    });
 7794
 7795    cx.set_state(
 7796        &r#"
 7797            fn main() {
 7798                sampleˇ
 7799            }
 7800        "#
 7801        .unindent(),
 7802    );
 7803
 7804    cx.update_editor(|view, cx| {
 7805        view.handle_input("(", cx);
 7806    });
 7807    cx.assert_editor_state(
 7808        &"
 7809            fn main() {
 7810                sample(ˇ)
 7811            }
 7812        "
 7813        .unindent(),
 7814    );
 7815
 7816    let mocked_response = lsp::SignatureHelp {
 7817        signatures: vec![lsp::SignatureInformation {
 7818            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7819            documentation: None,
 7820            parameters: Some(vec![
 7821                lsp::ParameterInformation {
 7822                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7823                    documentation: None,
 7824                },
 7825                lsp::ParameterInformation {
 7826                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7827                    documentation: None,
 7828                },
 7829            ]),
 7830            active_parameter: None,
 7831        }],
 7832        active_signature: Some(0),
 7833        active_parameter: Some(0),
 7834    };
 7835    handle_signature_help_request(&mut cx, mocked_response).await;
 7836
 7837    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7838        .await;
 7839
 7840    cx.editor(|editor, _| {
 7841        let signature_help_state = editor.signature_help_state.popover().cloned();
 7842        assert!(signature_help_state.is_some());
 7843        let ParsedMarkdown {
 7844            text, highlights, ..
 7845        } = signature_help_state.unwrap().parsed_content;
 7846        assert_eq!(text, "param1: u8, param2: u8");
 7847        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7848    });
 7849}
 7850
 7851#[gpui::test]
 7852async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7853    init_test(cx, |_| {});
 7854
 7855    cx.update(|cx| {
 7856        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7857            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7858                settings.auto_signature_help = Some(false);
 7859                settings.show_signature_help_after_edits = Some(false);
 7860            });
 7861        });
 7862    });
 7863
 7864    let mut cx = EditorLspTestContext::new_rust(
 7865        lsp::ServerCapabilities {
 7866            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7867                ..Default::default()
 7868            }),
 7869            ..Default::default()
 7870        },
 7871        cx,
 7872    )
 7873    .await;
 7874
 7875    let language = Language::new(
 7876        LanguageConfig {
 7877            name: "Rust".into(),
 7878            brackets: BracketPairConfig {
 7879                pairs: vec![
 7880                    BracketPair {
 7881                        start: "{".to_string(),
 7882                        end: "}".to_string(),
 7883                        close: true,
 7884                        surround: true,
 7885                        newline: true,
 7886                    },
 7887                    BracketPair {
 7888                        start: "(".to_string(),
 7889                        end: ")".to_string(),
 7890                        close: true,
 7891                        surround: true,
 7892                        newline: true,
 7893                    },
 7894                    BracketPair {
 7895                        start: "/*".to_string(),
 7896                        end: " */".to_string(),
 7897                        close: true,
 7898                        surround: true,
 7899                        newline: true,
 7900                    },
 7901                    BracketPair {
 7902                        start: "[".to_string(),
 7903                        end: "]".to_string(),
 7904                        close: false,
 7905                        surround: false,
 7906                        newline: true,
 7907                    },
 7908                    BracketPair {
 7909                        start: "\"".to_string(),
 7910                        end: "\"".to_string(),
 7911                        close: true,
 7912                        surround: true,
 7913                        newline: false,
 7914                    },
 7915                    BracketPair {
 7916                        start: "<".to_string(),
 7917                        end: ">".to_string(),
 7918                        close: false,
 7919                        surround: true,
 7920                        newline: true,
 7921                    },
 7922                ],
 7923                ..Default::default()
 7924            },
 7925            autoclose_before: "})]".to_string(),
 7926            ..Default::default()
 7927        },
 7928        Some(tree_sitter_rust::LANGUAGE.into()),
 7929    );
 7930    let language = Arc::new(language);
 7931
 7932    cx.language_registry().add(language.clone());
 7933    cx.update_buffer(|buffer, cx| {
 7934        buffer.set_language(Some(language), cx);
 7935    });
 7936
 7937    // Ensure that signature_help is not called when no signature help is enabled.
 7938    cx.set_state(
 7939        &r#"
 7940            fn main() {
 7941                sampleˇ
 7942            }
 7943        "#
 7944        .unindent(),
 7945    );
 7946    cx.update_editor(|view, cx| {
 7947        view.handle_input("(", cx);
 7948    });
 7949    cx.assert_editor_state(
 7950        &"
 7951            fn main() {
 7952                sample(ˇ)
 7953            }
 7954        "
 7955        .unindent(),
 7956    );
 7957    cx.editor(|editor, _| {
 7958        assert!(editor.signature_help_state.task().is_none());
 7959    });
 7960
 7961    let mocked_response = lsp::SignatureHelp {
 7962        signatures: vec![lsp::SignatureInformation {
 7963            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7964            documentation: None,
 7965            parameters: Some(vec![
 7966                lsp::ParameterInformation {
 7967                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7968                    documentation: None,
 7969                },
 7970                lsp::ParameterInformation {
 7971                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7972                    documentation: None,
 7973                },
 7974            ]),
 7975            active_parameter: None,
 7976        }],
 7977        active_signature: Some(0),
 7978        active_parameter: Some(0),
 7979    };
 7980
 7981    // Ensure that signature_help is called when enabled afte edits
 7982    cx.update(|cx| {
 7983        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7984            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7985                settings.auto_signature_help = Some(false);
 7986                settings.show_signature_help_after_edits = Some(true);
 7987            });
 7988        });
 7989    });
 7990    cx.set_state(
 7991        &r#"
 7992            fn main() {
 7993                sampleˇ
 7994            }
 7995        "#
 7996        .unindent(),
 7997    );
 7998    cx.update_editor(|view, cx| {
 7999        view.handle_input("(", cx);
 8000    });
 8001    cx.assert_editor_state(
 8002        &"
 8003            fn main() {
 8004                sample(ˇ)
 8005            }
 8006        "
 8007        .unindent(),
 8008    );
 8009    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8010    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8011        .await;
 8012    cx.update_editor(|editor, _| {
 8013        let signature_help_state = editor.signature_help_state.popover().cloned();
 8014        assert!(signature_help_state.is_some());
 8015        let ParsedMarkdown {
 8016            text, highlights, ..
 8017        } = signature_help_state.unwrap().parsed_content;
 8018        assert_eq!(text, "param1: u8, param2: u8");
 8019        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8020        editor.signature_help_state = SignatureHelpState::default();
 8021    });
 8022
 8023    // Ensure that signature_help is called when auto signature help override is enabled
 8024    cx.update(|cx| {
 8025        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8026            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8027                settings.auto_signature_help = Some(true);
 8028                settings.show_signature_help_after_edits = Some(false);
 8029            });
 8030        });
 8031    });
 8032    cx.set_state(
 8033        &r#"
 8034            fn main() {
 8035                sampleˇ
 8036            }
 8037        "#
 8038        .unindent(),
 8039    );
 8040    cx.update_editor(|view, cx| {
 8041        view.handle_input("(", cx);
 8042    });
 8043    cx.assert_editor_state(
 8044        &"
 8045            fn main() {
 8046                sample(ˇ)
 8047            }
 8048        "
 8049        .unindent(),
 8050    );
 8051    handle_signature_help_request(&mut cx, mocked_response).await;
 8052    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8053        .await;
 8054    cx.editor(|editor, _| {
 8055        let signature_help_state = editor.signature_help_state.popover().cloned();
 8056        assert!(signature_help_state.is_some());
 8057        let ParsedMarkdown {
 8058            text, highlights, ..
 8059        } = signature_help_state.unwrap().parsed_content;
 8060        assert_eq!(text, "param1: u8, param2: u8");
 8061        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8062    });
 8063}
 8064
 8065#[gpui::test]
 8066async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8067    init_test(cx, |_| {});
 8068    cx.update(|cx| {
 8069        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8070            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8071                settings.auto_signature_help = Some(true);
 8072            });
 8073        });
 8074    });
 8075
 8076    let mut cx = EditorLspTestContext::new_rust(
 8077        lsp::ServerCapabilities {
 8078            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8079                ..Default::default()
 8080            }),
 8081            ..Default::default()
 8082        },
 8083        cx,
 8084    )
 8085    .await;
 8086
 8087    // A test that directly calls `show_signature_help`
 8088    cx.update_editor(|editor, cx| {
 8089        editor.show_signature_help(&ShowSignatureHelp, cx);
 8090    });
 8091
 8092    let mocked_response = lsp::SignatureHelp {
 8093        signatures: vec![lsp::SignatureInformation {
 8094            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8095            documentation: None,
 8096            parameters: Some(vec![
 8097                lsp::ParameterInformation {
 8098                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8099                    documentation: None,
 8100                },
 8101                lsp::ParameterInformation {
 8102                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8103                    documentation: None,
 8104                },
 8105            ]),
 8106            active_parameter: None,
 8107        }],
 8108        active_signature: Some(0),
 8109        active_parameter: Some(0),
 8110    };
 8111    handle_signature_help_request(&mut cx, mocked_response).await;
 8112
 8113    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8114        .await;
 8115
 8116    cx.editor(|editor, _| {
 8117        let signature_help_state = editor.signature_help_state.popover().cloned();
 8118        assert!(signature_help_state.is_some());
 8119        let ParsedMarkdown {
 8120            text, highlights, ..
 8121        } = signature_help_state.unwrap().parsed_content;
 8122        assert_eq!(text, "param1: u8, param2: u8");
 8123        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8124    });
 8125
 8126    // When exiting outside from inside the brackets, `signature_help` is closed.
 8127    cx.set_state(indoc! {"
 8128        fn main() {
 8129            sample(ˇ);
 8130        }
 8131
 8132        fn sample(param1: u8, param2: u8) {}
 8133    "});
 8134
 8135    cx.update_editor(|editor, cx| {
 8136        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8137    });
 8138
 8139    let mocked_response = lsp::SignatureHelp {
 8140        signatures: Vec::new(),
 8141        active_signature: None,
 8142        active_parameter: None,
 8143    };
 8144    handle_signature_help_request(&mut cx, mocked_response).await;
 8145
 8146    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8147        .await;
 8148
 8149    cx.editor(|editor, _| {
 8150        assert!(!editor.signature_help_state.is_shown());
 8151    });
 8152
 8153    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8154    cx.set_state(indoc! {"
 8155        fn main() {
 8156            sample(ˇ);
 8157        }
 8158
 8159        fn sample(param1: u8, param2: u8) {}
 8160    "});
 8161
 8162    let mocked_response = lsp::SignatureHelp {
 8163        signatures: vec![lsp::SignatureInformation {
 8164            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8165            documentation: None,
 8166            parameters: Some(vec![
 8167                lsp::ParameterInformation {
 8168                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8169                    documentation: None,
 8170                },
 8171                lsp::ParameterInformation {
 8172                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8173                    documentation: None,
 8174                },
 8175            ]),
 8176            active_parameter: None,
 8177        }],
 8178        active_signature: Some(0),
 8179        active_parameter: Some(0),
 8180    };
 8181    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8182    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8183        .await;
 8184    cx.editor(|editor, _| {
 8185        assert!(editor.signature_help_state.is_shown());
 8186    });
 8187
 8188    // Restore the popover with more parameter input
 8189    cx.set_state(indoc! {"
 8190        fn main() {
 8191            sample(param1, param2ˇ);
 8192        }
 8193
 8194        fn sample(param1: u8, param2: u8) {}
 8195    "});
 8196
 8197    let mocked_response = lsp::SignatureHelp {
 8198        signatures: vec![lsp::SignatureInformation {
 8199            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8200            documentation: None,
 8201            parameters: Some(vec![
 8202                lsp::ParameterInformation {
 8203                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8204                    documentation: None,
 8205                },
 8206                lsp::ParameterInformation {
 8207                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8208                    documentation: None,
 8209                },
 8210            ]),
 8211            active_parameter: None,
 8212        }],
 8213        active_signature: Some(0),
 8214        active_parameter: Some(1),
 8215    };
 8216    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8217    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8218        .await;
 8219
 8220    // When selecting a range, the popover is gone.
 8221    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8222    cx.update_editor(|editor, cx| {
 8223        editor.change_selections(None, cx, |s| {
 8224            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8225        })
 8226    });
 8227    cx.assert_editor_state(indoc! {"
 8228        fn main() {
 8229            sample(param1, «ˇparam2»);
 8230        }
 8231
 8232        fn sample(param1: u8, param2: u8) {}
 8233    "});
 8234    cx.editor(|editor, _| {
 8235        assert!(!editor.signature_help_state.is_shown());
 8236    });
 8237
 8238    // When unselecting again, the popover is back if within the brackets.
 8239    cx.update_editor(|editor, cx| {
 8240        editor.change_selections(None, cx, |s| {
 8241            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8242        })
 8243    });
 8244    cx.assert_editor_state(indoc! {"
 8245        fn main() {
 8246            sample(param1, ˇparam2);
 8247        }
 8248
 8249        fn sample(param1: u8, param2: u8) {}
 8250    "});
 8251    handle_signature_help_request(&mut cx, mocked_response).await;
 8252    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8253        .await;
 8254    cx.editor(|editor, _| {
 8255        assert!(editor.signature_help_state.is_shown());
 8256    });
 8257
 8258    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8259    cx.update_editor(|editor, cx| {
 8260        editor.change_selections(None, cx, |s| {
 8261            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8262            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8263        })
 8264    });
 8265    cx.assert_editor_state(indoc! {"
 8266        fn main() {
 8267            sample(param1, ˇparam2);
 8268        }
 8269
 8270        fn sample(param1: u8, param2: u8) {}
 8271    "});
 8272
 8273    let mocked_response = lsp::SignatureHelp {
 8274        signatures: vec![lsp::SignatureInformation {
 8275            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8276            documentation: None,
 8277            parameters: Some(vec![
 8278                lsp::ParameterInformation {
 8279                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8280                    documentation: None,
 8281                },
 8282                lsp::ParameterInformation {
 8283                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8284                    documentation: None,
 8285                },
 8286            ]),
 8287            active_parameter: None,
 8288        }],
 8289        active_signature: Some(0),
 8290        active_parameter: Some(1),
 8291    };
 8292    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8293    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8294        .await;
 8295    cx.update_editor(|editor, cx| {
 8296        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8297    });
 8298    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8299        .await;
 8300    cx.update_editor(|editor, cx| {
 8301        editor.change_selections(None, cx, |s| {
 8302            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8303        })
 8304    });
 8305    cx.assert_editor_state(indoc! {"
 8306        fn main() {
 8307            sample(param1, «ˇparam2»);
 8308        }
 8309
 8310        fn sample(param1: u8, param2: u8) {}
 8311    "});
 8312    cx.update_editor(|editor, cx| {
 8313        editor.change_selections(None, cx, |s| {
 8314            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8315        })
 8316    });
 8317    cx.assert_editor_state(indoc! {"
 8318        fn main() {
 8319            sample(param1, ˇparam2);
 8320        }
 8321
 8322        fn sample(param1: u8, param2: u8) {}
 8323    "});
 8324    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8325        .await;
 8326}
 8327
 8328#[gpui::test]
 8329async fn test_completion(cx: &mut gpui::TestAppContext) {
 8330    init_test(cx, |_| {});
 8331
 8332    let mut cx = EditorLspTestContext::new_rust(
 8333        lsp::ServerCapabilities {
 8334            completion_provider: Some(lsp::CompletionOptions {
 8335                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8336                resolve_provider: Some(true),
 8337                ..Default::default()
 8338            }),
 8339            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8340            ..Default::default()
 8341        },
 8342        cx,
 8343    )
 8344    .await;
 8345    let counter = Arc::new(AtomicUsize::new(0));
 8346
 8347    cx.set_state(indoc! {"
 8348        oneˇ
 8349        two
 8350        three
 8351    "});
 8352    cx.simulate_keystroke(".");
 8353    handle_completion_request(
 8354        &mut cx,
 8355        indoc! {"
 8356            one.|<>
 8357            two
 8358            three
 8359        "},
 8360        vec!["first_completion", "second_completion"],
 8361        counter.clone(),
 8362    )
 8363    .await;
 8364    cx.condition(|editor, _| editor.context_menu_visible())
 8365        .await;
 8366    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8367
 8368    let _handler = handle_signature_help_request(
 8369        &mut cx,
 8370        lsp::SignatureHelp {
 8371            signatures: vec![lsp::SignatureInformation {
 8372                label: "test signature".to_string(),
 8373                documentation: None,
 8374                parameters: Some(vec![lsp::ParameterInformation {
 8375                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8376                    documentation: None,
 8377                }]),
 8378                active_parameter: None,
 8379            }],
 8380            active_signature: None,
 8381            active_parameter: None,
 8382        },
 8383    );
 8384    cx.update_editor(|editor, cx| {
 8385        assert!(
 8386            !editor.signature_help_state.is_shown(),
 8387            "No signature help was called for"
 8388        );
 8389        editor.show_signature_help(&ShowSignatureHelp, cx);
 8390    });
 8391    cx.run_until_parked();
 8392    cx.update_editor(|editor, _| {
 8393        assert!(
 8394            !editor.signature_help_state.is_shown(),
 8395            "No signature help should be shown when completions menu is open"
 8396        );
 8397    });
 8398
 8399    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8400        editor.context_menu_next(&Default::default(), cx);
 8401        editor
 8402            .confirm_completion(&ConfirmCompletion::default(), cx)
 8403            .unwrap()
 8404    });
 8405    cx.assert_editor_state(indoc! {"
 8406        one.second_completionˇ
 8407        two
 8408        three
 8409    "});
 8410
 8411    handle_resolve_completion_request(
 8412        &mut cx,
 8413        Some(vec![
 8414            (
 8415                //This overlaps with the primary completion edit which is
 8416                //misbehavior from the LSP spec, test that we filter it out
 8417                indoc! {"
 8418                    one.second_ˇcompletion
 8419                    two
 8420                    threeˇ
 8421                "},
 8422                "overlapping additional edit",
 8423            ),
 8424            (
 8425                indoc! {"
 8426                    one.second_completion
 8427                    two
 8428                    threeˇ
 8429                "},
 8430                "\nadditional edit",
 8431            ),
 8432        ]),
 8433    )
 8434    .await;
 8435    apply_additional_edits.await.unwrap();
 8436    cx.assert_editor_state(indoc! {"
 8437        one.second_completionˇ
 8438        two
 8439        three
 8440        additional edit
 8441    "});
 8442
 8443    cx.set_state(indoc! {"
 8444        one.second_completion
 8445        twoˇ
 8446        threeˇ
 8447        additional edit
 8448    "});
 8449    cx.simulate_keystroke(" ");
 8450    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8451    cx.simulate_keystroke("s");
 8452    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8453
 8454    cx.assert_editor_state(indoc! {"
 8455        one.second_completion
 8456        two sˇ
 8457        three sˇ
 8458        additional edit
 8459    "});
 8460    handle_completion_request(
 8461        &mut cx,
 8462        indoc! {"
 8463            one.second_completion
 8464            two s
 8465            three <s|>
 8466            additional edit
 8467        "},
 8468        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8469        counter.clone(),
 8470    )
 8471    .await;
 8472    cx.condition(|editor, _| editor.context_menu_visible())
 8473        .await;
 8474    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8475
 8476    cx.simulate_keystroke("i");
 8477
 8478    handle_completion_request(
 8479        &mut cx,
 8480        indoc! {"
 8481            one.second_completion
 8482            two si
 8483            three <si|>
 8484            additional edit
 8485        "},
 8486        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8487        counter.clone(),
 8488    )
 8489    .await;
 8490    cx.condition(|editor, _| editor.context_menu_visible())
 8491        .await;
 8492    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8493
 8494    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8495        editor
 8496            .confirm_completion(&ConfirmCompletion::default(), cx)
 8497            .unwrap()
 8498    });
 8499    cx.assert_editor_state(indoc! {"
 8500        one.second_completion
 8501        two sixth_completionˇ
 8502        three sixth_completionˇ
 8503        additional edit
 8504    "});
 8505
 8506    apply_additional_edits.await.unwrap();
 8507
 8508    update_test_language_settings(&mut cx, |settings| {
 8509        settings.defaults.show_completions_on_input = Some(false);
 8510    });
 8511    cx.set_state("editorˇ");
 8512    cx.simulate_keystroke(".");
 8513    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8514    cx.simulate_keystroke("c");
 8515    cx.simulate_keystroke("l");
 8516    cx.simulate_keystroke("o");
 8517    cx.assert_editor_state("editor.cloˇ");
 8518    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8519    cx.update_editor(|editor, cx| {
 8520        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8521    });
 8522    handle_completion_request(
 8523        &mut cx,
 8524        "editor.<clo|>",
 8525        vec!["close", "clobber"],
 8526        counter.clone(),
 8527    )
 8528    .await;
 8529    cx.condition(|editor, _| editor.context_menu_visible())
 8530        .await;
 8531    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8532
 8533    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8534        editor
 8535            .confirm_completion(&ConfirmCompletion::default(), cx)
 8536            .unwrap()
 8537    });
 8538    cx.assert_editor_state("editor.closeˇ");
 8539    handle_resolve_completion_request(&mut cx, None).await;
 8540    apply_additional_edits.await.unwrap();
 8541}
 8542
 8543#[gpui::test]
 8544async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8545    init_test(cx, |_| {});
 8546
 8547    let fs = FakeFs::new(cx.executor());
 8548    fs.insert_tree(
 8549        "/a",
 8550        json!({
 8551            "main.ts": "a",
 8552        }),
 8553    )
 8554    .await;
 8555
 8556    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8557    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8558    let typescript_language = Arc::new(Language::new(
 8559        LanguageConfig {
 8560            name: "TypeScript".into(),
 8561            matcher: LanguageMatcher {
 8562                path_suffixes: vec!["ts".to_string()],
 8563                ..LanguageMatcher::default()
 8564            },
 8565            line_comments: vec!["// ".into()],
 8566            ..LanguageConfig::default()
 8567        },
 8568        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8569    ));
 8570    language_registry.add(typescript_language.clone());
 8571    let mut fake_servers = language_registry.register_fake_lsp(
 8572        "TypeScript",
 8573        FakeLspAdapter {
 8574            capabilities: lsp::ServerCapabilities {
 8575                completion_provider: Some(lsp::CompletionOptions {
 8576                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8577                    ..lsp::CompletionOptions::default()
 8578                }),
 8579                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8580                ..lsp::ServerCapabilities::default()
 8581            },
 8582            // Emulate vtsls label generation
 8583            label_for_completion: Some(Box::new(|item, _| {
 8584                let text = if let Some(description) = item
 8585                    .label_details
 8586                    .as_ref()
 8587                    .and_then(|label_details| label_details.description.as_ref())
 8588                {
 8589                    format!("{} {}", item.label, description)
 8590                } else if let Some(detail) = &item.detail {
 8591                    format!("{} {}", item.label, detail)
 8592                } else {
 8593                    item.label.clone()
 8594                };
 8595                let len = text.len();
 8596                Some(language::CodeLabel {
 8597                    text,
 8598                    runs: Vec::new(),
 8599                    filter_range: 0..len,
 8600                })
 8601            })),
 8602            ..FakeLspAdapter::default()
 8603        },
 8604    );
 8605    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8606    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8607    let worktree_id = workspace
 8608        .update(cx, |workspace, cx| {
 8609            workspace.project().update(cx, |project, cx| {
 8610                project.worktrees(cx).next().unwrap().read(cx).id()
 8611            })
 8612        })
 8613        .unwrap();
 8614    let _buffer = project
 8615        .update(cx, |project, cx| {
 8616            project.open_local_buffer_with_lsp("/a/main.ts", cx)
 8617        })
 8618        .await
 8619        .unwrap();
 8620    let editor = workspace
 8621        .update(cx, |workspace, cx| {
 8622            workspace.open_path((worktree_id, "main.ts"), None, true, cx)
 8623        })
 8624        .unwrap()
 8625        .await
 8626        .unwrap()
 8627        .downcast::<Editor>()
 8628        .unwrap();
 8629    let fake_server = fake_servers.next().await.unwrap();
 8630
 8631    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8632    let multiline_label_2 = "a\nb\nc\n";
 8633    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8634    let multiline_description = "d\ne\nf\n";
 8635    let multiline_detail_2 = "g\nh\ni\n";
 8636
 8637    let mut completion_handle =
 8638        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8639            Ok(Some(lsp::CompletionResponse::Array(vec![
 8640                lsp::CompletionItem {
 8641                    label: multiline_label.to_string(),
 8642                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8643                        range: lsp::Range {
 8644                            start: lsp::Position {
 8645                                line: params.text_document_position.position.line,
 8646                                character: params.text_document_position.position.character,
 8647                            },
 8648                            end: lsp::Position {
 8649                                line: params.text_document_position.position.line,
 8650                                character: params.text_document_position.position.character,
 8651                            },
 8652                        },
 8653                        new_text: "new_text_1".to_string(),
 8654                    })),
 8655                    ..lsp::CompletionItem::default()
 8656                },
 8657                lsp::CompletionItem {
 8658                    label: "single line label 1".to_string(),
 8659                    detail: Some(multiline_detail.to_string()),
 8660                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8661                        range: lsp::Range {
 8662                            start: lsp::Position {
 8663                                line: params.text_document_position.position.line,
 8664                                character: params.text_document_position.position.character,
 8665                            },
 8666                            end: lsp::Position {
 8667                                line: params.text_document_position.position.line,
 8668                                character: params.text_document_position.position.character,
 8669                            },
 8670                        },
 8671                        new_text: "new_text_2".to_string(),
 8672                    })),
 8673                    ..lsp::CompletionItem::default()
 8674                },
 8675                lsp::CompletionItem {
 8676                    label: "single line label 2".to_string(),
 8677                    label_details: Some(lsp::CompletionItemLabelDetails {
 8678                        description: Some(multiline_description.to_string()),
 8679                        detail: None,
 8680                    }),
 8681                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8682                        range: lsp::Range {
 8683                            start: lsp::Position {
 8684                                line: params.text_document_position.position.line,
 8685                                character: params.text_document_position.position.character,
 8686                            },
 8687                            end: lsp::Position {
 8688                                line: params.text_document_position.position.line,
 8689                                character: params.text_document_position.position.character,
 8690                            },
 8691                        },
 8692                        new_text: "new_text_2".to_string(),
 8693                    })),
 8694                    ..lsp::CompletionItem::default()
 8695                },
 8696                lsp::CompletionItem {
 8697                    label: multiline_label_2.to_string(),
 8698                    detail: Some(multiline_detail_2.to_string()),
 8699                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8700                        range: lsp::Range {
 8701                            start: lsp::Position {
 8702                                line: params.text_document_position.position.line,
 8703                                character: params.text_document_position.position.character,
 8704                            },
 8705                            end: lsp::Position {
 8706                                line: params.text_document_position.position.line,
 8707                                character: params.text_document_position.position.character,
 8708                            },
 8709                        },
 8710                        new_text: "new_text_3".to_string(),
 8711                    })),
 8712                    ..lsp::CompletionItem::default()
 8713                },
 8714                lsp::CompletionItem {
 8715                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8716                    detail: Some(
 8717                        "Details with many     spaces and \t but without newlines".to_string(),
 8718                    ),
 8719                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8720                        range: lsp::Range {
 8721                            start: lsp::Position {
 8722                                line: params.text_document_position.position.line,
 8723                                character: params.text_document_position.position.character,
 8724                            },
 8725                            end: lsp::Position {
 8726                                line: params.text_document_position.position.line,
 8727                                character: params.text_document_position.position.character,
 8728                            },
 8729                        },
 8730                        new_text: "new_text_4".to_string(),
 8731                    })),
 8732                    ..lsp::CompletionItem::default()
 8733                },
 8734            ])))
 8735        });
 8736
 8737    editor.update(cx, |editor, cx| {
 8738        editor.focus(cx);
 8739        editor.move_to_end(&MoveToEnd, cx);
 8740        editor.handle_input(".", cx);
 8741    });
 8742    cx.run_until_parked();
 8743    completion_handle.next().await.unwrap();
 8744
 8745    editor.update(cx, |editor, _| {
 8746        assert!(editor.context_menu_visible());
 8747        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8748        {
 8749            let completion_labels = menu
 8750                .completions
 8751                .borrow()
 8752                .iter()
 8753                .map(|c| c.label.text.clone())
 8754                .collect::<Vec<_>>();
 8755            assert_eq!(
 8756                completion_labels,
 8757                &[
 8758                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8759                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8760                    "single line label 2 d e f ",
 8761                    "a b c g h i ",
 8762                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8763                ],
 8764                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8765            );
 8766
 8767            for completion in menu
 8768                .completions
 8769                .borrow()
 8770                .iter() {
 8771                    assert_eq!(
 8772                        completion.label.filter_range,
 8773                        0..completion.label.text.len(),
 8774                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8775                    );
 8776                }
 8777
 8778        } else {
 8779            panic!("expected completion menu to be open");
 8780        }
 8781    });
 8782}
 8783
 8784#[gpui::test]
 8785async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8786    init_test(cx, |_| {});
 8787    let mut cx = EditorLspTestContext::new_rust(
 8788        lsp::ServerCapabilities {
 8789            completion_provider: Some(lsp::CompletionOptions {
 8790                trigger_characters: Some(vec![".".to_string()]),
 8791                ..Default::default()
 8792            }),
 8793            ..Default::default()
 8794        },
 8795        cx,
 8796    )
 8797    .await;
 8798    cx.lsp
 8799        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8800            Ok(Some(lsp::CompletionResponse::Array(vec![
 8801                lsp::CompletionItem {
 8802                    label: "first".into(),
 8803                    ..Default::default()
 8804                },
 8805                lsp::CompletionItem {
 8806                    label: "last".into(),
 8807                    ..Default::default()
 8808                },
 8809            ])))
 8810        });
 8811    cx.set_state("variableˇ");
 8812    cx.simulate_keystroke(".");
 8813    cx.executor().run_until_parked();
 8814
 8815    cx.update_editor(|editor, _| {
 8816        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8817        {
 8818            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 8819        } else {
 8820            panic!("expected completion menu to be open");
 8821        }
 8822    });
 8823
 8824    cx.update_editor(|editor, cx| {
 8825        editor.move_page_down(&MovePageDown::default(), cx);
 8826        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8827        {
 8828            assert!(
 8829                menu.selected_item == 1,
 8830                "expected PageDown to select the last item from the context menu"
 8831            );
 8832        } else {
 8833            panic!("expected completion menu to stay open after PageDown");
 8834        }
 8835    });
 8836
 8837    cx.update_editor(|editor, cx| {
 8838        editor.move_page_up(&MovePageUp::default(), cx);
 8839        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8840        {
 8841            assert!(
 8842                menu.selected_item == 0,
 8843                "expected PageUp to select the first item from the context menu"
 8844            );
 8845        } else {
 8846            panic!("expected completion menu to stay open after PageUp");
 8847        }
 8848    });
 8849}
 8850
 8851#[gpui::test]
 8852async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8853    init_test(cx, |_| {});
 8854    let mut cx = EditorLspTestContext::new_rust(
 8855        lsp::ServerCapabilities {
 8856            completion_provider: Some(lsp::CompletionOptions {
 8857                trigger_characters: Some(vec![".".to_string()]),
 8858                ..Default::default()
 8859            }),
 8860            ..Default::default()
 8861        },
 8862        cx,
 8863    )
 8864    .await;
 8865    cx.lsp
 8866        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8867            Ok(Some(lsp::CompletionResponse::Array(vec![
 8868                lsp::CompletionItem {
 8869                    label: "Range".into(),
 8870                    sort_text: Some("a".into()),
 8871                    ..Default::default()
 8872                },
 8873                lsp::CompletionItem {
 8874                    label: "r".into(),
 8875                    sort_text: Some("b".into()),
 8876                    ..Default::default()
 8877                },
 8878                lsp::CompletionItem {
 8879                    label: "ret".into(),
 8880                    sort_text: Some("c".into()),
 8881                    ..Default::default()
 8882                },
 8883                lsp::CompletionItem {
 8884                    label: "return".into(),
 8885                    sort_text: Some("d".into()),
 8886                    ..Default::default()
 8887                },
 8888                lsp::CompletionItem {
 8889                    label: "slice".into(),
 8890                    sort_text: Some("d".into()),
 8891                    ..Default::default()
 8892                },
 8893            ])))
 8894        });
 8895    cx.set_state("");
 8896    cx.executor().run_until_parked();
 8897    cx.update_editor(|editor, cx| {
 8898        editor.show_completions(
 8899            &ShowCompletions {
 8900                trigger: Some("r".into()),
 8901            },
 8902            cx,
 8903        );
 8904    });
 8905    cx.executor().run_until_parked();
 8906
 8907    cx.update_editor(|editor, _| {
 8908        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8909        {
 8910            assert_eq!(
 8911                completion_menu_entries(&menu),
 8912                &["r", "ret", "Range", "return"]
 8913            );
 8914        } else {
 8915            panic!("expected completion menu to be open");
 8916        }
 8917    });
 8918}
 8919
 8920#[gpui::test]
 8921async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8922    init_test(cx, |_| {});
 8923
 8924    let mut cx = EditorLspTestContext::new_rust(
 8925        lsp::ServerCapabilities {
 8926            completion_provider: Some(lsp::CompletionOptions {
 8927                trigger_characters: Some(vec![".".to_string()]),
 8928                resolve_provider: Some(true),
 8929                ..Default::default()
 8930            }),
 8931            ..Default::default()
 8932        },
 8933        cx,
 8934    )
 8935    .await;
 8936
 8937    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8938    cx.simulate_keystroke(".");
 8939    let completion_item = lsp::CompletionItem {
 8940        label: "Some".into(),
 8941        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8942        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8943        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8944            kind: lsp::MarkupKind::Markdown,
 8945            value: "```rust\nSome(2)\n```".to_string(),
 8946        })),
 8947        deprecated: Some(false),
 8948        sort_text: Some("Some".to_string()),
 8949        filter_text: Some("Some".to_string()),
 8950        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8951        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8952            range: lsp::Range {
 8953                start: lsp::Position {
 8954                    line: 0,
 8955                    character: 22,
 8956                },
 8957                end: lsp::Position {
 8958                    line: 0,
 8959                    character: 22,
 8960                },
 8961            },
 8962            new_text: "Some(2)".to_string(),
 8963        })),
 8964        additional_text_edits: Some(vec![lsp::TextEdit {
 8965            range: lsp::Range {
 8966                start: lsp::Position {
 8967                    line: 0,
 8968                    character: 20,
 8969                },
 8970                end: lsp::Position {
 8971                    line: 0,
 8972                    character: 22,
 8973                },
 8974            },
 8975            new_text: "".to_string(),
 8976        }]),
 8977        ..Default::default()
 8978    };
 8979
 8980    let closure_completion_item = completion_item.clone();
 8981    let counter = Arc::new(AtomicUsize::new(0));
 8982    let counter_clone = counter.clone();
 8983    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8984        let task_completion_item = closure_completion_item.clone();
 8985        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8986        async move {
 8987            Ok(Some(lsp::CompletionResponse::Array(vec![
 8988                task_completion_item,
 8989            ])))
 8990        }
 8991    });
 8992
 8993    cx.condition(|editor, _| editor.context_menu_visible())
 8994        .await;
 8995    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8996    assert!(request.next().await.is_some());
 8997    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8998
 8999    cx.simulate_keystroke("S");
 9000    cx.simulate_keystroke("o");
 9001    cx.simulate_keystroke("m");
 9002    cx.condition(|editor, _| editor.context_menu_visible())
 9003        .await;
 9004    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9005    assert!(request.next().await.is_some());
 9006    assert!(request.next().await.is_some());
 9007    assert!(request.next().await.is_some());
 9008    request.close();
 9009    assert!(request.next().await.is_none());
 9010    assert_eq!(
 9011        counter.load(atomic::Ordering::Acquire),
 9012        4,
 9013        "With the completions menu open, only one LSP request should happen per input"
 9014    );
 9015}
 9016
 9017#[gpui::test]
 9018async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9019    init_test(cx, |_| {});
 9020    let mut cx = EditorTestContext::new(cx).await;
 9021    let language = Arc::new(Language::new(
 9022        LanguageConfig {
 9023            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9024            ..Default::default()
 9025        },
 9026        Some(tree_sitter_rust::LANGUAGE.into()),
 9027    ));
 9028    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9029
 9030    // If multiple selections intersect a line, the line is only toggled once.
 9031    cx.set_state(indoc! {"
 9032        fn a() {
 9033            «//b();
 9034            ˇ»// «c();
 9035            //ˇ»  d();
 9036        }
 9037    "});
 9038
 9039    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9040
 9041    cx.assert_editor_state(indoc! {"
 9042        fn a() {
 9043            «b();
 9044            c();
 9045            ˇ» d();
 9046        }
 9047    "});
 9048
 9049    // The comment prefix is inserted at the same column for every line in a
 9050    // selection.
 9051    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9052
 9053    cx.assert_editor_state(indoc! {"
 9054        fn a() {
 9055            // «b();
 9056            // c();
 9057            ˇ»//  d();
 9058        }
 9059    "});
 9060
 9061    // If a selection ends at the beginning of a line, that line is not toggled.
 9062    cx.set_selections_state(indoc! {"
 9063        fn a() {
 9064            // b();
 9065            «// c();
 9066        ˇ»    //  d();
 9067        }
 9068    "});
 9069
 9070    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9071
 9072    cx.assert_editor_state(indoc! {"
 9073        fn a() {
 9074            // b();
 9075            «c();
 9076        ˇ»    //  d();
 9077        }
 9078    "});
 9079
 9080    // If a selection span a single line and is empty, the line is toggled.
 9081    cx.set_state(indoc! {"
 9082        fn a() {
 9083            a();
 9084            b();
 9085        ˇ
 9086        }
 9087    "});
 9088
 9089    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9090
 9091    cx.assert_editor_state(indoc! {"
 9092        fn a() {
 9093            a();
 9094            b();
 9095        //•ˇ
 9096        }
 9097    "});
 9098
 9099    // If a selection span multiple lines, empty lines are not toggled.
 9100    cx.set_state(indoc! {"
 9101        fn a() {
 9102            «a();
 9103
 9104            c();ˇ»
 9105        }
 9106    "});
 9107
 9108    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9109
 9110    cx.assert_editor_state(indoc! {"
 9111        fn a() {
 9112            // «a();
 9113
 9114            // c();ˇ»
 9115        }
 9116    "});
 9117
 9118    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9119    cx.set_state(indoc! {"
 9120        fn a() {
 9121            «// a();
 9122            /// b();
 9123            //! c();ˇ»
 9124        }
 9125    "});
 9126
 9127    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9128
 9129    cx.assert_editor_state(indoc! {"
 9130        fn a() {
 9131            «a();
 9132            b();
 9133            c();ˇ»
 9134        }
 9135    "});
 9136}
 9137
 9138#[gpui::test]
 9139async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9140    init_test(cx, |_| {});
 9141    let mut cx = EditorTestContext::new(cx).await;
 9142    let language = Arc::new(Language::new(
 9143        LanguageConfig {
 9144            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9145            ..Default::default()
 9146        },
 9147        Some(tree_sitter_rust::LANGUAGE.into()),
 9148    ));
 9149    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9150
 9151    let toggle_comments = &ToggleComments {
 9152        advance_downwards: false,
 9153        ignore_indent: true,
 9154    };
 9155
 9156    // If multiple selections intersect a line, the line is only toggled once.
 9157    cx.set_state(indoc! {"
 9158        fn a() {
 9159        //    «b();
 9160        //    c();
 9161        //    ˇ» d();
 9162        }
 9163    "});
 9164
 9165    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9166
 9167    cx.assert_editor_state(indoc! {"
 9168        fn a() {
 9169            «b();
 9170            c();
 9171            ˇ» d();
 9172        }
 9173    "});
 9174
 9175    // The comment prefix is inserted at the beginning of each line
 9176    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9177
 9178    cx.assert_editor_state(indoc! {"
 9179        fn a() {
 9180        //    «b();
 9181        //    c();
 9182        //    ˇ» d();
 9183        }
 9184    "});
 9185
 9186    // If a selection ends at the beginning of a line, that line is not toggled.
 9187    cx.set_selections_state(indoc! {"
 9188        fn a() {
 9189        //    b();
 9190        //    «c();
 9191        ˇ»//     d();
 9192        }
 9193    "});
 9194
 9195    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9196
 9197    cx.assert_editor_state(indoc! {"
 9198        fn a() {
 9199        //    b();
 9200            «c();
 9201        ˇ»//     d();
 9202        }
 9203    "});
 9204
 9205    // If a selection span a single line and is empty, the line is toggled.
 9206    cx.set_state(indoc! {"
 9207        fn a() {
 9208            a();
 9209            b();
 9210        ˇ
 9211        }
 9212    "});
 9213
 9214    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9215
 9216    cx.assert_editor_state(indoc! {"
 9217        fn a() {
 9218            a();
 9219            b();
 9220        //ˇ
 9221        }
 9222    "});
 9223
 9224    // If a selection span multiple lines, empty lines are not toggled.
 9225    cx.set_state(indoc! {"
 9226        fn a() {
 9227            «a();
 9228
 9229            c();ˇ»
 9230        }
 9231    "});
 9232
 9233    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9234
 9235    cx.assert_editor_state(indoc! {"
 9236        fn a() {
 9237        //    «a();
 9238
 9239        //    c();ˇ»
 9240        }
 9241    "});
 9242
 9243    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9244    cx.set_state(indoc! {"
 9245        fn a() {
 9246        //    «a();
 9247        ///    b();
 9248        //!    c();ˇ»
 9249        }
 9250    "});
 9251
 9252    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9253
 9254    cx.assert_editor_state(indoc! {"
 9255        fn a() {
 9256            «a();
 9257            b();
 9258            c();ˇ»
 9259        }
 9260    "});
 9261}
 9262
 9263#[gpui::test]
 9264async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9265    init_test(cx, |_| {});
 9266
 9267    let language = Arc::new(Language::new(
 9268        LanguageConfig {
 9269            line_comments: vec!["// ".into()],
 9270            ..Default::default()
 9271        },
 9272        Some(tree_sitter_rust::LANGUAGE.into()),
 9273    ));
 9274
 9275    let mut cx = EditorTestContext::new(cx).await;
 9276
 9277    cx.language_registry().add(language.clone());
 9278    cx.update_buffer(|buffer, cx| {
 9279        buffer.set_language(Some(language), cx);
 9280    });
 9281
 9282    let toggle_comments = &ToggleComments {
 9283        advance_downwards: true,
 9284        ignore_indent: false,
 9285    };
 9286
 9287    // Single cursor on one line -> advance
 9288    // Cursor moves horizontally 3 characters as well on non-blank line
 9289    cx.set_state(indoc!(
 9290        "fn a() {
 9291             ˇdog();
 9292             cat();
 9293        }"
 9294    ));
 9295    cx.update_editor(|editor, cx| {
 9296        editor.toggle_comments(toggle_comments, cx);
 9297    });
 9298    cx.assert_editor_state(indoc!(
 9299        "fn a() {
 9300             // dog();
 9301             catˇ();
 9302        }"
 9303    ));
 9304
 9305    // Single selection on one line -> don't advance
 9306    cx.set_state(indoc!(
 9307        "fn a() {
 9308             «dog()ˇ»;
 9309             cat();
 9310        }"
 9311    ));
 9312    cx.update_editor(|editor, cx| {
 9313        editor.toggle_comments(toggle_comments, cx);
 9314    });
 9315    cx.assert_editor_state(indoc!(
 9316        "fn a() {
 9317             // «dog()ˇ»;
 9318             cat();
 9319        }"
 9320    ));
 9321
 9322    // Multiple cursors on one line -> advance
 9323    cx.set_state(indoc!(
 9324        "fn a() {
 9325             ˇdˇog();
 9326             cat();
 9327        }"
 9328    ));
 9329    cx.update_editor(|editor, cx| {
 9330        editor.toggle_comments(toggle_comments, cx);
 9331    });
 9332    cx.assert_editor_state(indoc!(
 9333        "fn a() {
 9334             // dog();
 9335             catˇ(ˇ);
 9336        }"
 9337    ));
 9338
 9339    // Multiple cursors on one line, with selection -> don't advance
 9340    cx.set_state(indoc!(
 9341        "fn a() {
 9342             ˇdˇog«()ˇ»;
 9343             cat();
 9344        }"
 9345    ));
 9346    cx.update_editor(|editor, cx| {
 9347        editor.toggle_comments(toggle_comments, cx);
 9348    });
 9349    cx.assert_editor_state(indoc!(
 9350        "fn a() {
 9351             // ˇdˇog«()ˇ»;
 9352             cat();
 9353        }"
 9354    ));
 9355
 9356    // Single cursor on one line -> advance
 9357    // Cursor moves to column 0 on blank line
 9358    cx.set_state(indoc!(
 9359        "fn a() {
 9360             ˇdog();
 9361
 9362             cat();
 9363        }"
 9364    ));
 9365    cx.update_editor(|editor, cx| {
 9366        editor.toggle_comments(toggle_comments, cx);
 9367    });
 9368    cx.assert_editor_state(indoc!(
 9369        "fn a() {
 9370             // dog();
 9371        ˇ
 9372             cat();
 9373        }"
 9374    ));
 9375
 9376    // Single cursor on one line -> advance
 9377    // Cursor starts and ends at column 0
 9378    cx.set_state(indoc!(
 9379        "fn a() {
 9380         ˇ    dog();
 9381             cat();
 9382        }"
 9383    ));
 9384    cx.update_editor(|editor, cx| {
 9385        editor.toggle_comments(toggle_comments, cx);
 9386    });
 9387    cx.assert_editor_state(indoc!(
 9388        "fn a() {
 9389             // dog();
 9390         ˇ    cat();
 9391        }"
 9392    ));
 9393}
 9394
 9395#[gpui::test]
 9396async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9397    init_test(cx, |_| {});
 9398
 9399    let mut cx = EditorTestContext::new(cx).await;
 9400
 9401    let html_language = Arc::new(
 9402        Language::new(
 9403            LanguageConfig {
 9404                name: "HTML".into(),
 9405                block_comment: Some(("<!-- ".into(), " -->".into())),
 9406                ..Default::default()
 9407            },
 9408            Some(tree_sitter_html::language()),
 9409        )
 9410        .with_injection_query(
 9411            r#"
 9412            (script_element
 9413                (raw_text) @injection.content
 9414                (#set! injection.language "javascript"))
 9415            "#,
 9416        )
 9417        .unwrap(),
 9418    );
 9419
 9420    let javascript_language = Arc::new(Language::new(
 9421        LanguageConfig {
 9422            name: "JavaScript".into(),
 9423            line_comments: vec!["// ".into()],
 9424            ..Default::default()
 9425        },
 9426        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9427    ));
 9428
 9429    cx.language_registry().add(html_language.clone());
 9430    cx.language_registry().add(javascript_language.clone());
 9431    cx.update_buffer(|buffer, cx| {
 9432        buffer.set_language(Some(html_language), cx);
 9433    });
 9434
 9435    // Toggle comments for empty selections
 9436    cx.set_state(
 9437        &r#"
 9438            <p>A</p>ˇ
 9439            <p>B</p>ˇ
 9440            <p>C</p>ˇ
 9441        "#
 9442        .unindent(),
 9443    );
 9444    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9445    cx.assert_editor_state(
 9446        &r#"
 9447            <!-- <p>A</p>ˇ -->
 9448            <!-- <p>B</p>ˇ -->
 9449            <!-- <p>C</p>ˇ -->
 9450        "#
 9451        .unindent(),
 9452    );
 9453    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9454    cx.assert_editor_state(
 9455        &r#"
 9456            <p>A</p>ˇ
 9457            <p>B</p>ˇ
 9458            <p>C</p>ˇ
 9459        "#
 9460        .unindent(),
 9461    );
 9462
 9463    // Toggle comments for mixture of empty and non-empty selections, where
 9464    // multiple selections occupy a given line.
 9465    cx.set_state(
 9466        &r#"
 9467            <p>A«</p>
 9468            <p>ˇ»B</p>ˇ
 9469            <p>C«</p>
 9470            <p>ˇ»D</p>ˇ
 9471        "#
 9472        .unindent(),
 9473    );
 9474
 9475    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9476    cx.assert_editor_state(
 9477        &r#"
 9478            <!-- <p>A«</p>
 9479            <p>ˇ»B</p>ˇ -->
 9480            <!-- <p>C«</p>
 9481            <p>ˇ»D</p>ˇ -->
 9482        "#
 9483        .unindent(),
 9484    );
 9485    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9486    cx.assert_editor_state(
 9487        &r#"
 9488            <p>A«</p>
 9489            <p>ˇ»B</p>ˇ
 9490            <p>C«</p>
 9491            <p>ˇ»D</p>ˇ
 9492        "#
 9493        .unindent(),
 9494    );
 9495
 9496    // Toggle comments when different languages are active for different
 9497    // selections.
 9498    cx.set_state(
 9499        &r#"
 9500            ˇ<script>
 9501                ˇvar x = new Y();
 9502            ˇ</script>
 9503        "#
 9504        .unindent(),
 9505    );
 9506    cx.executor().run_until_parked();
 9507    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9508    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9509    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9510    cx.assert_editor_state(
 9511        &r#"
 9512            <!-- ˇ<script> -->
 9513                // ˇvar x = new Y();
 9514            // ˇ</script>
 9515        "#
 9516        .unindent(),
 9517    );
 9518}
 9519
 9520#[gpui::test]
 9521fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9522    init_test(cx, |_| {});
 9523
 9524    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9525    let multibuffer = cx.new_model(|cx| {
 9526        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9527        multibuffer.push_excerpts(
 9528            buffer.clone(),
 9529            [
 9530                ExcerptRange {
 9531                    context: Point::new(0, 0)..Point::new(0, 4),
 9532                    primary: None,
 9533                },
 9534                ExcerptRange {
 9535                    context: Point::new(1, 0)..Point::new(1, 4),
 9536                    primary: None,
 9537                },
 9538            ],
 9539            cx,
 9540        );
 9541        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9542        multibuffer
 9543    });
 9544
 9545    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9546    view.update(cx, |view, cx| {
 9547        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9548        view.change_selections(None, cx, |s| {
 9549            s.select_ranges([
 9550                Point::new(0, 0)..Point::new(0, 0),
 9551                Point::new(1, 0)..Point::new(1, 0),
 9552            ])
 9553        });
 9554
 9555        view.handle_input("X", cx);
 9556        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9557        assert_eq!(
 9558            view.selections.ranges(cx),
 9559            [
 9560                Point::new(0, 1)..Point::new(0, 1),
 9561                Point::new(1, 1)..Point::new(1, 1),
 9562            ]
 9563        );
 9564
 9565        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9566        view.change_selections(None, cx, |s| {
 9567            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9568        });
 9569        view.backspace(&Default::default(), cx);
 9570        assert_eq!(view.text(cx), "Xa\nbbb");
 9571        assert_eq!(
 9572            view.selections.ranges(cx),
 9573            [Point::new(1, 0)..Point::new(1, 0)]
 9574        );
 9575
 9576        view.change_selections(None, cx, |s| {
 9577            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9578        });
 9579        view.backspace(&Default::default(), cx);
 9580        assert_eq!(view.text(cx), "X\nbb");
 9581        assert_eq!(
 9582            view.selections.ranges(cx),
 9583            [Point::new(0, 1)..Point::new(0, 1)]
 9584        );
 9585    });
 9586}
 9587
 9588#[gpui::test]
 9589fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9590    init_test(cx, |_| {});
 9591
 9592    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9593    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9594        indoc! {"
 9595            [aaaa
 9596            (bbbb]
 9597            cccc)",
 9598        },
 9599        markers.clone(),
 9600    );
 9601    let excerpt_ranges = markers.into_iter().map(|marker| {
 9602        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9603        ExcerptRange {
 9604            context,
 9605            primary: None,
 9606        }
 9607    });
 9608    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9609    let multibuffer = cx.new_model(|cx| {
 9610        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9611        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9612        multibuffer
 9613    });
 9614
 9615    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9616    view.update(cx, |view, cx| {
 9617        let (expected_text, selection_ranges) = marked_text_ranges(
 9618            indoc! {"
 9619                aaaa
 9620                bˇbbb
 9621                bˇbbˇb
 9622                cccc"
 9623            },
 9624            true,
 9625        );
 9626        assert_eq!(view.text(cx), expected_text);
 9627        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9628
 9629        view.handle_input("X", cx);
 9630
 9631        let (expected_text, expected_selections) = marked_text_ranges(
 9632            indoc! {"
 9633                aaaa
 9634                bXˇbbXb
 9635                bXˇbbXˇb
 9636                cccc"
 9637            },
 9638            false,
 9639        );
 9640        assert_eq!(view.text(cx), expected_text);
 9641        assert_eq!(view.selections.ranges(cx), expected_selections);
 9642
 9643        view.newline(&Newline, cx);
 9644        let (expected_text, expected_selections) = marked_text_ranges(
 9645            indoc! {"
 9646                aaaa
 9647                bX
 9648                ˇbbX
 9649                b
 9650                bX
 9651                ˇbbX
 9652                ˇb
 9653                cccc"
 9654            },
 9655            false,
 9656        );
 9657        assert_eq!(view.text(cx), expected_text);
 9658        assert_eq!(view.selections.ranges(cx), expected_selections);
 9659    });
 9660}
 9661
 9662#[gpui::test]
 9663fn test_refresh_selections(cx: &mut TestAppContext) {
 9664    init_test(cx, |_| {});
 9665
 9666    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9667    let mut excerpt1_id = None;
 9668    let multibuffer = cx.new_model(|cx| {
 9669        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9670        excerpt1_id = multibuffer
 9671            .push_excerpts(
 9672                buffer.clone(),
 9673                [
 9674                    ExcerptRange {
 9675                        context: Point::new(0, 0)..Point::new(1, 4),
 9676                        primary: None,
 9677                    },
 9678                    ExcerptRange {
 9679                        context: Point::new(1, 0)..Point::new(2, 4),
 9680                        primary: None,
 9681                    },
 9682                ],
 9683                cx,
 9684            )
 9685            .into_iter()
 9686            .next();
 9687        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9688        multibuffer
 9689    });
 9690
 9691    let editor = cx.add_window(|cx| {
 9692        let mut editor = build_editor(multibuffer.clone(), cx);
 9693        let snapshot = editor.snapshot(cx);
 9694        editor.change_selections(None, cx, |s| {
 9695            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9696        });
 9697        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9698        assert_eq!(
 9699            editor.selections.ranges(cx),
 9700            [
 9701                Point::new(1, 3)..Point::new(1, 3),
 9702                Point::new(2, 1)..Point::new(2, 1),
 9703            ]
 9704        );
 9705        editor
 9706    });
 9707
 9708    // Refreshing selections is a no-op when excerpts haven't changed.
 9709    _ = editor.update(cx, |editor, cx| {
 9710        editor.change_selections(None, cx, |s| s.refresh());
 9711        assert_eq!(
 9712            editor.selections.ranges(cx),
 9713            [
 9714                Point::new(1, 3)..Point::new(1, 3),
 9715                Point::new(2, 1)..Point::new(2, 1),
 9716            ]
 9717        );
 9718    });
 9719
 9720    multibuffer.update(cx, |multibuffer, cx| {
 9721        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9722    });
 9723    _ = editor.update(cx, |editor, cx| {
 9724        // Removing an excerpt causes the first selection to become degenerate.
 9725        assert_eq!(
 9726            editor.selections.ranges(cx),
 9727            [
 9728                Point::new(0, 0)..Point::new(0, 0),
 9729                Point::new(0, 1)..Point::new(0, 1)
 9730            ]
 9731        );
 9732
 9733        // Refreshing selections will relocate the first selection to the original buffer
 9734        // location.
 9735        editor.change_selections(None, cx, |s| s.refresh());
 9736        assert_eq!(
 9737            editor.selections.ranges(cx),
 9738            [
 9739                Point::new(0, 1)..Point::new(0, 1),
 9740                Point::new(0, 3)..Point::new(0, 3)
 9741            ]
 9742        );
 9743        assert!(editor.selections.pending_anchor().is_some());
 9744    });
 9745}
 9746
 9747#[gpui::test]
 9748fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9749    init_test(cx, |_| {});
 9750
 9751    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9752    let mut excerpt1_id = None;
 9753    let multibuffer = cx.new_model(|cx| {
 9754        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9755        excerpt1_id = multibuffer
 9756            .push_excerpts(
 9757                buffer.clone(),
 9758                [
 9759                    ExcerptRange {
 9760                        context: Point::new(0, 0)..Point::new(1, 4),
 9761                        primary: None,
 9762                    },
 9763                    ExcerptRange {
 9764                        context: Point::new(1, 0)..Point::new(2, 4),
 9765                        primary: None,
 9766                    },
 9767                ],
 9768                cx,
 9769            )
 9770            .into_iter()
 9771            .next();
 9772        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9773        multibuffer
 9774    });
 9775
 9776    let editor = cx.add_window(|cx| {
 9777        let mut editor = build_editor(multibuffer.clone(), cx);
 9778        let snapshot = editor.snapshot(cx);
 9779        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9780        assert_eq!(
 9781            editor.selections.ranges(cx),
 9782            [Point::new(1, 3)..Point::new(1, 3)]
 9783        );
 9784        editor
 9785    });
 9786
 9787    multibuffer.update(cx, |multibuffer, cx| {
 9788        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9789    });
 9790    _ = editor.update(cx, |editor, cx| {
 9791        assert_eq!(
 9792            editor.selections.ranges(cx),
 9793            [Point::new(0, 0)..Point::new(0, 0)]
 9794        );
 9795
 9796        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9797        editor.change_selections(None, cx, |s| s.refresh());
 9798        assert_eq!(
 9799            editor.selections.ranges(cx),
 9800            [Point::new(0, 3)..Point::new(0, 3)]
 9801        );
 9802        assert!(editor.selections.pending_anchor().is_some());
 9803    });
 9804}
 9805
 9806#[gpui::test]
 9807async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9808    init_test(cx, |_| {});
 9809
 9810    let language = Arc::new(
 9811        Language::new(
 9812            LanguageConfig {
 9813                brackets: BracketPairConfig {
 9814                    pairs: vec![
 9815                        BracketPair {
 9816                            start: "{".to_string(),
 9817                            end: "}".to_string(),
 9818                            close: true,
 9819                            surround: true,
 9820                            newline: true,
 9821                        },
 9822                        BracketPair {
 9823                            start: "/* ".to_string(),
 9824                            end: " */".to_string(),
 9825                            close: true,
 9826                            surround: true,
 9827                            newline: true,
 9828                        },
 9829                    ],
 9830                    ..Default::default()
 9831                },
 9832                ..Default::default()
 9833            },
 9834            Some(tree_sitter_rust::LANGUAGE.into()),
 9835        )
 9836        .with_indents_query("")
 9837        .unwrap(),
 9838    );
 9839
 9840    let text = concat!(
 9841        "{   }\n",     //
 9842        "  x\n",       //
 9843        "  /*   */\n", //
 9844        "x\n",         //
 9845        "{{} }\n",     //
 9846    );
 9847
 9848    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9849    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9850    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9851    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9852        .await;
 9853
 9854    view.update(cx, |view, cx| {
 9855        view.change_selections(None, cx, |s| {
 9856            s.select_display_ranges([
 9857                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9858                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9859                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9860            ])
 9861        });
 9862        view.newline(&Newline, cx);
 9863
 9864        assert_eq!(
 9865            view.buffer().read(cx).read(cx).text(),
 9866            concat!(
 9867                "{ \n",    // Suppress rustfmt
 9868                "\n",      //
 9869                "}\n",     //
 9870                "  x\n",   //
 9871                "  /* \n", //
 9872                "  \n",    //
 9873                "  */\n",  //
 9874                "x\n",     //
 9875                "{{} \n",  //
 9876                "}\n",     //
 9877            )
 9878        );
 9879    });
 9880}
 9881
 9882#[gpui::test]
 9883fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9884    init_test(cx, |_| {});
 9885
 9886    let editor = cx.add_window(|cx| {
 9887        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9888        build_editor(buffer.clone(), cx)
 9889    });
 9890
 9891    _ = editor.update(cx, |editor, cx| {
 9892        struct Type1;
 9893        struct Type2;
 9894
 9895        let buffer = editor.buffer.read(cx).snapshot(cx);
 9896
 9897        let anchor_range =
 9898            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9899
 9900        editor.highlight_background::<Type1>(
 9901            &[
 9902                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9903                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9904                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9905                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9906            ],
 9907            |_| Hsla::red(),
 9908            cx,
 9909        );
 9910        editor.highlight_background::<Type2>(
 9911            &[
 9912                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9913                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9914                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9915                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9916            ],
 9917            |_| Hsla::green(),
 9918            cx,
 9919        );
 9920
 9921        let snapshot = editor.snapshot(cx);
 9922        let mut highlighted_ranges = editor.background_highlights_in_range(
 9923            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9924            &snapshot,
 9925            cx.theme().colors(),
 9926        );
 9927        // Enforce a consistent ordering based on color without relying on the ordering of the
 9928        // highlight's `TypeId` which is non-executor.
 9929        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9930        assert_eq!(
 9931            highlighted_ranges,
 9932            &[
 9933                (
 9934                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9935                    Hsla::red(),
 9936                ),
 9937                (
 9938                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9939                    Hsla::red(),
 9940                ),
 9941                (
 9942                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9943                    Hsla::green(),
 9944                ),
 9945                (
 9946                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9947                    Hsla::green(),
 9948                ),
 9949            ]
 9950        );
 9951        assert_eq!(
 9952            editor.background_highlights_in_range(
 9953                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9954                &snapshot,
 9955                cx.theme().colors(),
 9956            ),
 9957            &[(
 9958                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9959                Hsla::red(),
 9960            )]
 9961        );
 9962    });
 9963}
 9964
 9965#[gpui::test]
 9966async fn test_following(cx: &mut gpui::TestAppContext) {
 9967    init_test(cx, |_| {});
 9968
 9969    let fs = FakeFs::new(cx.executor());
 9970    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9971
 9972    let buffer = project.update(cx, |project, cx| {
 9973        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9974        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9975    });
 9976    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9977    let follower = cx.update(|cx| {
 9978        cx.open_window(
 9979            WindowOptions {
 9980                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9981                    gpui::Point::new(px(0.), px(0.)),
 9982                    gpui::Point::new(px(10.), px(80.)),
 9983                ))),
 9984                ..Default::default()
 9985            },
 9986            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9987        )
 9988        .unwrap()
 9989    });
 9990
 9991    let is_still_following = Rc::new(RefCell::new(true));
 9992    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9993    let pending_update = Rc::new(RefCell::new(None));
 9994    _ = follower.update(cx, {
 9995        let update = pending_update.clone();
 9996        let is_still_following = is_still_following.clone();
 9997        let follower_edit_event_count = follower_edit_event_count.clone();
 9998        |_, cx| {
 9999            cx.subscribe(
10000                &leader.root_view(cx).unwrap(),
10001                move |_, leader, event, cx| {
10002                    leader
10003                        .read(cx)
10004                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
10005                },
10006            )
10007            .detach();
10008
10009            cx.subscribe(
10010                &follower.root_view(cx).unwrap(),
10011                move |_, _, event: &EditorEvent, _cx| {
10012                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10013                        *is_still_following.borrow_mut() = false;
10014                    }
10015
10016                    if let EditorEvent::BufferEdited = event {
10017                        *follower_edit_event_count.borrow_mut() += 1;
10018                    }
10019                },
10020            )
10021            .detach();
10022        }
10023    });
10024
10025    // Update the selections only
10026    _ = leader.update(cx, |leader, cx| {
10027        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10028    });
10029    follower
10030        .update(cx, |follower, cx| {
10031            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10032        })
10033        .unwrap()
10034        .await
10035        .unwrap();
10036    _ = follower.update(cx, |follower, cx| {
10037        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10038    });
10039    assert!(*is_still_following.borrow());
10040    assert_eq!(*follower_edit_event_count.borrow(), 0);
10041
10042    // Update the scroll position only
10043    _ = leader.update(cx, |leader, cx| {
10044        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
10045    });
10046    follower
10047        .update(cx, |follower, cx| {
10048            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10049        })
10050        .unwrap()
10051        .await
10052        .unwrap();
10053    assert_eq!(
10054        follower
10055            .update(cx, |follower, cx| follower.scroll_position(cx))
10056            .unwrap(),
10057        gpui::Point::new(1.5, 3.5)
10058    );
10059    assert!(*is_still_following.borrow());
10060    assert_eq!(*follower_edit_event_count.borrow(), 0);
10061
10062    // Update the selections and scroll position. The follower's scroll position is updated
10063    // via autoscroll, not via the leader's exact scroll position.
10064    _ = leader.update(cx, |leader, cx| {
10065        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10066        leader.request_autoscroll(Autoscroll::newest(), cx);
10067        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
10068    });
10069    follower
10070        .update(cx, |follower, cx| {
10071            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10072        })
10073        .unwrap()
10074        .await
10075        .unwrap();
10076    _ = follower.update(cx, |follower, cx| {
10077        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10078        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10079    });
10080    assert!(*is_still_following.borrow());
10081
10082    // Creating a pending selection that precedes another selection
10083    _ = leader.update(cx, |leader, cx| {
10084        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10085        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
10086    });
10087    follower
10088        .update(cx, |follower, cx| {
10089            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10090        })
10091        .unwrap()
10092        .await
10093        .unwrap();
10094    _ = follower.update(cx, |follower, cx| {
10095        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10096    });
10097    assert!(*is_still_following.borrow());
10098
10099    // Extend the pending selection so that it surrounds another selection
10100    _ = leader.update(cx, |leader, cx| {
10101        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
10102    });
10103    follower
10104        .update(cx, |follower, cx| {
10105            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10106        })
10107        .unwrap()
10108        .await
10109        .unwrap();
10110    _ = follower.update(cx, |follower, cx| {
10111        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10112    });
10113
10114    // Scrolling locally breaks the follow
10115    _ = follower.update(cx, |follower, cx| {
10116        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10117        follower.set_scroll_anchor(
10118            ScrollAnchor {
10119                anchor: top_anchor,
10120                offset: gpui::Point::new(0.0, 0.5),
10121            },
10122            cx,
10123        );
10124    });
10125    assert!(!(*is_still_following.borrow()));
10126}
10127
10128#[gpui::test]
10129async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10130    init_test(cx, |_| {});
10131
10132    let fs = FakeFs::new(cx.executor());
10133    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10134    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10135    let pane = workspace
10136        .update(cx, |workspace, _| workspace.active_pane().clone())
10137        .unwrap();
10138
10139    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10140
10141    let leader = pane.update(cx, |_, cx| {
10142        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
10143        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
10144    });
10145
10146    // Start following the editor when it has no excerpts.
10147    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
10148    let follower_1 = cx
10149        .update_window(*workspace.deref(), |_, cx| {
10150            Editor::from_state_proto(
10151                workspace.root_view(cx).unwrap(),
10152                ViewId {
10153                    creator: Default::default(),
10154                    id: 0,
10155                },
10156                &mut state_message,
10157                cx,
10158            )
10159        })
10160        .unwrap()
10161        .unwrap()
10162        .await
10163        .unwrap();
10164
10165    let update_message = Rc::new(RefCell::new(None));
10166    follower_1.update(cx, {
10167        let update = update_message.clone();
10168        |_, cx| {
10169            cx.subscribe(&leader, move |_, leader, event, cx| {
10170                leader
10171                    .read(cx)
10172                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
10173            })
10174            .detach();
10175        }
10176    });
10177
10178    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10179        (
10180            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10181            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10182        )
10183    });
10184
10185    // Insert some excerpts.
10186    leader.update(cx, |leader, cx| {
10187        leader.buffer.update(cx, |multibuffer, cx| {
10188            let excerpt_ids = multibuffer.push_excerpts(
10189                buffer_1.clone(),
10190                [
10191                    ExcerptRange {
10192                        context: 1..6,
10193                        primary: None,
10194                    },
10195                    ExcerptRange {
10196                        context: 12..15,
10197                        primary: None,
10198                    },
10199                    ExcerptRange {
10200                        context: 0..3,
10201                        primary: None,
10202                    },
10203                ],
10204                cx,
10205            );
10206            multibuffer.insert_excerpts_after(
10207                excerpt_ids[0],
10208                buffer_2.clone(),
10209                [
10210                    ExcerptRange {
10211                        context: 8..12,
10212                        primary: None,
10213                    },
10214                    ExcerptRange {
10215                        context: 0..6,
10216                        primary: None,
10217                    },
10218                ],
10219                cx,
10220            );
10221        });
10222    });
10223
10224    // Apply the update of adding the excerpts.
10225    follower_1
10226        .update(cx, |follower, cx| {
10227            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10228        })
10229        .await
10230        .unwrap();
10231    assert_eq!(
10232        follower_1.update(cx, |editor, cx| editor.text(cx)),
10233        leader.update(cx, |editor, cx| editor.text(cx))
10234    );
10235    update_message.borrow_mut().take();
10236
10237    // Start following separately after it already has excerpts.
10238    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
10239    let follower_2 = cx
10240        .update_window(*workspace.deref(), |_, cx| {
10241            Editor::from_state_proto(
10242                workspace.root_view(cx).unwrap().clone(),
10243                ViewId {
10244                    creator: Default::default(),
10245                    id: 0,
10246                },
10247                &mut state_message,
10248                cx,
10249            )
10250        })
10251        .unwrap()
10252        .unwrap()
10253        .await
10254        .unwrap();
10255    assert_eq!(
10256        follower_2.update(cx, |editor, cx| editor.text(cx)),
10257        leader.update(cx, |editor, cx| editor.text(cx))
10258    );
10259
10260    // Remove some excerpts.
10261    leader.update(cx, |leader, cx| {
10262        leader.buffer.update(cx, |multibuffer, cx| {
10263            let excerpt_ids = multibuffer.excerpt_ids();
10264            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10265            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10266        });
10267    });
10268
10269    // Apply the update of removing the excerpts.
10270    follower_1
10271        .update(cx, |follower, cx| {
10272            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10273        })
10274        .await
10275        .unwrap();
10276    follower_2
10277        .update(cx, |follower, cx| {
10278            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10279        })
10280        .await
10281        .unwrap();
10282    update_message.borrow_mut().take();
10283    assert_eq!(
10284        follower_1.update(cx, |editor, cx| editor.text(cx)),
10285        leader.update(cx, |editor, cx| editor.text(cx))
10286    );
10287}
10288
10289#[gpui::test]
10290async fn go_to_prev_overlapping_diagnostic(
10291    executor: BackgroundExecutor,
10292    cx: &mut gpui::TestAppContext,
10293) {
10294    init_test(cx, |_| {});
10295
10296    let mut cx = EditorTestContext::new(cx).await;
10297    let lsp_store =
10298        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10299
10300    cx.set_state(indoc! {"
10301        ˇfn func(abc def: i32) -> u32 {
10302        }
10303    "});
10304
10305    cx.update(|cx| {
10306        lsp_store.update(cx, |lsp_store, cx| {
10307            lsp_store
10308                .update_diagnostics(
10309                    LanguageServerId(0),
10310                    lsp::PublishDiagnosticsParams {
10311                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
10312                        version: None,
10313                        diagnostics: vec![
10314                            lsp::Diagnostic {
10315                                range: lsp::Range::new(
10316                                    lsp::Position::new(0, 11),
10317                                    lsp::Position::new(0, 12),
10318                                ),
10319                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10320                                ..Default::default()
10321                            },
10322                            lsp::Diagnostic {
10323                                range: lsp::Range::new(
10324                                    lsp::Position::new(0, 12),
10325                                    lsp::Position::new(0, 15),
10326                                ),
10327                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10328                                ..Default::default()
10329                            },
10330                            lsp::Diagnostic {
10331                                range: lsp::Range::new(
10332                                    lsp::Position::new(0, 25),
10333                                    lsp::Position::new(0, 28),
10334                                ),
10335                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10336                                ..Default::default()
10337                            },
10338                        ],
10339                    },
10340                    &[],
10341                    cx,
10342                )
10343                .unwrap()
10344        });
10345    });
10346
10347    executor.run_until_parked();
10348
10349    cx.update_editor(|editor, cx| {
10350        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10351    });
10352
10353    cx.assert_editor_state(indoc! {"
10354        fn func(abc def: i32) -> ˇu32 {
10355        }
10356    "});
10357
10358    cx.update_editor(|editor, cx| {
10359        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10360    });
10361
10362    cx.assert_editor_state(indoc! {"
10363        fn func(abc ˇdef: i32) -> u32 {
10364        }
10365    "});
10366
10367    cx.update_editor(|editor, cx| {
10368        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10369    });
10370
10371    cx.assert_editor_state(indoc! {"
10372        fn func(abcˇ def: i32) -> u32 {
10373        }
10374    "});
10375
10376    cx.update_editor(|editor, cx| {
10377        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10378    });
10379
10380    cx.assert_editor_state(indoc! {"
10381        fn func(abc def: i32) -> ˇu32 {
10382        }
10383    "});
10384}
10385
10386#[gpui::test]
10387async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10388    init_test(cx, |_| {});
10389
10390    let mut cx = EditorTestContext::new(cx).await;
10391
10392    cx.set_state(indoc! {"
10393        fn func(abˇc def: i32) -> u32 {
10394        }
10395    "});
10396    let lsp_store =
10397        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10398
10399    cx.update(|cx| {
10400        lsp_store.update(cx, |lsp_store, cx| {
10401            lsp_store.update_diagnostics(
10402                LanguageServerId(0),
10403                lsp::PublishDiagnosticsParams {
10404                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10405                    version: None,
10406                    diagnostics: vec![lsp::Diagnostic {
10407                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10408                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10409                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10410                        ..Default::default()
10411                    }],
10412                },
10413                &[],
10414                cx,
10415            )
10416        })
10417    }).unwrap();
10418    cx.run_until_parked();
10419    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10420    cx.run_until_parked();
10421    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10422}
10423
10424#[gpui::test]
10425async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10426    init_test(cx, |_| {});
10427
10428    let mut cx = EditorTestContext::new(cx).await;
10429
10430    let diff_base = r#"
10431        use some::mod;
10432
10433        const A: u32 = 42;
10434
10435        fn main() {
10436            println!("hello");
10437
10438            println!("world");
10439        }
10440        "#
10441    .unindent();
10442
10443    // Edits are modified, removed, modified, added
10444    cx.set_state(
10445        &r#"
10446        use some::modified;
10447
10448        ˇ
10449        fn main() {
10450            println!("hello there");
10451
10452            println!("around the");
10453            println!("world");
10454        }
10455        "#
10456        .unindent(),
10457    );
10458
10459    cx.set_diff_base(&diff_base);
10460    executor.run_until_parked();
10461
10462    cx.update_editor(|editor, cx| {
10463        //Wrap around the bottom of the buffer
10464        for _ in 0..3 {
10465            editor.go_to_next_hunk(&GoToHunk, cx);
10466        }
10467    });
10468
10469    cx.assert_editor_state(
10470        &r#"
10471        ˇuse some::modified;
10472
10473
10474        fn main() {
10475            println!("hello there");
10476
10477            println!("around the");
10478            println!("world");
10479        }
10480        "#
10481        .unindent(),
10482    );
10483
10484    cx.update_editor(|editor, cx| {
10485        //Wrap around the top of the buffer
10486        for _ in 0..2 {
10487            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10488        }
10489    });
10490
10491    cx.assert_editor_state(
10492        &r#"
10493        use some::modified;
10494
10495
10496        fn main() {
10497        ˇ    println!("hello there");
10498
10499            println!("around the");
10500            println!("world");
10501        }
10502        "#
10503        .unindent(),
10504    );
10505
10506    cx.update_editor(|editor, cx| {
10507        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10508    });
10509
10510    cx.assert_editor_state(
10511        &r#"
10512        use some::modified;
10513
10514        ˇ
10515        fn main() {
10516            println!("hello there");
10517
10518            println!("around the");
10519            println!("world");
10520        }
10521        "#
10522        .unindent(),
10523    );
10524
10525    cx.update_editor(|editor, cx| {
10526        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10527    });
10528
10529    cx.assert_editor_state(
10530        &r#"
10531        ˇuse some::modified;
10532
10533
10534        fn main() {
10535            println!("hello there");
10536
10537            println!("around the");
10538            println!("world");
10539        }
10540        "#
10541        .unindent(),
10542    );
10543
10544    cx.update_editor(|editor, cx| {
10545        for _ in 0..2 {
10546            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10547        }
10548    });
10549
10550    cx.assert_editor_state(
10551        &r#"
10552        use some::modified;
10553
10554
10555        fn main() {
10556        ˇ    println!("hello there");
10557
10558            println!("around the");
10559            println!("world");
10560        }
10561        "#
10562        .unindent(),
10563    );
10564
10565    cx.update_editor(|editor, cx| {
10566        editor.fold(&Fold, cx);
10567    });
10568
10569    cx.update_editor(|editor, cx| {
10570        editor.go_to_next_hunk(&GoToHunk, cx);
10571    });
10572
10573    cx.assert_editor_state(
10574        &r#"
10575        ˇuse some::modified;
10576
10577
10578        fn main() {
10579            println!("hello there");
10580
10581            println!("around the");
10582            println!("world");
10583        }
10584        "#
10585        .unindent(),
10586    );
10587}
10588
10589#[test]
10590fn test_split_words() {
10591    fn split(text: &str) -> Vec<&str> {
10592        split_words(text).collect()
10593    }
10594
10595    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10596    assert_eq!(split("hello_world"), &["hello_", "world"]);
10597    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10598    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10599    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10600    assert_eq!(split("helloworld"), &["helloworld"]);
10601
10602    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10603}
10604
10605#[gpui::test]
10606async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10607    init_test(cx, |_| {});
10608
10609    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10610    let mut assert = |before, after| {
10611        let _state_context = cx.set_state(before);
10612        cx.run_until_parked();
10613        cx.update_editor(|editor, cx| {
10614            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10615        });
10616        cx.assert_editor_state(after);
10617    };
10618
10619    // Outside bracket jumps to outside of matching bracket
10620    assert("console.logˇ(var);", "console.log(var)ˇ;");
10621    assert("console.log(var)ˇ;", "console.logˇ(var);");
10622
10623    // Inside bracket jumps to inside of matching bracket
10624    assert("console.log(ˇvar);", "console.log(varˇ);");
10625    assert("console.log(varˇ);", "console.log(ˇvar);");
10626
10627    // When outside a bracket and inside, favor jumping to the inside bracket
10628    assert(
10629        "console.log('foo', [1, 2, 3]ˇ);",
10630        "console.log(ˇ'foo', [1, 2, 3]);",
10631    );
10632    assert(
10633        "console.log(ˇ'foo', [1, 2, 3]);",
10634        "console.log('foo', [1, 2, 3]ˇ);",
10635    );
10636
10637    // Bias forward if two options are equally likely
10638    assert(
10639        "let result = curried_fun()ˇ();",
10640        "let result = curried_fun()()ˇ;",
10641    );
10642
10643    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10644    assert(
10645        indoc! {"
10646            function test() {
10647                console.log('test')ˇ
10648            }"},
10649        indoc! {"
10650            function test() {
10651                console.logˇ('test')
10652            }"},
10653    );
10654}
10655
10656#[gpui::test]
10657async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10658    init_test(cx, |_| {});
10659
10660    let fs = FakeFs::new(cx.executor());
10661    fs.insert_tree(
10662        "/a",
10663        json!({
10664            "main.rs": "fn main() { let a = 5; }",
10665            "other.rs": "// Test file",
10666        }),
10667    )
10668    .await;
10669    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10670
10671    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10672    language_registry.add(Arc::new(Language::new(
10673        LanguageConfig {
10674            name: "Rust".into(),
10675            matcher: LanguageMatcher {
10676                path_suffixes: vec!["rs".to_string()],
10677                ..Default::default()
10678            },
10679            brackets: BracketPairConfig {
10680                pairs: vec![BracketPair {
10681                    start: "{".to_string(),
10682                    end: "}".to_string(),
10683                    close: true,
10684                    surround: true,
10685                    newline: true,
10686                }],
10687                disabled_scopes_by_bracket_ix: Vec::new(),
10688            },
10689            ..Default::default()
10690        },
10691        Some(tree_sitter_rust::LANGUAGE.into()),
10692    )));
10693    let mut fake_servers = language_registry.register_fake_lsp(
10694        "Rust",
10695        FakeLspAdapter {
10696            capabilities: lsp::ServerCapabilities {
10697                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10698                    first_trigger_character: "{".to_string(),
10699                    more_trigger_character: None,
10700                }),
10701                ..Default::default()
10702            },
10703            ..Default::default()
10704        },
10705    );
10706
10707    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10708
10709    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10710
10711    let worktree_id = workspace
10712        .update(cx, |workspace, cx| {
10713            workspace.project().update(cx, |project, cx| {
10714                project.worktrees(cx).next().unwrap().read(cx).id()
10715            })
10716        })
10717        .unwrap();
10718
10719    let buffer = project
10720        .update(cx, |project, cx| {
10721            project.open_local_buffer("/a/main.rs", cx)
10722        })
10723        .await
10724        .unwrap();
10725    let editor_handle = workspace
10726        .update(cx, |workspace, cx| {
10727            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10728        })
10729        .unwrap()
10730        .await
10731        .unwrap()
10732        .downcast::<Editor>()
10733        .unwrap();
10734
10735    cx.executor().start_waiting();
10736    let fake_server = fake_servers.next().await.unwrap();
10737
10738    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10739        assert_eq!(
10740            params.text_document_position.text_document.uri,
10741            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10742        );
10743        assert_eq!(
10744            params.text_document_position.position,
10745            lsp::Position::new(0, 21),
10746        );
10747
10748        Ok(Some(vec![lsp::TextEdit {
10749            new_text: "]".to_string(),
10750            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10751        }]))
10752    });
10753
10754    editor_handle.update(cx, |editor, cx| {
10755        editor.focus(cx);
10756        editor.change_selections(None, cx, |s| {
10757            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10758        });
10759        editor.handle_input("{", cx);
10760    });
10761
10762    cx.executor().run_until_parked();
10763
10764    buffer.update(cx, |buffer, _| {
10765        assert_eq!(
10766            buffer.text(),
10767            "fn main() { let a = {5}; }",
10768            "No extra braces from on type formatting should appear in the buffer"
10769        )
10770    });
10771}
10772
10773#[gpui::test]
10774async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10775    init_test(cx, |_| {});
10776
10777    let fs = FakeFs::new(cx.executor());
10778    fs.insert_tree(
10779        "/a",
10780        json!({
10781            "main.rs": "fn main() { let a = 5; }",
10782            "other.rs": "// Test file",
10783        }),
10784    )
10785    .await;
10786
10787    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10788
10789    let server_restarts = Arc::new(AtomicUsize::new(0));
10790    let closure_restarts = Arc::clone(&server_restarts);
10791    let language_server_name = "test language server";
10792    let language_name: LanguageName = "Rust".into();
10793
10794    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10795    language_registry.add(Arc::new(Language::new(
10796        LanguageConfig {
10797            name: language_name.clone(),
10798            matcher: LanguageMatcher {
10799                path_suffixes: vec!["rs".to_string()],
10800                ..Default::default()
10801            },
10802            ..Default::default()
10803        },
10804        Some(tree_sitter_rust::LANGUAGE.into()),
10805    )));
10806    let mut fake_servers = language_registry.register_fake_lsp(
10807        "Rust",
10808        FakeLspAdapter {
10809            name: language_server_name,
10810            initialization_options: Some(json!({
10811                "testOptionValue": true
10812            })),
10813            initializer: Some(Box::new(move |fake_server| {
10814                let task_restarts = Arc::clone(&closure_restarts);
10815                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10816                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10817                    futures::future::ready(Ok(()))
10818                });
10819            })),
10820            ..Default::default()
10821        },
10822    );
10823
10824    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10825    let _buffer = project
10826        .update(cx, |project, cx| {
10827            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10828        })
10829        .await
10830        .unwrap();
10831    let _fake_server = fake_servers.next().await.unwrap();
10832    update_test_language_settings(cx, |language_settings| {
10833        language_settings.languages.insert(
10834            language_name.clone(),
10835            LanguageSettingsContent {
10836                tab_size: NonZeroU32::new(8),
10837                ..Default::default()
10838            },
10839        );
10840    });
10841    cx.executor().run_until_parked();
10842    assert_eq!(
10843        server_restarts.load(atomic::Ordering::Acquire),
10844        0,
10845        "Should not restart LSP server on an unrelated change"
10846    );
10847
10848    update_test_project_settings(cx, |project_settings| {
10849        project_settings.lsp.insert(
10850            "Some other server name".into(),
10851            LspSettings {
10852                binary: None,
10853                settings: None,
10854                initialization_options: Some(json!({
10855                    "some other init value": false
10856                })),
10857            },
10858        );
10859    });
10860    cx.executor().run_until_parked();
10861    assert_eq!(
10862        server_restarts.load(atomic::Ordering::Acquire),
10863        0,
10864        "Should not restart LSP server on an unrelated LSP settings change"
10865    );
10866
10867    update_test_project_settings(cx, |project_settings| {
10868        project_settings.lsp.insert(
10869            language_server_name.into(),
10870            LspSettings {
10871                binary: None,
10872                settings: None,
10873                initialization_options: Some(json!({
10874                    "anotherInitValue": false
10875                })),
10876            },
10877        );
10878    });
10879    cx.executor().run_until_parked();
10880    assert_eq!(
10881        server_restarts.load(atomic::Ordering::Acquire),
10882        1,
10883        "Should restart LSP server on a related LSP settings change"
10884    );
10885
10886    update_test_project_settings(cx, |project_settings| {
10887        project_settings.lsp.insert(
10888            language_server_name.into(),
10889            LspSettings {
10890                binary: None,
10891                settings: None,
10892                initialization_options: Some(json!({
10893                    "anotherInitValue": false
10894                })),
10895            },
10896        );
10897    });
10898    cx.executor().run_until_parked();
10899    assert_eq!(
10900        server_restarts.load(atomic::Ordering::Acquire),
10901        1,
10902        "Should not restart LSP server on a related LSP settings change that is the same"
10903    );
10904
10905    update_test_project_settings(cx, |project_settings| {
10906        project_settings.lsp.insert(
10907            language_server_name.into(),
10908            LspSettings {
10909                binary: None,
10910                settings: None,
10911                initialization_options: None,
10912            },
10913        );
10914    });
10915    cx.executor().run_until_parked();
10916    assert_eq!(
10917        server_restarts.load(atomic::Ordering::Acquire),
10918        2,
10919        "Should restart LSP server on another related LSP settings change"
10920    );
10921}
10922
10923#[gpui::test]
10924async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10925    init_test(cx, |_| {});
10926
10927    let mut cx = EditorLspTestContext::new_rust(
10928        lsp::ServerCapabilities {
10929            completion_provider: Some(lsp::CompletionOptions {
10930                trigger_characters: Some(vec![".".to_string()]),
10931                resolve_provider: Some(true),
10932                ..Default::default()
10933            }),
10934            ..Default::default()
10935        },
10936        cx,
10937    )
10938    .await;
10939
10940    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10941    cx.simulate_keystroke(".");
10942    let completion_item = lsp::CompletionItem {
10943        label: "some".into(),
10944        kind: Some(lsp::CompletionItemKind::SNIPPET),
10945        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10946        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10947            kind: lsp::MarkupKind::Markdown,
10948            value: "```rust\nSome(2)\n```".to_string(),
10949        })),
10950        deprecated: Some(false),
10951        sort_text: Some("fffffff2".to_string()),
10952        filter_text: Some("some".to_string()),
10953        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10954        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10955            range: lsp::Range {
10956                start: lsp::Position {
10957                    line: 0,
10958                    character: 22,
10959                },
10960                end: lsp::Position {
10961                    line: 0,
10962                    character: 22,
10963                },
10964            },
10965            new_text: "Some(2)".to_string(),
10966        })),
10967        additional_text_edits: Some(vec![lsp::TextEdit {
10968            range: lsp::Range {
10969                start: lsp::Position {
10970                    line: 0,
10971                    character: 20,
10972                },
10973                end: lsp::Position {
10974                    line: 0,
10975                    character: 22,
10976                },
10977            },
10978            new_text: "".to_string(),
10979        }]),
10980        ..Default::default()
10981    };
10982
10983    let closure_completion_item = completion_item.clone();
10984    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10985        let task_completion_item = closure_completion_item.clone();
10986        async move {
10987            Ok(Some(lsp::CompletionResponse::Array(vec![
10988                task_completion_item,
10989            ])))
10990        }
10991    });
10992
10993    request.next().await;
10994
10995    cx.condition(|editor, _| editor.context_menu_visible())
10996        .await;
10997    let apply_additional_edits = cx.update_editor(|editor, cx| {
10998        editor
10999            .confirm_completion(&ConfirmCompletion::default(), cx)
11000            .unwrap()
11001    });
11002    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11003
11004    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11005        let task_completion_item = completion_item.clone();
11006        async move { Ok(task_completion_item) }
11007    })
11008    .next()
11009    .await
11010    .unwrap();
11011    apply_additional_edits.await.unwrap();
11012    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11013}
11014
11015#[gpui::test]
11016async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11017    cx: &mut gpui::TestAppContext,
11018) {
11019    init_test(cx, |_| {});
11020
11021    let mut cx = EditorLspTestContext::new_rust(
11022        lsp::ServerCapabilities {
11023            completion_provider: Some(lsp::CompletionOptions {
11024                trigger_characters: Some(vec![".".to_string()]),
11025                resolve_provider: Some(true),
11026                ..Default::default()
11027            }),
11028            ..Default::default()
11029        },
11030        cx,
11031    )
11032    .await;
11033
11034    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11035    cx.simulate_keystroke(".");
11036
11037    let item1 = lsp::CompletionItem {
11038        label: "id".to_string(),
11039        filter_text: Some("id".to_string()),
11040        detail: None,
11041        documentation: None,
11042        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11043            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11044            new_text: ".id".to_string(),
11045        })),
11046        ..lsp::CompletionItem::default()
11047    };
11048
11049    let item2 = lsp::CompletionItem {
11050        label: "other".to_string(),
11051        filter_text: Some("other".to_string()),
11052        detail: None,
11053        documentation: None,
11054        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11055            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11056            new_text: ".other".to_string(),
11057        })),
11058        ..lsp::CompletionItem::default()
11059    };
11060
11061    let item1 = item1.clone();
11062    cx.handle_request::<lsp::request::Completion, _, _>({
11063        let item1 = item1.clone();
11064        move |_, _, _| {
11065            let item1 = item1.clone();
11066            let item2 = item2.clone();
11067            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11068        }
11069    })
11070    .next()
11071    .await;
11072
11073    cx.condition(|editor, _| editor.context_menu_visible())
11074        .await;
11075    cx.update_editor(|editor, _| {
11076        let context_menu = editor.context_menu.borrow_mut();
11077        let context_menu = context_menu
11078            .as_ref()
11079            .expect("Should have the context menu deployed");
11080        match context_menu {
11081            CodeContextMenu::Completions(completions_menu) => {
11082                let completions = completions_menu.completions.borrow_mut();
11083                assert_eq!(
11084                    completions
11085                        .iter()
11086                        .map(|completion| &completion.label.text)
11087                        .collect::<Vec<_>>(),
11088                    vec!["id", "other"]
11089                )
11090            }
11091            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11092        }
11093    });
11094
11095    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11096        let item1 = item1.clone();
11097        move |_, item_to_resolve, _| {
11098            let item1 = item1.clone();
11099            async move {
11100                if item1 == item_to_resolve {
11101                    Ok(lsp::CompletionItem {
11102                        label: "method id()".to_string(),
11103                        filter_text: Some("id".to_string()),
11104                        detail: Some("Now resolved!".to_string()),
11105                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11106                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11107                            range: lsp::Range::new(
11108                                lsp::Position::new(0, 22),
11109                                lsp::Position::new(0, 22),
11110                            ),
11111                            new_text: ".id".to_string(),
11112                        })),
11113                        ..lsp::CompletionItem::default()
11114                    })
11115                } else {
11116                    Ok(item_to_resolve)
11117                }
11118            }
11119        }
11120    })
11121    .next()
11122    .await
11123    .unwrap();
11124    cx.run_until_parked();
11125
11126    cx.update_editor(|editor, cx| {
11127        editor.context_menu_next(&Default::default(), cx);
11128    });
11129
11130    cx.update_editor(|editor, _| {
11131        let context_menu = editor.context_menu.borrow_mut();
11132        let context_menu = context_menu
11133            .as_ref()
11134            .expect("Should have the context menu deployed");
11135        match context_menu {
11136            CodeContextMenu::Completions(completions_menu) => {
11137                let completions = completions_menu.completions.borrow_mut();
11138                assert_eq!(
11139                    completions
11140                        .iter()
11141                        .map(|completion| &completion.label.text)
11142                        .collect::<Vec<_>>(),
11143                    vec!["method id()", "other"],
11144                    "Should update first completion label, but not second as the filter text did not match."
11145                );
11146            }
11147            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11148        }
11149    });
11150}
11151
11152#[gpui::test]
11153async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11154    init_test(cx, |_| {});
11155
11156    let mut cx = EditorLspTestContext::new_rust(
11157        lsp::ServerCapabilities {
11158            completion_provider: Some(lsp::CompletionOptions {
11159                trigger_characters: Some(vec![".".to_string()]),
11160                resolve_provider: Some(true),
11161                ..Default::default()
11162            }),
11163            ..Default::default()
11164        },
11165        cx,
11166    )
11167    .await;
11168
11169    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11170    cx.simulate_keystroke(".");
11171
11172    let unresolved_item_1 = lsp::CompletionItem {
11173        label: "id".to_string(),
11174        filter_text: Some("id".to_string()),
11175        detail: None,
11176        documentation: None,
11177        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11178            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11179            new_text: ".id".to_string(),
11180        })),
11181        ..lsp::CompletionItem::default()
11182    };
11183    let resolved_item_1 = lsp::CompletionItem {
11184        additional_text_edits: Some(vec![lsp::TextEdit {
11185            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11186            new_text: "!!".to_string(),
11187        }]),
11188        ..unresolved_item_1.clone()
11189    };
11190    let unresolved_item_2 = lsp::CompletionItem {
11191        label: "other".to_string(),
11192        filter_text: Some("other".to_string()),
11193        detail: None,
11194        documentation: None,
11195        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11196            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11197            new_text: ".other".to_string(),
11198        })),
11199        ..lsp::CompletionItem::default()
11200    };
11201    let resolved_item_2 = lsp::CompletionItem {
11202        additional_text_edits: Some(vec![lsp::TextEdit {
11203            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11204            new_text: "??".to_string(),
11205        }]),
11206        ..unresolved_item_2.clone()
11207    };
11208
11209    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11210    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11211    cx.lsp
11212        .server
11213        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11214            let unresolved_item_1 = unresolved_item_1.clone();
11215            let resolved_item_1 = resolved_item_1.clone();
11216            let unresolved_item_2 = unresolved_item_2.clone();
11217            let resolved_item_2 = resolved_item_2.clone();
11218            let resolve_requests_1 = resolve_requests_1.clone();
11219            let resolve_requests_2 = resolve_requests_2.clone();
11220            move |unresolved_request, _| {
11221                let unresolved_item_1 = unresolved_item_1.clone();
11222                let resolved_item_1 = resolved_item_1.clone();
11223                let unresolved_item_2 = unresolved_item_2.clone();
11224                let resolved_item_2 = resolved_item_2.clone();
11225                let resolve_requests_1 = resolve_requests_1.clone();
11226                let resolve_requests_2 = resolve_requests_2.clone();
11227                async move {
11228                    if unresolved_request == unresolved_item_1 {
11229                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11230                        Ok(resolved_item_1.clone())
11231                    } else if unresolved_request == unresolved_item_2 {
11232                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11233                        Ok(resolved_item_2.clone())
11234                    } else {
11235                        panic!("Unexpected completion item {unresolved_request:?}")
11236                    }
11237                }
11238            }
11239        })
11240        .detach();
11241
11242    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11243        let unresolved_item_1 = unresolved_item_1.clone();
11244        let unresolved_item_2 = unresolved_item_2.clone();
11245        async move {
11246            Ok(Some(lsp::CompletionResponse::Array(vec![
11247                unresolved_item_1,
11248                unresolved_item_2,
11249            ])))
11250        }
11251    })
11252    .next()
11253    .await;
11254
11255    cx.condition(|editor, _| editor.context_menu_visible())
11256        .await;
11257    cx.update_editor(|editor, _| {
11258        let context_menu = editor.context_menu.borrow_mut();
11259        let context_menu = context_menu
11260            .as_ref()
11261            .expect("Should have the context menu deployed");
11262        match context_menu {
11263            CodeContextMenu::Completions(completions_menu) => {
11264                let completions = completions_menu.completions.borrow_mut();
11265                assert_eq!(
11266                    completions
11267                        .iter()
11268                        .map(|completion| &completion.label.text)
11269                        .collect::<Vec<_>>(),
11270                    vec!["id", "other"]
11271                )
11272            }
11273            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11274        }
11275    });
11276    cx.run_until_parked();
11277
11278    cx.update_editor(|editor, cx| {
11279        editor.context_menu_next(&ContextMenuNext, cx);
11280    });
11281    cx.run_until_parked();
11282    cx.update_editor(|editor, cx| {
11283        editor.context_menu_prev(&ContextMenuPrev, cx);
11284    });
11285    cx.run_until_parked();
11286    cx.update_editor(|editor, cx| {
11287        editor.context_menu_next(&ContextMenuNext, cx);
11288    });
11289    cx.run_until_parked();
11290    cx.update_editor(|editor, cx| {
11291        editor
11292            .compose_completion(&ComposeCompletion::default(), cx)
11293            .expect("No task returned")
11294    })
11295    .await
11296    .expect("Completion failed");
11297    cx.run_until_parked();
11298
11299    cx.update_editor(|editor, cx| {
11300        assert_eq!(
11301            resolve_requests_1.load(atomic::Ordering::Acquire),
11302            1,
11303            "Should always resolve once despite multiple selections"
11304        );
11305        assert_eq!(
11306            resolve_requests_2.load(atomic::Ordering::Acquire),
11307            1,
11308            "Should always resolve once after multiple selections and applying the completion"
11309        );
11310        assert_eq!(
11311            editor.text(cx),
11312            "fn main() { let a = ??.other; }",
11313            "Should use resolved data when applying the completion"
11314        );
11315    });
11316}
11317
11318#[gpui::test]
11319async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11320    init_test(cx, |_| {});
11321
11322    let item_0 = lsp::CompletionItem {
11323        label: "abs".into(),
11324        insert_text: Some("abs".into()),
11325        data: Some(json!({ "very": "special"})),
11326        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11327        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11328            lsp::InsertReplaceEdit {
11329                new_text: "abs".to_string(),
11330                insert: lsp::Range::default(),
11331                replace: lsp::Range::default(),
11332            },
11333        )),
11334        ..lsp::CompletionItem::default()
11335    };
11336    let items = iter::once(item_0.clone())
11337        .chain((11..51).map(|i| lsp::CompletionItem {
11338            label: format!("item_{}", i),
11339            insert_text: Some(format!("item_{}", i)),
11340            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11341            ..lsp::CompletionItem::default()
11342        }))
11343        .collect::<Vec<_>>();
11344
11345    let default_commit_characters = vec!["?".to_string()];
11346    let default_data = json!({ "default": "data"});
11347    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11348    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11349    let default_edit_range = lsp::Range {
11350        start: lsp::Position {
11351            line: 0,
11352            character: 5,
11353        },
11354        end: lsp::Position {
11355            line: 0,
11356            character: 5,
11357        },
11358    };
11359
11360    let item_0_out = lsp::CompletionItem {
11361        commit_characters: Some(default_commit_characters.clone()),
11362        insert_text_format: Some(default_insert_text_format),
11363        ..item_0
11364    };
11365    let items_out = iter::once(item_0_out)
11366        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11367            commit_characters: Some(default_commit_characters.clone()),
11368            data: Some(default_data.clone()),
11369            insert_text_mode: Some(default_insert_text_mode),
11370            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11371                range: default_edit_range,
11372                new_text: item.label.clone(),
11373            })),
11374            ..item.clone()
11375        }))
11376        .collect::<Vec<lsp::CompletionItem>>();
11377
11378    let mut cx = EditorLspTestContext::new_rust(
11379        lsp::ServerCapabilities {
11380            completion_provider: Some(lsp::CompletionOptions {
11381                trigger_characters: Some(vec![".".to_string()]),
11382                resolve_provider: Some(true),
11383                ..Default::default()
11384            }),
11385            ..Default::default()
11386        },
11387        cx,
11388    )
11389    .await;
11390
11391    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11392    cx.simulate_keystroke(".");
11393
11394    let completion_data = default_data.clone();
11395    let completion_characters = default_commit_characters.clone();
11396    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11397        let default_data = completion_data.clone();
11398        let default_commit_characters = completion_characters.clone();
11399        let items = items.clone();
11400        async move {
11401            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11402                items,
11403                item_defaults: Some(lsp::CompletionListItemDefaults {
11404                    data: Some(default_data.clone()),
11405                    commit_characters: Some(default_commit_characters.clone()),
11406                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11407                        default_edit_range,
11408                    )),
11409                    insert_text_format: Some(default_insert_text_format),
11410                    insert_text_mode: Some(default_insert_text_mode),
11411                }),
11412                ..lsp::CompletionList::default()
11413            })))
11414        }
11415    })
11416    .next()
11417    .await;
11418
11419    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11420    cx.lsp
11421        .server
11422        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11423            let closure_resolved_items = resolved_items.clone();
11424            move |item_to_resolve, _| {
11425                let closure_resolved_items = closure_resolved_items.clone();
11426                async move {
11427                    closure_resolved_items.lock().push(item_to_resolve.clone());
11428                    Ok(item_to_resolve)
11429                }
11430            }
11431        })
11432        .detach();
11433
11434    cx.condition(|editor, _| editor.context_menu_visible())
11435        .await;
11436    cx.run_until_parked();
11437    cx.update_editor(|editor, _| {
11438        let menu = editor.context_menu.borrow_mut();
11439        match menu.as_ref().expect("should have the completions menu") {
11440            CodeContextMenu::Completions(completions_menu) => {
11441                assert_eq!(
11442                    completions_menu
11443                        .entries
11444                        .borrow()
11445                        .iter()
11446                        .flat_map(|c| match c {
11447                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11448                            _ => None,
11449                        })
11450                        .collect::<Vec<String>>(),
11451                    items_out
11452                        .iter()
11453                        .map(|completion| completion.label.clone())
11454                        .collect::<Vec<String>>()
11455                );
11456            }
11457            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11458        }
11459    });
11460    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11461    // with 4 from the end.
11462    assert_eq!(
11463        *resolved_items.lock(),
11464        [
11465            &items_out[0..16],
11466            &items_out[items_out.len() - 4..items_out.len()]
11467        ]
11468        .concat()
11469        .iter()
11470        .cloned()
11471        .collect::<Vec<lsp::CompletionItem>>()
11472    );
11473    resolved_items.lock().clear();
11474
11475    cx.update_editor(|editor, cx| {
11476        editor.context_menu_prev(&ContextMenuPrev, cx);
11477    });
11478    cx.run_until_parked();
11479    // Completions that have already been resolved are skipped.
11480    assert_eq!(
11481        *resolved_items.lock(),
11482        items_out[items_out.len() - 16..items_out.len() - 4]
11483            .iter()
11484            .cloned()
11485            .collect::<Vec<lsp::CompletionItem>>()
11486    );
11487    resolved_items.lock().clear();
11488}
11489
11490#[gpui::test]
11491async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11492    init_test(cx, |_| {});
11493
11494    let mut cx = EditorLspTestContext::new(
11495        Language::new(
11496            LanguageConfig {
11497                matcher: LanguageMatcher {
11498                    path_suffixes: vec!["jsx".into()],
11499                    ..Default::default()
11500                },
11501                overrides: [(
11502                    "element".into(),
11503                    LanguageConfigOverride {
11504                        word_characters: Override::Set(['-'].into_iter().collect()),
11505                        ..Default::default()
11506                    },
11507                )]
11508                .into_iter()
11509                .collect(),
11510                ..Default::default()
11511            },
11512            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11513        )
11514        .with_override_query("(jsx_self_closing_element) @element")
11515        .unwrap(),
11516        lsp::ServerCapabilities {
11517            completion_provider: Some(lsp::CompletionOptions {
11518                trigger_characters: Some(vec![":".to_string()]),
11519                ..Default::default()
11520            }),
11521            ..Default::default()
11522        },
11523        cx,
11524    )
11525    .await;
11526
11527    cx.lsp
11528        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11529            Ok(Some(lsp::CompletionResponse::Array(vec![
11530                lsp::CompletionItem {
11531                    label: "bg-blue".into(),
11532                    ..Default::default()
11533                },
11534                lsp::CompletionItem {
11535                    label: "bg-red".into(),
11536                    ..Default::default()
11537                },
11538                lsp::CompletionItem {
11539                    label: "bg-yellow".into(),
11540                    ..Default::default()
11541                },
11542            ])))
11543        });
11544
11545    cx.set_state(r#"<p class="bgˇ" />"#);
11546
11547    // Trigger completion when typing a dash, because the dash is an extra
11548    // word character in the 'element' scope, which contains the cursor.
11549    cx.simulate_keystroke("-");
11550    cx.executor().run_until_parked();
11551    cx.update_editor(|editor, _| {
11552        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11553        {
11554            assert_eq!(
11555                completion_menu_entries(&menu),
11556                &["bg-red", "bg-blue", "bg-yellow"]
11557            );
11558        } else {
11559            panic!("expected completion menu to be open");
11560        }
11561    });
11562
11563    cx.simulate_keystroke("l");
11564    cx.executor().run_until_parked();
11565    cx.update_editor(|editor, _| {
11566        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11567        {
11568            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11569        } else {
11570            panic!("expected completion menu to be open");
11571        }
11572    });
11573
11574    // When filtering completions, consider the character after the '-' to
11575    // be the start of a subword.
11576    cx.set_state(r#"<p class="yelˇ" />"#);
11577    cx.simulate_keystroke("l");
11578    cx.executor().run_until_parked();
11579    cx.update_editor(|editor, _| {
11580        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11581        {
11582            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11583        } else {
11584            panic!("expected completion menu to be open");
11585        }
11586    });
11587}
11588
11589fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11590    let entries = menu.entries.borrow();
11591    entries
11592        .iter()
11593        .flat_map(|e| match e {
11594            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11595            _ => None,
11596        })
11597        .collect()
11598}
11599
11600#[gpui::test]
11601async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11602    init_test(cx, |settings| {
11603        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11604            FormatterList(vec![Formatter::Prettier].into()),
11605        ))
11606    });
11607
11608    let fs = FakeFs::new(cx.executor());
11609    fs.insert_file("/file.ts", Default::default()).await;
11610
11611    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11612    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11613
11614    language_registry.add(Arc::new(Language::new(
11615        LanguageConfig {
11616            name: "TypeScript".into(),
11617            matcher: LanguageMatcher {
11618                path_suffixes: vec!["ts".to_string()],
11619                ..Default::default()
11620            },
11621            ..Default::default()
11622        },
11623        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11624    )));
11625    update_test_language_settings(cx, |settings| {
11626        settings.defaults.prettier = Some(PrettierSettings {
11627            allowed: true,
11628            ..PrettierSettings::default()
11629        });
11630    });
11631
11632    let test_plugin = "test_plugin";
11633    let _ = language_registry.register_fake_lsp(
11634        "TypeScript",
11635        FakeLspAdapter {
11636            prettier_plugins: vec![test_plugin],
11637            ..Default::default()
11638        },
11639    );
11640
11641    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11642    let buffer = project
11643        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11644        .await
11645        .unwrap();
11646
11647    let buffer_text = "one\ntwo\nthree\n";
11648    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11649    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11650    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11651
11652    editor
11653        .update(cx, |editor, cx| {
11654            editor.perform_format(
11655                project.clone(),
11656                FormatTrigger::Manual,
11657                FormatTarget::Buffers,
11658                cx,
11659            )
11660        })
11661        .unwrap()
11662        .await;
11663    assert_eq!(
11664        editor.update(cx, |editor, cx| editor.text(cx)),
11665        buffer_text.to_string() + prettier_format_suffix,
11666        "Test prettier formatting was not applied to the original buffer text",
11667    );
11668
11669    update_test_language_settings(cx, |settings| {
11670        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11671    });
11672    let format = editor.update(cx, |editor, cx| {
11673        editor.perform_format(
11674            project.clone(),
11675            FormatTrigger::Manual,
11676            FormatTarget::Buffers,
11677            cx,
11678        )
11679    });
11680    format.await.unwrap();
11681    assert_eq!(
11682        editor.update(cx, |editor, cx| editor.text(cx)),
11683        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11684        "Autoformatting (via test prettier) was not applied to the original buffer text",
11685    );
11686}
11687
11688#[gpui::test]
11689async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11690    init_test(cx, |_| {});
11691    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11692    let base_text = indoc! {r#"
11693        struct Row;
11694        struct Row1;
11695        struct Row2;
11696
11697        struct Row4;
11698        struct Row5;
11699        struct Row6;
11700
11701        struct Row8;
11702        struct Row9;
11703        struct Row10;"#};
11704
11705    // When addition hunks are not adjacent to carets, no hunk revert is performed
11706    assert_hunk_revert(
11707        indoc! {r#"struct Row;
11708                   struct Row1;
11709                   struct Row1.1;
11710                   struct Row1.2;
11711                   struct Row2;ˇ
11712
11713                   struct Row4;
11714                   struct Row5;
11715                   struct Row6;
11716
11717                   struct Row8;
11718                   ˇstruct Row9;
11719                   struct Row9.1;
11720                   struct Row9.2;
11721                   struct Row9.3;
11722                   struct Row10;"#},
11723        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11724        indoc! {r#"struct Row;
11725                   struct Row1;
11726                   struct Row1.1;
11727                   struct Row1.2;
11728                   struct Row2;ˇ
11729
11730                   struct Row4;
11731                   struct Row5;
11732                   struct Row6;
11733
11734                   struct Row8;
11735                   ˇstruct Row9;
11736                   struct Row9.1;
11737                   struct Row9.2;
11738                   struct Row9.3;
11739                   struct Row10;"#},
11740        base_text,
11741        &mut cx,
11742    );
11743    // Same for selections
11744    assert_hunk_revert(
11745        indoc! {r#"struct Row;
11746                   struct Row1;
11747                   struct Row2;
11748                   struct Row2.1;
11749                   struct Row2.2;
11750                   «ˇ
11751                   struct Row4;
11752                   struct» Row5;
11753                   «struct Row6;
11754                   ˇ»
11755                   struct Row9.1;
11756                   struct Row9.2;
11757                   struct Row9.3;
11758                   struct Row8;
11759                   struct Row9;
11760                   struct Row10;"#},
11761        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11762        indoc! {r#"struct Row;
11763                   struct Row1;
11764                   struct Row2;
11765                   struct Row2.1;
11766                   struct Row2.2;
11767                   «ˇ
11768                   struct Row4;
11769                   struct» Row5;
11770                   «struct Row6;
11771                   ˇ»
11772                   struct Row9.1;
11773                   struct Row9.2;
11774                   struct Row9.3;
11775                   struct Row8;
11776                   struct Row9;
11777                   struct Row10;"#},
11778        base_text,
11779        &mut cx,
11780    );
11781
11782    // When carets and selections intersect the addition hunks, those are reverted.
11783    // Adjacent carets got merged.
11784    assert_hunk_revert(
11785        indoc! {r#"struct Row;
11786                   ˇ// something on the top
11787                   struct Row1;
11788                   struct Row2;
11789                   struct Roˇw3.1;
11790                   struct Row2.2;
11791                   struct Row2.3;ˇ
11792
11793                   struct Row4;
11794                   struct ˇRow5.1;
11795                   struct Row5.2;
11796                   struct «Rowˇ»5.3;
11797                   struct Row5;
11798                   struct Row6;
11799                   ˇ
11800                   struct Row9.1;
11801                   struct «Rowˇ»9.2;
11802                   struct «ˇRow»9.3;
11803                   struct Row8;
11804                   struct Row9;
11805                   «ˇ// something on bottom»
11806                   struct Row10;"#},
11807        vec![
11808            DiffHunkStatus::Added,
11809            DiffHunkStatus::Added,
11810            DiffHunkStatus::Added,
11811            DiffHunkStatus::Added,
11812            DiffHunkStatus::Added,
11813        ],
11814        indoc! {r#"struct Row;
11815                   ˇstruct Row1;
11816                   struct Row2;
11817                   ˇ
11818                   struct Row4;
11819                   ˇstruct Row5;
11820                   struct Row6;
11821                   ˇ
11822                   ˇstruct Row8;
11823                   struct Row9;
11824                   ˇstruct Row10;"#},
11825        base_text,
11826        &mut cx,
11827    );
11828}
11829
11830#[gpui::test]
11831async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11832    init_test(cx, |_| {});
11833    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11834    let base_text = indoc! {r#"
11835        struct Row;
11836        struct Row1;
11837        struct Row2;
11838
11839        struct Row4;
11840        struct Row5;
11841        struct Row6;
11842
11843        struct Row8;
11844        struct Row9;
11845        struct Row10;"#};
11846
11847    // Modification hunks behave the same as the addition ones.
11848    assert_hunk_revert(
11849        indoc! {r#"struct Row;
11850                   struct Row1;
11851                   struct Row33;
11852                   ˇ
11853                   struct Row4;
11854                   struct Row5;
11855                   struct Row6;
11856                   ˇ
11857                   struct Row99;
11858                   struct Row9;
11859                   struct Row10;"#},
11860        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11861        indoc! {r#"struct Row;
11862                   struct Row1;
11863                   struct Row33;
11864                   ˇ
11865                   struct Row4;
11866                   struct Row5;
11867                   struct Row6;
11868                   ˇ
11869                   struct Row99;
11870                   struct Row9;
11871                   struct Row10;"#},
11872        base_text,
11873        &mut cx,
11874    );
11875    assert_hunk_revert(
11876        indoc! {r#"struct Row;
11877                   struct Row1;
11878                   struct Row33;
11879                   «ˇ
11880                   struct Row4;
11881                   struct» Row5;
11882                   «struct Row6;
11883                   ˇ»
11884                   struct Row99;
11885                   struct Row9;
11886                   struct Row10;"#},
11887        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11888        indoc! {r#"struct Row;
11889                   struct Row1;
11890                   struct Row33;
11891                   «ˇ
11892                   struct Row4;
11893                   struct» Row5;
11894                   «struct Row6;
11895                   ˇ»
11896                   struct Row99;
11897                   struct Row9;
11898                   struct Row10;"#},
11899        base_text,
11900        &mut cx,
11901    );
11902
11903    assert_hunk_revert(
11904        indoc! {r#"ˇstruct Row1.1;
11905                   struct Row1;
11906                   «ˇstr»uct Row22;
11907
11908                   struct ˇRow44;
11909                   struct Row5;
11910                   struct «Rˇ»ow66;ˇ
11911
11912                   «struˇ»ct Row88;
11913                   struct Row9;
11914                   struct Row1011;ˇ"#},
11915        vec![
11916            DiffHunkStatus::Modified,
11917            DiffHunkStatus::Modified,
11918            DiffHunkStatus::Modified,
11919            DiffHunkStatus::Modified,
11920            DiffHunkStatus::Modified,
11921            DiffHunkStatus::Modified,
11922        ],
11923        indoc! {r#"struct Row;
11924                   ˇstruct Row1;
11925                   struct Row2;
11926                   ˇ
11927                   struct Row4;
11928                   ˇstruct Row5;
11929                   struct Row6;
11930                   ˇ
11931                   struct Row8;
11932                   ˇstruct Row9;
11933                   struct Row10;ˇ"#},
11934        base_text,
11935        &mut cx,
11936    );
11937}
11938
11939#[gpui::test]
11940async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
11941    init_test(cx, |_| {});
11942    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11943    let base_text = indoc! {r#"
11944        one
11945
11946        two
11947        three
11948        "#};
11949
11950    cx.set_diff_base(base_text);
11951    cx.set_state("\nˇ\n");
11952    cx.executor().run_until_parked();
11953    cx.update_editor(|editor, cx| {
11954        editor.expand_selected_diff_hunks(cx);
11955    });
11956    cx.executor().run_until_parked();
11957    cx.update_editor(|editor, cx| {
11958        editor.backspace(&Default::default(), cx);
11959    });
11960    cx.run_until_parked();
11961    cx.assert_state_with_diff(
11962        indoc! {r#"
11963
11964        - two
11965        - threeˇ
11966        +
11967        "#}
11968        .to_string(),
11969    );
11970}
11971
11972#[gpui::test]
11973async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11974    init_test(cx, |_| {});
11975    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11976    let base_text = indoc! {r#"struct Row;
11977struct Row1;
11978struct Row2;
11979
11980struct Row4;
11981struct Row5;
11982struct Row6;
11983
11984struct Row8;
11985struct Row9;
11986struct Row10;"#};
11987
11988    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11989    assert_hunk_revert(
11990        indoc! {r#"struct Row;
11991                   struct Row2;
11992
11993                   ˇstruct Row4;
11994                   struct Row5;
11995                   struct Row6;
11996                   ˇ
11997                   struct Row8;
11998                   struct Row10;"#},
11999        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12000        indoc! {r#"struct Row;
12001                   struct Row2;
12002
12003                   ˇstruct Row4;
12004                   struct Row5;
12005                   struct Row6;
12006                   ˇ
12007                   struct Row8;
12008                   struct Row10;"#},
12009        base_text,
12010        &mut cx,
12011    );
12012    assert_hunk_revert(
12013        indoc! {r#"struct Row;
12014                   struct Row2;
12015
12016                   «ˇstruct Row4;
12017                   struct» Row5;
12018                   «struct Row6;
12019                   ˇ»
12020                   struct Row8;
12021                   struct Row10;"#},
12022        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12023        indoc! {r#"struct Row;
12024                   struct Row2;
12025
12026                   «ˇstruct Row4;
12027                   struct» Row5;
12028                   «struct Row6;
12029                   ˇ»
12030                   struct Row8;
12031                   struct Row10;"#},
12032        base_text,
12033        &mut cx,
12034    );
12035
12036    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12037    assert_hunk_revert(
12038        indoc! {r#"struct Row;
12039                   ˇstruct Row2;
12040
12041                   struct Row4;
12042                   struct Row5;
12043                   struct Row6;
12044
12045                   struct Row8;ˇ
12046                   struct Row10;"#},
12047        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12048        indoc! {r#"struct Row;
12049                   struct Row1;
12050                   ˇstruct Row2;
12051
12052                   struct Row4;
12053                   struct Row5;
12054                   struct Row6;
12055
12056                   struct Row8;ˇ
12057                   struct Row9;
12058                   struct Row10;"#},
12059        base_text,
12060        &mut cx,
12061    );
12062    assert_hunk_revert(
12063        indoc! {r#"struct Row;
12064                   struct Row2«ˇ;
12065                   struct Row4;
12066                   struct» Row5;
12067                   «struct Row6;
12068
12069                   struct Row8;ˇ»
12070                   struct Row10;"#},
12071        vec![
12072            DiffHunkStatus::Removed,
12073            DiffHunkStatus::Removed,
12074            DiffHunkStatus::Removed,
12075        ],
12076        indoc! {r#"struct Row;
12077                   struct Row1;
12078                   struct Row2«ˇ;
12079
12080                   struct Row4;
12081                   struct» Row5;
12082                   «struct Row6;
12083
12084                   struct Row8;ˇ»
12085                   struct Row9;
12086                   struct Row10;"#},
12087        base_text,
12088        &mut cx,
12089    );
12090}
12091
12092#[gpui::test]
12093async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12094    init_test(cx, |_| {});
12095
12096    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12097    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12098    let base_text_3 =
12099        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12100
12101    let text_1 = edit_first_char_of_every_line(base_text_1);
12102    let text_2 = edit_first_char_of_every_line(base_text_2);
12103    let text_3 = edit_first_char_of_every_line(base_text_3);
12104
12105    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
12106    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
12107    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
12108
12109    let multibuffer = cx.new_model(|cx| {
12110        let mut multibuffer = MultiBuffer::new(ReadWrite);
12111        multibuffer.push_excerpts(
12112            buffer_1.clone(),
12113            [
12114                ExcerptRange {
12115                    context: Point::new(0, 0)..Point::new(3, 0),
12116                    primary: None,
12117                },
12118                ExcerptRange {
12119                    context: Point::new(5, 0)..Point::new(7, 0),
12120                    primary: None,
12121                },
12122                ExcerptRange {
12123                    context: Point::new(9, 0)..Point::new(10, 4),
12124                    primary: None,
12125                },
12126            ],
12127            cx,
12128        );
12129        multibuffer.push_excerpts(
12130            buffer_2.clone(),
12131            [
12132                ExcerptRange {
12133                    context: Point::new(0, 0)..Point::new(3, 0),
12134                    primary: None,
12135                },
12136                ExcerptRange {
12137                    context: Point::new(5, 0)..Point::new(7, 0),
12138                    primary: None,
12139                },
12140                ExcerptRange {
12141                    context: Point::new(9, 0)..Point::new(10, 4),
12142                    primary: None,
12143                },
12144            ],
12145            cx,
12146        );
12147        multibuffer.push_excerpts(
12148            buffer_3.clone(),
12149            [
12150                ExcerptRange {
12151                    context: Point::new(0, 0)..Point::new(3, 0),
12152                    primary: None,
12153                },
12154                ExcerptRange {
12155                    context: Point::new(5, 0)..Point::new(7, 0),
12156                    primary: None,
12157                },
12158                ExcerptRange {
12159                    context: Point::new(9, 0)..Point::new(10, 4),
12160                    primary: None,
12161                },
12162            ],
12163            cx,
12164        );
12165        multibuffer
12166    });
12167
12168    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
12169    editor.update(cx, |editor, cx| {
12170        for (buffer, diff_base) in [
12171            (buffer_1.clone(), base_text_1),
12172            (buffer_2.clone(), base_text_2),
12173            (buffer_3.clone(), base_text_3),
12174        ] {
12175            let change_set = cx.new_model(|cx| {
12176                BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
12177            });
12178            editor
12179                .buffer
12180                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12181        }
12182    });
12183    cx.executor().run_until_parked();
12184
12185    editor.update(cx, |editor, cx| {
12186        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}");
12187        editor.select_all(&SelectAll, cx);
12188        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12189    });
12190    cx.executor().run_until_parked();
12191
12192    // When all ranges are selected, all buffer hunks are reverted.
12193    editor.update(cx, |editor, cx| {
12194        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");
12195    });
12196    buffer_1.update(cx, |buffer, _| {
12197        assert_eq!(buffer.text(), base_text_1);
12198    });
12199    buffer_2.update(cx, |buffer, _| {
12200        assert_eq!(buffer.text(), base_text_2);
12201    });
12202    buffer_3.update(cx, |buffer, _| {
12203        assert_eq!(buffer.text(), base_text_3);
12204    });
12205
12206    editor.update(cx, |editor, cx| {
12207        editor.undo(&Default::default(), cx);
12208    });
12209
12210    editor.update(cx, |editor, cx| {
12211        editor.change_selections(None, cx, |s| {
12212            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12213        });
12214        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12215    });
12216
12217    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12218    // but not affect buffer_2 and its related excerpts.
12219    editor.update(cx, |editor, cx| {
12220        assert_eq!(
12221            editor.text(cx),
12222            "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}"
12223        );
12224    });
12225    buffer_1.update(cx, |buffer, _| {
12226        assert_eq!(buffer.text(), base_text_1);
12227    });
12228    buffer_2.update(cx, |buffer, _| {
12229        assert_eq!(
12230            buffer.text(),
12231            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12232        );
12233    });
12234    buffer_3.update(cx, |buffer, _| {
12235        assert_eq!(
12236            buffer.text(),
12237            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12238        );
12239    });
12240
12241    fn edit_first_char_of_every_line(text: &str) -> String {
12242        text.split('\n')
12243            .map(|line| format!("X{}", &line[1..]))
12244            .collect::<Vec<_>>()
12245            .join("\n")
12246    }
12247}
12248
12249#[gpui::test]
12250async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12251    init_test(cx, |_| {});
12252
12253    let cols = 4;
12254    let rows = 10;
12255    let sample_text_1 = sample_text(rows, cols, 'a');
12256    assert_eq!(
12257        sample_text_1,
12258        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12259    );
12260    let sample_text_2 = sample_text(rows, cols, 'l');
12261    assert_eq!(
12262        sample_text_2,
12263        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12264    );
12265    let sample_text_3 = sample_text(rows, cols, 'v');
12266    assert_eq!(
12267        sample_text_3,
12268        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12269    );
12270
12271    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
12272    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
12273    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
12274
12275    let multi_buffer = cx.new_model(|cx| {
12276        let mut multibuffer = MultiBuffer::new(ReadWrite);
12277        multibuffer.push_excerpts(
12278            buffer_1.clone(),
12279            [
12280                ExcerptRange {
12281                    context: Point::new(0, 0)..Point::new(3, 0),
12282                    primary: None,
12283                },
12284                ExcerptRange {
12285                    context: Point::new(5, 0)..Point::new(7, 0),
12286                    primary: None,
12287                },
12288                ExcerptRange {
12289                    context: Point::new(9, 0)..Point::new(10, 4),
12290                    primary: None,
12291                },
12292            ],
12293            cx,
12294        );
12295        multibuffer.push_excerpts(
12296            buffer_2.clone(),
12297            [
12298                ExcerptRange {
12299                    context: Point::new(0, 0)..Point::new(3, 0),
12300                    primary: None,
12301                },
12302                ExcerptRange {
12303                    context: Point::new(5, 0)..Point::new(7, 0),
12304                    primary: None,
12305                },
12306                ExcerptRange {
12307                    context: Point::new(9, 0)..Point::new(10, 4),
12308                    primary: None,
12309                },
12310            ],
12311            cx,
12312        );
12313        multibuffer.push_excerpts(
12314            buffer_3.clone(),
12315            [
12316                ExcerptRange {
12317                    context: Point::new(0, 0)..Point::new(3, 0),
12318                    primary: None,
12319                },
12320                ExcerptRange {
12321                    context: Point::new(5, 0)..Point::new(7, 0),
12322                    primary: None,
12323                },
12324                ExcerptRange {
12325                    context: Point::new(9, 0)..Point::new(10, 4),
12326                    primary: None,
12327                },
12328            ],
12329            cx,
12330        );
12331        multibuffer
12332    });
12333
12334    let fs = FakeFs::new(cx.executor());
12335    fs.insert_tree(
12336        "/a",
12337        json!({
12338            "main.rs": sample_text_1,
12339            "other.rs": sample_text_2,
12340            "lib.rs": sample_text_3,
12341        }),
12342    )
12343    .await;
12344    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12345    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
12346    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12347    let multi_buffer_editor = cx.new_view(|cx| {
12348        Editor::new(
12349            EditorMode::Full,
12350            multi_buffer,
12351            Some(project.clone()),
12352            true,
12353            cx,
12354        )
12355    });
12356    let multibuffer_item_id = workspace
12357        .update(cx, |workspace, cx| {
12358            assert!(
12359                workspace.active_item(cx).is_none(),
12360                "active item should be None before the first item is added"
12361            );
12362            workspace.add_item_to_active_pane(
12363                Box::new(multi_buffer_editor.clone()),
12364                None,
12365                true,
12366                cx,
12367            );
12368            let active_item = workspace
12369                .active_item(cx)
12370                .expect("should have an active item after adding the multi buffer");
12371            assert!(
12372                !active_item.is_singleton(cx),
12373                "A multi buffer was expected to active after adding"
12374            );
12375            active_item.item_id()
12376        })
12377        .unwrap();
12378    cx.executor().run_until_parked();
12379
12380    multi_buffer_editor.update(cx, |editor, cx| {
12381        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
12382        editor.open_excerpts(&OpenExcerpts, cx);
12383    });
12384    cx.executor().run_until_parked();
12385    let first_item_id = workspace
12386        .update(cx, |workspace, cx| {
12387            let active_item = workspace
12388                .active_item(cx)
12389                .expect("should have an active item after navigating into the 1st buffer");
12390            let first_item_id = active_item.item_id();
12391            assert_ne!(
12392                first_item_id, multibuffer_item_id,
12393                "Should navigate into the 1st buffer and activate it"
12394            );
12395            assert!(
12396                active_item.is_singleton(cx),
12397                "New active item should be a singleton buffer"
12398            );
12399            assert_eq!(
12400                active_item
12401                    .act_as::<Editor>(cx)
12402                    .expect("should have navigated into an editor for the 1st buffer")
12403                    .read(cx)
12404                    .text(cx),
12405                sample_text_1
12406            );
12407
12408            workspace
12409                .go_back(workspace.active_pane().downgrade(), cx)
12410                .detach_and_log_err(cx);
12411
12412            first_item_id
12413        })
12414        .unwrap();
12415    cx.executor().run_until_parked();
12416    workspace
12417        .update(cx, |workspace, cx| {
12418            let active_item = workspace
12419                .active_item(cx)
12420                .expect("should have an active item after navigating back");
12421            assert_eq!(
12422                active_item.item_id(),
12423                multibuffer_item_id,
12424                "Should navigate back to the multi buffer"
12425            );
12426            assert!(!active_item.is_singleton(cx));
12427        })
12428        .unwrap();
12429
12430    multi_buffer_editor.update(cx, |editor, cx| {
12431        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12432            s.select_ranges(Some(39..40))
12433        });
12434        editor.open_excerpts(&OpenExcerpts, cx);
12435    });
12436    cx.executor().run_until_parked();
12437    let second_item_id = workspace
12438        .update(cx, |workspace, cx| {
12439            let active_item = workspace
12440                .active_item(cx)
12441                .expect("should have an active item after navigating into the 2nd buffer");
12442            let second_item_id = active_item.item_id();
12443            assert_ne!(
12444                second_item_id, multibuffer_item_id,
12445                "Should navigate away from the multibuffer"
12446            );
12447            assert_ne!(
12448                second_item_id, first_item_id,
12449                "Should navigate into the 2nd buffer and activate it"
12450            );
12451            assert!(
12452                active_item.is_singleton(cx),
12453                "New active item should be a singleton buffer"
12454            );
12455            assert_eq!(
12456                active_item
12457                    .act_as::<Editor>(cx)
12458                    .expect("should have navigated into an editor")
12459                    .read(cx)
12460                    .text(cx),
12461                sample_text_2
12462            );
12463
12464            workspace
12465                .go_back(workspace.active_pane().downgrade(), cx)
12466                .detach_and_log_err(cx);
12467
12468            second_item_id
12469        })
12470        .unwrap();
12471    cx.executor().run_until_parked();
12472    workspace
12473        .update(cx, |workspace, cx| {
12474            let active_item = workspace
12475                .active_item(cx)
12476                .expect("should have an active item after navigating back from the 2nd buffer");
12477            assert_eq!(
12478                active_item.item_id(),
12479                multibuffer_item_id,
12480                "Should navigate back from the 2nd buffer to the multi buffer"
12481            );
12482            assert!(!active_item.is_singleton(cx));
12483        })
12484        .unwrap();
12485
12486    multi_buffer_editor.update(cx, |editor, cx| {
12487        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12488            s.select_ranges(Some(70..70))
12489        });
12490        editor.open_excerpts(&OpenExcerpts, cx);
12491    });
12492    cx.executor().run_until_parked();
12493    workspace
12494        .update(cx, |workspace, cx| {
12495            let active_item = workspace
12496                .active_item(cx)
12497                .expect("should have an active item after navigating into the 3rd buffer");
12498            let third_item_id = active_item.item_id();
12499            assert_ne!(
12500                third_item_id, multibuffer_item_id,
12501                "Should navigate into the 3rd buffer and activate it"
12502            );
12503            assert_ne!(third_item_id, first_item_id);
12504            assert_ne!(third_item_id, second_item_id);
12505            assert!(
12506                active_item.is_singleton(cx),
12507                "New active item should be a singleton buffer"
12508            );
12509            assert_eq!(
12510                active_item
12511                    .act_as::<Editor>(cx)
12512                    .expect("should have navigated into an editor")
12513                    .read(cx)
12514                    .text(cx),
12515                sample_text_3
12516            );
12517
12518            workspace
12519                .go_back(workspace.active_pane().downgrade(), cx)
12520                .detach_and_log_err(cx);
12521        })
12522        .unwrap();
12523    cx.executor().run_until_parked();
12524    workspace
12525        .update(cx, |workspace, cx| {
12526            let active_item = workspace
12527                .active_item(cx)
12528                .expect("should have an active item after navigating back from the 3rd buffer");
12529            assert_eq!(
12530                active_item.item_id(),
12531                multibuffer_item_id,
12532                "Should navigate back from the 3rd buffer to the multi buffer"
12533            );
12534            assert!(!active_item.is_singleton(cx));
12535        })
12536        .unwrap();
12537}
12538
12539#[gpui::test]
12540async fn test_toggle_selected_diff_hunks(
12541    executor: BackgroundExecutor,
12542    cx: &mut gpui::TestAppContext,
12543) {
12544    init_test(cx, |_| {});
12545
12546    let mut cx = EditorTestContext::new(cx).await;
12547
12548    let diff_base = r#"
12549        use some::mod;
12550
12551        const A: u32 = 42;
12552
12553        fn main() {
12554            println!("hello");
12555
12556            println!("world");
12557        }
12558        "#
12559    .unindent();
12560
12561    cx.set_state(
12562        &r#"
12563        use some::modified;
12564
12565        ˇ
12566        fn main() {
12567            println!("hello there");
12568
12569            println!("around the");
12570            println!("world");
12571        }
12572        "#
12573        .unindent(),
12574    );
12575
12576    cx.set_diff_base(&diff_base);
12577    executor.run_until_parked();
12578
12579    cx.update_editor(|editor, cx| {
12580        editor.go_to_next_hunk(&GoToHunk, cx);
12581        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, cx);
12582    });
12583    executor.run_until_parked();
12584    cx.assert_state_with_diff(
12585        r#"
12586          use some::modified;
12587
12588
12589          fn main() {
12590        -     println!("hello");
12591        + ˇ    println!("hello there");
12592
12593              println!("around the");
12594              println!("world");
12595          }
12596        "#
12597        .unindent(),
12598    );
12599
12600    cx.update_editor(|editor, cx| {
12601        for _ in 0..2 {
12602            editor.go_to_next_hunk(&GoToHunk, cx);
12603            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, cx);
12604        }
12605    });
12606    executor.run_until_parked();
12607    cx.assert_state_with_diff(
12608        r#"
12609        - use some::mod;
12610        + ˇuse some::modified;
12611
12612
12613          fn main() {
12614        -     println!("hello");
12615        +     println!("hello there");
12616
12617        +     println!("around the");
12618              println!("world");
12619          }
12620        "#
12621        .unindent(),
12622    );
12623
12624    cx.update_editor(|editor, cx| {
12625        editor.go_to_next_hunk(&GoToHunk, cx);
12626        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, cx);
12627    });
12628    executor.run_until_parked();
12629    cx.assert_state_with_diff(
12630        r#"
12631        - use some::mod;
12632        + use some::modified;
12633
12634        - const A: u32 = 42;
12635          ˇ
12636          fn main() {
12637        -     println!("hello");
12638        +     println!("hello there");
12639
12640        +     println!("around the");
12641              println!("world");
12642          }
12643        "#
12644        .unindent(),
12645    );
12646
12647    cx.update_editor(|editor, cx| {
12648        editor.cancel(&Cancel, cx);
12649    });
12650
12651    cx.assert_state_with_diff(
12652        r#"
12653          use some::modified;
12654
12655          ˇ
12656          fn main() {
12657              println!("hello there");
12658
12659              println!("around the");
12660              println!("world");
12661          }
12662        "#
12663        .unindent(),
12664    );
12665}
12666
12667#[gpui::test]
12668async fn test_diff_base_change_with_expanded_diff_hunks(
12669    executor: BackgroundExecutor,
12670    cx: &mut gpui::TestAppContext,
12671) {
12672    init_test(cx, |_| {});
12673
12674    let mut cx = EditorTestContext::new(cx).await;
12675
12676    let diff_base = r#"
12677        use some::mod1;
12678        use some::mod2;
12679
12680        const A: u32 = 42;
12681        const B: u32 = 42;
12682        const C: u32 = 42;
12683
12684        fn main() {
12685            println!("hello");
12686
12687            println!("world");
12688        }
12689        "#
12690    .unindent();
12691
12692    cx.set_state(
12693        &r#"
12694        use some::mod2;
12695
12696        const A: u32 = 42;
12697        const C: u32 = 42;
12698
12699        fn main(ˇ) {
12700            //println!("hello");
12701
12702            println!("world");
12703            //
12704            //
12705        }
12706        "#
12707        .unindent(),
12708    );
12709
12710    cx.set_diff_base(&diff_base);
12711    executor.run_until_parked();
12712
12713    cx.update_editor(|editor, cx| {
12714        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
12715    });
12716    executor.run_until_parked();
12717    cx.assert_state_with_diff(
12718        r#"
12719        - use some::mod1;
12720          use some::mod2;
12721
12722          const A: u32 = 42;
12723        - const B: u32 = 42;
12724          const C: u32 = 42;
12725
12726          fn main(ˇ) {
12727        -     println!("hello");
12728        +     //println!("hello");
12729
12730              println!("world");
12731        +     //
12732        +     //
12733          }
12734        "#
12735        .unindent(),
12736    );
12737
12738    cx.set_diff_base("new diff base!");
12739    executor.run_until_parked();
12740    cx.assert_state_with_diff(
12741        r#"
12742          use some::mod2;
12743
12744          const A: u32 = 42;
12745          const C: u32 = 42;
12746
12747          fn main(ˇ) {
12748              //println!("hello");
12749
12750              println!("world");
12751              //
12752              //
12753          }
12754        "#
12755        .unindent(),
12756    );
12757
12758    cx.update_editor(|editor, cx| {
12759        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
12760    });
12761    executor.run_until_parked();
12762    cx.assert_state_with_diff(
12763        r#"
12764        - new diff base!
12765        + use some::mod2;
12766        +
12767        + const A: u32 = 42;
12768        + const C: u32 = 42;
12769        +
12770        + fn main(ˇ) {
12771        +     //println!("hello");
12772        +
12773        +     println!("world");
12774        +     //
12775        +     //
12776        + }
12777        "#
12778        .unindent(),
12779    );
12780}
12781
12782#[gpui::test]
12783async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12784    init_test(cx, |_| {});
12785
12786    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12787    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12788    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12789    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12790    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12791    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12792
12793    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12794    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12795    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12796
12797    let multi_buffer = cx.new_model(|cx| {
12798        let mut multibuffer = MultiBuffer::new(ReadWrite);
12799        multibuffer.push_excerpts(
12800            buffer_1.clone(),
12801            [
12802                ExcerptRange {
12803                    context: Point::new(0, 0)..Point::new(3, 0),
12804                    primary: None,
12805                },
12806                ExcerptRange {
12807                    context: Point::new(5, 0)..Point::new(7, 0),
12808                    primary: None,
12809                },
12810                ExcerptRange {
12811                    context: Point::new(9, 0)..Point::new(10, 3),
12812                    primary: None,
12813                },
12814            ],
12815            cx,
12816        );
12817        multibuffer.push_excerpts(
12818            buffer_2.clone(),
12819            [
12820                ExcerptRange {
12821                    context: Point::new(0, 0)..Point::new(3, 0),
12822                    primary: None,
12823                },
12824                ExcerptRange {
12825                    context: Point::new(5, 0)..Point::new(7, 0),
12826                    primary: None,
12827                },
12828                ExcerptRange {
12829                    context: Point::new(9, 0)..Point::new(10, 3),
12830                    primary: None,
12831                },
12832            ],
12833            cx,
12834        );
12835        multibuffer.push_excerpts(
12836            buffer_3.clone(),
12837            [
12838                ExcerptRange {
12839                    context: Point::new(0, 0)..Point::new(3, 0),
12840                    primary: None,
12841                },
12842                ExcerptRange {
12843                    context: Point::new(5, 0)..Point::new(7, 0),
12844                    primary: None,
12845                },
12846                ExcerptRange {
12847                    context: Point::new(9, 0)..Point::new(10, 3),
12848                    primary: None,
12849                },
12850            ],
12851            cx,
12852        );
12853        multibuffer
12854    });
12855
12856    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12857    editor
12858        .update(cx, |editor, cx| {
12859            for (buffer, diff_base) in [
12860                (buffer_1.clone(), file_1_old),
12861                (buffer_2.clone(), file_2_old),
12862                (buffer_3.clone(), file_3_old),
12863            ] {
12864                let change_set = cx.new_model(|cx| {
12865                    BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
12866                });
12867                editor
12868                    .buffer
12869                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12870            }
12871        })
12872        .unwrap();
12873
12874    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12875    cx.run_until_parked();
12876
12877    cx.assert_editor_state(
12878        &"
12879            ˇaaa
12880            ccc
12881            ddd
12882
12883            ggg
12884            hhh
12885
12886
12887            lll
12888            mmm
12889            NNN
12890
12891            qqq
12892            rrr
12893
12894            uuu
12895            111
12896            222
12897            333
12898
12899            666
12900            777
12901
12902            000
12903            !!!"
12904        .unindent(),
12905    );
12906
12907    cx.update_editor(|editor, cx| {
12908        editor.select_all(&SelectAll, cx);
12909        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, cx);
12910    });
12911    cx.executor().run_until_parked();
12912
12913    cx.assert_state_with_diff(
12914        "
12915            «aaa
12916          - bbb
12917            ccc
12918            ddd
12919
12920            ggg
12921            hhh
12922
12923
12924            lll
12925            mmm
12926          - nnn
12927          + NNN
12928
12929            qqq
12930            rrr
12931
12932            uuu
12933            111
12934            222
12935            333
12936
12937          + 666
12938            777
12939
12940            000
12941            !!!ˇ»"
12942            .unindent(),
12943    );
12944}
12945
12946#[gpui::test]
12947async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12948    init_test(cx, |_| {});
12949
12950    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12951    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12952
12953    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12954    let multi_buffer = cx.new_model(|cx| {
12955        let mut multibuffer = MultiBuffer::new(ReadWrite);
12956        multibuffer.push_excerpts(
12957            buffer.clone(),
12958            [
12959                ExcerptRange {
12960                    context: Point::new(0, 0)..Point::new(2, 0),
12961                    primary: None,
12962                },
12963                ExcerptRange {
12964                    context: Point::new(5, 0)..Point::new(7, 0),
12965                    primary: None,
12966                },
12967            ],
12968            cx,
12969        );
12970        multibuffer
12971    });
12972
12973    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12974    editor
12975        .update(cx, |editor, cx| {
12976            let change_set = cx
12977                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
12978            editor
12979                .buffer
12980                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
12981        })
12982        .unwrap();
12983
12984    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12985    cx.run_until_parked();
12986
12987    cx.update_editor(|editor, cx| editor.expand_all_diff_hunks(&Default::default(), cx));
12988    cx.executor().run_until_parked();
12989
12990    cx.assert_state_with_diff(
12991        "
12992            ˇaaa
12993          - bbb
12994          + BBB
12995
12996          + EEE
12997            fff
12998        "
12999        .unindent(),
13000    );
13001}
13002
13003#[gpui::test]
13004async fn test_edits_around_expanded_insertion_hunks(
13005    executor: BackgroundExecutor,
13006    cx: &mut gpui::TestAppContext,
13007) {
13008    init_test(cx, |_| {});
13009
13010    let mut cx = EditorTestContext::new(cx).await;
13011
13012    let diff_base = r#"
13013        use some::mod1;
13014        use some::mod2;
13015
13016        const A: u32 = 42;
13017
13018        fn main() {
13019            println!("hello");
13020
13021            println!("world");
13022        }
13023        "#
13024    .unindent();
13025    executor.run_until_parked();
13026    cx.set_state(
13027        &r#"
13028        use some::mod1;
13029        use some::mod2;
13030
13031        const A: u32 = 42;
13032        const B: u32 = 42;
13033        const C: u32 = 42;
13034        ˇ
13035
13036        fn main() {
13037            println!("hello");
13038
13039            println!("world");
13040        }
13041        "#
13042        .unindent(),
13043    );
13044
13045    cx.set_diff_base(&diff_base);
13046    executor.run_until_parked();
13047
13048    cx.update_editor(|editor, cx| {
13049        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
13050    });
13051    executor.run_until_parked();
13052
13053    cx.assert_state_with_diff(
13054        r#"
13055        use some::mod1;
13056        use some::mod2;
13057
13058        const A: u32 = 42;
13059      + const B: u32 = 42;
13060      + const C: u32 = 42;
13061      + ˇ
13062
13063        fn main() {
13064            println!("hello");
13065
13066            println!("world");
13067        }
13068      "#
13069        .unindent(),
13070    );
13071
13072    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
13073    executor.run_until_parked();
13074
13075    cx.assert_state_with_diff(
13076        r#"
13077        use some::mod1;
13078        use some::mod2;
13079
13080        const A: u32 = 42;
13081      + const B: u32 = 42;
13082      + const C: u32 = 42;
13083      + const D: u32 = 42;
13084      + ˇ
13085
13086        fn main() {
13087            println!("hello");
13088
13089            println!("world");
13090        }
13091      "#
13092        .unindent(),
13093    );
13094
13095    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
13096    executor.run_until_parked();
13097
13098    cx.assert_state_with_diff(
13099        r#"
13100        use some::mod1;
13101        use some::mod2;
13102
13103        const A: u32 = 42;
13104      + const B: u32 = 42;
13105      + const C: u32 = 42;
13106      + const D: u32 = 42;
13107      + const E: u32 = 42;
13108      + ˇ
13109
13110        fn main() {
13111            println!("hello");
13112
13113            println!("world");
13114        }
13115      "#
13116        .unindent(),
13117    );
13118
13119    cx.update_editor(|editor, cx| {
13120        editor.delete_line(&DeleteLine, cx);
13121    });
13122    executor.run_until_parked();
13123
13124    cx.assert_state_with_diff(
13125        r#"
13126        use some::mod1;
13127        use some::mod2;
13128
13129        const A: u32 = 42;
13130      + const B: u32 = 42;
13131      + const C: u32 = 42;
13132      + const D: u32 = 42;
13133      + const E: u32 = 42;
13134        ˇ
13135        fn main() {
13136            println!("hello");
13137
13138            println!("world");
13139        }
13140      "#
13141        .unindent(),
13142    );
13143
13144    cx.update_editor(|editor, cx| {
13145        editor.move_up(&MoveUp, cx);
13146        editor.delete_line(&DeleteLine, cx);
13147        editor.move_up(&MoveUp, cx);
13148        editor.delete_line(&DeleteLine, cx);
13149        editor.move_up(&MoveUp, cx);
13150        editor.delete_line(&DeleteLine, cx);
13151    });
13152    executor.run_until_parked();
13153    cx.assert_state_with_diff(
13154        r#"
13155        use some::mod1;
13156        use some::mod2;
13157
13158        const A: u32 = 42;
13159      + const B: u32 = 42;
13160        ˇ
13161        fn main() {
13162            println!("hello");
13163
13164            println!("world");
13165        }
13166      "#
13167        .unindent(),
13168    );
13169
13170    cx.update_editor(|editor, cx| {
13171        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
13172        editor.delete_line(&DeleteLine, cx);
13173    });
13174    executor.run_until_parked();
13175    cx.assert_state_with_diff(
13176        r#"
13177        ˇ
13178        fn main() {
13179            println!("hello");
13180
13181            println!("world");
13182        }
13183      "#
13184        .unindent(),
13185    );
13186}
13187
13188#[gpui::test]
13189async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13190    init_test(cx, |_| {});
13191
13192    let mut cx = EditorTestContext::new(cx).await;
13193    cx.set_diff_base(indoc! { "
13194        one
13195        two
13196        three
13197        four
13198        five
13199        "
13200    });
13201    cx.set_state(indoc! { "
13202        one
13203        ˇthree
13204        five
13205    "});
13206    cx.run_until_parked();
13207    cx.update_editor(|editor, cx| {
13208        editor.toggle_selected_diff_hunks(&Default::default(), cx);
13209    });
13210    cx.assert_state_with_diff(
13211        indoc! { "
13212        one
13213      - two
13214        ˇthree
13215      - four
13216        five
13217    "}
13218        .to_string(),
13219    );
13220    cx.update_editor(|editor, cx| {
13221        editor.toggle_selected_diff_hunks(&Default::default(), cx);
13222    });
13223
13224    cx.assert_state_with_diff(
13225        indoc! { "
13226        one
13227        ˇthree
13228        five
13229    "}
13230        .to_string(),
13231    );
13232}
13233
13234#[gpui::test]
13235async fn test_edits_around_expanded_deletion_hunks(
13236    executor: BackgroundExecutor,
13237    cx: &mut gpui::TestAppContext,
13238) {
13239    init_test(cx, |_| {});
13240
13241    let mut cx = EditorTestContext::new(cx).await;
13242
13243    let diff_base = r#"
13244        use some::mod1;
13245        use some::mod2;
13246
13247        const A: u32 = 42;
13248        const B: u32 = 42;
13249        const C: u32 = 42;
13250
13251
13252        fn main() {
13253            println!("hello");
13254
13255            println!("world");
13256        }
13257    "#
13258    .unindent();
13259    executor.run_until_parked();
13260    cx.set_state(
13261        &r#"
13262        use some::mod1;
13263        use some::mod2;
13264
13265        ˇconst B: u32 = 42;
13266        const C: u32 = 42;
13267
13268
13269        fn main() {
13270            println!("hello");
13271
13272            println!("world");
13273        }
13274        "#
13275        .unindent(),
13276    );
13277
13278    cx.set_diff_base(&diff_base);
13279    executor.run_until_parked();
13280
13281    cx.update_editor(|editor, cx| {
13282        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
13283    });
13284    executor.run_until_parked();
13285
13286    cx.assert_state_with_diff(
13287        r#"
13288        use some::mod1;
13289        use some::mod2;
13290
13291      - const A: u32 = 42;
13292        ˇconst B: u32 = 42;
13293        const C: u32 = 42;
13294
13295
13296        fn main() {
13297            println!("hello");
13298
13299            println!("world");
13300        }
13301      "#
13302        .unindent(),
13303    );
13304
13305    cx.update_editor(|editor, cx| {
13306        editor.delete_line(&DeleteLine, cx);
13307    });
13308    executor.run_until_parked();
13309    cx.assert_state_with_diff(
13310        r#"
13311        use some::mod1;
13312        use some::mod2;
13313
13314      - const A: u32 = 42;
13315      - const B: u32 = 42;
13316        ˇconst C: u32 = 42;
13317
13318
13319        fn main() {
13320            println!("hello");
13321
13322            println!("world");
13323        }
13324      "#
13325        .unindent(),
13326    );
13327
13328    cx.update_editor(|editor, cx| {
13329        editor.delete_line(&DeleteLine, cx);
13330    });
13331    executor.run_until_parked();
13332    cx.assert_state_with_diff(
13333        r#"
13334        use some::mod1;
13335        use some::mod2;
13336
13337      - const A: u32 = 42;
13338      - const B: u32 = 42;
13339      - const C: u32 = 42;
13340        ˇ
13341
13342        fn main() {
13343            println!("hello");
13344
13345            println!("world");
13346        }
13347      "#
13348        .unindent(),
13349    );
13350
13351    cx.update_editor(|editor, cx| {
13352        editor.handle_input("replacement", cx);
13353    });
13354    executor.run_until_parked();
13355    cx.assert_state_with_diff(
13356        r#"
13357        use some::mod1;
13358        use some::mod2;
13359
13360      - const A: u32 = 42;
13361      - const B: u32 = 42;
13362      - const C: u32 = 42;
13363      -
13364      + replacementˇ
13365
13366        fn main() {
13367            println!("hello");
13368
13369            println!("world");
13370        }
13371      "#
13372        .unindent(),
13373    );
13374}
13375
13376#[gpui::test]
13377async fn test_backspace_after_deletion_hunk(
13378    executor: BackgroundExecutor,
13379    cx: &mut gpui::TestAppContext,
13380) {
13381    init_test(cx, |_| {});
13382
13383    let mut cx = EditorTestContext::new(cx).await;
13384
13385    let base_text = r#"
13386        one
13387        two
13388        three
13389        four
13390        five
13391    "#
13392    .unindent();
13393    executor.run_until_parked();
13394    cx.set_state(
13395        &r#"
13396        one
13397        two
13398        fˇour
13399        five
13400        "#
13401        .unindent(),
13402    );
13403
13404    cx.set_diff_base(&base_text);
13405    executor.run_until_parked();
13406
13407    cx.update_editor(|editor, cx| {
13408        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
13409    });
13410    executor.run_until_parked();
13411
13412    cx.assert_state_with_diff(
13413        r#"
13414          one
13415          two
13416        - three
13417          fˇour
13418          five
13419        "#
13420        .unindent(),
13421    );
13422
13423    cx.update_editor(|editor, cx| {
13424        editor.backspace(&Backspace, cx);
13425        editor.backspace(&Backspace, cx);
13426    });
13427    executor.run_until_parked();
13428    cx.assert_state_with_diff(
13429        r#"
13430          one
13431          two
13432        - threeˇ
13433        - four
13434        + our
13435          five
13436        "#
13437        .unindent(),
13438    );
13439}
13440
13441#[gpui::test]
13442async fn test_edit_after_expanded_modification_hunk(
13443    executor: BackgroundExecutor,
13444    cx: &mut gpui::TestAppContext,
13445) {
13446    init_test(cx, |_| {});
13447
13448    let mut cx = EditorTestContext::new(cx).await;
13449
13450    let diff_base = r#"
13451        use some::mod1;
13452        use some::mod2;
13453
13454        const A: u32 = 42;
13455        const B: u32 = 42;
13456        const C: u32 = 42;
13457        const D: u32 = 42;
13458
13459
13460        fn main() {
13461            println!("hello");
13462
13463            println!("world");
13464        }"#
13465    .unindent();
13466
13467    cx.set_state(
13468        &r#"
13469        use some::mod1;
13470        use some::mod2;
13471
13472        const A: u32 = 42;
13473        const B: u32 = 42;
13474        const C: u32 = 43ˇ
13475        const D: u32 = 42;
13476
13477
13478        fn main() {
13479            println!("hello");
13480
13481            println!("world");
13482        }"#
13483        .unindent(),
13484    );
13485
13486    cx.set_diff_base(&diff_base);
13487    executor.run_until_parked();
13488    cx.update_editor(|editor, cx| {
13489        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, cx);
13490    });
13491    executor.run_until_parked();
13492
13493    cx.assert_state_with_diff(
13494        r#"
13495        use some::mod1;
13496        use some::mod2;
13497
13498        const A: u32 = 42;
13499        const B: u32 = 42;
13500      - const C: u32 = 42;
13501      + const C: u32 = 43ˇ
13502        const D: u32 = 42;
13503
13504
13505        fn main() {
13506            println!("hello");
13507
13508            println!("world");
13509        }"#
13510        .unindent(),
13511    );
13512
13513    cx.update_editor(|editor, cx| {
13514        editor.handle_input("\nnew_line\n", cx);
13515    });
13516    executor.run_until_parked();
13517
13518    cx.assert_state_with_diff(
13519        r#"
13520        use some::mod1;
13521        use some::mod2;
13522
13523        const A: u32 = 42;
13524        const B: u32 = 42;
13525      - const C: u32 = 42;
13526      + const C: u32 = 43
13527      + new_line
13528      + ˇ
13529        const D: u32 = 42;
13530
13531
13532        fn main() {
13533            println!("hello");
13534
13535            println!("world");
13536        }"#
13537        .unindent(),
13538    );
13539}
13540
13541async fn setup_indent_guides_editor(
13542    text: &str,
13543    cx: &mut gpui::TestAppContext,
13544) -> (BufferId, EditorTestContext) {
13545    init_test(cx, |_| {});
13546
13547    let mut cx = EditorTestContext::new(cx).await;
13548
13549    let buffer_id = cx.update_editor(|editor, cx| {
13550        editor.set_text(text, cx);
13551        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13552
13553        buffer_ids[0]
13554    });
13555
13556    (buffer_id, cx)
13557}
13558
13559fn assert_indent_guides(
13560    range: Range<u32>,
13561    expected: Vec<IndentGuide>,
13562    active_indices: Option<Vec<usize>>,
13563    cx: &mut EditorTestContext,
13564) {
13565    let indent_guides = cx.update_editor(|editor, cx| {
13566        let snapshot = editor.snapshot(cx).display_snapshot;
13567        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13568            editor,
13569            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13570            true,
13571            &snapshot,
13572            cx,
13573        );
13574
13575        indent_guides.sort_by(|a, b| {
13576            a.depth.cmp(&b.depth).then(
13577                a.start_row
13578                    .cmp(&b.start_row)
13579                    .then(a.end_row.cmp(&b.end_row)),
13580            )
13581        });
13582        indent_guides
13583    });
13584
13585    if let Some(expected) = active_indices {
13586        let active_indices = cx.update_editor(|editor, cx| {
13587            let snapshot = editor.snapshot(cx).display_snapshot;
13588            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13589        });
13590
13591        assert_eq!(
13592            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13593            expected,
13594            "Active indent guide indices do not match"
13595        );
13596    }
13597
13598    assert_eq!(indent_guides, expected, "Indent guides do not match");
13599}
13600
13601fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13602    IndentGuide {
13603        buffer_id,
13604        start_row: MultiBufferRow(start_row),
13605        end_row: MultiBufferRow(end_row),
13606        depth,
13607        tab_size: 4,
13608        settings: IndentGuideSettings {
13609            enabled: true,
13610            line_width: 1,
13611            active_line_width: 1,
13612            ..Default::default()
13613        },
13614    }
13615}
13616
13617#[gpui::test]
13618async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13619    let (buffer_id, mut cx) = setup_indent_guides_editor(
13620        &"
13621    fn main() {
13622        let a = 1;
13623    }"
13624        .unindent(),
13625        cx,
13626    )
13627    .await;
13628
13629    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13630}
13631
13632#[gpui::test]
13633async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13634    let (buffer_id, mut cx) = setup_indent_guides_editor(
13635        &"
13636    fn main() {
13637        let a = 1;
13638        let b = 2;
13639    }"
13640        .unindent(),
13641        cx,
13642    )
13643    .await;
13644
13645    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13646}
13647
13648#[gpui::test]
13649async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13650    let (buffer_id, mut cx) = setup_indent_guides_editor(
13651        &"
13652    fn main() {
13653        let a = 1;
13654        if a == 3 {
13655            let b = 2;
13656        } else {
13657            let c = 3;
13658        }
13659    }"
13660        .unindent(),
13661        cx,
13662    )
13663    .await;
13664
13665    assert_indent_guides(
13666        0..8,
13667        vec![
13668            indent_guide(buffer_id, 1, 6, 0),
13669            indent_guide(buffer_id, 3, 3, 1),
13670            indent_guide(buffer_id, 5, 5, 1),
13671        ],
13672        None,
13673        &mut cx,
13674    );
13675}
13676
13677#[gpui::test]
13678async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13679    let (buffer_id, mut cx) = setup_indent_guides_editor(
13680        &"
13681    fn main() {
13682        let a = 1;
13683            let b = 2;
13684        let c = 3;
13685    }"
13686        .unindent(),
13687        cx,
13688    )
13689    .await;
13690
13691    assert_indent_guides(
13692        0..5,
13693        vec![
13694            indent_guide(buffer_id, 1, 3, 0),
13695            indent_guide(buffer_id, 2, 2, 1),
13696        ],
13697        None,
13698        &mut cx,
13699    );
13700}
13701
13702#[gpui::test]
13703async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13704    let (buffer_id, mut cx) = setup_indent_guides_editor(
13705        &"
13706        fn main() {
13707            let a = 1;
13708
13709            let c = 3;
13710        }"
13711        .unindent(),
13712        cx,
13713    )
13714    .await;
13715
13716    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13717}
13718
13719#[gpui::test]
13720async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13721    let (buffer_id, mut cx) = setup_indent_guides_editor(
13722        &"
13723        fn main() {
13724            let a = 1;
13725
13726            let c = 3;
13727
13728            if a == 3 {
13729                let b = 2;
13730            } else {
13731                let c = 3;
13732            }
13733        }"
13734        .unindent(),
13735        cx,
13736    )
13737    .await;
13738
13739    assert_indent_guides(
13740        0..11,
13741        vec![
13742            indent_guide(buffer_id, 1, 9, 0),
13743            indent_guide(buffer_id, 6, 6, 1),
13744            indent_guide(buffer_id, 8, 8, 1),
13745        ],
13746        None,
13747        &mut cx,
13748    );
13749}
13750
13751#[gpui::test]
13752async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13753    let (buffer_id, mut cx) = setup_indent_guides_editor(
13754        &"
13755        fn main() {
13756            let a = 1;
13757
13758            let c = 3;
13759
13760            if a == 3 {
13761                let b = 2;
13762            } else {
13763                let c = 3;
13764            }
13765        }"
13766        .unindent(),
13767        cx,
13768    )
13769    .await;
13770
13771    assert_indent_guides(
13772        1..11,
13773        vec![
13774            indent_guide(buffer_id, 1, 9, 0),
13775            indent_guide(buffer_id, 6, 6, 1),
13776            indent_guide(buffer_id, 8, 8, 1),
13777        ],
13778        None,
13779        &mut cx,
13780    );
13781}
13782
13783#[gpui::test]
13784async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13785    let (buffer_id, mut cx) = setup_indent_guides_editor(
13786        &"
13787        fn main() {
13788            let a = 1;
13789
13790            let c = 3;
13791
13792            if a == 3 {
13793                let b = 2;
13794            } else {
13795                let c = 3;
13796            }
13797        }"
13798        .unindent(),
13799        cx,
13800    )
13801    .await;
13802
13803    assert_indent_guides(
13804        1..10,
13805        vec![
13806            indent_guide(buffer_id, 1, 9, 0),
13807            indent_guide(buffer_id, 6, 6, 1),
13808            indent_guide(buffer_id, 8, 8, 1),
13809        ],
13810        None,
13811        &mut cx,
13812    );
13813}
13814
13815#[gpui::test]
13816async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13817    let (buffer_id, mut cx) = setup_indent_guides_editor(
13818        &"
13819        block1
13820            block2
13821                block3
13822                    block4
13823            block2
13824        block1
13825        block1"
13826            .unindent(),
13827        cx,
13828    )
13829    .await;
13830
13831    assert_indent_guides(
13832        1..10,
13833        vec![
13834            indent_guide(buffer_id, 1, 4, 0),
13835            indent_guide(buffer_id, 2, 3, 1),
13836            indent_guide(buffer_id, 3, 3, 2),
13837        ],
13838        None,
13839        &mut cx,
13840    );
13841}
13842
13843#[gpui::test]
13844async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13845    let (buffer_id, mut cx) = setup_indent_guides_editor(
13846        &"
13847        block1
13848            block2
13849                block3
13850
13851        block1
13852        block1"
13853            .unindent(),
13854        cx,
13855    )
13856    .await;
13857
13858    assert_indent_guides(
13859        0..6,
13860        vec![
13861            indent_guide(buffer_id, 1, 2, 0),
13862            indent_guide(buffer_id, 2, 2, 1),
13863        ],
13864        None,
13865        &mut cx,
13866    );
13867}
13868
13869#[gpui::test]
13870async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13871    let (buffer_id, mut cx) = setup_indent_guides_editor(
13872        &"
13873        block1
13874
13875
13876
13877            block2
13878        "
13879        .unindent(),
13880        cx,
13881    )
13882    .await;
13883
13884    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13885}
13886
13887#[gpui::test]
13888async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13889    let (buffer_id, mut cx) = setup_indent_guides_editor(
13890        &"
13891        def a:
13892        \tb = 3
13893        \tif True:
13894        \t\tc = 4
13895        \t\td = 5
13896        \tprint(b)
13897        "
13898        .unindent(),
13899        cx,
13900    )
13901    .await;
13902
13903    assert_indent_guides(
13904        0..6,
13905        vec![
13906            indent_guide(buffer_id, 1, 6, 0),
13907            indent_guide(buffer_id, 3, 4, 1),
13908        ],
13909        None,
13910        &mut cx,
13911    );
13912}
13913
13914#[gpui::test]
13915async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13916    let (buffer_id, mut cx) = setup_indent_guides_editor(
13917        &"
13918    fn main() {
13919        let a = 1;
13920    }"
13921        .unindent(),
13922        cx,
13923    )
13924    .await;
13925
13926    cx.update_editor(|editor, cx| {
13927        editor.change_selections(None, cx, |s| {
13928            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13929        });
13930    });
13931
13932    assert_indent_guides(
13933        0..3,
13934        vec![indent_guide(buffer_id, 1, 1, 0)],
13935        Some(vec![0]),
13936        &mut cx,
13937    );
13938}
13939
13940#[gpui::test]
13941async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13942    let (buffer_id, mut cx) = setup_indent_guides_editor(
13943        &"
13944    fn main() {
13945        if 1 == 2 {
13946            let a = 1;
13947        }
13948    }"
13949        .unindent(),
13950        cx,
13951    )
13952    .await;
13953
13954    cx.update_editor(|editor, cx| {
13955        editor.change_selections(None, cx, |s| {
13956            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13957        });
13958    });
13959
13960    assert_indent_guides(
13961        0..4,
13962        vec![
13963            indent_guide(buffer_id, 1, 3, 0),
13964            indent_guide(buffer_id, 2, 2, 1),
13965        ],
13966        Some(vec![1]),
13967        &mut cx,
13968    );
13969
13970    cx.update_editor(|editor, cx| {
13971        editor.change_selections(None, cx, |s| {
13972            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13973        });
13974    });
13975
13976    assert_indent_guides(
13977        0..4,
13978        vec![
13979            indent_guide(buffer_id, 1, 3, 0),
13980            indent_guide(buffer_id, 2, 2, 1),
13981        ],
13982        Some(vec![1]),
13983        &mut cx,
13984    );
13985
13986    cx.update_editor(|editor, cx| {
13987        editor.change_selections(None, cx, |s| {
13988            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13989        });
13990    });
13991
13992    assert_indent_guides(
13993        0..4,
13994        vec![
13995            indent_guide(buffer_id, 1, 3, 0),
13996            indent_guide(buffer_id, 2, 2, 1),
13997        ],
13998        Some(vec![0]),
13999        &mut cx,
14000    );
14001}
14002
14003#[gpui::test]
14004async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14005    let (buffer_id, mut cx) = setup_indent_guides_editor(
14006        &"
14007    fn main() {
14008        let a = 1;
14009
14010        let b = 2;
14011    }"
14012        .unindent(),
14013        cx,
14014    )
14015    .await;
14016
14017    cx.update_editor(|editor, cx| {
14018        editor.change_selections(None, cx, |s| {
14019            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14020        });
14021    });
14022
14023    assert_indent_guides(
14024        0..5,
14025        vec![indent_guide(buffer_id, 1, 3, 0)],
14026        Some(vec![0]),
14027        &mut cx,
14028    );
14029}
14030
14031#[gpui::test]
14032async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14033    let (buffer_id, mut cx) = setup_indent_guides_editor(
14034        &"
14035    def m:
14036        a = 1
14037        pass"
14038            .unindent(),
14039        cx,
14040    )
14041    .await;
14042
14043    cx.update_editor(|editor, cx| {
14044        editor.change_selections(None, cx, |s| {
14045            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14046        });
14047    });
14048
14049    assert_indent_guides(
14050        0..3,
14051        vec![indent_guide(buffer_id, 1, 2, 0)],
14052        Some(vec![0]),
14053        &mut cx,
14054    );
14055}
14056
14057#[gpui::test]
14058async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14059    init_test(cx, |_| {});
14060    let mut cx = EditorTestContext::new(cx).await;
14061    let text = indoc! {
14062        "
14063        impl A {
14064            fn b() {
14065                0;
14066                3;
14067                5;
14068                6;
14069                7;
14070            }
14071        }
14072        "
14073    };
14074    let base_text = indoc! {
14075        "
14076        impl A {
14077            fn b() {
14078                0;
14079                1;
14080                2;
14081                3;
14082                4;
14083            }
14084            fn c() {
14085                5;
14086                6;
14087                7;
14088            }
14089        }
14090        "
14091    };
14092
14093    cx.update_editor(|editor, cx| {
14094        editor.set_text(text, cx);
14095
14096        editor.buffer().update(cx, |multibuffer, cx| {
14097            let buffer = multibuffer.as_singleton().unwrap();
14098            let change_set = cx.new_model(|cx| {
14099                let mut change_set = BufferChangeSet::new(&buffer, cx);
14100                change_set.recalculate_diff_sync(
14101                    base_text.into(),
14102                    buffer.read(cx).text_snapshot(),
14103                    true,
14104                    cx,
14105                );
14106                change_set
14107            });
14108
14109            multibuffer.set_all_diff_hunks_expanded(cx);
14110            multibuffer.add_change_set(change_set, cx);
14111
14112            buffer.read(cx).remote_id()
14113        })
14114    });
14115
14116    cx.assert_state_with_diff(
14117        indoc! { "
14118          impl A {
14119              fn b() {
14120                  0;
14121        -         1;
14122        -         2;
14123                  3;
14124        -         4;
14125        -     }
14126        -     fn c() {
14127                  5;
14128                  6;
14129                  7;
14130              }
14131          }
14132          ˇ"
14133        }
14134        .to_string(),
14135    );
14136
14137    let mut actual_guides = cx.update_editor(|editor, cx| {
14138        editor
14139            .snapshot(cx)
14140            .buffer_snapshot
14141            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14142            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14143            .collect::<Vec<_>>()
14144    });
14145    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14146    assert_eq!(
14147        actual_guides,
14148        vec![
14149            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14150            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14151            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14152        ]
14153    );
14154}
14155
14156#[gpui::test]
14157fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14158    init_test(cx, |_| {});
14159
14160    let editor = cx.add_window(|cx| {
14161        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14162        build_editor(buffer, cx)
14163    });
14164
14165    let render_args = Arc::new(Mutex::new(None));
14166    let snapshot = editor
14167        .update(cx, |editor, cx| {
14168            let snapshot = editor.buffer().read(cx).snapshot(cx);
14169            let range =
14170                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14171
14172            struct RenderArgs {
14173                row: MultiBufferRow,
14174                folded: bool,
14175                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
14176            }
14177
14178            let crease = Crease::inline(
14179                range,
14180                FoldPlaceholder::test(),
14181                {
14182                    let toggle_callback = render_args.clone();
14183                    move |row, folded, callback, _cx| {
14184                        *toggle_callback.lock() = Some(RenderArgs {
14185                            row,
14186                            folded,
14187                            callback,
14188                        });
14189                        div()
14190                    }
14191                },
14192                |_row, _folded, _cx| div(),
14193            );
14194
14195            editor.insert_creases(Some(crease), cx);
14196            let snapshot = editor.snapshot(cx);
14197            let _div =
14198                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
14199            snapshot
14200        })
14201        .unwrap();
14202
14203    let render_args = render_args.lock().take().unwrap();
14204    assert_eq!(render_args.row, MultiBufferRow(1));
14205    assert!(!render_args.folded);
14206    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14207
14208    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
14209        .unwrap();
14210    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
14211    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14212
14213    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
14214        .unwrap();
14215    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
14216    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14217}
14218
14219#[gpui::test]
14220async fn test_input_text(cx: &mut gpui::TestAppContext) {
14221    init_test(cx, |_| {});
14222    let mut cx = EditorTestContext::new(cx).await;
14223
14224    cx.set_state(
14225        &r#"ˇone
14226        two
14227
14228        three
14229        fourˇ
14230        five
14231
14232        siˇx"#
14233            .unindent(),
14234    );
14235
14236    cx.dispatch_action(HandleInput(String::new()));
14237    cx.assert_editor_state(
14238        &r#"ˇone
14239        two
14240
14241        three
14242        fourˇ
14243        five
14244
14245        siˇx"#
14246            .unindent(),
14247    );
14248
14249    cx.dispatch_action(HandleInput("AAAA".to_string()));
14250    cx.assert_editor_state(
14251        &r#"AAAAˇone
14252        two
14253
14254        three
14255        fourAAAAˇ
14256        five
14257
14258        siAAAAˇx"#
14259            .unindent(),
14260    );
14261}
14262
14263#[gpui::test]
14264async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14265    init_test(cx, |_| {});
14266
14267    let mut cx = EditorTestContext::new(cx).await;
14268    cx.set_state(
14269        r#"let foo = 1;
14270let foo = 2;
14271let foo = 3;
14272let fooˇ = 4;
14273let foo = 5;
14274let foo = 6;
14275let foo = 7;
14276let foo = 8;
14277let foo = 9;
14278let foo = 10;
14279let foo = 11;
14280let foo = 12;
14281let foo = 13;
14282let foo = 14;
14283let foo = 15;"#,
14284    );
14285
14286    cx.update_editor(|e, cx| {
14287        assert_eq!(
14288            e.next_scroll_position,
14289            NextScrollCursorCenterTopBottom::Center,
14290            "Default next scroll direction is center",
14291        );
14292
14293        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14294        assert_eq!(
14295            e.next_scroll_position,
14296            NextScrollCursorCenterTopBottom::Top,
14297            "After center, next scroll direction should be top",
14298        );
14299
14300        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14301        assert_eq!(
14302            e.next_scroll_position,
14303            NextScrollCursorCenterTopBottom::Bottom,
14304            "After top, next scroll direction should be bottom",
14305        );
14306
14307        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14308        assert_eq!(
14309            e.next_scroll_position,
14310            NextScrollCursorCenterTopBottom::Center,
14311            "After bottom, scrolling should start over",
14312        );
14313
14314        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14315        assert_eq!(
14316            e.next_scroll_position,
14317            NextScrollCursorCenterTopBottom::Top,
14318            "Scrolling continues if retriggered fast enough"
14319        );
14320    });
14321
14322    cx.executor()
14323        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14324    cx.executor().run_until_parked();
14325    cx.update_editor(|e, _| {
14326        assert_eq!(
14327            e.next_scroll_position,
14328            NextScrollCursorCenterTopBottom::Center,
14329            "If scrolling is not triggered fast enough, it should reset"
14330        );
14331    });
14332}
14333
14334#[gpui::test]
14335async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14336    init_test(cx, |_| {});
14337    let mut cx = EditorLspTestContext::new_rust(
14338        lsp::ServerCapabilities {
14339            definition_provider: Some(lsp::OneOf::Left(true)),
14340            references_provider: Some(lsp::OneOf::Left(true)),
14341            ..lsp::ServerCapabilities::default()
14342        },
14343        cx,
14344    )
14345    .await;
14346
14347    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14348        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14349            move |params, _| async move {
14350                if empty_go_to_definition {
14351                    Ok(None)
14352                } else {
14353                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14354                        uri: params.text_document_position_params.text_document.uri,
14355                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14356                    })))
14357                }
14358            },
14359        );
14360        let references =
14361            cx.lsp
14362                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14363                    Ok(Some(vec![lsp::Location {
14364                        uri: params.text_document_position.text_document.uri,
14365                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14366                    }]))
14367                });
14368        (go_to_definition, references)
14369    };
14370
14371    cx.set_state(
14372        &r#"fn one() {
14373            let mut a = ˇtwo();
14374        }
14375
14376        fn two() {}"#
14377            .unindent(),
14378    );
14379    set_up_lsp_handlers(false, &mut cx);
14380    let navigated = cx
14381        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
14382        .await
14383        .expect("Failed to navigate to definition");
14384    assert_eq!(
14385        navigated,
14386        Navigated::Yes,
14387        "Should have navigated to definition from the GetDefinition response"
14388    );
14389    cx.assert_editor_state(
14390        &r#"fn one() {
14391            let mut a = two();
14392        }
14393
14394        fn «twoˇ»() {}"#
14395            .unindent(),
14396    );
14397
14398    let editors = cx.update_workspace(|workspace, cx| {
14399        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14400    });
14401    cx.update_editor(|_, test_editor_cx| {
14402        assert_eq!(
14403            editors.len(),
14404            1,
14405            "Initially, only one, test, editor should be open in the workspace"
14406        );
14407        assert_eq!(
14408            test_editor_cx.view(),
14409            editors.last().expect("Asserted len is 1")
14410        );
14411    });
14412
14413    set_up_lsp_handlers(true, &mut cx);
14414    let navigated = cx
14415        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
14416        .await
14417        .expect("Failed to navigate to lookup references");
14418    assert_eq!(
14419        navigated,
14420        Navigated::Yes,
14421        "Should have navigated to references as a fallback after empty GoToDefinition response"
14422    );
14423    // We should not change the selections in the existing file,
14424    // if opening another milti buffer with the references
14425    cx.assert_editor_state(
14426        &r#"fn one() {
14427            let mut a = two();
14428        }
14429
14430        fn «twoˇ»() {}"#
14431            .unindent(),
14432    );
14433    let editors = cx.update_workspace(|workspace, cx| {
14434        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14435    });
14436    cx.update_editor(|_, test_editor_cx| {
14437        assert_eq!(
14438            editors.len(),
14439            2,
14440            "After falling back to references search, we open a new editor with the results"
14441        );
14442        let references_fallback_text = editors
14443            .into_iter()
14444            .find(|new_editor| new_editor != test_editor_cx.view())
14445            .expect("Should have one non-test editor now")
14446            .read(test_editor_cx)
14447            .text(test_editor_cx);
14448        assert_eq!(
14449            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14450            "Should use the range from the references response and not the GoToDefinition one"
14451        );
14452    });
14453}
14454
14455#[gpui::test]
14456async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14457    init_test(cx, |_| {});
14458
14459    let language = Arc::new(Language::new(
14460        LanguageConfig::default(),
14461        Some(tree_sitter_rust::LANGUAGE.into()),
14462    ));
14463
14464    let text = r#"
14465        #[cfg(test)]
14466        mod tests() {
14467            #[test]
14468            fn runnable_1() {
14469                let a = 1;
14470            }
14471
14472            #[test]
14473            fn runnable_2() {
14474                let a = 1;
14475                let b = 2;
14476            }
14477        }
14478    "#
14479    .unindent();
14480
14481    let fs = FakeFs::new(cx.executor());
14482    fs.insert_file("/file.rs", Default::default()).await;
14483
14484    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14485    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14486    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14487    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
14488    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14489
14490    let editor = cx.new_view(|cx| {
14491        Editor::new(
14492            EditorMode::Full,
14493            multi_buffer,
14494            Some(project.clone()),
14495            true,
14496            cx,
14497        )
14498    });
14499
14500    editor.update(cx, |editor, cx| {
14501        editor.tasks.insert(
14502            (buffer.read(cx).remote_id(), 3),
14503            RunnableTasks {
14504                templates: vec![],
14505                offset: MultiBufferOffset(43),
14506                column: 0,
14507                extra_variables: HashMap::default(),
14508                context_range: BufferOffset(43)..BufferOffset(85),
14509            },
14510        );
14511        editor.tasks.insert(
14512            (buffer.read(cx).remote_id(), 8),
14513            RunnableTasks {
14514                templates: vec![],
14515                offset: MultiBufferOffset(86),
14516                column: 0,
14517                extra_variables: HashMap::default(),
14518                context_range: BufferOffset(86)..BufferOffset(191),
14519            },
14520        );
14521
14522        // Test finding task when cursor is inside function body
14523        editor.change_selections(None, cx, |s| {
14524            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14525        });
14526        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14527        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14528
14529        // Test finding task when cursor is on function name
14530        editor.change_selections(None, cx, |s| {
14531            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14532        });
14533        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14534        assert_eq!(row, 8, "Should find task when cursor is on function name");
14535    });
14536}
14537
14538#[gpui::test]
14539async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14540    init_test(cx, |_| {});
14541
14542    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14543    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14544    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14545
14546    let fs = FakeFs::new(cx.executor());
14547    fs.insert_tree(
14548        "/a",
14549        json!({
14550            "first.rs": sample_text_1,
14551            "second.rs": sample_text_2,
14552            "third.rs": sample_text_3,
14553        }),
14554    )
14555    .await;
14556    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14557    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14558    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14559    let worktree = project.update(cx, |project, cx| {
14560        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14561        assert_eq!(worktrees.len(), 1);
14562        worktrees.pop().unwrap()
14563    });
14564    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14565
14566    let buffer_1 = project
14567        .update(cx, |project, cx| {
14568            project.open_buffer((worktree_id, "first.rs"), cx)
14569        })
14570        .await
14571        .unwrap();
14572    let buffer_2 = project
14573        .update(cx, |project, cx| {
14574            project.open_buffer((worktree_id, "second.rs"), cx)
14575        })
14576        .await
14577        .unwrap();
14578    let buffer_3 = project
14579        .update(cx, |project, cx| {
14580            project.open_buffer((worktree_id, "third.rs"), cx)
14581        })
14582        .await
14583        .unwrap();
14584
14585    let multi_buffer = cx.new_model(|cx| {
14586        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14587        multi_buffer.push_excerpts(
14588            buffer_1.clone(),
14589            [
14590                ExcerptRange {
14591                    context: Point::new(0, 0)..Point::new(3, 0),
14592                    primary: None,
14593                },
14594                ExcerptRange {
14595                    context: Point::new(5, 0)..Point::new(7, 0),
14596                    primary: None,
14597                },
14598                ExcerptRange {
14599                    context: Point::new(9, 0)..Point::new(10, 4),
14600                    primary: None,
14601                },
14602            ],
14603            cx,
14604        );
14605        multi_buffer.push_excerpts(
14606            buffer_2.clone(),
14607            [
14608                ExcerptRange {
14609                    context: Point::new(0, 0)..Point::new(3, 0),
14610                    primary: None,
14611                },
14612                ExcerptRange {
14613                    context: Point::new(5, 0)..Point::new(7, 0),
14614                    primary: None,
14615                },
14616                ExcerptRange {
14617                    context: Point::new(9, 0)..Point::new(10, 4),
14618                    primary: None,
14619                },
14620            ],
14621            cx,
14622        );
14623        multi_buffer.push_excerpts(
14624            buffer_3.clone(),
14625            [
14626                ExcerptRange {
14627                    context: Point::new(0, 0)..Point::new(3, 0),
14628                    primary: None,
14629                },
14630                ExcerptRange {
14631                    context: Point::new(5, 0)..Point::new(7, 0),
14632                    primary: None,
14633                },
14634                ExcerptRange {
14635                    context: Point::new(9, 0)..Point::new(10, 4),
14636                    primary: None,
14637                },
14638            ],
14639            cx,
14640        );
14641        multi_buffer
14642    });
14643    let multi_buffer_editor = cx.new_view(|cx| {
14644        Editor::new(
14645            EditorMode::Full,
14646            multi_buffer,
14647            Some(project.clone()),
14648            true,
14649            cx,
14650        )
14651    });
14652
14653    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
14654    assert_eq!(
14655        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14656        full_text,
14657    );
14658
14659    multi_buffer_editor.update(cx, |editor, cx| {
14660        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14661    });
14662    assert_eq!(
14663        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14664        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14665        "After folding the first buffer, its text should not be displayed"
14666    );
14667
14668    multi_buffer_editor.update(cx, |editor, cx| {
14669        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14670    });
14671    assert_eq!(
14672        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14673        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14674        "After folding the second buffer, its text should not be displayed"
14675    );
14676
14677    multi_buffer_editor.update(cx, |editor, cx| {
14678        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14679    });
14680    assert_eq!(
14681        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14682        "\n\n\n\n\n",
14683        "After folding the third buffer, its text should not be displayed"
14684    );
14685
14686    // Emulate selection inside the fold logic, that should work
14687    multi_buffer_editor.update(cx, |editor, cx| {
14688        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14689    });
14690
14691    multi_buffer_editor.update(cx, |editor, cx| {
14692        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14693    });
14694    assert_eq!(
14695        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14696        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14697        "After unfolding the second buffer, its text should be displayed"
14698    );
14699
14700    multi_buffer_editor.update(cx, |editor, cx| {
14701        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14702    });
14703    assert_eq!(
14704        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14705        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14706        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14707    );
14708
14709    multi_buffer_editor.update(cx, |editor, cx| {
14710        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14711    });
14712    assert_eq!(
14713        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14714        full_text,
14715        "After unfolding the all buffers, all original text should be displayed"
14716    );
14717}
14718
14719#[gpui::test]
14720async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14721    init_test(cx, |_| {});
14722
14723    let sample_text_1 = "1111\n2222\n3333".to_string();
14724    let sample_text_2 = "4444\n5555\n6666".to_string();
14725    let sample_text_3 = "7777\n8888\n9999".to_string();
14726
14727    let fs = FakeFs::new(cx.executor());
14728    fs.insert_tree(
14729        "/a",
14730        json!({
14731            "first.rs": sample_text_1,
14732            "second.rs": sample_text_2,
14733            "third.rs": sample_text_3,
14734        }),
14735    )
14736    .await;
14737    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14738    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14739    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14740    let worktree = project.update(cx, |project, cx| {
14741        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14742        assert_eq!(worktrees.len(), 1);
14743        worktrees.pop().unwrap()
14744    });
14745    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14746
14747    let buffer_1 = project
14748        .update(cx, |project, cx| {
14749            project.open_buffer((worktree_id, "first.rs"), cx)
14750        })
14751        .await
14752        .unwrap();
14753    let buffer_2 = project
14754        .update(cx, |project, cx| {
14755            project.open_buffer((worktree_id, "second.rs"), cx)
14756        })
14757        .await
14758        .unwrap();
14759    let buffer_3 = project
14760        .update(cx, |project, cx| {
14761            project.open_buffer((worktree_id, "third.rs"), cx)
14762        })
14763        .await
14764        .unwrap();
14765
14766    let multi_buffer = cx.new_model(|cx| {
14767        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14768        multi_buffer.push_excerpts(
14769            buffer_1.clone(),
14770            [ExcerptRange {
14771                context: Point::new(0, 0)..Point::new(3, 0),
14772                primary: None,
14773            }],
14774            cx,
14775        );
14776        multi_buffer.push_excerpts(
14777            buffer_2.clone(),
14778            [ExcerptRange {
14779                context: Point::new(0, 0)..Point::new(3, 0),
14780                primary: None,
14781            }],
14782            cx,
14783        );
14784        multi_buffer.push_excerpts(
14785            buffer_3.clone(),
14786            [ExcerptRange {
14787                context: Point::new(0, 0)..Point::new(3, 0),
14788                primary: None,
14789            }],
14790            cx,
14791        );
14792        multi_buffer
14793    });
14794
14795    let multi_buffer_editor = cx.new_view(|cx| {
14796        Editor::new(
14797            EditorMode::Full,
14798            multi_buffer,
14799            Some(project.clone()),
14800            true,
14801            cx,
14802        )
14803    });
14804
14805    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14806    assert_eq!(
14807        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14808        full_text,
14809    );
14810
14811    multi_buffer_editor.update(cx, |editor, cx| {
14812        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14813    });
14814    assert_eq!(
14815        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14816        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14817        "After folding the first buffer, its text should not be displayed"
14818    );
14819
14820    multi_buffer_editor.update(cx, |editor, cx| {
14821        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14822    });
14823
14824    assert_eq!(
14825        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14826        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14827        "After folding the second buffer, its text should not be displayed"
14828    );
14829
14830    multi_buffer_editor.update(cx, |editor, cx| {
14831        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14832    });
14833    assert_eq!(
14834        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14835        "\n\n\n\n\n",
14836        "After folding the third buffer, its text should not be displayed"
14837    );
14838
14839    multi_buffer_editor.update(cx, |editor, cx| {
14840        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14841    });
14842    assert_eq!(
14843        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14844        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14845        "After unfolding the second buffer, its text should be displayed"
14846    );
14847
14848    multi_buffer_editor.update(cx, |editor, cx| {
14849        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14850    });
14851    assert_eq!(
14852        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14853        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14854        "After unfolding the first buffer, its text should be displayed"
14855    );
14856
14857    multi_buffer_editor.update(cx, |editor, cx| {
14858        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14859    });
14860    assert_eq!(
14861        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14862        full_text,
14863        "After unfolding all buffers, all original text should be displayed"
14864    );
14865}
14866
14867#[gpui::test]
14868async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14869    init_test(cx, |_| {});
14870
14871    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14872
14873    let fs = FakeFs::new(cx.executor());
14874    fs.insert_tree(
14875        "/a",
14876        json!({
14877            "main.rs": sample_text,
14878        }),
14879    )
14880    .await;
14881    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14882    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14883    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14884    let worktree = project.update(cx, |project, cx| {
14885        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14886        assert_eq!(worktrees.len(), 1);
14887        worktrees.pop().unwrap()
14888    });
14889    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14890
14891    let buffer_1 = project
14892        .update(cx, |project, cx| {
14893            project.open_buffer((worktree_id, "main.rs"), cx)
14894        })
14895        .await
14896        .unwrap();
14897
14898    let multi_buffer = cx.new_model(|cx| {
14899        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14900        multi_buffer.push_excerpts(
14901            buffer_1.clone(),
14902            [ExcerptRange {
14903                context: Point::new(0, 0)
14904                    ..Point::new(
14905                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14906                        0,
14907                    ),
14908                primary: None,
14909            }],
14910            cx,
14911        );
14912        multi_buffer
14913    });
14914    let multi_buffer_editor = cx.new_view(|cx| {
14915        Editor::new(
14916            EditorMode::Full,
14917            multi_buffer,
14918            Some(project.clone()),
14919            true,
14920            cx,
14921        )
14922    });
14923
14924    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14925    multi_buffer_editor.update(cx, |editor, cx| {
14926        enum TestHighlight {}
14927        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14928        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14929        editor.highlight_text::<TestHighlight>(
14930            vec![highlight_range.clone()],
14931            HighlightStyle::color(Hsla::green()),
14932            cx,
14933        );
14934        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14935    });
14936
14937    let full_text = format!("\n\n\n{sample_text}\n");
14938    assert_eq!(
14939        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14940        full_text,
14941    );
14942}
14943
14944#[gpui::test]
14945async fn test_inline_completion_text(cx: &mut TestAppContext) {
14946    init_test(cx, |_| {});
14947
14948    // Simple insertion
14949    assert_highlighted_edits(
14950        "Hello, world!",
14951        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
14952        true,
14953        cx,
14954        |highlighted_edits, cx| {
14955            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
14956            assert_eq!(highlighted_edits.highlights.len(), 1);
14957            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
14958            assert_eq!(
14959                highlighted_edits.highlights[0].1.background_color,
14960                Some(cx.theme().status().created_background)
14961            );
14962        },
14963    )
14964    .await;
14965
14966    // Replacement
14967    assert_highlighted_edits(
14968        "This is a test.",
14969        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
14970        false,
14971        cx,
14972        |highlighted_edits, cx| {
14973            assert_eq!(highlighted_edits.text, "That is a test.");
14974            assert_eq!(highlighted_edits.highlights.len(), 1);
14975            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
14976            assert_eq!(
14977                highlighted_edits.highlights[0].1.background_color,
14978                Some(cx.theme().status().created_background)
14979            );
14980        },
14981    )
14982    .await;
14983
14984    // Multiple edits
14985    assert_highlighted_edits(
14986        "Hello, world!",
14987        vec![
14988            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
14989            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
14990        ],
14991        false,
14992        cx,
14993        |highlighted_edits, cx| {
14994            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
14995            assert_eq!(highlighted_edits.highlights.len(), 2);
14996            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
14997            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
14998            assert_eq!(
14999                highlighted_edits.highlights[0].1.background_color,
15000                Some(cx.theme().status().created_background)
15001            );
15002            assert_eq!(
15003                highlighted_edits.highlights[1].1.background_color,
15004                Some(cx.theme().status().created_background)
15005            );
15006        },
15007    )
15008    .await;
15009
15010    // Multiple lines with edits
15011    assert_highlighted_edits(
15012        "First line\nSecond line\nThird line\nFourth line",
15013        vec![
15014            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15015            (
15016                Point::new(2, 0)..Point::new(2, 10),
15017                "New third line".to_string(),
15018            ),
15019            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15020        ],
15021        false,
15022        cx,
15023        |highlighted_edits, cx| {
15024            assert_eq!(
15025                highlighted_edits.text,
15026                "Second modified\nNew third line\nFourth updated line"
15027            );
15028            assert_eq!(highlighted_edits.highlights.len(), 3);
15029            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15030            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15031            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15032            for highlight in &highlighted_edits.highlights {
15033                assert_eq!(
15034                    highlight.1.background_color,
15035                    Some(cx.theme().status().created_background)
15036                );
15037            }
15038        },
15039    )
15040    .await;
15041}
15042
15043#[gpui::test]
15044async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15045    init_test(cx, |_| {});
15046
15047    // Deletion
15048    assert_highlighted_edits(
15049        "Hello, world!",
15050        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15051        true,
15052        cx,
15053        |highlighted_edits, cx| {
15054            assert_eq!(highlighted_edits.text, "Hello, world!");
15055            assert_eq!(highlighted_edits.highlights.len(), 1);
15056            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15057            assert_eq!(
15058                highlighted_edits.highlights[0].1.background_color,
15059                Some(cx.theme().status().deleted_background)
15060            );
15061        },
15062    )
15063    .await;
15064
15065    // Insertion
15066    assert_highlighted_edits(
15067        "Hello, world!",
15068        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15069        true,
15070        cx,
15071        |highlighted_edits, cx| {
15072            assert_eq!(highlighted_edits.highlights.len(), 1);
15073            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15074            assert_eq!(
15075                highlighted_edits.highlights[0].1.background_color,
15076                Some(cx.theme().status().created_background)
15077            );
15078        },
15079    )
15080    .await;
15081}
15082
15083async fn assert_highlighted_edits(
15084    text: &str,
15085    edits: Vec<(Range<Point>, String)>,
15086    include_deletions: bool,
15087    cx: &mut TestAppContext,
15088    assertion_fn: impl Fn(HighlightedEdits, &AppContext),
15089) {
15090    let window = cx.add_window(|cx| {
15091        let buffer = MultiBuffer::build_simple(text, cx);
15092        Editor::new(EditorMode::Full, buffer, None, true, cx)
15093    });
15094    let cx = &mut VisualTestContext::from_window(*window, cx);
15095
15096    let (buffer, snapshot) = window
15097        .update(cx, |editor, cx| {
15098            (
15099                editor.buffer().clone(),
15100                editor.buffer().read(cx).snapshot(cx),
15101            )
15102        })
15103        .unwrap();
15104
15105    let edits = edits
15106        .into_iter()
15107        .map(|(range, edit)| {
15108            (
15109                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15110                edit,
15111            )
15112        })
15113        .collect::<Vec<_>>();
15114
15115    let text_anchor_edits = edits
15116        .clone()
15117        .into_iter()
15118        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15119        .collect::<Vec<_>>();
15120
15121    let edit_preview = window
15122        .update(cx, |_, cx| {
15123            buffer
15124                .read(cx)
15125                .as_singleton()
15126                .unwrap()
15127                .read(cx)
15128                .preview_edits(text_anchor_edits.into(), cx)
15129        })
15130        .unwrap()
15131        .await;
15132
15133    cx.update(|cx| {
15134        let highlighted_edits = inline_completion_edit_text(
15135            &snapshot.as_singleton().unwrap().2,
15136            &edits,
15137            &edit_preview,
15138            include_deletions,
15139            cx,
15140        )
15141        .expect("Missing highlighted edits");
15142        assertion_fn(highlighted_edits, cx)
15143    });
15144}
15145
15146#[gpui::test]
15147async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15148    init_test(cx, |_| {});
15149    let capabilities = lsp::ServerCapabilities {
15150        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15151            prepare_provider: Some(true),
15152            work_done_progress_options: Default::default(),
15153        })),
15154        ..Default::default()
15155    };
15156    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15157
15158    cx.set_state(indoc! {"
15159        struct Fˇoo {}
15160    "});
15161
15162    cx.update_editor(|editor, cx| {
15163        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15164        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15165        editor.highlight_background::<DocumentHighlightRead>(
15166            &[highlight_range],
15167            |c| c.editor_document_highlight_read_background,
15168            cx,
15169        );
15170    });
15171
15172    let mut prepare_rename_handler =
15173        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15174            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15175                start: lsp::Position {
15176                    line: 0,
15177                    character: 7,
15178                },
15179                end: lsp::Position {
15180                    line: 0,
15181                    character: 10,
15182                },
15183            })))
15184        });
15185    let prepare_rename_task = cx
15186        .update_editor(|e, cx| e.rename(&Rename, cx))
15187        .expect("Prepare rename was not started");
15188    prepare_rename_handler.next().await.unwrap();
15189    prepare_rename_task.await.expect("Prepare rename failed");
15190
15191    let mut rename_handler =
15192        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15193            let edit = lsp::TextEdit {
15194                range: lsp::Range {
15195                    start: lsp::Position {
15196                        line: 0,
15197                        character: 7,
15198                    },
15199                    end: lsp::Position {
15200                        line: 0,
15201                        character: 10,
15202                    },
15203                },
15204                new_text: "FooRenamed".to_string(),
15205            };
15206            Ok(Some(lsp::WorkspaceEdit::new(
15207                // Specify the same edit twice
15208                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15209            )))
15210        });
15211    let rename_task = cx
15212        .update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
15213        .expect("Confirm rename was not started");
15214    rename_handler.next().await.unwrap();
15215    rename_task.await.expect("Confirm rename failed");
15216    cx.run_until_parked();
15217
15218    // Despite two edits, only one is actually applied as those are identical
15219    cx.assert_editor_state(indoc! {"
15220        struct FooRenamedˇ {}
15221    "});
15222}
15223
15224#[gpui::test]
15225async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15226    init_test(cx, |_| {});
15227    // These capabilities indicate that the server does not support prepare rename.
15228    let capabilities = lsp::ServerCapabilities {
15229        rename_provider: Some(lsp::OneOf::Left(true)),
15230        ..Default::default()
15231    };
15232    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15233
15234    cx.set_state(indoc! {"
15235        struct Fˇoo {}
15236    "});
15237
15238    cx.update_editor(|editor, cx| {
15239        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15240        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15241        editor.highlight_background::<DocumentHighlightRead>(
15242            &[highlight_range],
15243            |c| c.editor_document_highlight_read_background,
15244            cx,
15245        );
15246    });
15247
15248    cx.update_editor(|e, cx| e.rename(&Rename, cx))
15249        .expect("Prepare rename was not started")
15250        .await
15251        .expect("Prepare rename failed");
15252
15253    let mut rename_handler =
15254        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15255            let edit = lsp::TextEdit {
15256                range: lsp::Range {
15257                    start: lsp::Position {
15258                        line: 0,
15259                        character: 7,
15260                    },
15261                    end: lsp::Position {
15262                        line: 0,
15263                        character: 10,
15264                    },
15265                },
15266                new_text: "FooRenamed".to_string(),
15267            };
15268            Ok(Some(lsp::WorkspaceEdit::new(
15269                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15270            )))
15271        });
15272    let rename_task = cx
15273        .update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
15274        .expect("Confirm rename was not started");
15275    rename_handler.next().await.unwrap();
15276    rename_task.await.expect("Confirm rename failed");
15277    cx.run_until_parked();
15278
15279    // Correct range is renamed, as `surrounding_word` is used to find it.
15280    cx.assert_editor_state(indoc! {"
15281        struct FooRenamedˇ {}
15282    "});
15283}
15284
15285fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15286    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15287    point..point
15288}
15289
15290fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
15291    let (text, ranges) = marked_text_ranges(marked_text, true);
15292    assert_eq!(view.text(cx), text);
15293    assert_eq!(
15294        view.selections.ranges(cx),
15295        ranges,
15296        "Assert selections are {}",
15297        marked_text
15298    );
15299}
15300
15301pub fn handle_signature_help_request(
15302    cx: &mut EditorLspTestContext,
15303    mocked_response: lsp::SignatureHelp,
15304) -> impl Future<Output = ()> {
15305    let mut request =
15306        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15307            let mocked_response = mocked_response.clone();
15308            async move { Ok(Some(mocked_response)) }
15309        });
15310
15311    async move {
15312        request.next().await;
15313    }
15314}
15315
15316/// Handle completion request passing a marked string specifying where the completion
15317/// should be triggered from using '|' character, what range should be replaced, and what completions
15318/// should be returned using '<' and '>' to delimit the range
15319pub fn handle_completion_request(
15320    cx: &mut EditorLspTestContext,
15321    marked_string: &str,
15322    completions: Vec<&'static str>,
15323    counter: Arc<AtomicUsize>,
15324) -> impl Future<Output = ()> {
15325    let complete_from_marker: TextRangeMarker = '|'.into();
15326    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15327    let (_, mut marked_ranges) = marked_text_ranges_by(
15328        marked_string,
15329        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15330    );
15331
15332    let complete_from_position =
15333        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15334    let replace_range =
15335        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15336
15337    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15338        let completions = completions.clone();
15339        counter.fetch_add(1, atomic::Ordering::Release);
15340        async move {
15341            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15342            assert_eq!(
15343                params.text_document_position.position,
15344                complete_from_position
15345            );
15346            Ok(Some(lsp::CompletionResponse::Array(
15347                completions
15348                    .iter()
15349                    .map(|completion_text| lsp::CompletionItem {
15350                        label: completion_text.to_string(),
15351                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15352                            range: replace_range,
15353                            new_text: completion_text.to_string(),
15354                        })),
15355                        ..Default::default()
15356                    })
15357                    .collect(),
15358            )))
15359        }
15360    });
15361
15362    async move {
15363        request.next().await;
15364    }
15365}
15366
15367fn handle_resolve_completion_request(
15368    cx: &mut EditorLspTestContext,
15369    edits: Option<Vec<(&'static str, &'static str)>>,
15370) -> impl Future<Output = ()> {
15371    let edits = edits.map(|edits| {
15372        edits
15373            .iter()
15374            .map(|(marked_string, new_text)| {
15375                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15376                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15377                lsp::TextEdit::new(replace_range, new_text.to_string())
15378            })
15379            .collect::<Vec<_>>()
15380    });
15381
15382    let mut request =
15383        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15384            let edits = edits.clone();
15385            async move {
15386                Ok(lsp::CompletionItem {
15387                    additional_text_edits: edits,
15388                    ..Default::default()
15389                })
15390            }
15391        });
15392
15393    async move {
15394        request.next().await;
15395    }
15396}
15397
15398pub(crate) fn update_test_language_settings(
15399    cx: &mut TestAppContext,
15400    f: impl Fn(&mut AllLanguageSettingsContent),
15401) {
15402    cx.update(|cx| {
15403        SettingsStore::update_global(cx, |store, cx| {
15404            store.update_user_settings::<AllLanguageSettings>(cx, f);
15405        });
15406    });
15407}
15408
15409pub(crate) fn update_test_project_settings(
15410    cx: &mut TestAppContext,
15411    f: impl Fn(&mut ProjectSettings),
15412) {
15413    cx.update(|cx| {
15414        SettingsStore::update_global(cx, |store, cx| {
15415            store.update_user_settings::<ProjectSettings>(cx, f);
15416        });
15417    });
15418}
15419
15420pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15421    cx.update(|cx| {
15422        assets::Assets.load_test_fonts(cx);
15423        let store = SettingsStore::test(cx);
15424        cx.set_global(store);
15425        theme::init(theme::LoadThemes::JustBase, cx);
15426        release_channel::init(SemanticVersion::default(), cx);
15427        client::init_settings(cx);
15428        language::init(cx);
15429        Project::init_settings(cx);
15430        workspace::init_settings(cx);
15431        crate::init(cx);
15432    });
15433
15434    update_test_language_settings(cx, f);
15435}
15436
15437#[track_caller]
15438fn assert_hunk_revert(
15439    not_reverted_text_with_selections: &str,
15440    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15441    expected_reverted_text_with_selections: &str,
15442    base_text: &str,
15443    cx: &mut EditorLspTestContext,
15444) {
15445    cx.set_state(not_reverted_text_with_selections);
15446    cx.set_diff_base(base_text);
15447    cx.executor().run_until_parked();
15448
15449    let actual_hunk_statuses_before = cx.update_editor(|editor, cx| {
15450        let snapshot = editor.snapshot(cx);
15451        let reverted_hunk_statuses = snapshot
15452            .buffer_snapshot
15453            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15454            .map(|hunk| hunk.status())
15455            .collect::<Vec<_>>();
15456
15457        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
15458        reverted_hunk_statuses
15459    });
15460    cx.executor().run_until_parked();
15461    cx.assert_editor_state(expected_reverted_text_with_selections);
15462    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15463}