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, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
   23    LanguageName, Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::MultiBufferIndentGuide;
   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                .diff_map
 3367                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
 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_autoindent(cx: &mut gpui::TestAppContext) {
 5485    init_test(cx, |_| {});
 5486
 5487    let language = Arc::new(
 5488        Language::new(
 5489            LanguageConfig {
 5490                brackets: BracketPairConfig {
 5491                    pairs: vec![
 5492                        BracketPair {
 5493                            start: "{".to_string(),
 5494                            end: "}".to_string(),
 5495                            close: false,
 5496                            surround: false,
 5497                            newline: true,
 5498                        },
 5499                        BracketPair {
 5500                            start: "(".to_string(),
 5501                            end: ")".to_string(),
 5502                            close: false,
 5503                            surround: false,
 5504                            newline: true,
 5505                        },
 5506                    ],
 5507                    ..Default::default()
 5508                },
 5509                ..Default::default()
 5510            },
 5511            Some(tree_sitter_rust::LANGUAGE.into()),
 5512        )
 5513        .with_indents_query(
 5514            r#"
 5515                (_ "(" ")" @end) @indent
 5516                (_ "{" "}" @end) @indent
 5517            "#,
 5518        )
 5519        .unwrap(),
 5520    );
 5521
 5522    let text = "fn a() {}";
 5523
 5524    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 5525    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 5526    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 5527    editor
 5528        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5529        .await;
 5530
 5531    editor.update(cx, |editor, cx| {
 5532        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5533        editor.newline(&Newline, cx);
 5534        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5535        assert_eq!(
 5536            editor.selections.ranges(cx),
 5537            &[
 5538                Point::new(1, 4)..Point::new(1, 4),
 5539                Point::new(3, 4)..Point::new(3, 4),
 5540                Point::new(5, 0)..Point::new(5, 0)
 5541            ]
 5542        );
 5543    });
 5544}
 5545
 5546#[gpui::test]
 5547async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5548    init_test(cx, |_| {});
 5549
 5550    {
 5551        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5552        cx.set_state(indoc! {"
 5553            impl A {
 5554
 5555                fn b() {}
 5556
 5557            «fn c() {
 5558
 5559            }ˇ»
 5560            }
 5561        "});
 5562
 5563        cx.update_editor(|editor, cx| {
 5564            editor.autoindent(&Default::default(), cx);
 5565        });
 5566
 5567        cx.assert_editor_state(indoc! {"
 5568            impl A {
 5569
 5570                fn b() {}
 5571
 5572                «fn c() {
 5573
 5574                }ˇ»
 5575            }
 5576        "});
 5577    }
 5578
 5579    {
 5580        let mut cx = EditorTestContext::new_multibuffer(
 5581            cx,
 5582            [indoc! { "
 5583                impl A {
 5584                «
 5585                // a
 5586                fn b(){}
 5587                »
 5588                «
 5589                    }
 5590                    fn c(){}
 5591                »
 5592            "}],
 5593        );
 5594
 5595        let buffer = cx.update_editor(|editor, cx| {
 5596            let buffer = editor.buffer().update(cx, |buffer, _| {
 5597                buffer.all_buffers().iter().next().unwrap().clone()
 5598            });
 5599            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5600            buffer
 5601        });
 5602
 5603        cx.run_until_parked();
 5604        cx.update_editor(|editor, cx| {
 5605            editor.select_all(&Default::default(), cx);
 5606            editor.autoindent(&Default::default(), cx)
 5607        });
 5608        cx.run_until_parked();
 5609
 5610        cx.update(|cx| {
 5611            pretty_assertions::assert_eq!(
 5612                buffer.read(cx).text(),
 5613                indoc! { "
 5614                    impl A {
 5615
 5616                        // a
 5617                        fn b(){}
 5618
 5619
 5620                    }
 5621                    fn c(){}
 5622
 5623                " }
 5624            )
 5625        });
 5626    }
 5627}
 5628
 5629#[gpui::test]
 5630async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5631    init_test(cx, |_| {});
 5632
 5633    let mut cx = EditorTestContext::new(cx).await;
 5634
 5635    let language = Arc::new(Language::new(
 5636        LanguageConfig {
 5637            brackets: BracketPairConfig {
 5638                pairs: vec![
 5639                    BracketPair {
 5640                        start: "{".to_string(),
 5641                        end: "}".to_string(),
 5642                        close: true,
 5643                        surround: true,
 5644                        newline: true,
 5645                    },
 5646                    BracketPair {
 5647                        start: "(".to_string(),
 5648                        end: ")".to_string(),
 5649                        close: true,
 5650                        surround: true,
 5651                        newline: true,
 5652                    },
 5653                    BracketPair {
 5654                        start: "/*".to_string(),
 5655                        end: " */".to_string(),
 5656                        close: true,
 5657                        surround: true,
 5658                        newline: true,
 5659                    },
 5660                    BracketPair {
 5661                        start: "[".to_string(),
 5662                        end: "]".to_string(),
 5663                        close: false,
 5664                        surround: false,
 5665                        newline: true,
 5666                    },
 5667                    BracketPair {
 5668                        start: "\"".to_string(),
 5669                        end: "\"".to_string(),
 5670                        close: true,
 5671                        surround: true,
 5672                        newline: false,
 5673                    },
 5674                    BracketPair {
 5675                        start: "<".to_string(),
 5676                        end: ">".to_string(),
 5677                        close: false,
 5678                        surround: true,
 5679                        newline: true,
 5680                    },
 5681                ],
 5682                ..Default::default()
 5683            },
 5684            autoclose_before: "})]".to_string(),
 5685            ..Default::default()
 5686        },
 5687        Some(tree_sitter_rust::LANGUAGE.into()),
 5688    ));
 5689
 5690    cx.language_registry().add(language.clone());
 5691    cx.update_buffer(|buffer, cx| {
 5692        buffer.set_language(Some(language), cx);
 5693    });
 5694
 5695    cx.set_state(
 5696        &r#"
 5697            🏀ˇ
 5698            εˇ
 5699            ❤️ˇ
 5700        "#
 5701        .unindent(),
 5702    );
 5703
 5704    // autoclose multiple nested brackets at multiple cursors
 5705    cx.update_editor(|view, cx| {
 5706        view.handle_input("{", cx);
 5707        view.handle_input("{", cx);
 5708        view.handle_input("{", cx);
 5709    });
 5710    cx.assert_editor_state(
 5711        &"
 5712            🏀{{{ˇ}}}
 5713            ε{{{ˇ}}}
 5714            ❤️{{{ˇ}}}
 5715        "
 5716        .unindent(),
 5717    );
 5718
 5719    // insert a different closing bracket
 5720    cx.update_editor(|view, cx| {
 5721        view.handle_input(")", cx);
 5722    });
 5723    cx.assert_editor_state(
 5724        &"
 5725            🏀{{{)ˇ}}}
 5726            ε{{{)ˇ}}}
 5727            ❤️{{{)ˇ}}}
 5728        "
 5729        .unindent(),
 5730    );
 5731
 5732    // skip over the auto-closed brackets when typing a closing bracket
 5733    cx.update_editor(|view, cx| {
 5734        view.move_right(&MoveRight, cx);
 5735        view.handle_input("}", cx);
 5736        view.handle_input("}", cx);
 5737        view.handle_input("}", cx);
 5738    });
 5739    cx.assert_editor_state(
 5740        &"
 5741            🏀{{{)}}}}ˇ
 5742            ε{{{)}}}}ˇ
 5743            ❤️{{{)}}}}ˇ
 5744        "
 5745        .unindent(),
 5746    );
 5747
 5748    // autoclose multi-character pairs
 5749    cx.set_state(
 5750        &"
 5751            ˇ
 5752            ˇ
 5753        "
 5754        .unindent(),
 5755    );
 5756    cx.update_editor(|view, cx| {
 5757        view.handle_input("/", cx);
 5758        view.handle_input("*", cx);
 5759    });
 5760    cx.assert_editor_state(
 5761        &"
 5762            /*ˇ */
 5763            /*ˇ */
 5764        "
 5765        .unindent(),
 5766    );
 5767
 5768    // one cursor autocloses a multi-character pair, one cursor
 5769    // does not autoclose.
 5770    cx.set_state(
 5771        &"
 5772 5773            ˇ
 5774        "
 5775        .unindent(),
 5776    );
 5777    cx.update_editor(|view, cx| view.handle_input("*", cx));
 5778    cx.assert_editor_state(
 5779        &"
 5780            /*ˇ */
 5781 5782        "
 5783        .unindent(),
 5784    );
 5785
 5786    // Don't autoclose if the next character isn't whitespace and isn't
 5787    // listed in the language's "autoclose_before" section.
 5788    cx.set_state("ˇa b");
 5789    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5790    cx.assert_editor_state("{ˇa b");
 5791
 5792    // Don't autoclose if `close` is false for the bracket pair
 5793    cx.set_state("ˇ");
 5794    cx.update_editor(|view, cx| view.handle_input("[", cx));
 5795    cx.assert_editor_state("");
 5796
 5797    // Surround with brackets if text is selected
 5798    cx.set_state("«aˇ» b");
 5799    cx.update_editor(|view, cx| view.handle_input("{", cx));
 5800    cx.assert_editor_state("{«aˇ»} b");
 5801
 5802    // Autclose pair where the start and end characters are the same
 5803    cx.set_state("");
 5804    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5805    cx.assert_editor_state("a\"ˇ\"");
 5806    cx.update_editor(|view, cx| view.handle_input("\"", cx));
 5807    cx.assert_editor_state("a\"\"ˇ");
 5808
 5809    // Don't autoclose pair if autoclose is disabled
 5810    cx.set_state("ˇ");
 5811    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5812    cx.assert_editor_state("");
 5813
 5814    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 5815    cx.set_state("«aˇ» b");
 5816    cx.update_editor(|view, cx| view.handle_input("<", cx));
 5817    cx.assert_editor_state("<«aˇ»> b");
 5818}
 5819
 5820#[gpui::test]
 5821async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 5822    init_test(cx, |settings| {
 5823        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 5824    });
 5825
 5826    let mut cx = EditorTestContext::new(cx).await;
 5827
 5828    let language = Arc::new(Language::new(
 5829        LanguageConfig {
 5830            brackets: BracketPairConfig {
 5831                pairs: vec![
 5832                    BracketPair {
 5833                        start: "{".to_string(),
 5834                        end: "}".to_string(),
 5835                        close: true,
 5836                        surround: true,
 5837                        newline: true,
 5838                    },
 5839                    BracketPair {
 5840                        start: "(".to_string(),
 5841                        end: ")".to_string(),
 5842                        close: true,
 5843                        surround: true,
 5844                        newline: true,
 5845                    },
 5846                    BracketPair {
 5847                        start: "[".to_string(),
 5848                        end: "]".to_string(),
 5849                        close: false,
 5850                        surround: false,
 5851                        newline: true,
 5852                    },
 5853                ],
 5854                ..Default::default()
 5855            },
 5856            autoclose_before: "})]".to_string(),
 5857            ..Default::default()
 5858        },
 5859        Some(tree_sitter_rust::LANGUAGE.into()),
 5860    ));
 5861
 5862    cx.language_registry().add(language.clone());
 5863    cx.update_buffer(|buffer, cx| {
 5864        buffer.set_language(Some(language), cx);
 5865    });
 5866
 5867    cx.set_state(
 5868        &"
 5869            ˇ
 5870            ˇ
 5871            ˇ
 5872        "
 5873        .unindent(),
 5874    );
 5875
 5876    // ensure only matching closing brackets are skipped over
 5877    cx.update_editor(|view, cx| {
 5878        view.handle_input("}", cx);
 5879        view.move_left(&MoveLeft, cx);
 5880        view.handle_input(")", cx);
 5881        view.move_left(&MoveLeft, cx);
 5882    });
 5883    cx.assert_editor_state(
 5884        &"
 5885            ˇ)}
 5886            ˇ)}
 5887            ˇ)}
 5888        "
 5889        .unindent(),
 5890    );
 5891
 5892    // skip-over closing brackets at multiple cursors
 5893    cx.update_editor(|view, cx| {
 5894        view.handle_input(")", cx);
 5895        view.handle_input("}", cx);
 5896    });
 5897    cx.assert_editor_state(
 5898        &"
 5899            )}ˇ
 5900            )}ˇ
 5901            )}ˇ
 5902        "
 5903        .unindent(),
 5904    );
 5905
 5906    // ignore non-close brackets
 5907    cx.update_editor(|view, cx| {
 5908        view.handle_input("]", cx);
 5909        view.move_left(&MoveLeft, cx);
 5910        view.handle_input("]", cx);
 5911    });
 5912    cx.assert_editor_state(
 5913        &"
 5914            )}]ˇ]
 5915            )}]ˇ]
 5916            )}]ˇ]
 5917        "
 5918        .unindent(),
 5919    );
 5920}
 5921
 5922#[gpui::test]
 5923async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 5924    init_test(cx, |_| {});
 5925
 5926    let mut cx = EditorTestContext::new(cx).await;
 5927
 5928    let html_language = Arc::new(
 5929        Language::new(
 5930            LanguageConfig {
 5931                name: "HTML".into(),
 5932                brackets: BracketPairConfig {
 5933                    pairs: vec![
 5934                        BracketPair {
 5935                            start: "<".into(),
 5936                            end: ">".into(),
 5937                            close: true,
 5938                            ..Default::default()
 5939                        },
 5940                        BracketPair {
 5941                            start: "{".into(),
 5942                            end: "}".into(),
 5943                            close: true,
 5944                            ..Default::default()
 5945                        },
 5946                        BracketPair {
 5947                            start: "(".into(),
 5948                            end: ")".into(),
 5949                            close: true,
 5950                            ..Default::default()
 5951                        },
 5952                    ],
 5953                    ..Default::default()
 5954                },
 5955                autoclose_before: "})]>".into(),
 5956                ..Default::default()
 5957            },
 5958            Some(tree_sitter_html::language()),
 5959        )
 5960        .with_injection_query(
 5961            r#"
 5962            (script_element
 5963                (raw_text) @injection.content
 5964                (#set! injection.language "javascript"))
 5965            "#,
 5966        )
 5967        .unwrap(),
 5968    );
 5969
 5970    let javascript_language = Arc::new(Language::new(
 5971        LanguageConfig {
 5972            name: "JavaScript".into(),
 5973            brackets: BracketPairConfig {
 5974                pairs: vec![
 5975                    BracketPair {
 5976                        start: "/*".into(),
 5977                        end: " */".into(),
 5978                        close: true,
 5979                        ..Default::default()
 5980                    },
 5981                    BracketPair {
 5982                        start: "{".into(),
 5983                        end: "}".into(),
 5984                        close: true,
 5985                        ..Default::default()
 5986                    },
 5987                    BracketPair {
 5988                        start: "(".into(),
 5989                        end: ")".into(),
 5990                        close: true,
 5991                        ..Default::default()
 5992                    },
 5993                ],
 5994                ..Default::default()
 5995            },
 5996            autoclose_before: "})]>".into(),
 5997            ..Default::default()
 5998        },
 5999        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6000    ));
 6001
 6002    cx.language_registry().add(html_language.clone());
 6003    cx.language_registry().add(javascript_language.clone());
 6004
 6005    cx.update_buffer(|buffer, cx| {
 6006        buffer.set_language(Some(html_language), cx);
 6007    });
 6008
 6009    cx.set_state(
 6010        &r#"
 6011            <body>ˇ
 6012                <script>
 6013                    var x = 1;ˇ
 6014                </script>
 6015            </body>ˇ
 6016        "#
 6017        .unindent(),
 6018    );
 6019
 6020    // Precondition: different languages are active at different locations.
 6021    cx.update_editor(|editor, cx| {
 6022        let snapshot = editor.snapshot(cx);
 6023        let cursors = editor.selections.ranges::<usize>(cx);
 6024        let languages = cursors
 6025            .iter()
 6026            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6027            .collect::<Vec<_>>();
 6028        assert_eq!(
 6029            languages,
 6030            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6031        );
 6032    });
 6033
 6034    // Angle brackets autoclose in HTML, but not JavaScript.
 6035    cx.update_editor(|editor, cx| {
 6036        editor.handle_input("<", cx);
 6037        editor.handle_input("a", cx);
 6038    });
 6039    cx.assert_editor_state(
 6040        &r#"
 6041            <body><aˇ>
 6042                <script>
 6043                    var x = 1;<aˇ
 6044                </script>
 6045            </body><aˇ>
 6046        "#
 6047        .unindent(),
 6048    );
 6049
 6050    // Curly braces and parens autoclose in both HTML and JavaScript.
 6051    cx.update_editor(|editor, cx| {
 6052        editor.handle_input(" b=", cx);
 6053        editor.handle_input("{", cx);
 6054        editor.handle_input("c", cx);
 6055        editor.handle_input("(", cx);
 6056    });
 6057    cx.assert_editor_state(
 6058        &r#"
 6059            <body><a b={c(ˇ)}>
 6060                <script>
 6061                    var x = 1;<a b={c(ˇ)}
 6062                </script>
 6063            </body><a b={c(ˇ)}>
 6064        "#
 6065        .unindent(),
 6066    );
 6067
 6068    // Brackets that were already autoclosed are skipped.
 6069    cx.update_editor(|editor, cx| {
 6070        editor.handle_input(")", cx);
 6071        editor.handle_input("d", cx);
 6072        editor.handle_input("}", cx);
 6073    });
 6074    cx.assert_editor_state(
 6075        &r#"
 6076            <body><a b={c()d}ˇ>
 6077                <script>
 6078                    var x = 1;<a b={c()d}ˇ
 6079                </script>
 6080            </body><a b={c()d}ˇ>
 6081        "#
 6082        .unindent(),
 6083    );
 6084    cx.update_editor(|editor, cx| {
 6085        editor.handle_input(">", cx);
 6086    });
 6087    cx.assert_editor_state(
 6088        &r#"
 6089            <body><a b={c()d}>ˇ
 6090                <script>
 6091                    var x = 1;<a b={c()d}>ˇ
 6092                </script>
 6093            </body><a b={c()d}>ˇ
 6094        "#
 6095        .unindent(),
 6096    );
 6097
 6098    // Reset
 6099    cx.set_state(
 6100        &r#"
 6101            <body>ˇ
 6102                <script>
 6103                    var x = 1;ˇ
 6104                </script>
 6105            </body>ˇ
 6106        "#
 6107        .unindent(),
 6108    );
 6109
 6110    cx.update_editor(|editor, cx| {
 6111        editor.handle_input("<", cx);
 6112    });
 6113    cx.assert_editor_state(
 6114        &r#"
 6115            <body><ˇ>
 6116                <script>
 6117                    var x = 1;<ˇ
 6118                </script>
 6119            </body><ˇ>
 6120        "#
 6121        .unindent(),
 6122    );
 6123
 6124    // When backspacing, the closing angle brackets are removed.
 6125    cx.update_editor(|editor, cx| {
 6126        editor.backspace(&Backspace, cx);
 6127    });
 6128    cx.assert_editor_state(
 6129        &r#"
 6130            <body>ˇ
 6131                <script>
 6132                    var x = 1;ˇ
 6133                </script>
 6134            </body>ˇ
 6135        "#
 6136        .unindent(),
 6137    );
 6138
 6139    // Block comments autoclose in JavaScript, but not HTML.
 6140    cx.update_editor(|editor, cx| {
 6141        editor.handle_input("/", cx);
 6142        editor.handle_input("*", cx);
 6143    });
 6144    cx.assert_editor_state(
 6145        &r#"
 6146            <body>/*ˇ
 6147                <script>
 6148                    var x = 1;/*ˇ */
 6149                </script>
 6150            </body>/*ˇ
 6151        "#
 6152        .unindent(),
 6153    );
 6154}
 6155
 6156#[gpui::test]
 6157async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6158    init_test(cx, |_| {});
 6159
 6160    let mut cx = EditorTestContext::new(cx).await;
 6161
 6162    let rust_language = Arc::new(
 6163        Language::new(
 6164            LanguageConfig {
 6165                name: "Rust".into(),
 6166                brackets: serde_json::from_value(json!([
 6167                    { "start": "{", "end": "}", "close": true, "newline": true },
 6168                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6169                ]))
 6170                .unwrap(),
 6171                autoclose_before: "})]>".into(),
 6172                ..Default::default()
 6173            },
 6174            Some(tree_sitter_rust::LANGUAGE.into()),
 6175        )
 6176        .with_override_query("(string_literal) @string")
 6177        .unwrap(),
 6178    );
 6179
 6180    cx.language_registry().add(rust_language.clone());
 6181    cx.update_buffer(|buffer, cx| {
 6182        buffer.set_language(Some(rust_language), cx);
 6183    });
 6184
 6185    cx.set_state(
 6186        &r#"
 6187            let x = ˇ
 6188        "#
 6189        .unindent(),
 6190    );
 6191
 6192    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6193    cx.update_editor(|editor, cx| {
 6194        editor.handle_input("\"", cx);
 6195    });
 6196    cx.assert_editor_state(
 6197        &r#"
 6198            let x = "ˇ"
 6199        "#
 6200        .unindent(),
 6201    );
 6202
 6203    // Inserting another quotation mark. The cursor moves across the existing
 6204    // automatically-inserted quotation mark.
 6205    cx.update_editor(|editor, cx| {
 6206        editor.handle_input("\"", cx);
 6207    });
 6208    cx.assert_editor_state(
 6209        &r#"
 6210            let x = ""ˇ
 6211        "#
 6212        .unindent(),
 6213    );
 6214
 6215    // Reset
 6216    cx.set_state(
 6217        &r#"
 6218            let x = ˇ
 6219        "#
 6220        .unindent(),
 6221    );
 6222
 6223    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6224    cx.update_editor(|editor, cx| {
 6225        editor.handle_input("\"", cx);
 6226        editor.handle_input(" ", cx);
 6227        editor.move_left(&Default::default(), cx);
 6228        editor.handle_input("\\", cx);
 6229        editor.handle_input("\"", cx);
 6230    });
 6231    cx.assert_editor_state(
 6232        &r#"
 6233            let x = "\"ˇ "
 6234        "#
 6235        .unindent(),
 6236    );
 6237
 6238    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6239    // mark. Nothing is inserted.
 6240    cx.update_editor(|editor, cx| {
 6241        editor.move_right(&Default::default(), cx);
 6242        editor.handle_input("\"", cx);
 6243    });
 6244    cx.assert_editor_state(
 6245        &r#"
 6246            let x = "\" "ˇ
 6247        "#
 6248        .unindent(),
 6249    );
 6250}
 6251
 6252#[gpui::test]
 6253async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6254    init_test(cx, |_| {});
 6255
 6256    let language = Arc::new(Language::new(
 6257        LanguageConfig {
 6258            brackets: BracketPairConfig {
 6259                pairs: vec![
 6260                    BracketPair {
 6261                        start: "{".to_string(),
 6262                        end: "}".to_string(),
 6263                        close: true,
 6264                        surround: true,
 6265                        newline: true,
 6266                    },
 6267                    BracketPair {
 6268                        start: "/* ".to_string(),
 6269                        end: "*/".to_string(),
 6270                        close: true,
 6271                        surround: true,
 6272                        ..Default::default()
 6273                    },
 6274                ],
 6275                ..Default::default()
 6276            },
 6277            ..Default::default()
 6278        },
 6279        Some(tree_sitter_rust::LANGUAGE.into()),
 6280    ));
 6281
 6282    let text = r#"
 6283        a
 6284        b
 6285        c
 6286    "#
 6287    .unindent();
 6288
 6289    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6290    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6291    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6292    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6293        .await;
 6294
 6295    view.update(cx, |view, cx| {
 6296        view.change_selections(None, cx, |s| {
 6297            s.select_display_ranges([
 6298                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6299                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6300                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6301            ])
 6302        });
 6303
 6304        view.handle_input("{", cx);
 6305        view.handle_input("{", cx);
 6306        view.handle_input("{", cx);
 6307        assert_eq!(
 6308            view.text(cx),
 6309            "
 6310                {{{a}}}
 6311                {{{b}}}
 6312                {{{c}}}
 6313            "
 6314            .unindent()
 6315        );
 6316        assert_eq!(
 6317            view.selections.display_ranges(cx),
 6318            [
 6319                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6320                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6321                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6322            ]
 6323        );
 6324
 6325        view.undo(&Undo, cx);
 6326        view.undo(&Undo, cx);
 6327        view.undo(&Undo, cx);
 6328        assert_eq!(
 6329            view.text(cx),
 6330            "
 6331                a
 6332                b
 6333                c
 6334            "
 6335            .unindent()
 6336        );
 6337        assert_eq!(
 6338            view.selections.display_ranges(cx),
 6339            [
 6340                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6341                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6342                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6343            ]
 6344        );
 6345
 6346        // Ensure inserting the first character of a multi-byte bracket pair
 6347        // doesn't surround the selections with the bracket.
 6348        view.handle_input("/", cx);
 6349        assert_eq!(
 6350            view.text(cx),
 6351            "
 6352                /
 6353                /
 6354                /
 6355            "
 6356            .unindent()
 6357        );
 6358        assert_eq!(
 6359            view.selections.display_ranges(cx),
 6360            [
 6361                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6362                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6363                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6364            ]
 6365        );
 6366
 6367        view.undo(&Undo, cx);
 6368        assert_eq!(
 6369            view.text(cx),
 6370            "
 6371                a
 6372                b
 6373                c
 6374            "
 6375            .unindent()
 6376        );
 6377        assert_eq!(
 6378            view.selections.display_ranges(cx),
 6379            [
 6380                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6381                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6382                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6383            ]
 6384        );
 6385
 6386        // Ensure inserting the last character of a multi-byte bracket pair
 6387        // doesn't surround the selections with the bracket.
 6388        view.handle_input("*", cx);
 6389        assert_eq!(
 6390            view.text(cx),
 6391            "
 6392                *
 6393                *
 6394                *
 6395            "
 6396            .unindent()
 6397        );
 6398        assert_eq!(
 6399            view.selections.display_ranges(cx),
 6400            [
 6401                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6402                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6403                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6404            ]
 6405        );
 6406    });
 6407}
 6408
 6409#[gpui::test]
 6410async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6411    init_test(cx, |_| {});
 6412
 6413    let language = Arc::new(Language::new(
 6414        LanguageConfig {
 6415            brackets: BracketPairConfig {
 6416                pairs: vec![BracketPair {
 6417                    start: "{".to_string(),
 6418                    end: "}".to_string(),
 6419                    close: true,
 6420                    surround: true,
 6421                    newline: true,
 6422                }],
 6423                ..Default::default()
 6424            },
 6425            autoclose_before: "}".to_string(),
 6426            ..Default::default()
 6427        },
 6428        Some(tree_sitter_rust::LANGUAGE.into()),
 6429    ));
 6430
 6431    let text = r#"
 6432        a
 6433        b
 6434        c
 6435    "#
 6436    .unindent();
 6437
 6438    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 6439    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6440    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6441    editor
 6442        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6443        .await;
 6444
 6445    editor.update(cx, |editor, cx| {
 6446        editor.change_selections(None, cx, |s| {
 6447            s.select_ranges([
 6448                Point::new(0, 1)..Point::new(0, 1),
 6449                Point::new(1, 1)..Point::new(1, 1),
 6450                Point::new(2, 1)..Point::new(2, 1),
 6451            ])
 6452        });
 6453
 6454        editor.handle_input("{", cx);
 6455        editor.handle_input("{", cx);
 6456        editor.handle_input("_", cx);
 6457        assert_eq!(
 6458            editor.text(cx),
 6459            "
 6460                a{{_}}
 6461                b{{_}}
 6462                c{{_}}
 6463            "
 6464            .unindent()
 6465        );
 6466        assert_eq!(
 6467            editor.selections.ranges::<Point>(cx),
 6468            [
 6469                Point::new(0, 4)..Point::new(0, 4),
 6470                Point::new(1, 4)..Point::new(1, 4),
 6471                Point::new(2, 4)..Point::new(2, 4)
 6472            ]
 6473        );
 6474
 6475        editor.backspace(&Default::default(), cx);
 6476        editor.backspace(&Default::default(), cx);
 6477        assert_eq!(
 6478            editor.text(cx),
 6479            "
 6480                a{}
 6481                b{}
 6482                c{}
 6483            "
 6484            .unindent()
 6485        );
 6486        assert_eq!(
 6487            editor.selections.ranges::<Point>(cx),
 6488            [
 6489                Point::new(0, 2)..Point::new(0, 2),
 6490                Point::new(1, 2)..Point::new(1, 2),
 6491                Point::new(2, 2)..Point::new(2, 2)
 6492            ]
 6493        );
 6494
 6495        editor.delete_to_previous_word_start(&Default::default(), cx);
 6496        assert_eq!(
 6497            editor.text(cx),
 6498            "
 6499                a
 6500                b
 6501                c
 6502            "
 6503            .unindent()
 6504        );
 6505        assert_eq!(
 6506            editor.selections.ranges::<Point>(cx),
 6507            [
 6508                Point::new(0, 1)..Point::new(0, 1),
 6509                Point::new(1, 1)..Point::new(1, 1),
 6510                Point::new(2, 1)..Point::new(2, 1)
 6511            ]
 6512        );
 6513    });
 6514}
 6515
 6516#[gpui::test]
 6517async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6518    init_test(cx, |settings| {
 6519        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6520    });
 6521
 6522    let mut cx = EditorTestContext::new(cx).await;
 6523
 6524    let language = Arc::new(Language::new(
 6525        LanguageConfig {
 6526            brackets: BracketPairConfig {
 6527                pairs: vec![
 6528                    BracketPair {
 6529                        start: "{".to_string(),
 6530                        end: "}".to_string(),
 6531                        close: true,
 6532                        surround: true,
 6533                        newline: true,
 6534                    },
 6535                    BracketPair {
 6536                        start: "(".to_string(),
 6537                        end: ")".to_string(),
 6538                        close: true,
 6539                        surround: true,
 6540                        newline: true,
 6541                    },
 6542                    BracketPair {
 6543                        start: "[".to_string(),
 6544                        end: "]".to_string(),
 6545                        close: false,
 6546                        surround: true,
 6547                        newline: true,
 6548                    },
 6549                ],
 6550                ..Default::default()
 6551            },
 6552            autoclose_before: "})]".to_string(),
 6553            ..Default::default()
 6554        },
 6555        Some(tree_sitter_rust::LANGUAGE.into()),
 6556    ));
 6557
 6558    cx.language_registry().add(language.clone());
 6559    cx.update_buffer(|buffer, cx| {
 6560        buffer.set_language(Some(language), cx);
 6561    });
 6562
 6563    cx.set_state(
 6564        &"
 6565            {(ˇ)}
 6566            [[ˇ]]
 6567            {(ˇ)}
 6568        "
 6569        .unindent(),
 6570    );
 6571
 6572    cx.update_editor(|view, cx| {
 6573        view.backspace(&Default::default(), cx);
 6574        view.backspace(&Default::default(), cx);
 6575    });
 6576
 6577    cx.assert_editor_state(
 6578        &"
 6579            ˇ
 6580            ˇ]]
 6581            ˇ
 6582        "
 6583        .unindent(),
 6584    );
 6585
 6586    cx.update_editor(|view, cx| {
 6587        view.handle_input("{", cx);
 6588        view.handle_input("{", cx);
 6589        view.move_right(&MoveRight, cx);
 6590        view.move_right(&MoveRight, cx);
 6591        view.move_left(&MoveLeft, cx);
 6592        view.move_left(&MoveLeft, cx);
 6593        view.backspace(&Default::default(), cx);
 6594    });
 6595
 6596    cx.assert_editor_state(
 6597        &"
 6598            {ˇ}
 6599            {ˇ}]]
 6600            {ˇ}
 6601        "
 6602        .unindent(),
 6603    );
 6604
 6605    cx.update_editor(|view, cx| {
 6606        view.backspace(&Default::default(), cx);
 6607    });
 6608
 6609    cx.assert_editor_state(
 6610        &"
 6611            ˇ
 6612            ˇ]]
 6613            ˇ
 6614        "
 6615        .unindent(),
 6616    );
 6617}
 6618
 6619#[gpui::test]
 6620async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6621    init_test(cx, |_| {});
 6622
 6623    let language = Arc::new(Language::new(
 6624        LanguageConfig::default(),
 6625        Some(tree_sitter_rust::LANGUAGE.into()),
 6626    ));
 6627
 6628    let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
 6629    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6630    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6631    editor
 6632        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 6633        .await;
 6634
 6635    editor.update(cx, |editor, cx| {
 6636        editor.set_auto_replace_emoji_shortcode(true);
 6637
 6638        editor.handle_input("Hello ", cx);
 6639        editor.handle_input(":wave", cx);
 6640        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6641
 6642        editor.handle_input(":", cx);
 6643        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6644
 6645        editor.handle_input(" :smile", cx);
 6646        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6647
 6648        editor.handle_input(":", cx);
 6649        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6650
 6651        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6652        editor.handle_input(":wave", cx);
 6653        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6654
 6655        editor.handle_input(":", cx);
 6656        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6657
 6658        editor.handle_input(":1", cx);
 6659        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6660
 6661        editor.handle_input(":", cx);
 6662        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6663
 6664        // Ensure shortcode does not get replaced when it is part of a word
 6665        editor.handle_input(" Test:wave", cx);
 6666        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6667
 6668        editor.handle_input(":", cx);
 6669        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6670
 6671        editor.set_auto_replace_emoji_shortcode(false);
 6672
 6673        // Ensure shortcode does not get replaced when auto replace is off
 6674        editor.handle_input(" :wave", cx);
 6675        assert_eq!(
 6676            editor.text(cx),
 6677            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6678        );
 6679
 6680        editor.handle_input(":", cx);
 6681        assert_eq!(
 6682            editor.text(cx),
 6683            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6684        );
 6685    });
 6686}
 6687
 6688#[gpui::test]
 6689async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6690    init_test(cx, |_| {});
 6691
 6692    let (text, insertion_ranges) = marked_text_ranges(
 6693        indoc! {"
 6694            ˇ
 6695        "},
 6696        false,
 6697    );
 6698
 6699    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6700    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6701
 6702    _ = editor.update(cx, |editor, cx| {
 6703        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6704
 6705        editor
 6706            .insert_snippet(&insertion_ranges, snippet, cx)
 6707            .unwrap();
 6708
 6709        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6710            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6711            assert_eq!(editor.text(cx), expected_text);
 6712            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6713        }
 6714
 6715        assert(
 6716            editor,
 6717            cx,
 6718            indoc! {"
 6719            type «» =•
 6720            "},
 6721        );
 6722
 6723        assert!(editor.context_menu_visible(), "There should be a matches");
 6724    });
 6725}
 6726
 6727#[gpui::test]
 6728async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6729    init_test(cx, |_| {});
 6730
 6731    let (text, insertion_ranges) = marked_text_ranges(
 6732        indoc! {"
 6733            a.ˇ b
 6734            a.ˇ b
 6735            a.ˇ b
 6736        "},
 6737        false,
 6738    );
 6739
 6740    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6741    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 6742
 6743    editor.update(cx, |editor, cx| {
 6744        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6745
 6746        editor
 6747            .insert_snippet(&insertion_ranges, snippet, cx)
 6748            .unwrap();
 6749
 6750        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
 6751            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6752            assert_eq!(editor.text(cx), expected_text);
 6753            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6754        }
 6755
 6756        assert(
 6757            editor,
 6758            cx,
 6759            indoc! {"
 6760                a.f(«one», two, «three») b
 6761                a.f(«one», two, «three») b
 6762                a.f(«one», two, «three») b
 6763            "},
 6764        );
 6765
 6766        // Can't move earlier than the first tab stop
 6767        assert!(!editor.move_to_prev_snippet_tabstop(cx));
 6768        assert(
 6769            editor,
 6770            cx,
 6771            indoc! {"
 6772                a.f(«one», two, «three») b
 6773                a.f(«one», two, «three») b
 6774                a.f(«one», two, «three») b
 6775            "},
 6776        );
 6777
 6778        assert!(editor.move_to_next_snippet_tabstop(cx));
 6779        assert(
 6780            editor,
 6781            cx,
 6782            indoc! {"
 6783                a.f(one, «two», three) b
 6784                a.f(one, «two», three) b
 6785                a.f(one, «two», three) b
 6786            "},
 6787        );
 6788
 6789        editor.move_to_prev_snippet_tabstop(cx);
 6790        assert(
 6791            editor,
 6792            cx,
 6793            indoc! {"
 6794                a.f(«one», two, «three») b
 6795                a.f(«one», two, «three») b
 6796                a.f(«one», two, «three») b
 6797            "},
 6798        );
 6799
 6800        assert!(editor.move_to_next_snippet_tabstop(cx));
 6801        assert(
 6802            editor,
 6803            cx,
 6804            indoc! {"
 6805                a.f(one, «two», three) b
 6806                a.f(one, «two», three) b
 6807                a.f(one, «two», three) b
 6808            "},
 6809        );
 6810        assert!(editor.move_to_next_snippet_tabstop(cx));
 6811        assert(
 6812            editor,
 6813            cx,
 6814            indoc! {"
 6815                a.f(one, two, three)ˇ b
 6816                a.f(one, two, three)ˇ b
 6817                a.f(one, two, three)ˇ b
 6818            "},
 6819        );
 6820
 6821        // As soon as the last tab stop is reached, snippet state is gone
 6822        editor.move_to_prev_snippet_tabstop(cx);
 6823        assert(
 6824            editor,
 6825            cx,
 6826            indoc! {"
 6827                a.f(one, two, three)ˇ b
 6828                a.f(one, two, three)ˇ b
 6829                a.f(one, two, three)ˇ b
 6830            "},
 6831        );
 6832    });
 6833}
 6834
 6835#[gpui::test]
 6836async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 6837    init_test(cx, |_| {});
 6838
 6839    let fs = FakeFs::new(cx.executor());
 6840    fs.insert_file("/file.rs", Default::default()).await;
 6841
 6842    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 6843
 6844    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 6845    language_registry.add(rust_lang());
 6846    let mut fake_servers = language_registry.register_fake_lsp(
 6847        "Rust",
 6848        FakeLspAdapter {
 6849            capabilities: lsp::ServerCapabilities {
 6850                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6851                ..Default::default()
 6852            },
 6853            ..Default::default()
 6854        },
 6855    );
 6856
 6857    let buffer = project
 6858        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 6859        .await
 6860        .unwrap();
 6861
 6862    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 6863    let (editor, cx) =
 6864        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 6865    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6866    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6867
 6868    cx.executor().start_waiting();
 6869    let fake_server = fake_servers.next().await.unwrap();
 6870
 6871    let save = editor
 6872        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6873        .unwrap();
 6874    fake_server
 6875        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6876            assert_eq!(
 6877                params.text_document.uri,
 6878                lsp::Url::from_file_path("/file.rs").unwrap()
 6879            );
 6880            assert_eq!(params.options.tab_size, 4);
 6881            Ok(Some(vec![lsp::TextEdit::new(
 6882                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 6883                ", ".to_string(),
 6884            )]))
 6885        })
 6886        .next()
 6887        .await;
 6888    cx.executor().start_waiting();
 6889    save.await;
 6890
 6891    assert_eq!(
 6892        editor.update(cx, |editor, cx| editor.text(cx)),
 6893        "one, two\nthree\n"
 6894    );
 6895    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6896
 6897    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 6898    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6899
 6900    // Ensure we can still save even if formatting hangs.
 6901    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6902        assert_eq!(
 6903            params.text_document.uri,
 6904            lsp::Url::from_file_path("/file.rs").unwrap()
 6905        );
 6906        futures::future::pending::<()>().await;
 6907        unreachable!()
 6908    });
 6909    let save = editor
 6910        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6911        .unwrap();
 6912    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 6913    cx.executor().start_waiting();
 6914    save.await;
 6915    assert_eq!(
 6916        editor.update(cx, |editor, cx| editor.text(cx)),
 6917        "one\ntwo\nthree\n"
 6918    );
 6919    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 6920
 6921    // For non-dirty buffer, no formatting request should be sent
 6922    let save = editor
 6923        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6924        .unwrap();
 6925    let _pending_format_request = fake_server
 6926        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 6927            panic!("Should not be invoked on non-dirty buffer");
 6928        })
 6929        .next();
 6930    cx.executor().start_waiting();
 6931    save.await;
 6932
 6933    // Set rust language override and assert overridden tabsize is sent to language server
 6934    update_test_language_settings(cx, |settings| {
 6935        settings.languages.insert(
 6936            "Rust".into(),
 6937            LanguageSettingsContent {
 6938                tab_size: NonZeroU32::new(8),
 6939                ..Default::default()
 6940            },
 6941        );
 6942    });
 6943
 6944    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 6945    assert!(cx.read(|cx| editor.is_dirty(cx)));
 6946    let save = editor
 6947        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 6948        .unwrap();
 6949    fake_server
 6950        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 6951            assert_eq!(
 6952                params.text_document.uri,
 6953                lsp::Url::from_file_path("/file.rs").unwrap()
 6954            );
 6955            assert_eq!(params.options.tab_size, 8);
 6956            Ok(Some(vec![]))
 6957        })
 6958        .next()
 6959        .await;
 6960    cx.executor().start_waiting();
 6961    save.await;
 6962}
 6963
 6964#[gpui::test]
 6965async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 6966    init_test(cx, |_| {});
 6967
 6968    let cols = 4;
 6969    let rows = 10;
 6970    let sample_text_1 = sample_text(rows, cols, 'a');
 6971    assert_eq!(
 6972        sample_text_1,
 6973        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 6974    );
 6975    let sample_text_2 = sample_text(rows, cols, 'l');
 6976    assert_eq!(
 6977        sample_text_2,
 6978        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 6979    );
 6980    let sample_text_3 = sample_text(rows, cols, 'v');
 6981    assert_eq!(
 6982        sample_text_3,
 6983        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 6984    );
 6985
 6986    let fs = FakeFs::new(cx.executor());
 6987    fs.insert_tree(
 6988        "/a",
 6989        json!({
 6990            "main.rs": sample_text_1,
 6991            "other.rs": sample_text_2,
 6992            "lib.rs": sample_text_3,
 6993        }),
 6994    )
 6995    .await;
 6996
 6997    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 6998    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 6999    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7000
 7001    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7002    language_registry.add(rust_lang());
 7003    let mut fake_servers = language_registry.register_fake_lsp(
 7004        "Rust",
 7005        FakeLspAdapter {
 7006            capabilities: lsp::ServerCapabilities {
 7007                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7008                ..Default::default()
 7009            },
 7010            ..Default::default()
 7011        },
 7012    );
 7013
 7014    let worktree = project.update(cx, |project, cx| {
 7015        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7016        assert_eq!(worktrees.len(), 1);
 7017        worktrees.pop().unwrap()
 7018    });
 7019    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7020
 7021    let buffer_1 = project
 7022        .update(cx, |project, cx| {
 7023            project.open_buffer((worktree_id, "main.rs"), cx)
 7024        })
 7025        .await
 7026        .unwrap();
 7027    let buffer_2 = project
 7028        .update(cx, |project, cx| {
 7029            project.open_buffer((worktree_id, "other.rs"), cx)
 7030        })
 7031        .await
 7032        .unwrap();
 7033    let buffer_3 = project
 7034        .update(cx, |project, cx| {
 7035            project.open_buffer((worktree_id, "lib.rs"), cx)
 7036        })
 7037        .await
 7038        .unwrap();
 7039
 7040    let multi_buffer = cx.new_model(|cx| {
 7041        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7042        multi_buffer.push_excerpts(
 7043            buffer_1.clone(),
 7044            [
 7045                ExcerptRange {
 7046                    context: Point::new(0, 0)..Point::new(3, 0),
 7047                    primary: None,
 7048                },
 7049                ExcerptRange {
 7050                    context: Point::new(5, 0)..Point::new(7, 0),
 7051                    primary: None,
 7052                },
 7053                ExcerptRange {
 7054                    context: Point::new(9, 0)..Point::new(10, 4),
 7055                    primary: None,
 7056                },
 7057            ],
 7058            cx,
 7059        );
 7060        multi_buffer.push_excerpts(
 7061            buffer_2.clone(),
 7062            [
 7063                ExcerptRange {
 7064                    context: Point::new(0, 0)..Point::new(3, 0),
 7065                    primary: None,
 7066                },
 7067                ExcerptRange {
 7068                    context: Point::new(5, 0)..Point::new(7, 0),
 7069                    primary: None,
 7070                },
 7071                ExcerptRange {
 7072                    context: Point::new(9, 0)..Point::new(10, 4),
 7073                    primary: None,
 7074                },
 7075            ],
 7076            cx,
 7077        );
 7078        multi_buffer.push_excerpts(
 7079            buffer_3.clone(),
 7080            [
 7081                ExcerptRange {
 7082                    context: Point::new(0, 0)..Point::new(3, 0),
 7083                    primary: None,
 7084                },
 7085                ExcerptRange {
 7086                    context: Point::new(5, 0)..Point::new(7, 0),
 7087                    primary: None,
 7088                },
 7089                ExcerptRange {
 7090                    context: Point::new(9, 0)..Point::new(10, 4),
 7091                    primary: None,
 7092                },
 7093            ],
 7094            cx,
 7095        );
 7096        multi_buffer
 7097    });
 7098    let multi_buffer_editor = cx.new_view(|cx| {
 7099        Editor::new(
 7100            EditorMode::Full,
 7101            multi_buffer,
 7102            Some(project.clone()),
 7103            true,
 7104            cx,
 7105        )
 7106    });
 7107
 7108    multi_buffer_editor.update(cx, |editor, cx| {
 7109        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
 7110        editor.insert("|one|two|three|", cx);
 7111    });
 7112    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7113    multi_buffer_editor.update(cx, |editor, cx| {
 7114        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
 7115            s.select_ranges(Some(60..70))
 7116        });
 7117        editor.insert("|four|five|six|", cx);
 7118    });
 7119    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7120
 7121    // First two buffers should be edited, but not the third one.
 7122    assert_eq!(
 7123        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7124        "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}",
 7125    );
 7126    buffer_1.update(cx, |buffer, _| {
 7127        assert!(buffer.is_dirty());
 7128        assert_eq!(
 7129            buffer.text(),
 7130            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7131        )
 7132    });
 7133    buffer_2.update(cx, |buffer, _| {
 7134        assert!(buffer.is_dirty());
 7135        assert_eq!(
 7136            buffer.text(),
 7137            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7138        )
 7139    });
 7140    buffer_3.update(cx, |buffer, _| {
 7141        assert!(!buffer.is_dirty());
 7142        assert_eq!(buffer.text(), sample_text_3,)
 7143    });
 7144    cx.executor().run_until_parked();
 7145
 7146    cx.executor().start_waiting();
 7147    let save = multi_buffer_editor
 7148        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7149        .unwrap();
 7150
 7151    let fake_server = fake_servers.next().await.unwrap();
 7152    fake_server
 7153        .server
 7154        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7155            Ok(Some(vec![lsp::TextEdit::new(
 7156                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7157                format!("[{} formatted]", params.text_document.uri),
 7158            )]))
 7159        })
 7160        .detach();
 7161    save.await;
 7162
 7163    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7164    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7165    assert_eq!(
 7166        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7167        "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}",
 7168    );
 7169    buffer_1.update(cx, |buffer, _| {
 7170        assert!(!buffer.is_dirty());
 7171        assert_eq!(
 7172            buffer.text(),
 7173            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
 7174        )
 7175    });
 7176    buffer_2.update(cx, |buffer, _| {
 7177        assert!(!buffer.is_dirty());
 7178        assert_eq!(
 7179            buffer.text(),
 7180            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
 7181        )
 7182    });
 7183    buffer_3.update(cx, |buffer, _| {
 7184        assert!(!buffer.is_dirty());
 7185        assert_eq!(buffer.text(), sample_text_3,)
 7186    });
 7187}
 7188
 7189#[gpui::test]
 7190async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7191    init_test(cx, |_| {});
 7192
 7193    let fs = FakeFs::new(cx.executor());
 7194    fs.insert_file("/file.rs", Default::default()).await;
 7195
 7196    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7197
 7198    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7199    language_registry.add(rust_lang());
 7200    let mut fake_servers = language_registry.register_fake_lsp(
 7201        "Rust",
 7202        FakeLspAdapter {
 7203            capabilities: lsp::ServerCapabilities {
 7204                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7205                ..Default::default()
 7206            },
 7207            ..Default::default()
 7208        },
 7209    );
 7210
 7211    let buffer = project
 7212        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7213        .await
 7214        .unwrap();
 7215
 7216    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7217    let (editor, cx) =
 7218        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7219    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7220    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7221
 7222    cx.executor().start_waiting();
 7223    let fake_server = fake_servers.next().await.unwrap();
 7224
 7225    let save = editor
 7226        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7227        .unwrap();
 7228    fake_server
 7229        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7230            assert_eq!(
 7231                params.text_document.uri,
 7232                lsp::Url::from_file_path("/file.rs").unwrap()
 7233            );
 7234            assert_eq!(params.options.tab_size, 4);
 7235            Ok(Some(vec![lsp::TextEdit::new(
 7236                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7237                ", ".to_string(),
 7238            )]))
 7239        })
 7240        .next()
 7241        .await;
 7242    cx.executor().start_waiting();
 7243    save.await;
 7244    assert_eq!(
 7245        editor.update(cx, |editor, cx| editor.text(cx)),
 7246        "one, two\nthree\n"
 7247    );
 7248    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7249
 7250    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7251    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7252
 7253    // Ensure we can still save even if formatting hangs.
 7254    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7255        move |params, _| async move {
 7256            assert_eq!(
 7257                params.text_document.uri,
 7258                lsp::Url::from_file_path("/file.rs").unwrap()
 7259            );
 7260            futures::future::pending::<()>().await;
 7261            unreachable!()
 7262        },
 7263    );
 7264    let save = editor
 7265        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7266        .unwrap();
 7267    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7268    cx.executor().start_waiting();
 7269    save.await;
 7270    assert_eq!(
 7271        editor.update(cx, |editor, cx| editor.text(cx)),
 7272        "one\ntwo\nthree\n"
 7273    );
 7274    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7275
 7276    // For non-dirty buffer, no formatting request should be sent
 7277    let save = editor
 7278        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7279        .unwrap();
 7280    let _pending_format_request = fake_server
 7281        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7282            panic!("Should not be invoked on non-dirty buffer");
 7283        })
 7284        .next();
 7285    cx.executor().start_waiting();
 7286    save.await;
 7287
 7288    // Set Rust language override and assert overridden tabsize is sent to language server
 7289    update_test_language_settings(cx, |settings| {
 7290        settings.languages.insert(
 7291            "Rust".into(),
 7292            LanguageSettingsContent {
 7293                tab_size: NonZeroU32::new(8),
 7294                ..Default::default()
 7295            },
 7296        );
 7297    });
 7298
 7299    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
 7300    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7301    let save = editor
 7302        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
 7303        .unwrap();
 7304    fake_server
 7305        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7306            assert_eq!(
 7307                params.text_document.uri,
 7308                lsp::Url::from_file_path("/file.rs").unwrap()
 7309            );
 7310            assert_eq!(params.options.tab_size, 8);
 7311            Ok(Some(vec![]))
 7312        })
 7313        .next()
 7314        .await;
 7315    cx.executor().start_waiting();
 7316    save.await;
 7317}
 7318
 7319#[gpui::test]
 7320async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7321    init_test(cx, |settings| {
 7322        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7323            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7324        ))
 7325    });
 7326
 7327    let fs = FakeFs::new(cx.executor());
 7328    fs.insert_file("/file.rs", Default::default()).await;
 7329
 7330    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 7331
 7332    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7333    language_registry.add(Arc::new(Language::new(
 7334        LanguageConfig {
 7335            name: "Rust".into(),
 7336            matcher: LanguageMatcher {
 7337                path_suffixes: vec!["rs".to_string()],
 7338                ..Default::default()
 7339            },
 7340            ..LanguageConfig::default()
 7341        },
 7342        Some(tree_sitter_rust::LANGUAGE.into()),
 7343    )));
 7344    update_test_language_settings(cx, |settings| {
 7345        // Enable Prettier formatting for the same buffer, and ensure
 7346        // LSP is called instead of Prettier.
 7347        settings.defaults.prettier = Some(PrettierSettings {
 7348            allowed: true,
 7349            ..PrettierSettings::default()
 7350        });
 7351    });
 7352    let mut fake_servers = language_registry.register_fake_lsp(
 7353        "Rust",
 7354        FakeLspAdapter {
 7355            capabilities: lsp::ServerCapabilities {
 7356                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7357                ..Default::default()
 7358            },
 7359            ..Default::default()
 7360        },
 7361    );
 7362
 7363    let buffer = project
 7364        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
 7365        .await
 7366        .unwrap();
 7367
 7368    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 7369    let (editor, cx) =
 7370        cx.add_window_view(|cx| build_editor_with_project(project.clone(), buffer, cx));
 7371    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7372
 7373    cx.executor().start_waiting();
 7374    let fake_server = fake_servers.next().await.unwrap();
 7375
 7376    let format = editor
 7377        .update(cx, |editor, cx| {
 7378            editor.perform_format(
 7379                project.clone(),
 7380                FormatTrigger::Manual,
 7381                FormatTarget::Buffers,
 7382                cx,
 7383            )
 7384        })
 7385        .unwrap();
 7386    fake_server
 7387        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7388            assert_eq!(
 7389                params.text_document.uri,
 7390                lsp::Url::from_file_path("/file.rs").unwrap()
 7391            );
 7392            assert_eq!(params.options.tab_size, 4);
 7393            Ok(Some(vec![lsp::TextEdit::new(
 7394                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7395                ", ".to_string(),
 7396            )]))
 7397        })
 7398        .next()
 7399        .await;
 7400    cx.executor().start_waiting();
 7401    format.await;
 7402    assert_eq!(
 7403        editor.update(cx, |editor, cx| editor.text(cx)),
 7404        "one, two\nthree\n"
 7405    );
 7406
 7407    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 7408    // Ensure we don't lock if formatting hangs.
 7409    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7410        assert_eq!(
 7411            params.text_document.uri,
 7412            lsp::Url::from_file_path("/file.rs").unwrap()
 7413        );
 7414        futures::future::pending::<()>().await;
 7415        unreachable!()
 7416    });
 7417    let format = editor
 7418        .update(cx, |editor, cx| {
 7419            editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffers, cx)
 7420        })
 7421        .unwrap();
 7422    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7423    cx.executor().start_waiting();
 7424    format.await;
 7425    assert_eq!(
 7426        editor.update(cx, |editor, cx| editor.text(cx)),
 7427        "one\ntwo\nthree\n"
 7428    );
 7429}
 7430
 7431#[gpui::test]
 7432async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7433    init_test(cx, |_| {});
 7434
 7435    let mut cx = EditorLspTestContext::new_rust(
 7436        lsp::ServerCapabilities {
 7437            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7438            ..Default::default()
 7439        },
 7440        cx,
 7441    )
 7442    .await;
 7443
 7444    cx.set_state(indoc! {"
 7445        one.twoˇ
 7446    "});
 7447
 7448    // The format request takes a long time. When it completes, it inserts
 7449    // a newline and an indent before the `.`
 7450    cx.lsp
 7451        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7452            let executor = cx.background_executor().clone();
 7453            async move {
 7454                executor.timer(Duration::from_millis(100)).await;
 7455                Ok(Some(vec![lsp::TextEdit {
 7456                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7457                    new_text: "\n    ".into(),
 7458                }]))
 7459            }
 7460        });
 7461
 7462    // Submit a format request.
 7463    let format_1 = cx
 7464        .update_editor(|editor, cx| editor.format(&Format, cx))
 7465        .unwrap();
 7466    cx.executor().run_until_parked();
 7467
 7468    // Submit a second format request.
 7469    let format_2 = cx
 7470        .update_editor(|editor, cx| editor.format(&Format, cx))
 7471        .unwrap();
 7472    cx.executor().run_until_parked();
 7473
 7474    // Wait for both format requests to complete
 7475    cx.executor().advance_clock(Duration::from_millis(200));
 7476    cx.executor().start_waiting();
 7477    format_1.await.unwrap();
 7478    cx.executor().start_waiting();
 7479    format_2.await.unwrap();
 7480
 7481    // The formatting edits only happens once.
 7482    cx.assert_editor_state(indoc! {"
 7483        one
 7484            .twoˇ
 7485    "});
 7486}
 7487
 7488#[gpui::test]
 7489async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7490    init_test(cx, |settings| {
 7491        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7492    });
 7493
 7494    let mut cx = EditorLspTestContext::new_rust(
 7495        lsp::ServerCapabilities {
 7496            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7497            ..Default::default()
 7498        },
 7499        cx,
 7500    )
 7501    .await;
 7502
 7503    // Set up a buffer white some trailing whitespace and no trailing newline.
 7504    cx.set_state(
 7505        &[
 7506            "one ",   //
 7507            "twoˇ",   //
 7508            "three ", //
 7509            "four",   //
 7510        ]
 7511        .join("\n"),
 7512    );
 7513
 7514    // Submit a format request.
 7515    let format = cx
 7516        .update_editor(|editor, cx| editor.format(&Format, cx))
 7517        .unwrap();
 7518
 7519    // Record which buffer changes have been sent to the language server
 7520    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7521    cx.lsp
 7522        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7523            let buffer_changes = buffer_changes.clone();
 7524            move |params, _| {
 7525                buffer_changes.lock().extend(
 7526                    params
 7527                        .content_changes
 7528                        .into_iter()
 7529                        .map(|e| (e.range.unwrap(), e.text)),
 7530                );
 7531            }
 7532        });
 7533
 7534    // Handle formatting requests to the language server.
 7535    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7536        let buffer_changes = buffer_changes.clone();
 7537        move |_, _| {
 7538            // When formatting is requested, trailing whitespace has already been stripped,
 7539            // and the trailing newline has already been added.
 7540            assert_eq!(
 7541                &buffer_changes.lock()[1..],
 7542                &[
 7543                    (
 7544                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7545                        "".into()
 7546                    ),
 7547                    (
 7548                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7549                        "".into()
 7550                    ),
 7551                    (
 7552                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7553                        "\n".into()
 7554                    ),
 7555                ]
 7556            );
 7557
 7558            // Insert blank lines between each line of the buffer.
 7559            async move {
 7560                Ok(Some(vec![
 7561                    lsp::TextEdit {
 7562                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7563                        new_text: "\n".into(),
 7564                    },
 7565                    lsp::TextEdit {
 7566                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7567                        new_text: "\n".into(),
 7568                    },
 7569                ]))
 7570            }
 7571        }
 7572    });
 7573
 7574    // After formatting the buffer, the trailing whitespace is stripped,
 7575    // a newline is appended, and the edits provided by the language server
 7576    // have been applied.
 7577    format.await.unwrap();
 7578    cx.assert_editor_state(
 7579        &[
 7580            "one",   //
 7581            "",      //
 7582            "twoˇ",  //
 7583            "",      //
 7584            "three", //
 7585            "four",  //
 7586            "",      //
 7587        ]
 7588        .join("\n"),
 7589    );
 7590
 7591    // Undoing the formatting undoes the trailing whitespace removal, the
 7592    // trailing newline, and the LSP edits.
 7593    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7594    cx.assert_editor_state(
 7595        &[
 7596            "one ",   //
 7597            "twoˇ",   //
 7598            "three ", //
 7599            "four",   //
 7600        ]
 7601        .join("\n"),
 7602    );
 7603}
 7604
 7605#[gpui::test]
 7606async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7607    cx: &mut gpui::TestAppContext,
 7608) {
 7609    init_test(cx, |_| {});
 7610
 7611    cx.update(|cx| {
 7612        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7613            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7614                settings.auto_signature_help = Some(true);
 7615            });
 7616        });
 7617    });
 7618
 7619    let mut cx = EditorLspTestContext::new_rust(
 7620        lsp::ServerCapabilities {
 7621            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7622                ..Default::default()
 7623            }),
 7624            ..Default::default()
 7625        },
 7626        cx,
 7627    )
 7628    .await;
 7629
 7630    let language = Language::new(
 7631        LanguageConfig {
 7632            name: "Rust".into(),
 7633            brackets: BracketPairConfig {
 7634                pairs: vec![
 7635                    BracketPair {
 7636                        start: "{".to_string(),
 7637                        end: "}".to_string(),
 7638                        close: true,
 7639                        surround: true,
 7640                        newline: true,
 7641                    },
 7642                    BracketPair {
 7643                        start: "(".to_string(),
 7644                        end: ")".to_string(),
 7645                        close: true,
 7646                        surround: true,
 7647                        newline: true,
 7648                    },
 7649                    BracketPair {
 7650                        start: "/*".to_string(),
 7651                        end: " */".to_string(),
 7652                        close: true,
 7653                        surround: true,
 7654                        newline: true,
 7655                    },
 7656                    BracketPair {
 7657                        start: "[".to_string(),
 7658                        end: "]".to_string(),
 7659                        close: false,
 7660                        surround: false,
 7661                        newline: true,
 7662                    },
 7663                    BracketPair {
 7664                        start: "\"".to_string(),
 7665                        end: "\"".to_string(),
 7666                        close: true,
 7667                        surround: true,
 7668                        newline: false,
 7669                    },
 7670                    BracketPair {
 7671                        start: "<".to_string(),
 7672                        end: ">".to_string(),
 7673                        close: false,
 7674                        surround: true,
 7675                        newline: true,
 7676                    },
 7677                ],
 7678                ..Default::default()
 7679            },
 7680            autoclose_before: "})]".to_string(),
 7681            ..Default::default()
 7682        },
 7683        Some(tree_sitter_rust::LANGUAGE.into()),
 7684    );
 7685    let language = Arc::new(language);
 7686
 7687    cx.language_registry().add(language.clone());
 7688    cx.update_buffer(|buffer, cx| {
 7689        buffer.set_language(Some(language), cx);
 7690    });
 7691
 7692    cx.set_state(
 7693        &r#"
 7694            fn main() {
 7695                sampleˇ
 7696            }
 7697        "#
 7698        .unindent(),
 7699    );
 7700
 7701    cx.update_editor(|view, cx| {
 7702        view.handle_input("(", cx);
 7703    });
 7704    cx.assert_editor_state(
 7705        &"
 7706            fn main() {
 7707                sample(ˇ)
 7708            }
 7709        "
 7710        .unindent(),
 7711    );
 7712
 7713    let mocked_response = lsp::SignatureHelp {
 7714        signatures: vec![lsp::SignatureInformation {
 7715            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7716            documentation: None,
 7717            parameters: Some(vec![
 7718                lsp::ParameterInformation {
 7719                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7720                    documentation: None,
 7721                },
 7722                lsp::ParameterInformation {
 7723                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7724                    documentation: None,
 7725                },
 7726            ]),
 7727            active_parameter: None,
 7728        }],
 7729        active_signature: Some(0),
 7730        active_parameter: Some(0),
 7731    };
 7732    handle_signature_help_request(&mut cx, mocked_response).await;
 7733
 7734    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7735        .await;
 7736
 7737    cx.editor(|editor, _| {
 7738        let signature_help_state = editor.signature_help_state.popover().cloned();
 7739        assert!(signature_help_state.is_some());
 7740        let ParsedMarkdown {
 7741            text, highlights, ..
 7742        } = signature_help_state.unwrap().parsed_content;
 7743        assert_eq!(text, "param1: u8, param2: u8");
 7744        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7745    });
 7746}
 7747
 7748#[gpui::test]
 7749async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 7750    init_test(cx, |_| {});
 7751
 7752    cx.update(|cx| {
 7753        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7754            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7755                settings.auto_signature_help = Some(false);
 7756                settings.show_signature_help_after_edits = Some(false);
 7757            });
 7758        });
 7759    });
 7760
 7761    let mut cx = EditorLspTestContext::new_rust(
 7762        lsp::ServerCapabilities {
 7763            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7764                ..Default::default()
 7765            }),
 7766            ..Default::default()
 7767        },
 7768        cx,
 7769    )
 7770    .await;
 7771
 7772    let language = Language::new(
 7773        LanguageConfig {
 7774            name: "Rust".into(),
 7775            brackets: BracketPairConfig {
 7776                pairs: vec![
 7777                    BracketPair {
 7778                        start: "{".to_string(),
 7779                        end: "}".to_string(),
 7780                        close: true,
 7781                        surround: true,
 7782                        newline: true,
 7783                    },
 7784                    BracketPair {
 7785                        start: "(".to_string(),
 7786                        end: ")".to_string(),
 7787                        close: true,
 7788                        surround: true,
 7789                        newline: true,
 7790                    },
 7791                    BracketPair {
 7792                        start: "/*".to_string(),
 7793                        end: " */".to_string(),
 7794                        close: true,
 7795                        surround: true,
 7796                        newline: true,
 7797                    },
 7798                    BracketPair {
 7799                        start: "[".to_string(),
 7800                        end: "]".to_string(),
 7801                        close: false,
 7802                        surround: false,
 7803                        newline: true,
 7804                    },
 7805                    BracketPair {
 7806                        start: "\"".to_string(),
 7807                        end: "\"".to_string(),
 7808                        close: true,
 7809                        surround: true,
 7810                        newline: false,
 7811                    },
 7812                    BracketPair {
 7813                        start: "<".to_string(),
 7814                        end: ">".to_string(),
 7815                        close: false,
 7816                        surround: true,
 7817                        newline: true,
 7818                    },
 7819                ],
 7820                ..Default::default()
 7821            },
 7822            autoclose_before: "})]".to_string(),
 7823            ..Default::default()
 7824        },
 7825        Some(tree_sitter_rust::LANGUAGE.into()),
 7826    );
 7827    let language = Arc::new(language);
 7828
 7829    cx.language_registry().add(language.clone());
 7830    cx.update_buffer(|buffer, cx| {
 7831        buffer.set_language(Some(language), cx);
 7832    });
 7833
 7834    // Ensure that signature_help is not called when no signature help is enabled.
 7835    cx.set_state(
 7836        &r#"
 7837            fn main() {
 7838                sampleˇ
 7839            }
 7840        "#
 7841        .unindent(),
 7842    );
 7843    cx.update_editor(|view, cx| {
 7844        view.handle_input("(", cx);
 7845    });
 7846    cx.assert_editor_state(
 7847        &"
 7848            fn main() {
 7849                sample(ˇ)
 7850            }
 7851        "
 7852        .unindent(),
 7853    );
 7854    cx.editor(|editor, _| {
 7855        assert!(editor.signature_help_state.task().is_none());
 7856    });
 7857
 7858    let mocked_response = lsp::SignatureHelp {
 7859        signatures: vec![lsp::SignatureInformation {
 7860            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7861            documentation: None,
 7862            parameters: Some(vec![
 7863                lsp::ParameterInformation {
 7864                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7865                    documentation: None,
 7866                },
 7867                lsp::ParameterInformation {
 7868                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 7869                    documentation: None,
 7870                },
 7871            ]),
 7872            active_parameter: None,
 7873        }],
 7874        active_signature: Some(0),
 7875        active_parameter: Some(0),
 7876    };
 7877
 7878    // Ensure that signature_help is called when enabled afte edits
 7879    cx.update(|cx| {
 7880        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7881            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7882                settings.auto_signature_help = Some(false);
 7883                settings.show_signature_help_after_edits = Some(true);
 7884            });
 7885        });
 7886    });
 7887    cx.set_state(
 7888        &r#"
 7889            fn main() {
 7890                sampleˇ
 7891            }
 7892        "#
 7893        .unindent(),
 7894    );
 7895    cx.update_editor(|view, cx| {
 7896        view.handle_input("(", cx);
 7897    });
 7898    cx.assert_editor_state(
 7899        &"
 7900            fn main() {
 7901                sample(ˇ)
 7902            }
 7903        "
 7904        .unindent(),
 7905    );
 7906    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 7907    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7908        .await;
 7909    cx.update_editor(|editor, _| {
 7910        let signature_help_state = editor.signature_help_state.popover().cloned();
 7911        assert!(signature_help_state.is_some());
 7912        let ParsedMarkdown {
 7913            text, highlights, ..
 7914        } = signature_help_state.unwrap().parsed_content;
 7915        assert_eq!(text, "param1: u8, param2: u8");
 7916        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7917        editor.signature_help_state = SignatureHelpState::default();
 7918    });
 7919
 7920    // Ensure that signature_help is called when auto signature help override is enabled
 7921    cx.update(|cx| {
 7922        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7923            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7924                settings.auto_signature_help = Some(true);
 7925                settings.show_signature_help_after_edits = Some(false);
 7926            });
 7927        });
 7928    });
 7929    cx.set_state(
 7930        &r#"
 7931            fn main() {
 7932                sampleˇ
 7933            }
 7934        "#
 7935        .unindent(),
 7936    );
 7937    cx.update_editor(|view, cx| {
 7938        view.handle_input("(", cx);
 7939    });
 7940    cx.assert_editor_state(
 7941        &"
 7942            fn main() {
 7943                sample(ˇ)
 7944            }
 7945        "
 7946        .unindent(),
 7947    );
 7948    handle_signature_help_request(&mut cx, mocked_response).await;
 7949    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 7950        .await;
 7951    cx.editor(|editor, _| {
 7952        let signature_help_state = editor.signature_help_state.popover().cloned();
 7953        assert!(signature_help_state.is_some());
 7954        let ParsedMarkdown {
 7955            text, highlights, ..
 7956        } = signature_help_state.unwrap().parsed_content;
 7957        assert_eq!(text, "param1: u8, param2: u8");
 7958        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 7959    });
 7960}
 7961
 7962#[gpui::test]
 7963async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 7964    init_test(cx, |_| {});
 7965    cx.update(|cx| {
 7966        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7967            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7968                settings.auto_signature_help = Some(true);
 7969            });
 7970        });
 7971    });
 7972
 7973    let mut cx = EditorLspTestContext::new_rust(
 7974        lsp::ServerCapabilities {
 7975            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7976                ..Default::default()
 7977            }),
 7978            ..Default::default()
 7979        },
 7980        cx,
 7981    )
 7982    .await;
 7983
 7984    // A test that directly calls `show_signature_help`
 7985    cx.update_editor(|editor, cx| {
 7986        editor.show_signature_help(&ShowSignatureHelp, cx);
 7987    });
 7988
 7989    let mocked_response = lsp::SignatureHelp {
 7990        signatures: vec![lsp::SignatureInformation {
 7991            label: "fn sample(param1: u8, param2: u8)".to_string(),
 7992            documentation: None,
 7993            parameters: Some(vec![
 7994                lsp::ParameterInformation {
 7995                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 7996                    documentation: None,
 7997                },
 7998                lsp::ParameterInformation {
 7999                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8000                    documentation: None,
 8001                },
 8002            ]),
 8003            active_parameter: None,
 8004        }],
 8005        active_signature: Some(0),
 8006        active_parameter: Some(0),
 8007    };
 8008    handle_signature_help_request(&mut cx, mocked_response).await;
 8009
 8010    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8011        .await;
 8012
 8013    cx.editor(|editor, _| {
 8014        let signature_help_state = editor.signature_help_state.popover().cloned();
 8015        assert!(signature_help_state.is_some());
 8016        let ParsedMarkdown {
 8017            text, highlights, ..
 8018        } = signature_help_state.unwrap().parsed_content;
 8019        assert_eq!(text, "param1: u8, param2: u8");
 8020        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8021    });
 8022
 8023    // When exiting outside from inside the brackets, `signature_help` is closed.
 8024    cx.set_state(indoc! {"
 8025        fn main() {
 8026            sample(ˇ);
 8027        }
 8028
 8029        fn sample(param1: u8, param2: u8) {}
 8030    "});
 8031
 8032    cx.update_editor(|editor, cx| {
 8033        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 8034    });
 8035
 8036    let mocked_response = lsp::SignatureHelp {
 8037        signatures: Vec::new(),
 8038        active_signature: None,
 8039        active_parameter: None,
 8040    };
 8041    handle_signature_help_request(&mut cx, mocked_response).await;
 8042
 8043    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8044        .await;
 8045
 8046    cx.editor(|editor, _| {
 8047        assert!(!editor.signature_help_state.is_shown());
 8048    });
 8049
 8050    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8051    cx.set_state(indoc! {"
 8052        fn main() {
 8053            sample(ˇ);
 8054        }
 8055
 8056        fn sample(param1: u8, param2: u8) {}
 8057    "});
 8058
 8059    let mocked_response = lsp::SignatureHelp {
 8060        signatures: vec![lsp::SignatureInformation {
 8061            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8062            documentation: None,
 8063            parameters: Some(vec![
 8064                lsp::ParameterInformation {
 8065                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8066                    documentation: None,
 8067                },
 8068                lsp::ParameterInformation {
 8069                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8070                    documentation: None,
 8071                },
 8072            ]),
 8073            active_parameter: None,
 8074        }],
 8075        active_signature: Some(0),
 8076        active_parameter: Some(0),
 8077    };
 8078    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8079    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8080        .await;
 8081    cx.editor(|editor, _| {
 8082        assert!(editor.signature_help_state.is_shown());
 8083    });
 8084
 8085    // Restore the popover with more parameter input
 8086    cx.set_state(indoc! {"
 8087        fn main() {
 8088            sample(param1, param2ˇ);
 8089        }
 8090
 8091        fn sample(param1: u8, param2: u8) {}
 8092    "});
 8093
 8094    let mocked_response = lsp::SignatureHelp {
 8095        signatures: vec![lsp::SignatureInformation {
 8096            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8097            documentation: None,
 8098            parameters: Some(vec![
 8099                lsp::ParameterInformation {
 8100                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8101                    documentation: None,
 8102                },
 8103                lsp::ParameterInformation {
 8104                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8105                    documentation: None,
 8106                },
 8107            ]),
 8108            active_parameter: None,
 8109        }],
 8110        active_signature: Some(0),
 8111        active_parameter: Some(1),
 8112    };
 8113    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8114    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8115        .await;
 8116
 8117    // When selecting a range, the popover is gone.
 8118    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8119    cx.update_editor(|editor, cx| {
 8120        editor.change_selections(None, cx, |s| {
 8121            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8122        })
 8123    });
 8124    cx.assert_editor_state(indoc! {"
 8125        fn main() {
 8126            sample(param1, «ˇparam2»);
 8127        }
 8128
 8129        fn sample(param1: u8, param2: u8) {}
 8130    "});
 8131    cx.editor(|editor, _| {
 8132        assert!(!editor.signature_help_state.is_shown());
 8133    });
 8134
 8135    // When unselecting again, the popover is back if within the brackets.
 8136    cx.update_editor(|editor, cx| {
 8137        editor.change_selections(None, cx, |s| {
 8138            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8139        })
 8140    });
 8141    cx.assert_editor_state(indoc! {"
 8142        fn main() {
 8143            sample(param1, ˇparam2);
 8144        }
 8145
 8146        fn sample(param1: u8, param2: u8) {}
 8147    "});
 8148    handle_signature_help_request(&mut cx, mocked_response).await;
 8149    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8150        .await;
 8151    cx.editor(|editor, _| {
 8152        assert!(editor.signature_help_state.is_shown());
 8153    });
 8154
 8155    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8156    cx.update_editor(|editor, cx| {
 8157        editor.change_selections(None, cx, |s| {
 8158            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8159            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8160        })
 8161    });
 8162    cx.assert_editor_state(indoc! {"
 8163        fn main() {
 8164            sample(param1, ˇparam2);
 8165        }
 8166
 8167        fn sample(param1: u8, param2: u8) {}
 8168    "});
 8169
 8170    let mocked_response = lsp::SignatureHelp {
 8171        signatures: vec![lsp::SignatureInformation {
 8172            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8173            documentation: None,
 8174            parameters: Some(vec![
 8175                lsp::ParameterInformation {
 8176                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8177                    documentation: None,
 8178                },
 8179                lsp::ParameterInformation {
 8180                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8181                    documentation: None,
 8182                },
 8183            ]),
 8184            active_parameter: None,
 8185        }],
 8186        active_signature: Some(0),
 8187        active_parameter: Some(1),
 8188    };
 8189    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8190    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8191        .await;
 8192    cx.update_editor(|editor, cx| {
 8193        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8194    });
 8195    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8196        .await;
 8197    cx.update_editor(|editor, cx| {
 8198        editor.change_selections(None, cx, |s| {
 8199            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8200        })
 8201    });
 8202    cx.assert_editor_state(indoc! {"
 8203        fn main() {
 8204            sample(param1, «ˇparam2»);
 8205        }
 8206
 8207        fn sample(param1: u8, param2: u8) {}
 8208    "});
 8209    cx.update_editor(|editor, cx| {
 8210        editor.change_selections(None, cx, |s| {
 8211            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8212        })
 8213    });
 8214    cx.assert_editor_state(indoc! {"
 8215        fn main() {
 8216            sample(param1, ˇparam2);
 8217        }
 8218
 8219        fn sample(param1: u8, param2: u8) {}
 8220    "});
 8221    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8222        .await;
 8223}
 8224
 8225#[gpui::test]
 8226async fn test_completion(cx: &mut gpui::TestAppContext) {
 8227    init_test(cx, |_| {});
 8228
 8229    let mut cx = EditorLspTestContext::new_rust(
 8230        lsp::ServerCapabilities {
 8231            completion_provider: Some(lsp::CompletionOptions {
 8232                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8233                resolve_provider: Some(true),
 8234                ..Default::default()
 8235            }),
 8236            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8237            ..Default::default()
 8238        },
 8239        cx,
 8240    )
 8241    .await;
 8242    let counter = Arc::new(AtomicUsize::new(0));
 8243
 8244    cx.set_state(indoc! {"
 8245        oneˇ
 8246        two
 8247        three
 8248    "});
 8249    cx.simulate_keystroke(".");
 8250    handle_completion_request(
 8251        &mut cx,
 8252        indoc! {"
 8253            one.|<>
 8254            two
 8255            three
 8256        "},
 8257        vec!["first_completion", "second_completion"],
 8258        counter.clone(),
 8259    )
 8260    .await;
 8261    cx.condition(|editor, _| editor.context_menu_visible())
 8262        .await;
 8263    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8264
 8265    let _handler = handle_signature_help_request(
 8266        &mut cx,
 8267        lsp::SignatureHelp {
 8268            signatures: vec![lsp::SignatureInformation {
 8269                label: "test signature".to_string(),
 8270                documentation: None,
 8271                parameters: Some(vec![lsp::ParameterInformation {
 8272                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8273                    documentation: None,
 8274                }]),
 8275                active_parameter: None,
 8276            }],
 8277            active_signature: None,
 8278            active_parameter: None,
 8279        },
 8280    );
 8281    cx.update_editor(|editor, cx| {
 8282        assert!(
 8283            !editor.signature_help_state.is_shown(),
 8284            "No signature help was called for"
 8285        );
 8286        editor.show_signature_help(&ShowSignatureHelp, cx);
 8287    });
 8288    cx.run_until_parked();
 8289    cx.update_editor(|editor, _| {
 8290        assert!(
 8291            !editor.signature_help_state.is_shown(),
 8292            "No signature help should be shown when completions menu is open"
 8293        );
 8294    });
 8295
 8296    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8297        editor.context_menu_next(&Default::default(), cx);
 8298        editor
 8299            .confirm_completion(&ConfirmCompletion::default(), cx)
 8300            .unwrap()
 8301    });
 8302    cx.assert_editor_state(indoc! {"
 8303        one.second_completionˇ
 8304        two
 8305        three
 8306    "});
 8307
 8308    handle_resolve_completion_request(
 8309        &mut cx,
 8310        Some(vec![
 8311            (
 8312                //This overlaps with the primary completion edit which is
 8313                //misbehavior from the LSP spec, test that we filter it out
 8314                indoc! {"
 8315                    one.second_ˇcompletion
 8316                    two
 8317                    threeˇ
 8318                "},
 8319                "overlapping additional edit",
 8320            ),
 8321            (
 8322                indoc! {"
 8323                    one.second_completion
 8324                    two
 8325                    threeˇ
 8326                "},
 8327                "\nadditional edit",
 8328            ),
 8329        ]),
 8330    )
 8331    .await;
 8332    apply_additional_edits.await.unwrap();
 8333    cx.assert_editor_state(indoc! {"
 8334        one.second_completionˇ
 8335        two
 8336        three
 8337        additional edit
 8338    "});
 8339
 8340    cx.set_state(indoc! {"
 8341        one.second_completion
 8342        twoˇ
 8343        threeˇ
 8344        additional edit
 8345    "});
 8346    cx.simulate_keystroke(" ");
 8347    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8348    cx.simulate_keystroke("s");
 8349    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8350
 8351    cx.assert_editor_state(indoc! {"
 8352        one.second_completion
 8353        two sˇ
 8354        three sˇ
 8355        additional edit
 8356    "});
 8357    handle_completion_request(
 8358        &mut cx,
 8359        indoc! {"
 8360            one.second_completion
 8361            two s
 8362            three <s|>
 8363            additional edit
 8364        "},
 8365        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8366        counter.clone(),
 8367    )
 8368    .await;
 8369    cx.condition(|editor, _| editor.context_menu_visible())
 8370        .await;
 8371    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8372
 8373    cx.simulate_keystroke("i");
 8374
 8375    handle_completion_request(
 8376        &mut cx,
 8377        indoc! {"
 8378            one.second_completion
 8379            two si
 8380            three <si|>
 8381            additional edit
 8382        "},
 8383        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8384        counter.clone(),
 8385    )
 8386    .await;
 8387    cx.condition(|editor, _| editor.context_menu_visible())
 8388        .await;
 8389    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8390
 8391    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8392        editor
 8393            .confirm_completion(&ConfirmCompletion::default(), cx)
 8394            .unwrap()
 8395    });
 8396    cx.assert_editor_state(indoc! {"
 8397        one.second_completion
 8398        two sixth_completionˇ
 8399        three sixth_completionˇ
 8400        additional edit
 8401    "});
 8402
 8403    apply_additional_edits.await.unwrap();
 8404
 8405    update_test_language_settings(&mut cx, |settings| {
 8406        settings.defaults.show_completions_on_input = Some(false);
 8407    });
 8408    cx.set_state("editorˇ");
 8409    cx.simulate_keystroke(".");
 8410    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8411    cx.simulate_keystroke("c");
 8412    cx.simulate_keystroke("l");
 8413    cx.simulate_keystroke("o");
 8414    cx.assert_editor_state("editor.cloˇ");
 8415    assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
 8416    cx.update_editor(|editor, cx| {
 8417        editor.show_completions(&ShowCompletions { trigger: None }, cx);
 8418    });
 8419    handle_completion_request(
 8420        &mut cx,
 8421        "editor.<clo|>",
 8422        vec!["close", "clobber"],
 8423        counter.clone(),
 8424    )
 8425    .await;
 8426    cx.condition(|editor, _| editor.context_menu_visible())
 8427        .await;
 8428    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8429
 8430    let apply_additional_edits = cx.update_editor(|editor, cx| {
 8431        editor
 8432            .confirm_completion(&ConfirmCompletion::default(), cx)
 8433            .unwrap()
 8434    });
 8435    cx.assert_editor_state("editor.closeˇ");
 8436    handle_resolve_completion_request(&mut cx, None).await;
 8437    apply_additional_edits.await.unwrap();
 8438}
 8439
 8440#[gpui::test]
 8441async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8442    init_test(cx, |_| {});
 8443
 8444    let fs = FakeFs::new(cx.executor());
 8445    fs.insert_tree(
 8446        "/a",
 8447        json!({
 8448            "main.ts": "a",
 8449        }),
 8450    )
 8451    .await;
 8452
 8453    let project = Project::test(fs, ["/a".as_ref()], cx).await;
 8454    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8455    let typescript_language = Arc::new(Language::new(
 8456        LanguageConfig {
 8457            name: "TypeScript".into(),
 8458            matcher: LanguageMatcher {
 8459                path_suffixes: vec!["ts".to_string()],
 8460                ..LanguageMatcher::default()
 8461            },
 8462            line_comments: vec!["// ".into()],
 8463            ..LanguageConfig::default()
 8464        },
 8465        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8466    ));
 8467    language_registry.add(typescript_language.clone());
 8468    let mut fake_servers = language_registry.register_fake_lsp(
 8469        "TypeScript",
 8470        FakeLspAdapter {
 8471            capabilities: lsp::ServerCapabilities {
 8472                completion_provider: Some(lsp::CompletionOptions {
 8473                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8474                    ..lsp::CompletionOptions::default()
 8475                }),
 8476                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8477                ..lsp::ServerCapabilities::default()
 8478            },
 8479            // Emulate vtsls label generation
 8480            label_for_completion: Some(Box::new(|item, _| {
 8481                let text = if let Some(description) = item
 8482                    .label_details
 8483                    .as_ref()
 8484                    .and_then(|label_details| label_details.description.as_ref())
 8485                {
 8486                    format!("{} {}", item.label, description)
 8487                } else if let Some(detail) = &item.detail {
 8488                    format!("{} {}", item.label, detail)
 8489                } else {
 8490                    item.label.clone()
 8491                };
 8492                let len = text.len();
 8493                Some(language::CodeLabel {
 8494                    text,
 8495                    runs: Vec::new(),
 8496                    filter_range: 0..len,
 8497                })
 8498            })),
 8499            ..FakeLspAdapter::default()
 8500        },
 8501    );
 8502    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
 8503    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8504    let worktree_id = workspace
 8505        .update(cx, |workspace, cx| {
 8506            workspace.project().update(cx, |project, cx| {
 8507                project.worktrees(cx).next().unwrap().read(cx).id()
 8508            })
 8509        })
 8510        .unwrap();
 8511    let _buffer = project
 8512        .update(cx, |project, cx| {
 8513            project.open_local_buffer_with_lsp("/a/main.ts", cx)
 8514        })
 8515        .await
 8516        .unwrap();
 8517    let editor = workspace
 8518        .update(cx, |workspace, cx| {
 8519            workspace.open_path((worktree_id, "main.ts"), None, true, cx)
 8520        })
 8521        .unwrap()
 8522        .await
 8523        .unwrap()
 8524        .downcast::<Editor>()
 8525        .unwrap();
 8526    let fake_server = fake_servers.next().await.unwrap();
 8527
 8528    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8529    let multiline_label_2 = "a\nb\nc\n";
 8530    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8531    let multiline_description = "d\ne\nf\n";
 8532    let multiline_detail_2 = "g\nh\ni\n";
 8533
 8534    let mut completion_handle =
 8535        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8536            Ok(Some(lsp::CompletionResponse::Array(vec![
 8537                lsp::CompletionItem {
 8538                    label: multiline_label.to_string(),
 8539                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8540                        range: lsp::Range {
 8541                            start: lsp::Position {
 8542                                line: params.text_document_position.position.line,
 8543                                character: params.text_document_position.position.character,
 8544                            },
 8545                            end: lsp::Position {
 8546                                line: params.text_document_position.position.line,
 8547                                character: params.text_document_position.position.character,
 8548                            },
 8549                        },
 8550                        new_text: "new_text_1".to_string(),
 8551                    })),
 8552                    ..lsp::CompletionItem::default()
 8553                },
 8554                lsp::CompletionItem {
 8555                    label: "single line label 1".to_string(),
 8556                    detail: Some(multiline_detail.to_string()),
 8557                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8558                        range: lsp::Range {
 8559                            start: lsp::Position {
 8560                                line: params.text_document_position.position.line,
 8561                                character: params.text_document_position.position.character,
 8562                            },
 8563                            end: lsp::Position {
 8564                                line: params.text_document_position.position.line,
 8565                                character: params.text_document_position.position.character,
 8566                            },
 8567                        },
 8568                        new_text: "new_text_2".to_string(),
 8569                    })),
 8570                    ..lsp::CompletionItem::default()
 8571                },
 8572                lsp::CompletionItem {
 8573                    label: "single line label 2".to_string(),
 8574                    label_details: Some(lsp::CompletionItemLabelDetails {
 8575                        description: Some(multiline_description.to_string()),
 8576                        detail: None,
 8577                    }),
 8578                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8579                        range: lsp::Range {
 8580                            start: lsp::Position {
 8581                                line: params.text_document_position.position.line,
 8582                                character: params.text_document_position.position.character,
 8583                            },
 8584                            end: lsp::Position {
 8585                                line: params.text_document_position.position.line,
 8586                                character: params.text_document_position.position.character,
 8587                            },
 8588                        },
 8589                        new_text: "new_text_2".to_string(),
 8590                    })),
 8591                    ..lsp::CompletionItem::default()
 8592                },
 8593                lsp::CompletionItem {
 8594                    label: multiline_label_2.to_string(),
 8595                    detail: Some(multiline_detail_2.to_string()),
 8596                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8597                        range: lsp::Range {
 8598                            start: lsp::Position {
 8599                                line: params.text_document_position.position.line,
 8600                                character: params.text_document_position.position.character,
 8601                            },
 8602                            end: lsp::Position {
 8603                                line: params.text_document_position.position.line,
 8604                                character: params.text_document_position.position.character,
 8605                            },
 8606                        },
 8607                        new_text: "new_text_3".to_string(),
 8608                    })),
 8609                    ..lsp::CompletionItem::default()
 8610                },
 8611                lsp::CompletionItem {
 8612                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8613                    detail: Some(
 8614                        "Details with many     spaces and \t but without newlines".to_string(),
 8615                    ),
 8616                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8617                        range: lsp::Range {
 8618                            start: lsp::Position {
 8619                                line: params.text_document_position.position.line,
 8620                                character: params.text_document_position.position.character,
 8621                            },
 8622                            end: lsp::Position {
 8623                                line: params.text_document_position.position.line,
 8624                                character: params.text_document_position.position.character,
 8625                            },
 8626                        },
 8627                        new_text: "new_text_4".to_string(),
 8628                    })),
 8629                    ..lsp::CompletionItem::default()
 8630                },
 8631            ])))
 8632        });
 8633
 8634    editor.update(cx, |editor, cx| {
 8635        editor.focus(cx);
 8636        editor.move_to_end(&MoveToEnd, cx);
 8637        editor.handle_input(".", cx);
 8638    });
 8639    cx.run_until_parked();
 8640    completion_handle.next().await.unwrap();
 8641
 8642    editor.update(cx, |editor, _| {
 8643        assert!(editor.context_menu_visible());
 8644        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8645        {
 8646            let completion_labels = menu
 8647                .completions
 8648                .borrow()
 8649                .iter()
 8650                .map(|c| c.label.text.clone())
 8651                .collect::<Vec<_>>();
 8652            assert_eq!(
 8653                completion_labels,
 8654                &[
 8655                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8656                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8657                    "single line label 2 d e f ",
 8658                    "a b c g h i ",
 8659                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8660                ],
 8661                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8662            );
 8663
 8664            for completion in menu
 8665                .completions
 8666                .borrow()
 8667                .iter() {
 8668                    assert_eq!(
 8669                        completion.label.filter_range,
 8670                        0..completion.label.text.len(),
 8671                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8672                    );
 8673                }
 8674
 8675        } else {
 8676            panic!("expected completion menu to be open");
 8677        }
 8678    });
 8679}
 8680
 8681#[gpui::test]
 8682async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8683    init_test(cx, |_| {});
 8684    let mut cx = EditorLspTestContext::new_rust(
 8685        lsp::ServerCapabilities {
 8686            completion_provider: Some(lsp::CompletionOptions {
 8687                trigger_characters: Some(vec![".".to_string()]),
 8688                ..Default::default()
 8689            }),
 8690            ..Default::default()
 8691        },
 8692        cx,
 8693    )
 8694    .await;
 8695    cx.lsp
 8696        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8697            Ok(Some(lsp::CompletionResponse::Array(vec![
 8698                lsp::CompletionItem {
 8699                    label: "first".into(),
 8700                    ..Default::default()
 8701                },
 8702                lsp::CompletionItem {
 8703                    label: "last".into(),
 8704                    ..Default::default()
 8705                },
 8706            ])))
 8707        });
 8708    cx.set_state("variableˇ");
 8709    cx.simulate_keystroke(".");
 8710    cx.executor().run_until_parked();
 8711
 8712    cx.update_editor(|editor, _| {
 8713        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8714        {
 8715            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 8716        } else {
 8717            panic!("expected completion menu to be open");
 8718        }
 8719    });
 8720
 8721    cx.update_editor(|editor, cx| {
 8722        editor.move_page_down(&MovePageDown::default(), cx);
 8723        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8724        {
 8725            assert!(
 8726                menu.selected_item == 1,
 8727                "expected PageDown to select the last item from the context menu"
 8728            );
 8729        } else {
 8730            panic!("expected completion menu to stay open after PageDown");
 8731        }
 8732    });
 8733
 8734    cx.update_editor(|editor, cx| {
 8735        editor.move_page_up(&MovePageUp::default(), cx);
 8736        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8737        {
 8738            assert!(
 8739                menu.selected_item == 0,
 8740                "expected PageUp to select the first item from the context menu"
 8741            );
 8742        } else {
 8743            panic!("expected completion menu to stay open after PageUp");
 8744        }
 8745    });
 8746}
 8747
 8748#[gpui::test]
 8749async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 8750    init_test(cx, |_| {});
 8751    let mut cx = EditorLspTestContext::new_rust(
 8752        lsp::ServerCapabilities {
 8753            completion_provider: Some(lsp::CompletionOptions {
 8754                trigger_characters: Some(vec![".".to_string()]),
 8755                ..Default::default()
 8756            }),
 8757            ..Default::default()
 8758        },
 8759        cx,
 8760    )
 8761    .await;
 8762    cx.lsp
 8763        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8764            Ok(Some(lsp::CompletionResponse::Array(vec![
 8765                lsp::CompletionItem {
 8766                    label: "Range".into(),
 8767                    sort_text: Some("a".into()),
 8768                    ..Default::default()
 8769                },
 8770                lsp::CompletionItem {
 8771                    label: "r".into(),
 8772                    sort_text: Some("b".into()),
 8773                    ..Default::default()
 8774                },
 8775                lsp::CompletionItem {
 8776                    label: "ret".into(),
 8777                    sort_text: Some("c".into()),
 8778                    ..Default::default()
 8779                },
 8780                lsp::CompletionItem {
 8781                    label: "return".into(),
 8782                    sort_text: Some("d".into()),
 8783                    ..Default::default()
 8784                },
 8785                lsp::CompletionItem {
 8786                    label: "slice".into(),
 8787                    sort_text: Some("d".into()),
 8788                    ..Default::default()
 8789                },
 8790            ])))
 8791        });
 8792    cx.set_state("");
 8793    cx.executor().run_until_parked();
 8794    cx.update_editor(|editor, cx| {
 8795        editor.show_completions(
 8796            &ShowCompletions {
 8797                trigger: Some("r".into()),
 8798            },
 8799            cx,
 8800        );
 8801    });
 8802    cx.executor().run_until_parked();
 8803
 8804    cx.update_editor(|editor, _| {
 8805        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8806        {
 8807            assert_eq!(
 8808                completion_menu_entries(&menu),
 8809                &["r", "ret", "Range", "return"]
 8810            );
 8811        } else {
 8812            panic!("expected completion menu to be open");
 8813        }
 8814    });
 8815}
 8816
 8817#[gpui::test]
 8818async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 8819    init_test(cx, |_| {});
 8820
 8821    let mut cx = EditorLspTestContext::new_rust(
 8822        lsp::ServerCapabilities {
 8823            completion_provider: Some(lsp::CompletionOptions {
 8824                trigger_characters: Some(vec![".".to_string()]),
 8825                resolve_provider: Some(true),
 8826                ..Default::default()
 8827            }),
 8828            ..Default::default()
 8829        },
 8830        cx,
 8831    )
 8832    .await;
 8833
 8834    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 8835    cx.simulate_keystroke(".");
 8836    let completion_item = lsp::CompletionItem {
 8837        label: "Some".into(),
 8838        kind: Some(lsp::CompletionItemKind::SNIPPET),
 8839        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 8840        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 8841            kind: lsp::MarkupKind::Markdown,
 8842            value: "```rust\nSome(2)\n```".to_string(),
 8843        })),
 8844        deprecated: Some(false),
 8845        sort_text: Some("Some".to_string()),
 8846        filter_text: Some("Some".to_string()),
 8847        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 8848        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8849            range: lsp::Range {
 8850                start: lsp::Position {
 8851                    line: 0,
 8852                    character: 22,
 8853                },
 8854                end: lsp::Position {
 8855                    line: 0,
 8856                    character: 22,
 8857                },
 8858            },
 8859            new_text: "Some(2)".to_string(),
 8860        })),
 8861        additional_text_edits: Some(vec![lsp::TextEdit {
 8862            range: lsp::Range {
 8863                start: lsp::Position {
 8864                    line: 0,
 8865                    character: 20,
 8866                },
 8867                end: lsp::Position {
 8868                    line: 0,
 8869                    character: 22,
 8870                },
 8871            },
 8872            new_text: "".to_string(),
 8873        }]),
 8874        ..Default::default()
 8875    };
 8876
 8877    let closure_completion_item = completion_item.clone();
 8878    let counter = Arc::new(AtomicUsize::new(0));
 8879    let counter_clone = counter.clone();
 8880    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 8881        let task_completion_item = closure_completion_item.clone();
 8882        counter_clone.fetch_add(1, atomic::Ordering::Release);
 8883        async move {
 8884            Ok(Some(lsp::CompletionResponse::Array(vec![
 8885                task_completion_item,
 8886            ])))
 8887        }
 8888    });
 8889
 8890    cx.condition(|editor, _| editor.context_menu_visible())
 8891        .await;
 8892    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 8893    assert!(request.next().await.is_some());
 8894    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8895
 8896    cx.simulate_keystroke("S");
 8897    cx.simulate_keystroke("o");
 8898    cx.simulate_keystroke("m");
 8899    cx.condition(|editor, _| editor.context_menu_visible())
 8900        .await;
 8901    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 8902    assert!(request.next().await.is_some());
 8903    assert!(request.next().await.is_some());
 8904    assert!(request.next().await.is_some());
 8905    request.close();
 8906    assert!(request.next().await.is_none());
 8907    assert_eq!(
 8908        counter.load(atomic::Ordering::Acquire),
 8909        4,
 8910        "With the completions menu open, only one LSP request should happen per input"
 8911    );
 8912}
 8913
 8914#[gpui::test]
 8915async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 8916    init_test(cx, |_| {});
 8917    let mut cx = EditorTestContext::new(cx).await;
 8918    let language = Arc::new(Language::new(
 8919        LanguageConfig {
 8920            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 8921            ..Default::default()
 8922        },
 8923        Some(tree_sitter_rust::LANGUAGE.into()),
 8924    ));
 8925    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 8926
 8927    // If multiple selections intersect a line, the line is only toggled once.
 8928    cx.set_state(indoc! {"
 8929        fn a() {
 8930            «//b();
 8931            ˇ»// «c();
 8932            //ˇ»  d();
 8933        }
 8934    "});
 8935
 8936    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8937
 8938    cx.assert_editor_state(indoc! {"
 8939        fn a() {
 8940            «b();
 8941            c();
 8942            ˇ» d();
 8943        }
 8944    "});
 8945
 8946    // The comment prefix is inserted at the same column for every line in a
 8947    // selection.
 8948    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8949
 8950    cx.assert_editor_state(indoc! {"
 8951        fn a() {
 8952            // «b();
 8953            // c();
 8954            ˇ»//  d();
 8955        }
 8956    "});
 8957
 8958    // If a selection ends at the beginning of a line, that line is not toggled.
 8959    cx.set_selections_state(indoc! {"
 8960        fn a() {
 8961            // b();
 8962            «// c();
 8963        ˇ»    //  d();
 8964        }
 8965    "});
 8966
 8967    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8968
 8969    cx.assert_editor_state(indoc! {"
 8970        fn a() {
 8971            // b();
 8972            «c();
 8973        ˇ»    //  d();
 8974        }
 8975    "});
 8976
 8977    // If a selection span a single line and is empty, the line is toggled.
 8978    cx.set_state(indoc! {"
 8979        fn a() {
 8980            a();
 8981            b();
 8982        ˇ
 8983        }
 8984    "});
 8985
 8986    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 8987
 8988    cx.assert_editor_state(indoc! {"
 8989        fn a() {
 8990            a();
 8991            b();
 8992        //•ˇ
 8993        }
 8994    "});
 8995
 8996    // If a selection span multiple lines, empty lines are not toggled.
 8997    cx.set_state(indoc! {"
 8998        fn a() {
 8999            «a();
 9000
 9001            c();ˇ»
 9002        }
 9003    "});
 9004
 9005    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9006
 9007    cx.assert_editor_state(indoc! {"
 9008        fn a() {
 9009            // «a();
 9010
 9011            // c();ˇ»
 9012        }
 9013    "});
 9014
 9015    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9016    cx.set_state(indoc! {"
 9017        fn a() {
 9018            «// a();
 9019            /// b();
 9020            //! c();ˇ»
 9021        }
 9022    "});
 9023
 9024    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
 9025
 9026    cx.assert_editor_state(indoc! {"
 9027        fn a() {
 9028            «a();
 9029            b();
 9030            c();ˇ»
 9031        }
 9032    "});
 9033}
 9034
 9035#[gpui::test]
 9036async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9037    init_test(cx, |_| {});
 9038    let mut cx = EditorTestContext::new(cx).await;
 9039    let language = Arc::new(Language::new(
 9040        LanguageConfig {
 9041            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9042            ..Default::default()
 9043        },
 9044        Some(tree_sitter_rust::LANGUAGE.into()),
 9045    ));
 9046    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9047
 9048    let toggle_comments = &ToggleComments {
 9049        advance_downwards: false,
 9050        ignore_indent: true,
 9051    };
 9052
 9053    // If multiple selections intersect a line, the line is only toggled once.
 9054    cx.set_state(indoc! {"
 9055        fn a() {
 9056        //    «b();
 9057        //    c();
 9058        //    ˇ» d();
 9059        }
 9060    "});
 9061
 9062    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9063
 9064    cx.assert_editor_state(indoc! {"
 9065        fn a() {
 9066            «b();
 9067            c();
 9068            ˇ» d();
 9069        }
 9070    "});
 9071
 9072    // The comment prefix is inserted at the beginning of each line
 9073    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9074
 9075    cx.assert_editor_state(indoc! {"
 9076        fn a() {
 9077        //    «b();
 9078        //    c();
 9079        //    ˇ» d();
 9080        }
 9081    "});
 9082
 9083    // If a selection ends at the beginning of a line, that line is not toggled.
 9084    cx.set_selections_state(indoc! {"
 9085        fn a() {
 9086        //    b();
 9087        //    «c();
 9088        ˇ»//     d();
 9089        }
 9090    "});
 9091
 9092    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9093
 9094    cx.assert_editor_state(indoc! {"
 9095        fn a() {
 9096        //    b();
 9097            «c();
 9098        ˇ»//     d();
 9099        }
 9100    "});
 9101
 9102    // If a selection span a single line and is empty, the line is toggled.
 9103    cx.set_state(indoc! {"
 9104        fn a() {
 9105            a();
 9106            b();
 9107        ˇ
 9108        }
 9109    "});
 9110
 9111    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9112
 9113    cx.assert_editor_state(indoc! {"
 9114        fn a() {
 9115            a();
 9116            b();
 9117        //ˇ
 9118        }
 9119    "});
 9120
 9121    // If a selection span multiple lines, empty lines are not toggled.
 9122    cx.set_state(indoc! {"
 9123        fn a() {
 9124            «a();
 9125
 9126            c();ˇ»
 9127        }
 9128    "});
 9129
 9130    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9131
 9132    cx.assert_editor_state(indoc! {"
 9133        fn a() {
 9134        //    «a();
 9135
 9136        //    c();ˇ»
 9137        }
 9138    "});
 9139
 9140    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9141    cx.set_state(indoc! {"
 9142        fn a() {
 9143        //    «a();
 9144        ///    b();
 9145        //!    c();ˇ»
 9146        }
 9147    "});
 9148
 9149    cx.update_editor(|e, cx| e.toggle_comments(toggle_comments, cx));
 9150
 9151    cx.assert_editor_state(indoc! {"
 9152        fn a() {
 9153            «a();
 9154            b();
 9155            c();ˇ»
 9156        }
 9157    "});
 9158}
 9159
 9160#[gpui::test]
 9161async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9162    init_test(cx, |_| {});
 9163
 9164    let language = Arc::new(Language::new(
 9165        LanguageConfig {
 9166            line_comments: vec!["// ".into()],
 9167            ..Default::default()
 9168        },
 9169        Some(tree_sitter_rust::LANGUAGE.into()),
 9170    ));
 9171
 9172    let mut cx = EditorTestContext::new(cx).await;
 9173
 9174    cx.language_registry().add(language.clone());
 9175    cx.update_buffer(|buffer, cx| {
 9176        buffer.set_language(Some(language), cx);
 9177    });
 9178
 9179    let toggle_comments = &ToggleComments {
 9180        advance_downwards: true,
 9181        ignore_indent: false,
 9182    };
 9183
 9184    // Single cursor on one line -> advance
 9185    // Cursor moves horizontally 3 characters as well on non-blank line
 9186    cx.set_state(indoc!(
 9187        "fn a() {
 9188             ˇdog();
 9189             cat();
 9190        }"
 9191    ));
 9192    cx.update_editor(|editor, cx| {
 9193        editor.toggle_comments(toggle_comments, cx);
 9194    });
 9195    cx.assert_editor_state(indoc!(
 9196        "fn a() {
 9197             // dog();
 9198             catˇ();
 9199        }"
 9200    ));
 9201
 9202    // Single selection on one line -> don't advance
 9203    cx.set_state(indoc!(
 9204        "fn a() {
 9205             «dog()ˇ»;
 9206             cat();
 9207        }"
 9208    ));
 9209    cx.update_editor(|editor, cx| {
 9210        editor.toggle_comments(toggle_comments, cx);
 9211    });
 9212    cx.assert_editor_state(indoc!(
 9213        "fn a() {
 9214             // «dog()ˇ»;
 9215             cat();
 9216        }"
 9217    ));
 9218
 9219    // Multiple cursors on one line -> advance
 9220    cx.set_state(indoc!(
 9221        "fn a() {
 9222             ˇdˇog();
 9223             cat();
 9224        }"
 9225    ));
 9226    cx.update_editor(|editor, cx| {
 9227        editor.toggle_comments(toggle_comments, cx);
 9228    });
 9229    cx.assert_editor_state(indoc!(
 9230        "fn a() {
 9231             // dog();
 9232             catˇ(ˇ);
 9233        }"
 9234    ));
 9235
 9236    // Multiple cursors on one line, with selection -> don't advance
 9237    cx.set_state(indoc!(
 9238        "fn a() {
 9239             ˇdˇog«()ˇ»;
 9240             cat();
 9241        }"
 9242    ));
 9243    cx.update_editor(|editor, cx| {
 9244        editor.toggle_comments(toggle_comments, cx);
 9245    });
 9246    cx.assert_editor_state(indoc!(
 9247        "fn a() {
 9248             // ˇdˇog«()ˇ»;
 9249             cat();
 9250        }"
 9251    ));
 9252
 9253    // Single cursor on one line -> advance
 9254    // Cursor moves to column 0 on blank line
 9255    cx.set_state(indoc!(
 9256        "fn a() {
 9257             ˇdog();
 9258
 9259             cat();
 9260        }"
 9261    ));
 9262    cx.update_editor(|editor, cx| {
 9263        editor.toggle_comments(toggle_comments, cx);
 9264    });
 9265    cx.assert_editor_state(indoc!(
 9266        "fn a() {
 9267             // dog();
 9268        ˇ
 9269             cat();
 9270        }"
 9271    ));
 9272
 9273    // Single cursor on one line -> advance
 9274    // Cursor starts and ends at column 0
 9275    cx.set_state(indoc!(
 9276        "fn a() {
 9277         ˇ    dog();
 9278             cat();
 9279        }"
 9280    ));
 9281    cx.update_editor(|editor, cx| {
 9282        editor.toggle_comments(toggle_comments, cx);
 9283    });
 9284    cx.assert_editor_state(indoc!(
 9285        "fn a() {
 9286             // dog();
 9287         ˇ    cat();
 9288        }"
 9289    ));
 9290}
 9291
 9292#[gpui::test]
 9293async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9294    init_test(cx, |_| {});
 9295
 9296    let mut cx = EditorTestContext::new(cx).await;
 9297
 9298    let html_language = Arc::new(
 9299        Language::new(
 9300            LanguageConfig {
 9301                name: "HTML".into(),
 9302                block_comment: Some(("<!-- ".into(), " -->".into())),
 9303                ..Default::default()
 9304            },
 9305            Some(tree_sitter_html::language()),
 9306        )
 9307        .with_injection_query(
 9308            r#"
 9309            (script_element
 9310                (raw_text) @injection.content
 9311                (#set! injection.language "javascript"))
 9312            "#,
 9313        )
 9314        .unwrap(),
 9315    );
 9316
 9317    let javascript_language = Arc::new(Language::new(
 9318        LanguageConfig {
 9319            name: "JavaScript".into(),
 9320            line_comments: vec!["// ".into()],
 9321            ..Default::default()
 9322        },
 9323        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9324    ));
 9325
 9326    cx.language_registry().add(html_language.clone());
 9327    cx.language_registry().add(javascript_language.clone());
 9328    cx.update_buffer(|buffer, cx| {
 9329        buffer.set_language(Some(html_language), cx);
 9330    });
 9331
 9332    // Toggle comments for empty selections
 9333    cx.set_state(
 9334        &r#"
 9335            <p>A</p>ˇ
 9336            <p>B</p>ˇ
 9337            <p>C</p>ˇ
 9338        "#
 9339        .unindent(),
 9340    );
 9341    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9342    cx.assert_editor_state(
 9343        &r#"
 9344            <!-- <p>A</p>ˇ -->
 9345            <!-- <p>B</p>ˇ -->
 9346            <!-- <p>C</p>ˇ -->
 9347        "#
 9348        .unindent(),
 9349    );
 9350    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9351    cx.assert_editor_state(
 9352        &r#"
 9353            <p>A</p>ˇ
 9354            <p>B</p>ˇ
 9355            <p>C</p>ˇ
 9356        "#
 9357        .unindent(),
 9358    );
 9359
 9360    // Toggle comments for mixture of empty and non-empty selections, where
 9361    // multiple selections occupy a given line.
 9362    cx.set_state(
 9363        &r#"
 9364            <p>A«</p>
 9365            <p>ˇ»B</p>ˇ
 9366            <p>C«</p>
 9367            <p>ˇ»D</p>ˇ
 9368        "#
 9369        .unindent(),
 9370    );
 9371
 9372    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9373    cx.assert_editor_state(
 9374        &r#"
 9375            <!-- <p>A«</p>
 9376            <p>ˇ»B</p>ˇ -->
 9377            <!-- <p>C«</p>
 9378            <p>ˇ»D</p>ˇ -->
 9379        "#
 9380        .unindent(),
 9381    );
 9382    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9383    cx.assert_editor_state(
 9384        &r#"
 9385            <p>A«</p>
 9386            <p>ˇ»B</p>ˇ
 9387            <p>C«</p>
 9388            <p>ˇ»D</p>ˇ
 9389        "#
 9390        .unindent(),
 9391    );
 9392
 9393    // Toggle comments when different languages are active for different
 9394    // selections.
 9395    cx.set_state(
 9396        &r#"
 9397            ˇ<script>
 9398                ˇvar x = new Y();
 9399            ˇ</script>
 9400        "#
 9401        .unindent(),
 9402    );
 9403    cx.executor().run_until_parked();
 9404    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
 9405    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9406    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9407    cx.assert_editor_state(
 9408        &r#"
 9409            <!-- ˇ<script> -->
 9410                // ˇvar x = new Y();
 9411            // ˇ</script>
 9412        "#
 9413        .unindent(),
 9414    );
 9415}
 9416
 9417#[gpui::test]
 9418fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9419    init_test(cx, |_| {});
 9420
 9421    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9422    let multibuffer = cx.new_model(|cx| {
 9423        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9424        multibuffer.push_excerpts(
 9425            buffer.clone(),
 9426            [
 9427                ExcerptRange {
 9428                    context: Point::new(0, 0)..Point::new(0, 4),
 9429                    primary: None,
 9430                },
 9431                ExcerptRange {
 9432                    context: Point::new(1, 0)..Point::new(1, 4),
 9433                    primary: None,
 9434                },
 9435            ],
 9436            cx,
 9437        );
 9438        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9439        multibuffer
 9440    });
 9441
 9442    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9443    view.update(cx, |view, cx| {
 9444        assert_eq!(view.text(cx), "aaaa\nbbbb");
 9445        view.change_selections(None, cx, |s| {
 9446            s.select_ranges([
 9447                Point::new(0, 0)..Point::new(0, 0),
 9448                Point::new(1, 0)..Point::new(1, 0),
 9449            ])
 9450        });
 9451
 9452        view.handle_input("X", cx);
 9453        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
 9454        assert_eq!(
 9455            view.selections.ranges(cx),
 9456            [
 9457                Point::new(0, 1)..Point::new(0, 1),
 9458                Point::new(1, 1)..Point::new(1, 1),
 9459            ]
 9460        );
 9461
 9462        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9463        view.change_selections(None, cx, |s| {
 9464            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9465        });
 9466        view.backspace(&Default::default(), cx);
 9467        assert_eq!(view.text(cx), "Xa\nbbb");
 9468        assert_eq!(
 9469            view.selections.ranges(cx),
 9470            [Point::new(1, 0)..Point::new(1, 0)]
 9471        );
 9472
 9473        view.change_selections(None, cx, |s| {
 9474            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9475        });
 9476        view.backspace(&Default::default(), cx);
 9477        assert_eq!(view.text(cx), "X\nbb");
 9478        assert_eq!(
 9479            view.selections.ranges(cx),
 9480            [Point::new(0, 1)..Point::new(0, 1)]
 9481        );
 9482    });
 9483}
 9484
 9485#[gpui::test]
 9486fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9487    init_test(cx, |_| {});
 9488
 9489    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9490    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9491        indoc! {"
 9492            [aaaa
 9493            (bbbb]
 9494            cccc)",
 9495        },
 9496        markers.clone(),
 9497    );
 9498    let excerpt_ranges = markers.into_iter().map(|marker| {
 9499        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9500        ExcerptRange {
 9501            context,
 9502            primary: None,
 9503        }
 9504    });
 9505    let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
 9506    let multibuffer = cx.new_model(|cx| {
 9507        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9508        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9509        multibuffer
 9510    });
 9511
 9512    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
 9513    view.update(cx, |view, cx| {
 9514        let (expected_text, selection_ranges) = marked_text_ranges(
 9515            indoc! {"
 9516                aaaa
 9517                bˇbbb
 9518                bˇbbˇb
 9519                cccc"
 9520            },
 9521            true,
 9522        );
 9523        assert_eq!(view.text(cx), expected_text);
 9524        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
 9525
 9526        view.handle_input("X", cx);
 9527
 9528        let (expected_text, expected_selections) = marked_text_ranges(
 9529            indoc! {"
 9530                aaaa
 9531                bXˇbbXb
 9532                bXˇbbXˇb
 9533                cccc"
 9534            },
 9535            false,
 9536        );
 9537        assert_eq!(view.text(cx), expected_text);
 9538        assert_eq!(view.selections.ranges(cx), expected_selections);
 9539
 9540        view.newline(&Newline, cx);
 9541        let (expected_text, expected_selections) = marked_text_ranges(
 9542            indoc! {"
 9543                aaaa
 9544                bX
 9545                ˇbbX
 9546                b
 9547                bX
 9548                ˇbbX
 9549                ˇb
 9550                cccc"
 9551            },
 9552            false,
 9553        );
 9554        assert_eq!(view.text(cx), expected_text);
 9555        assert_eq!(view.selections.ranges(cx), expected_selections);
 9556    });
 9557}
 9558
 9559#[gpui::test]
 9560fn test_refresh_selections(cx: &mut TestAppContext) {
 9561    init_test(cx, |_| {});
 9562
 9563    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9564    let mut excerpt1_id = None;
 9565    let multibuffer = cx.new_model(|cx| {
 9566        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9567        excerpt1_id = multibuffer
 9568            .push_excerpts(
 9569                buffer.clone(),
 9570                [
 9571                    ExcerptRange {
 9572                        context: Point::new(0, 0)..Point::new(1, 4),
 9573                        primary: None,
 9574                    },
 9575                    ExcerptRange {
 9576                        context: Point::new(1, 0)..Point::new(2, 4),
 9577                        primary: None,
 9578                    },
 9579                ],
 9580                cx,
 9581            )
 9582            .into_iter()
 9583            .next();
 9584        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9585        multibuffer
 9586    });
 9587
 9588    let editor = cx.add_window(|cx| {
 9589        let mut editor = build_editor(multibuffer.clone(), cx);
 9590        let snapshot = editor.snapshot(cx);
 9591        editor.change_selections(None, cx, |s| {
 9592            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9593        });
 9594        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
 9595        assert_eq!(
 9596            editor.selections.ranges(cx),
 9597            [
 9598                Point::new(1, 3)..Point::new(1, 3),
 9599                Point::new(2, 1)..Point::new(2, 1),
 9600            ]
 9601        );
 9602        editor
 9603    });
 9604
 9605    // Refreshing selections is a no-op when excerpts haven't changed.
 9606    _ = editor.update(cx, |editor, cx| {
 9607        editor.change_selections(None, cx, |s| s.refresh());
 9608        assert_eq!(
 9609            editor.selections.ranges(cx),
 9610            [
 9611                Point::new(1, 3)..Point::new(1, 3),
 9612                Point::new(2, 1)..Point::new(2, 1),
 9613            ]
 9614        );
 9615    });
 9616
 9617    multibuffer.update(cx, |multibuffer, cx| {
 9618        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9619    });
 9620    _ = editor.update(cx, |editor, cx| {
 9621        // Removing an excerpt causes the first selection to become degenerate.
 9622        assert_eq!(
 9623            editor.selections.ranges(cx),
 9624            [
 9625                Point::new(0, 0)..Point::new(0, 0),
 9626                Point::new(0, 1)..Point::new(0, 1)
 9627            ]
 9628        );
 9629
 9630        // Refreshing selections will relocate the first selection to the original buffer
 9631        // location.
 9632        editor.change_selections(None, cx, |s| s.refresh());
 9633        assert_eq!(
 9634            editor.selections.ranges(cx),
 9635            [
 9636                Point::new(0, 1)..Point::new(0, 1),
 9637                Point::new(0, 3)..Point::new(0, 3)
 9638            ]
 9639        );
 9640        assert!(editor.selections.pending_anchor().is_some());
 9641    });
 9642}
 9643
 9644#[gpui::test]
 9645fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9646    init_test(cx, |_| {});
 9647
 9648    let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9649    let mut excerpt1_id = None;
 9650    let multibuffer = cx.new_model(|cx| {
 9651        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9652        excerpt1_id = multibuffer
 9653            .push_excerpts(
 9654                buffer.clone(),
 9655                [
 9656                    ExcerptRange {
 9657                        context: Point::new(0, 0)..Point::new(1, 4),
 9658                        primary: None,
 9659                    },
 9660                    ExcerptRange {
 9661                        context: Point::new(1, 0)..Point::new(2, 4),
 9662                        primary: None,
 9663                    },
 9664                ],
 9665                cx,
 9666            )
 9667            .into_iter()
 9668            .next();
 9669        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9670        multibuffer
 9671    });
 9672
 9673    let editor = cx.add_window(|cx| {
 9674        let mut editor = build_editor(multibuffer.clone(), cx);
 9675        let snapshot = editor.snapshot(cx);
 9676        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
 9677        assert_eq!(
 9678            editor.selections.ranges(cx),
 9679            [Point::new(1, 3)..Point::new(1, 3)]
 9680        );
 9681        editor
 9682    });
 9683
 9684    multibuffer.update(cx, |multibuffer, cx| {
 9685        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9686    });
 9687    _ = editor.update(cx, |editor, cx| {
 9688        assert_eq!(
 9689            editor.selections.ranges(cx),
 9690            [Point::new(0, 0)..Point::new(0, 0)]
 9691        );
 9692
 9693        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
 9694        editor.change_selections(None, cx, |s| s.refresh());
 9695        assert_eq!(
 9696            editor.selections.ranges(cx),
 9697            [Point::new(0, 3)..Point::new(0, 3)]
 9698        );
 9699        assert!(editor.selections.pending_anchor().is_some());
 9700    });
 9701}
 9702
 9703#[gpui::test]
 9704async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 9705    init_test(cx, |_| {});
 9706
 9707    let language = Arc::new(
 9708        Language::new(
 9709            LanguageConfig {
 9710                brackets: BracketPairConfig {
 9711                    pairs: vec![
 9712                        BracketPair {
 9713                            start: "{".to_string(),
 9714                            end: "}".to_string(),
 9715                            close: true,
 9716                            surround: true,
 9717                            newline: true,
 9718                        },
 9719                        BracketPair {
 9720                            start: "/* ".to_string(),
 9721                            end: " */".to_string(),
 9722                            close: true,
 9723                            surround: true,
 9724                            newline: true,
 9725                        },
 9726                    ],
 9727                    ..Default::default()
 9728                },
 9729                ..Default::default()
 9730            },
 9731            Some(tree_sitter_rust::LANGUAGE.into()),
 9732        )
 9733        .with_indents_query("")
 9734        .unwrap(),
 9735    );
 9736
 9737    let text = concat!(
 9738        "{   }\n",     //
 9739        "  x\n",       //
 9740        "  /*   */\n", //
 9741        "x\n",         //
 9742        "{{} }\n",     //
 9743    );
 9744
 9745    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
 9746    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 9747    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 9748    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
 9749        .await;
 9750
 9751    view.update(cx, |view, cx| {
 9752        view.change_selections(None, cx, |s| {
 9753            s.select_display_ranges([
 9754                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 9755                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 9756                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 9757            ])
 9758        });
 9759        view.newline(&Newline, cx);
 9760
 9761        assert_eq!(
 9762            view.buffer().read(cx).read(cx).text(),
 9763            concat!(
 9764                "{ \n",    // Suppress rustfmt
 9765                "\n",      //
 9766                "}\n",     //
 9767                "  x\n",   //
 9768                "  /* \n", //
 9769                "  \n",    //
 9770                "  */\n",  //
 9771                "x\n",     //
 9772                "{{} \n",  //
 9773                "}\n",     //
 9774            )
 9775        );
 9776    });
 9777}
 9778
 9779#[gpui::test]
 9780fn test_highlighted_ranges(cx: &mut TestAppContext) {
 9781    init_test(cx, |_| {});
 9782
 9783    let editor = cx.add_window(|cx| {
 9784        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
 9785        build_editor(buffer.clone(), cx)
 9786    });
 9787
 9788    _ = editor.update(cx, |editor, cx| {
 9789        struct Type1;
 9790        struct Type2;
 9791
 9792        let buffer = editor.buffer.read(cx).snapshot(cx);
 9793
 9794        let anchor_range =
 9795            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
 9796
 9797        editor.highlight_background::<Type1>(
 9798            &[
 9799                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
 9800                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
 9801                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
 9802                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
 9803            ],
 9804            |_| Hsla::red(),
 9805            cx,
 9806        );
 9807        editor.highlight_background::<Type2>(
 9808            &[
 9809                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
 9810                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
 9811                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
 9812                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
 9813            ],
 9814            |_| Hsla::green(),
 9815            cx,
 9816        );
 9817
 9818        let snapshot = editor.snapshot(cx);
 9819        let mut highlighted_ranges = editor.background_highlights_in_range(
 9820            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
 9821            &snapshot,
 9822            cx.theme().colors(),
 9823        );
 9824        // Enforce a consistent ordering based on color without relying on the ordering of the
 9825        // highlight's `TypeId` which is non-executor.
 9826        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
 9827        assert_eq!(
 9828            highlighted_ranges,
 9829            &[
 9830                (
 9831                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
 9832                    Hsla::red(),
 9833                ),
 9834                (
 9835                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9836                    Hsla::red(),
 9837                ),
 9838                (
 9839                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
 9840                    Hsla::green(),
 9841                ),
 9842                (
 9843                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
 9844                    Hsla::green(),
 9845                ),
 9846            ]
 9847        );
 9848        assert_eq!(
 9849            editor.background_highlights_in_range(
 9850                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
 9851                &snapshot,
 9852                cx.theme().colors(),
 9853            ),
 9854            &[(
 9855                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
 9856                Hsla::red(),
 9857            )]
 9858        );
 9859    });
 9860}
 9861
 9862#[gpui::test]
 9863async fn test_following(cx: &mut gpui::TestAppContext) {
 9864    init_test(cx, |_| {});
 9865
 9866    let fs = FakeFs::new(cx.executor());
 9867    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 9868
 9869    let buffer = project.update(cx, |project, cx| {
 9870        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
 9871        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
 9872    });
 9873    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 9874    let follower = cx.update(|cx| {
 9875        cx.open_window(
 9876            WindowOptions {
 9877                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
 9878                    gpui::Point::new(px(0.), px(0.)),
 9879                    gpui::Point::new(px(10.), px(80.)),
 9880                ))),
 9881                ..Default::default()
 9882            },
 9883            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
 9884        )
 9885        .unwrap()
 9886    });
 9887
 9888    let is_still_following = Rc::new(RefCell::new(true));
 9889    let follower_edit_event_count = Rc::new(RefCell::new(0));
 9890    let pending_update = Rc::new(RefCell::new(None));
 9891    _ = follower.update(cx, {
 9892        let update = pending_update.clone();
 9893        let is_still_following = is_still_following.clone();
 9894        let follower_edit_event_count = follower_edit_event_count.clone();
 9895        |_, cx| {
 9896            cx.subscribe(
 9897                &leader.root_view(cx).unwrap(),
 9898                move |_, leader, event, cx| {
 9899                    leader
 9900                        .read(cx)
 9901                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
 9902                },
 9903            )
 9904            .detach();
 9905
 9906            cx.subscribe(
 9907                &follower.root_view(cx).unwrap(),
 9908                move |_, _, event: &EditorEvent, _cx| {
 9909                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
 9910                        *is_still_following.borrow_mut() = false;
 9911                    }
 9912
 9913                    if let EditorEvent::BufferEdited = event {
 9914                        *follower_edit_event_count.borrow_mut() += 1;
 9915                    }
 9916                },
 9917            )
 9918            .detach();
 9919        }
 9920    });
 9921
 9922    // Update the selections only
 9923    _ = leader.update(cx, |leader, cx| {
 9924        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9925    });
 9926    follower
 9927        .update(cx, |follower, cx| {
 9928            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9929        })
 9930        .unwrap()
 9931        .await
 9932        .unwrap();
 9933    _ = follower.update(cx, |follower, cx| {
 9934        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
 9935    });
 9936    assert!(*is_still_following.borrow());
 9937    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9938
 9939    // Update the scroll position only
 9940    _ = leader.update(cx, |leader, cx| {
 9941        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9942    });
 9943    follower
 9944        .update(cx, |follower, cx| {
 9945            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9946        })
 9947        .unwrap()
 9948        .await
 9949        .unwrap();
 9950    assert_eq!(
 9951        follower
 9952            .update(cx, |follower, cx| follower.scroll_position(cx))
 9953            .unwrap(),
 9954        gpui::Point::new(1.5, 3.5)
 9955    );
 9956    assert!(*is_still_following.borrow());
 9957    assert_eq!(*follower_edit_event_count.borrow(), 0);
 9958
 9959    // Update the selections and scroll position. The follower's scroll position is updated
 9960    // via autoscroll, not via the leader's exact scroll position.
 9961    _ = leader.update(cx, |leader, cx| {
 9962        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
 9963        leader.request_autoscroll(Autoscroll::newest(), cx);
 9964        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
 9965    });
 9966    follower
 9967        .update(cx, |follower, cx| {
 9968            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9969        })
 9970        .unwrap()
 9971        .await
 9972        .unwrap();
 9973    _ = follower.update(cx, |follower, cx| {
 9974        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
 9975        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
 9976    });
 9977    assert!(*is_still_following.borrow());
 9978
 9979    // Creating a pending selection that precedes another selection
 9980    _ = leader.update(cx, |leader, cx| {
 9981        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
 9982        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, cx);
 9983    });
 9984    follower
 9985        .update(cx, |follower, cx| {
 9986            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
 9987        })
 9988        .unwrap()
 9989        .await
 9990        .unwrap();
 9991    _ = follower.update(cx, |follower, cx| {
 9992        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
 9993    });
 9994    assert!(*is_still_following.borrow());
 9995
 9996    // Extend the pending selection so that it surrounds another selection
 9997    _ = leader.update(cx, |leader, cx| {
 9998        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, cx);
 9999    });
10000    follower
10001        .update(cx, |follower, cx| {
10002            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
10003        })
10004        .unwrap()
10005        .await
10006        .unwrap();
10007    _ = follower.update(cx, |follower, cx| {
10008        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10009    });
10010
10011    // Scrolling locally breaks the follow
10012    _ = follower.update(cx, |follower, cx| {
10013        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10014        follower.set_scroll_anchor(
10015            ScrollAnchor {
10016                anchor: top_anchor,
10017                offset: gpui::Point::new(0.0, 0.5),
10018            },
10019            cx,
10020        );
10021    });
10022    assert!(!(*is_still_following.borrow()));
10023}
10024
10025#[gpui::test]
10026async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10027    init_test(cx, |_| {});
10028
10029    let fs = FakeFs::new(cx.executor());
10030    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10031    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10032    let pane = workspace
10033        .update(cx, |workspace, _| workspace.active_pane().clone())
10034        .unwrap();
10035
10036    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10037
10038    let leader = pane.update(cx, |_, cx| {
10039        let multibuffer = cx.new_model(|_| MultiBuffer::new(ReadWrite));
10040        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
10041    });
10042
10043    // Start following the editor when it has no excerpts.
10044    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
10045    let follower_1 = cx
10046        .update_window(*workspace.deref(), |_, cx| {
10047            Editor::from_state_proto(
10048                workspace.root_view(cx).unwrap(),
10049                ViewId {
10050                    creator: Default::default(),
10051                    id: 0,
10052                },
10053                &mut state_message,
10054                cx,
10055            )
10056        })
10057        .unwrap()
10058        .unwrap()
10059        .await
10060        .unwrap();
10061
10062    let update_message = Rc::new(RefCell::new(None));
10063    follower_1.update(cx, {
10064        let update = update_message.clone();
10065        |_, cx| {
10066            cx.subscribe(&leader, move |_, leader, event, cx| {
10067                leader
10068                    .read(cx)
10069                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
10070            })
10071            .detach();
10072        }
10073    });
10074
10075    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10076        (
10077            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10078            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10079        )
10080    });
10081
10082    // Insert some excerpts.
10083    leader.update(cx, |leader, cx| {
10084        leader.buffer.update(cx, |multibuffer, cx| {
10085            let excerpt_ids = multibuffer.push_excerpts(
10086                buffer_1.clone(),
10087                [
10088                    ExcerptRange {
10089                        context: 1..6,
10090                        primary: None,
10091                    },
10092                    ExcerptRange {
10093                        context: 12..15,
10094                        primary: None,
10095                    },
10096                    ExcerptRange {
10097                        context: 0..3,
10098                        primary: None,
10099                    },
10100                ],
10101                cx,
10102            );
10103            multibuffer.insert_excerpts_after(
10104                excerpt_ids[0],
10105                buffer_2.clone(),
10106                [
10107                    ExcerptRange {
10108                        context: 8..12,
10109                        primary: None,
10110                    },
10111                    ExcerptRange {
10112                        context: 0..6,
10113                        primary: None,
10114                    },
10115                ],
10116                cx,
10117            );
10118        });
10119    });
10120
10121    // Apply the update of adding the excerpts.
10122    follower_1
10123        .update(cx, |follower, cx| {
10124            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10125        })
10126        .await
10127        .unwrap();
10128    assert_eq!(
10129        follower_1.update(cx, |editor, cx| editor.text(cx)),
10130        leader.update(cx, |editor, cx| editor.text(cx))
10131    );
10132    update_message.borrow_mut().take();
10133
10134    // Start following separately after it already has excerpts.
10135    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
10136    let follower_2 = cx
10137        .update_window(*workspace.deref(), |_, cx| {
10138            Editor::from_state_proto(
10139                workspace.root_view(cx).unwrap().clone(),
10140                ViewId {
10141                    creator: Default::default(),
10142                    id: 0,
10143                },
10144                &mut state_message,
10145                cx,
10146            )
10147        })
10148        .unwrap()
10149        .unwrap()
10150        .await
10151        .unwrap();
10152    assert_eq!(
10153        follower_2.update(cx, |editor, cx| editor.text(cx)),
10154        leader.update(cx, |editor, cx| editor.text(cx))
10155    );
10156
10157    // Remove some excerpts.
10158    leader.update(cx, |leader, cx| {
10159        leader.buffer.update(cx, |multibuffer, cx| {
10160            let excerpt_ids = multibuffer.excerpt_ids();
10161            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10162            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10163        });
10164    });
10165
10166    // Apply the update of removing the excerpts.
10167    follower_1
10168        .update(cx, |follower, cx| {
10169            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10170        })
10171        .await
10172        .unwrap();
10173    follower_2
10174        .update(cx, |follower, cx| {
10175            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
10176        })
10177        .await
10178        .unwrap();
10179    update_message.borrow_mut().take();
10180    assert_eq!(
10181        follower_1.update(cx, |editor, cx| editor.text(cx)),
10182        leader.update(cx, |editor, cx| editor.text(cx))
10183    );
10184}
10185
10186#[gpui::test]
10187async fn go_to_prev_overlapping_diagnostic(
10188    executor: BackgroundExecutor,
10189    cx: &mut gpui::TestAppContext,
10190) {
10191    init_test(cx, |_| {});
10192
10193    let mut cx = EditorTestContext::new(cx).await;
10194    let lsp_store =
10195        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10196
10197    cx.set_state(indoc! {"
10198        ˇfn func(abc def: i32) -> u32 {
10199        }
10200    "});
10201
10202    cx.update(|cx| {
10203        lsp_store.update(cx, |lsp_store, cx| {
10204            lsp_store
10205                .update_diagnostics(
10206                    LanguageServerId(0),
10207                    lsp::PublishDiagnosticsParams {
10208                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
10209                        version: None,
10210                        diagnostics: vec![
10211                            lsp::Diagnostic {
10212                                range: lsp::Range::new(
10213                                    lsp::Position::new(0, 11),
10214                                    lsp::Position::new(0, 12),
10215                                ),
10216                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10217                                ..Default::default()
10218                            },
10219                            lsp::Diagnostic {
10220                                range: lsp::Range::new(
10221                                    lsp::Position::new(0, 12),
10222                                    lsp::Position::new(0, 15),
10223                                ),
10224                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10225                                ..Default::default()
10226                            },
10227                            lsp::Diagnostic {
10228                                range: lsp::Range::new(
10229                                    lsp::Position::new(0, 25),
10230                                    lsp::Position::new(0, 28),
10231                                ),
10232                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10233                                ..Default::default()
10234                            },
10235                        ],
10236                    },
10237                    &[],
10238                    cx,
10239                )
10240                .unwrap()
10241        });
10242    });
10243
10244    executor.run_until_parked();
10245
10246    cx.update_editor(|editor, cx| {
10247        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10248    });
10249
10250    cx.assert_editor_state(indoc! {"
10251        fn func(abc def: i32) -> ˇu32 {
10252        }
10253    "});
10254
10255    cx.update_editor(|editor, cx| {
10256        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10257    });
10258
10259    cx.assert_editor_state(indoc! {"
10260        fn func(abc ˇdef: i32) -> u32 {
10261        }
10262    "});
10263
10264    cx.update_editor(|editor, cx| {
10265        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10266    });
10267
10268    cx.assert_editor_state(indoc! {"
10269        fn func(abcˇ def: i32) -> u32 {
10270        }
10271    "});
10272
10273    cx.update_editor(|editor, cx| {
10274        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
10275    });
10276
10277    cx.assert_editor_state(indoc! {"
10278        fn func(abc def: i32) -> ˇu32 {
10279        }
10280    "});
10281}
10282
10283#[gpui::test]
10284async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10285    init_test(cx, |_| {});
10286
10287    let mut cx = EditorTestContext::new(cx).await;
10288
10289    cx.set_state(indoc! {"
10290        fn func(abˇc def: i32) -> u32 {
10291        }
10292    "});
10293    let lsp_store =
10294        cx.update_editor(|editor, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10295
10296    cx.update(|cx| {
10297        lsp_store.update(cx, |lsp_store, cx| {
10298            lsp_store.update_diagnostics(
10299                LanguageServerId(0),
10300                lsp::PublishDiagnosticsParams {
10301                    uri: lsp::Url::from_file_path("/root/file").unwrap(),
10302                    version: None,
10303                    diagnostics: vec![lsp::Diagnostic {
10304                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10305                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10306                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10307                        ..Default::default()
10308                    }],
10309                },
10310                &[],
10311                cx,
10312            )
10313        })
10314    }).unwrap();
10315    cx.run_until_parked();
10316    cx.update_editor(|editor, cx| hover_popover::hover(editor, &Default::default(), cx));
10317    cx.run_until_parked();
10318    cx.update_editor(|editor, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10319}
10320
10321#[gpui::test]
10322async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10323    init_test(cx, |_| {});
10324
10325    let mut cx = EditorTestContext::new(cx).await;
10326
10327    let diff_base = r#"
10328        use some::mod;
10329
10330        const A: u32 = 42;
10331
10332        fn main() {
10333            println!("hello");
10334
10335            println!("world");
10336        }
10337        "#
10338    .unindent();
10339
10340    // Edits are modified, removed, modified, added
10341    cx.set_state(
10342        &r#"
10343        use some::modified;
10344
10345        ˇ
10346        fn main() {
10347            println!("hello there");
10348
10349            println!("around the");
10350            println!("world");
10351        }
10352        "#
10353        .unindent(),
10354    );
10355
10356    cx.set_diff_base(&diff_base);
10357    executor.run_until_parked();
10358
10359    cx.update_editor(|editor, cx| {
10360        //Wrap around the bottom of the buffer
10361        for _ in 0..3 {
10362            editor.go_to_next_hunk(&GoToHunk, cx);
10363        }
10364    });
10365
10366    cx.assert_editor_state(
10367        &r#"
10368        ˇuse some::modified;
10369
10370
10371        fn main() {
10372            println!("hello there");
10373
10374            println!("around the");
10375            println!("world");
10376        }
10377        "#
10378        .unindent(),
10379    );
10380
10381    cx.update_editor(|editor, cx| {
10382        //Wrap around the top of the buffer
10383        for _ in 0..2 {
10384            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10385        }
10386    });
10387
10388    cx.assert_editor_state(
10389        &r#"
10390        use some::modified;
10391
10392
10393        fn main() {
10394        ˇ    println!("hello there");
10395
10396            println!("around the");
10397            println!("world");
10398        }
10399        "#
10400        .unindent(),
10401    );
10402
10403    cx.update_editor(|editor, cx| {
10404        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10405    });
10406
10407    cx.assert_editor_state(
10408        &r#"
10409        use some::modified;
10410
10411        ˇ
10412        fn main() {
10413            println!("hello there");
10414
10415            println!("around the");
10416            println!("world");
10417        }
10418        "#
10419        .unindent(),
10420    );
10421
10422    cx.update_editor(|editor, cx| {
10423        for _ in 0..3 {
10424            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
10425        }
10426    });
10427
10428    cx.assert_editor_state(
10429        &r#"
10430        use some::modified;
10431
10432
10433        fn main() {
10434        ˇ    println!("hello there");
10435
10436            println!("around the");
10437            println!("world");
10438        }
10439        "#
10440        .unindent(),
10441    );
10442
10443    cx.update_editor(|editor, cx| {
10444        editor.fold(&Fold, cx);
10445
10446        //Make sure that the fold only gets one hunk
10447        for _ in 0..4 {
10448            editor.go_to_next_hunk(&GoToHunk, cx);
10449        }
10450    });
10451
10452    cx.assert_editor_state(
10453        &r#"
10454        ˇuse some::modified;
10455
10456
10457        fn main() {
10458            println!("hello there");
10459
10460            println!("around the");
10461            println!("world");
10462        }
10463        "#
10464        .unindent(),
10465    );
10466}
10467
10468#[test]
10469fn test_split_words() {
10470    fn split(text: &str) -> Vec<&str> {
10471        split_words(text).collect()
10472    }
10473
10474    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10475    assert_eq!(split("hello_world"), &["hello_", "world"]);
10476    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10477    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10478    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10479    assert_eq!(split("helloworld"), &["helloworld"]);
10480
10481    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10482}
10483
10484#[gpui::test]
10485async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10486    init_test(cx, |_| {});
10487
10488    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10489    let mut assert = |before, after| {
10490        let _state_context = cx.set_state(before);
10491        cx.run_until_parked();
10492        cx.update_editor(|editor, cx| {
10493            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
10494        });
10495        cx.assert_editor_state(after);
10496    };
10497
10498    // Outside bracket jumps to outside of matching bracket
10499    assert("console.logˇ(var);", "console.log(var)ˇ;");
10500    assert("console.log(var)ˇ;", "console.logˇ(var);");
10501
10502    // Inside bracket jumps to inside of matching bracket
10503    assert("console.log(ˇvar);", "console.log(varˇ);");
10504    assert("console.log(varˇ);", "console.log(ˇvar);");
10505
10506    // When outside a bracket and inside, favor jumping to the inside bracket
10507    assert(
10508        "console.log('foo', [1, 2, 3]ˇ);",
10509        "console.log(ˇ'foo', [1, 2, 3]);",
10510    );
10511    assert(
10512        "console.log(ˇ'foo', [1, 2, 3]);",
10513        "console.log('foo', [1, 2, 3]ˇ);",
10514    );
10515
10516    // Bias forward if two options are equally likely
10517    assert(
10518        "let result = curried_fun()ˇ();",
10519        "let result = curried_fun()()ˇ;",
10520    );
10521
10522    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10523    assert(
10524        indoc! {"
10525            function test() {
10526                console.log('test')ˇ
10527            }"},
10528        indoc! {"
10529            function test() {
10530                console.logˇ('test')
10531            }"},
10532    );
10533}
10534
10535#[gpui::test]
10536async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10537    init_test(cx, |_| {});
10538
10539    let fs = FakeFs::new(cx.executor());
10540    fs.insert_tree(
10541        "/a",
10542        json!({
10543            "main.rs": "fn main() { let a = 5; }",
10544            "other.rs": "// Test file",
10545        }),
10546    )
10547    .await;
10548    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10549
10550    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10551    language_registry.add(Arc::new(Language::new(
10552        LanguageConfig {
10553            name: "Rust".into(),
10554            matcher: LanguageMatcher {
10555                path_suffixes: vec!["rs".to_string()],
10556                ..Default::default()
10557            },
10558            brackets: BracketPairConfig {
10559                pairs: vec![BracketPair {
10560                    start: "{".to_string(),
10561                    end: "}".to_string(),
10562                    close: true,
10563                    surround: true,
10564                    newline: true,
10565                }],
10566                disabled_scopes_by_bracket_ix: Vec::new(),
10567            },
10568            ..Default::default()
10569        },
10570        Some(tree_sitter_rust::LANGUAGE.into()),
10571    )));
10572    let mut fake_servers = language_registry.register_fake_lsp(
10573        "Rust",
10574        FakeLspAdapter {
10575            capabilities: lsp::ServerCapabilities {
10576                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10577                    first_trigger_character: "{".to_string(),
10578                    more_trigger_character: None,
10579                }),
10580                ..Default::default()
10581            },
10582            ..Default::default()
10583        },
10584    );
10585
10586    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10587
10588    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10589
10590    let worktree_id = workspace
10591        .update(cx, |workspace, cx| {
10592            workspace.project().update(cx, |project, cx| {
10593                project.worktrees(cx).next().unwrap().read(cx).id()
10594            })
10595        })
10596        .unwrap();
10597
10598    let buffer = project
10599        .update(cx, |project, cx| {
10600            project.open_local_buffer("/a/main.rs", cx)
10601        })
10602        .await
10603        .unwrap();
10604    let editor_handle = workspace
10605        .update(cx, |workspace, cx| {
10606            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
10607        })
10608        .unwrap()
10609        .await
10610        .unwrap()
10611        .downcast::<Editor>()
10612        .unwrap();
10613
10614    cx.executor().start_waiting();
10615    let fake_server = fake_servers.next().await.unwrap();
10616
10617    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
10618        assert_eq!(
10619            params.text_document_position.text_document.uri,
10620            lsp::Url::from_file_path("/a/main.rs").unwrap(),
10621        );
10622        assert_eq!(
10623            params.text_document_position.position,
10624            lsp::Position::new(0, 21),
10625        );
10626
10627        Ok(Some(vec![lsp::TextEdit {
10628            new_text: "]".to_string(),
10629            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10630        }]))
10631    });
10632
10633    editor_handle.update(cx, |editor, cx| {
10634        editor.focus(cx);
10635        editor.change_selections(None, cx, |s| {
10636            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
10637        });
10638        editor.handle_input("{", cx);
10639    });
10640
10641    cx.executor().run_until_parked();
10642
10643    buffer.update(cx, |buffer, _| {
10644        assert_eq!(
10645            buffer.text(),
10646            "fn main() { let a = {5}; }",
10647            "No extra braces from on type formatting should appear in the buffer"
10648        )
10649    });
10650}
10651
10652#[gpui::test]
10653async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
10654    init_test(cx, |_| {});
10655
10656    let fs = FakeFs::new(cx.executor());
10657    fs.insert_tree(
10658        "/a",
10659        json!({
10660            "main.rs": "fn main() { let a = 5; }",
10661            "other.rs": "// Test file",
10662        }),
10663    )
10664    .await;
10665
10666    let project = Project::test(fs, ["/a".as_ref()], cx).await;
10667
10668    let server_restarts = Arc::new(AtomicUsize::new(0));
10669    let closure_restarts = Arc::clone(&server_restarts);
10670    let language_server_name = "test language server";
10671    let language_name: LanguageName = "Rust".into();
10672
10673    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10674    language_registry.add(Arc::new(Language::new(
10675        LanguageConfig {
10676            name: language_name.clone(),
10677            matcher: LanguageMatcher {
10678                path_suffixes: vec!["rs".to_string()],
10679                ..Default::default()
10680            },
10681            ..Default::default()
10682        },
10683        Some(tree_sitter_rust::LANGUAGE.into()),
10684    )));
10685    let mut fake_servers = language_registry.register_fake_lsp(
10686        "Rust",
10687        FakeLspAdapter {
10688            name: language_server_name,
10689            initialization_options: Some(json!({
10690                "testOptionValue": true
10691            })),
10692            initializer: Some(Box::new(move |fake_server| {
10693                let task_restarts = Arc::clone(&closure_restarts);
10694                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
10695                    task_restarts.fetch_add(1, atomic::Ordering::Release);
10696                    futures::future::ready(Ok(()))
10697                });
10698            })),
10699            ..Default::default()
10700        },
10701    );
10702
10703    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
10704    let _buffer = project
10705        .update(cx, |project, cx| {
10706            project.open_local_buffer_with_lsp("/a/main.rs", cx)
10707        })
10708        .await
10709        .unwrap();
10710    let _fake_server = fake_servers.next().await.unwrap();
10711    update_test_language_settings(cx, |language_settings| {
10712        language_settings.languages.insert(
10713            language_name.clone(),
10714            LanguageSettingsContent {
10715                tab_size: NonZeroU32::new(8),
10716                ..Default::default()
10717            },
10718        );
10719    });
10720    cx.executor().run_until_parked();
10721    assert_eq!(
10722        server_restarts.load(atomic::Ordering::Acquire),
10723        0,
10724        "Should not restart LSP server on an unrelated change"
10725    );
10726
10727    update_test_project_settings(cx, |project_settings| {
10728        project_settings.lsp.insert(
10729            "Some other server name".into(),
10730            LspSettings {
10731                binary: None,
10732                settings: None,
10733                initialization_options: Some(json!({
10734                    "some other init value": false
10735                })),
10736            },
10737        );
10738    });
10739    cx.executor().run_until_parked();
10740    assert_eq!(
10741        server_restarts.load(atomic::Ordering::Acquire),
10742        0,
10743        "Should not restart LSP server on an unrelated LSP settings change"
10744    );
10745
10746    update_test_project_settings(cx, |project_settings| {
10747        project_settings.lsp.insert(
10748            language_server_name.into(),
10749            LspSettings {
10750                binary: None,
10751                settings: None,
10752                initialization_options: Some(json!({
10753                    "anotherInitValue": false
10754                })),
10755            },
10756        );
10757    });
10758    cx.executor().run_until_parked();
10759    assert_eq!(
10760        server_restarts.load(atomic::Ordering::Acquire),
10761        1,
10762        "Should restart LSP server on a related LSP settings change"
10763    );
10764
10765    update_test_project_settings(cx, |project_settings| {
10766        project_settings.lsp.insert(
10767            language_server_name.into(),
10768            LspSettings {
10769                binary: None,
10770                settings: None,
10771                initialization_options: Some(json!({
10772                    "anotherInitValue": false
10773                })),
10774            },
10775        );
10776    });
10777    cx.executor().run_until_parked();
10778    assert_eq!(
10779        server_restarts.load(atomic::Ordering::Acquire),
10780        1,
10781        "Should not restart LSP server on a related LSP settings change that is the same"
10782    );
10783
10784    update_test_project_settings(cx, |project_settings| {
10785        project_settings.lsp.insert(
10786            language_server_name.into(),
10787            LspSettings {
10788                binary: None,
10789                settings: None,
10790                initialization_options: None,
10791            },
10792        );
10793    });
10794    cx.executor().run_until_parked();
10795    assert_eq!(
10796        server_restarts.load(atomic::Ordering::Acquire),
10797        2,
10798        "Should restart LSP server on another related LSP settings change"
10799    );
10800}
10801
10802#[gpui::test]
10803async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
10804    init_test(cx, |_| {});
10805
10806    let mut cx = EditorLspTestContext::new_rust(
10807        lsp::ServerCapabilities {
10808            completion_provider: Some(lsp::CompletionOptions {
10809                trigger_characters: Some(vec![".".to_string()]),
10810                resolve_provider: Some(true),
10811                ..Default::default()
10812            }),
10813            ..Default::default()
10814        },
10815        cx,
10816    )
10817    .await;
10818
10819    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10820    cx.simulate_keystroke(".");
10821    let completion_item = lsp::CompletionItem {
10822        label: "some".into(),
10823        kind: Some(lsp::CompletionItemKind::SNIPPET),
10824        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10825        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10826            kind: lsp::MarkupKind::Markdown,
10827            value: "```rust\nSome(2)\n```".to_string(),
10828        })),
10829        deprecated: Some(false),
10830        sort_text: Some("fffffff2".to_string()),
10831        filter_text: Some("some".to_string()),
10832        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10833        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10834            range: lsp::Range {
10835                start: lsp::Position {
10836                    line: 0,
10837                    character: 22,
10838                },
10839                end: lsp::Position {
10840                    line: 0,
10841                    character: 22,
10842                },
10843            },
10844            new_text: "Some(2)".to_string(),
10845        })),
10846        additional_text_edits: Some(vec![lsp::TextEdit {
10847            range: lsp::Range {
10848                start: lsp::Position {
10849                    line: 0,
10850                    character: 20,
10851                },
10852                end: lsp::Position {
10853                    line: 0,
10854                    character: 22,
10855                },
10856            },
10857            new_text: "".to_string(),
10858        }]),
10859        ..Default::default()
10860    };
10861
10862    let closure_completion_item = completion_item.clone();
10863    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
10864        let task_completion_item = closure_completion_item.clone();
10865        async move {
10866            Ok(Some(lsp::CompletionResponse::Array(vec![
10867                task_completion_item,
10868            ])))
10869        }
10870    });
10871
10872    request.next().await;
10873
10874    cx.condition(|editor, _| editor.context_menu_visible())
10875        .await;
10876    let apply_additional_edits = cx.update_editor(|editor, cx| {
10877        editor
10878            .confirm_completion(&ConfirmCompletion::default(), cx)
10879            .unwrap()
10880    });
10881    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
10882
10883    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10884        let task_completion_item = completion_item.clone();
10885        async move { Ok(task_completion_item) }
10886    })
10887    .next()
10888    .await
10889    .unwrap();
10890    apply_additional_edits.await.unwrap();
10891    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
10892}
10893
10894#[gpui::test]
10895async fn test_completions_resolve_updates_labels_if_filter_text_matches(
10896    cx: &mut gpui::TestAppContext,
10897) {
10898    init_test(cx, |_| {});
10899
10900    let mut cx = EditorLspTestContext::new_rust(
10901        lsp::ServerCapabilities {
10902            completion_provider: Some(lsp::CompletionOptions {
10903                trigger_characters: Some(vec![".".to_string()]),
10904                resolve_provider: Some(true),
10905                ..Default::default()
10906            }),
10907            ..Default::default()
10908        },
10909        cx,
10910    )
10911    .await;
10912
10913    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
10914    cx.simulate_keystroke(".");
10915
10916    let item1 = lsp::CompletionItem {
10917        label: "id".to_string(),
10918        filter_text: Some("id".to_string()),
10919        detail: None,
10920        documentation: None,
10921        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10922            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10923            new_text: ".id".to_string(),
10924        })),
10925        ..lsp::CompletionItem::default()
10926    };
10927
10928    let item2 = lsp::CompletionItem {
10929        label: "other".to_string(),
10930        filter_text: Some("other".to_string()),
10931        detail: None,
10932        documentation: None,
10933        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10934            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
10935            new_text: ".other".to_string(),
10936        })),
10937        ..lsp::CompletionItem::default()
10938    };
10939
10940    let item1 = item1.clone();
10941    cx.handle_request::<lsp::request::Completion, _, _>({
10942        let item1 = item1.clone();
10943        move |_, _, _| {
10944            let item1 = item1.clone();
10945            let item2 = item2.clone();
10946            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
10947        }
10948    })
10949    .next()
10950    .await;
10951
10952    cx.condition(|editor, _| editor.context_menu_visible())
10953        .await;
10954    cx.update_editor(|editor, _| {
10955        let context_menu = editor.context_menu.borrow_mut();
10956        let context_menu = context_menu
10957            .as_ref()
10958            .expect("Should have the context menu deployed");
10959        match context_menu {
10960            CodeContextMenu::Completions(completions_menu) => {
10961                let completions = completions_menu.completions.borrow_mut();
10962                assert_eq!(
10963                    completions
10964                        .iter()
10965                        .map(|completion| &completion.label.text)
10966                        .collect::<Vec<_>>(),
10967                    vec!["id", "other"]
10968                )
10969            }
10970            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
10971        }
10972    });
10973
10974    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
10975        let item1 = item1.clone();
10976        move |_, item_to_resolve, _| {
10977            let item1 = item1.clone();
10978            async move {
10979                if item1 == item_to_resolve {
10980                    Ok(lsp::CompletionItem {
10981                        label: "method id()".to_string(),
10982                        filter_text: Some("id".to_string()),
10983                        detail: Some("Now resolved!".to_string()),
10984                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
10985                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10986                            range: lsp::Range::new(
10987                                lsp::Position::new(0, 22),
10988                                lsp::Position::new(0, 22),
10989                            ),
10990                            new_text: ".id".to_string(),
10991                        })),
10992                        ..lsp::CompletionItem::default()
10993                    })
10994                } else {
10995                    Ok(item_to_resolve)
10996                }
10997            }
10998        }
10999    })
11000    .next()
11001    .await
11002    .unwrap();
11003    cx.run_until_parked();
11004
11005    cx.update_editor(|editor, cx| {
11006        editor.context_menu_next(&Default::default(), cx);
11007    });
11008
11009    cx.update_editor(|editor, _| {
11010        let context_menu = editor.context_menu.borrow_mut();
11011        let context_menu = context_menu
11012            .as_ref()
11013            .expect("Should have the context menu deployed");
11014        match context_menu {
11015            CodeContextMenu::Completions(completions_menu) => {
11016                let completions = completions_menu.completions.borrow_mut();
11017                assert_eq!(
11018                    completions
11019                        .iter()
11020                        .map(|completion| &completion.label.text)
11021                        .collect::<Vec<_>>(),
11022                    vec!["method id()", "other"],
11023                    "Should update first completion label, but not second as the filter text did not match."
11024                );
11025            }
11026            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11027        }
11028    });
11029}
11030
11031#[gpui::test]
11032async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11033    init_test(cx, |_| {});
11034
11035    let mut cx = EditorLspTestContext::new_rust(
11036        lsp::ServerCapabilities {
11037            completion_provider: Some(lsp::CompletionOptions {
11038                trigger_characters: Some(vec![".".to_string()]),
11039                resolve_provider: Some(true),
11040                ..Default::default()
11041            }),
11042            ..Default::default()
11043        },
11044        cx,
11045    )
11046    .await;
11047
11048    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11049    cx.simulate_keystroke(".");
11050
11051    let unresolved_item_1 = lsp::CompletionItem {
11052        label: "id".to_string(),
11053        filter_text: Some("id".to_string()),
11054        detail: None,
11055        documentation: None,
11056        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11057            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11058            new_text: ".id".to_string(),
11059        })),
11060        ..lsp::CompletionItem::default()
11061    };
11062    let resolved_item_1 = lsp::CompletionItem {
11063        additional_text_edits: Some(vec![lsp::TextEdit {
11064            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11065            new_text: "!!".to_string(),
11066        }]),
11067        ..unresolved_item_1.clone()
11068    };
11069    let unresolved_item_2 = lsp::CompletionItem {
11070        label: "other".to_string(),
11071        filter_text: Some("other".to_string()),
11072        detail: None,
11073        documentation: None,
11074        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11075            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11076            new_text: ".other".to_string(),
11077        })),
11078        ..lsp::CompletionItem::default()
11079    };
11080    let resolved_item_2 = lsp::CompletionItem {
11081        additional_text_edits: Some(vec![lsp::TextEdit {
11082            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11083            new_text: "??".to_string(),
11084        }]),
11085        ..unresolved_item_2.clone()
11086    };
11087
11088    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11089    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11090    cx.lsp
11091        .server
11092        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11093            let unresolved_item_1 = unresolved_item_1.clone();
11094            let resolved_item_1 = resolved_item_1.clone();
11095            let unresolved_item_2 = unresolved_item_2.clone();
11096            let resolved_item_2 = resolved_item_2.clone();
11097            let resolve_requests_1 = resolve_requests_1.clone();
11098            let resolve_requests_2 = resolve_requests_2.clone();
11099            move |unresolved_request, _| {
11100                let unresolved_item_1 = unresolved_item_1.clone();
11101                let resolved_item_1 = resolved_item_1.clone();
11102                let unresolved_item_2 = unresolved_item_2.clone();
11103                let resolved_item_2 = resolved_item_2.clone();
11104                let resolve_requests_1 = resolve_requests_1.clone();
11105                let resolve_requests_2 = resolve_requests_2.clone();
11106                async move {
11107                    if unresolved_request == unresolved_item_1 {
11108                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11109                        Ok(resolved_item_1.clone())
11110                    } else if unresolved_request == unresolved_item_2 {
11111                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11112                        Ok(resolved_item_2.clone())
11113                    } else {
11114                        panic!("Unexpected completion item {unresolved_request:?}")
11115                    }
11116                }
11117            }
11118        })
11119        .detach();
11120
11121    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11122        let unresolved_item_1 = unresolved_item_1.clone();
11123        let unresolved_item_2 = unresolved_item_2.clone();
11124        async move {
11125            Ok(Some(lsp::CompletionResponse::Array(vec![
11126                unresolved_item_1,
11127                unresolved_item_2,
11128            ])))
11129        }
11130    })
11131    .next()
11132    .await;
11133
11134    cx.condition(|editor, _| editor.context_menu_visible())
11135        .await;
11136    cx.update_editor(|editor, _| {
11137        let context_menu = editor.context_menu.borrow_mut();
11138        let context_menu = context_menu
11139            .as_ref()
11140            .expect("Should have the context menu deployed");
11141        match context_menu {
11142            CodeContextMenu::Completions(completions_menu) => {
11143                let completions = completions_menu.completions.borrow_mut();
11144                assert_eq!(
11145                    completions
11146                        .iter()
11147                        .map(|completion| &completion.label.text)
11148                        .collect::<Vec<_>>(),
11149                    vec!["id", "other"]
11150                )
11151            }
11152            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11153        }
11154    });
11155    cx.run_until_parked();
11156
11157    cx.update_editor(|editor, cx| {
11158        editor.context_menu_next(&ContextMenuNext, cx);
11159    });
11160    cx.run_until_parked();
11161    cx.update_editor(|editor, cx| {
11162        editor.context_menu_prev(&ContextMenuPrev, cx);
11163    });
11164    cx.run_until_parked();
11165    cx.update_editor(|editor, cx| {
11166        editor.context_menu_next(&ContextMenuNext, cx);
11167    });
11168    cx.run_until_parked();
11169    cx.update_editor(|editor, cx| {
11170        editor
11171            .compose_completion(&ComposeCompletion::default(), cx)
11172            .expect("No task returned")
11173    })
11174    .await
11175    .expect("Completion failed");
11176    cx.run_until_parked();
11177
11178    cx.update_editor(|editor, cx| {
11179        assert_eq!(
11180            resolve_requests_1.load(atomic::Ordering::Acquire),
11181            1,
11182            "Should always resolve once despite multiple selections"
11183        );
11184        assert_eq!(
11185            resolve_requests_2.load(atomic::Ordering::Acquire),
11186            1,
11187            "Should always resolve once after multiple selections and applying the completion"
11188        );
11189        assert_eq!(
11190            editor.text(cx),
11191            "fn main() { let a = ??.other; }",
11192            "Should use resolved data when applying the completion"
11193        );
11194    });
11195}
11196
11197#[gpui::test]
11198async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11199    init_test(cx, |_| {});
11200
11201    let item_0 = lsp::CompletionItem {
11202        label: "abs".into(),
11203        insert_text: Some("abs".into()),
11204        data: Some(json!({ "very": "special"})),
11205        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11206        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11207            lsp::InsertReplaceEdit {
11208                new_text: "abs".to_string(),
11209                insert: lsp::Range::default(),
11210                replace: lsp::Range::default(),
11211            },
11212        )),
11213        ..lsp::CompletionItem::default()
11214    };
11215    let items = iter::once(item_0.clone())
11216        .chain((11..51).map(|i| lsp::CompletionItem {
11217            label: format!("item_{}", i),
11218            insert_text: Some(format!("item_{}", i)),
11219            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11220            ..lsp::CompletionItem::default()
11221        }))
11222        .collect::<Vec<_>>();
11223
11224    let default_commit_characters = vec!["?".to_string()];
11225    let default_data = json!({ "default": "data"});
11226    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11227    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11228    let default_edit_range = lsp::Range {
11229        start: lsp::Position {
11230            line: 0,
11231            character: 5,
11232        },
11233        end: lsp::Position {
11234            line: 0,
11235            character: 5,
11236        },
11237    };
11238
11239    let item_0_out = lsp::CompletionItem {
11240        commit_characters: Some(default_commit_characters.clone()),
11241        insert_text_format: Some(default_insert_text_format),
11242        ..item_0
11243    };
11244    let items_out = iter::once(item_0_out)
11245        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11246            commit_characters: Some(default_commit_characters.clone()),
11247            data: Some(default_data.clone()),
11248            insert_text_mode: Some(default_insert_text_mode),
11249            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11250                range: default_edit_range,
11251                new_text: item.label.clone(),
11252            })),
11253            ..item.clone()
11254        }))
11255        .collect::<Vec<lsp::CompletionItem>>();
11256
11257    let mut cx = EditorLspTestContext::new_rust(
11258        lsp::ServerCapabilities {
11259            completion_provider: Some(lsp::CompletionOptions {
11260                trigger_characters: Some(vec![".".to_string()]),
11261                resolve_provider: Some(true),
11262                ..Default::default()
11263            }),
11264            ..Default::default()
11265        },
11266        cx,
11267    )
11268    .await;
11269
11270    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11271    cx.simulate_keystroke(".");
11272
11273    let completion_data = default_data.clone();
11274    let completion_characters = default_commit_characters.clone();
11275    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11276        let default_data = completion_data.clone();
11277        let default_commit_characters = completion_characters.clone();
11278        let items = items.clone();
11279        async move {
11280            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11281                items,
11282                item_defaults: Some(lsp::CompletionListItemDefaults {
11283                    data: Some(default_data.clone()),
11284                    commit_characters: Some(default_commit_characters.clone()),
11285                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11286                        default_edit_range,
11287                    )),
11288                    insert_text_format: Some(default_insert_text_format),
11289                    insert_text_mode: Some(default_insert_text_mode),
11290                }),
11291                ..lsp::CompletionList::default()
11292            })))
11293        }
11294    })
11295    .next()
11296    .await;
11297
11298    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11299    cx.lsp
11300        .server
11301        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11302            let closure_resolved_items = resolved_items.clone();
11303            move |item_to_resolve, _| {
11304                let closure_resolved_items = closure_resolved_items.clone();
11305                async move {
11306                    closure_resolved_items.lock().push(item_to_resolve.clone());
11307                    Ok(item_to_resolve)
11308                }
11309            }
11310        })
11311        .detach();
11312
11313    cx.condition(|editor, _| editor.context_menu_visible())
11314        .await;
11315    cx.run_until_parked();
11316    cx.update_editor(|editor, _| {
11317        let menu = editor.context_menu.borrow_mut();
11318        match menu.as_ref().expect("should have the completions menu") {
11319            CodeContextMenu::Completions(completions_menu) => {
11320                assert_eq!(
11321                    completions_menu
11322                        .entries
11323                        .borrow()
11324                        .iter()
11325                        .flat_map(|c| match c {
11326                            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11327                            _ => None,
11328                        })
11329                        .collect::<Vec<String>>(),
11330                    items_out
11331                        .iter()
11332                        .map(|completion| completion.label.clone())
11333                        .collect::<Vec<String>>()
11334                );
11335            }
11336            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11337        }
11338    });
11339    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11340    // with 4 from the end.
11341    assert_eq!(
11342        *resolved_items.lock(),
11343        [
11344            &items_out[0..16],
11345            &items_out[items_out.len() - 4..items_out.len()]
11346        ]
11347        .concat()
11348        .iter()
11349        .cloned()
11350        .collect::<Vec<lsp::CompletionItem>>()
11351    );
11352    resolved_items.lock().clear();
11353
11354    cx.update_editor(|editor, cx| {
11355        editor.context_menu_prev(&ContextMenuPrev, cx);
11356    });
11357    cx.run_until_parked();
11358    // Completions that have already been resolved are skipped.
11359    assert_eq!(
11360        *resolved_items.lock(),
11361        items_out[items_out.len() - 16..items_out.len() - 4]
11362            .iter()
11363            .cloned()
11364            .collect::<Vec<lsp::CompletionItem>>()
11365    );
11366    resolved_items.lock().clear();
11367}
11368
11369#[gpui::test]
11370async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11371    init_test(cx, |_| {});
11372
11373    let mut cx = EditorLspTestContext::new(
11374        Language::new(
11375            LanguageConfig {
11376                matcher: LanguageMatcher {
11377                    path_suffixes: vec!["jsx".into()],
11378                    ..Default::default()
11379                },
11380                overrides: [(
11381                    "element".into(),
11382                    LanguageConfigOverride {
11383                        word_characters: Override::Set(['-'].into_iter().collect()),
11384                        ..Default::default()
11385                    },
11386                )]
11387                .into_iter()
11388                .collect(),
11389                ..Default::default()
11390            },
11391            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11392        )
11393        .with_override_query("(jsx_self_closing_element) @element")
11394        .unwrap(),
11395        lsp::ServerCapabilities {
11396            completion_provider: Some(lsp::CompletionOptions {
11397                trigger_characters: Some(vec![":".to_string()]),
11398                ..Default::default()
11399            }),
11400            ..Default::default()
11401        },
11402        cx,
11403    )
11404    .await;
11405
11406    cx.lsp
11407        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11408            Ok(Some(lsp::CompletionResponse::Array(vec![
11409                lsp::CompletionItem {
11410                    label: "bg-blue".into(),
11411                    ..Default::default()
11412                },
11413                lsp::CompletionItem {
11414                    label: "bg-red".into(),
11415                    ..Default::default()
11416                },
11417                lsp::CompletionItem {
11418                    label: "bg-yellow".into(),
11419                    ..Default::default()
11420                },
11421            ])))
11422        });
11423
11424    cx.set_state(r#"<p class="bgˇ" />"#);
11425
11426    // Trigger completion when typing a dash, because the dash is an extra
11427    // word character in the 'element' scope, which contains the cursor.
11428    cx.simulate_keystroke("-");
11429    cx.executor().run_until_parked();
11430    cx.update_editor(|editor, _| {
11431        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11432        {
11433            assert_eq!(
11434                completion_menu_entries(&menu),
11435                &["bg-red", "bg-blue", "bg-yellow"]
11436            );
11437        } else {
11438            panic!("expected completion menu to be open");
11439        }
11440    });
11441
11442    cx.simulate_keystroke("l");
11443    cx.executor().run_until_parked();
11444    cx.update_editor(|editor, _| {
11445        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11446        {
11447            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11448        } else {
11449            panic!("expected completion menu to be open");
11450        }
11451    });
11452
11453    // When filtering completions, consider the character after the '-' to
11454    // be the start of a subword.
11455    cx.set_state(r#"<p class="yelˇ" />"#);
11456    cx.simulate_keystroke("l");
11457    cx.executor().run_until_parked();
11458    cx.update_editor(|editor, _| {
11459        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11460        {
11461            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11462        } else {
11463            panic!("expected completion menu to be open");
11464        }
11465    });
11466}
11467
11468fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11469    let entries = menu.entries.borrow();
11470    entries
11471        .iter()
11472        .flat_map(|e| match e {
11473            CompletionEntry::Match(mat) => Some(mat.string.clone()),
11474            _ => None,
11475        })
11476        .collect()
11477}
11478
11479#[gpui::test]
11480async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11481    init_test(cx, |settings| {
11482        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11483            FormatterList(vec![Formatter::Prettier].into()),
11484        ))
11485    });
11486
11487    let fs = FakeFs::new(cx.executor());
11488    fs.insert_file("/file.ts", Default::default()).await;
11489
11490    let project = Project::test(fs, ["/file.ts".as_ref()], cx).await;
11491    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11492
11493    language_registry.add(Arc::new(Language::new(
11494        LanguageConfig {
11495            name: "TypeScript".into(),
11496            matcher: LanguageMatcher {
11497                path_suffixes: vec!["ts".to_string()],
11498                ..Default::default()
11499            },
11500            ..Default::default()
11501        },
11502        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11503    )));
11504    update_test_language_settings(cx, |settings| {
11505        settings.defaults.prettier = Some(PrettierSettings {
11506            allowed: true,
11507            ..PrettierSettings::default()
11508        });
11509    });
11510
11511    let test_plugin = "test_plugin";
11512    let _ = language_registry.register_fake_lsp(
11513        "TypeScript",
11514        FakeLspAdapter {
11515            prettier_plugins: vec![test_plugin],
11516            ..Default::default()
11517        },
11518    );
11519
11520    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11521    let buffer = project
11522        .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx))
11523        .await
11524        .unwrap();
11525
11526    let buffer_text = "one\ntwo\nthree\n";
11527    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
11528    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
11529    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
11530
11531    editor
11532        .update(cx, |editor, cx| {
11533            editor.perform_format(
11534                project.clone(),
11535                FormatTrigger::Manual,
11536                FormatTarget::Buffers,
11537                cx,
11538            )
11539        })
11540        .unwrap()
11541        .await;
11542    assert_eq!(
11543        editor.update(cx, |editor, cx| editor.text(cx)),
11544        buffer_text.to_string() + prettier_format_suffix,
11545        "Test prettier formatting was not applied to the original buffer text",
11546    );
11547
11548    update_test_language_settings(cx, |settings| {
11549        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11550    });
11551    let format = editor.update(cx, |editor, cx| {
11552        editor.perform_format(
11553            project.clone(),
11554            FormatTrigger::Manual,
11555            FormatTarget::Buffers,
11556            cx,
11557        )
11558    });
11559    format.await.unwrap();
11560    assert_eq!(
11561        editor.update(cx, |editor, cx| editor.text(cx)),
11562        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11563        "Autoformatting (via test prettier) was not applied to the original buffer text",
11564    );
11565}
11566
11567#[gpui::test]
11568async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11569    init_test(cx, |_| {});
11570    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11571    let base_text = indoc! {r#"
11572        struct Row;
11573        struct Row1;
11574        struct Row2;
11575
11576        struct Row4;
11577        struct Row5;
11578        struct Row6;
11579
11580        struct Row8;
11581        struct Row9;
11582        struct Row10;"#};
11583
11584    // When addition hunks are not adjacent to carets, no hunk revert is performed
11585    assert_hunk_revert(
11586        indoc! {r#"struct Row;
11587                   struct Row1;
11588                   struct Row1.1;
11589                   struct Row1.2;
11590                   struct Row2;ˇ
11591
11592                   struct Row4;
11593                   struct Row5;
11594                   struct Row6;
11595
11596                   struct Row8;
11597                   ˇstruct Row9;
11598                   struct Row9.1;
11599                   struct Row9.2;
11600                   struct Row9.3;
11601                   struct Row10;"#},
11602        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11603        indoc! {r#"struct Row;
11604                   struct Row1;
11605                   struct Row1.1;
11606                   struct Row1.2;
11607                   struct Row2;ˇ
11608
11609                   struct Row4;
11610                   struct Row5;
11611                   struct Row6;
11612
11613                   struct Row8;
11614                   ˇstruct Row9;
11615                   struct Row9.1;
11616                   struct Row9.2;
11617                   struct Row9.3;
11618                   struct Row10;"#},
11619        base_text,
11620        &mut cx,
11621    );
11622    // Same for selections
11623    assert_hunk_revert(
11624        indoc! {r#"struct Row;
11625                   struct Row1;
11626                   struct Row2;
11627                   struct Row2.1;
11628                   struct Row2.2;
11629                   «ˇ
11630                   struct Row4;
11631                   struct» Row5;
11632                   «struct Row6;
11633                   ˇ»
11634                   struct Row9.1;
11635                   struct Row9.2;
11636                   struct Row9.3;
11637                   struct Row8;
11638                   struct Row9;
11639                   struct Row10;"#},
11640        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11641        indoc! {r#"struct Row;
11642                   struct Row1;
11643                   struct Row2;
11644                   struct Row2.1;
11645                   struct Row2.2;
11646                   «ˇ
11647                   struct Row4;
11648                   struct» Row5;
11649                   «struct Row6;
11650                   ˇ»
11651                   struct Row9.1;
11652                   struct Row9.2;
11653                   struct Row9.3;
11654                   struct Row8;
11655                   struct Row9;
11656                   struct Row10;"#},
11657        base_text,
11658        &mut cx,
11659    );
11660
11661    // When carets and selections intersect the addition hunks, those are reverted.
11662    // Adjacent carets got merged.
11663    assert_hunk_revert(
11664        indoc! {r#"struct Row;
11665                   ˇ// something on the top
11666                   struct Row1;
11667                   struct Row2;
11668                   struct Roˇw3.1;
11669                   struct Row2.2;
11670                   struct Row2.3;ˇ
11671
11672                   struct Row4;
11673                   struct ˇRow5.1;
11674                   struct Row5.2;
11675                   struct «Rowˇ»5.3;
11676                   struct Row5;
11677                   struct Row6;
11678                   ˇ
11679                   struct Row9.1;
11680                   struct «Rowˇ»9.2;
11681                   struct «ˇRow»9.3;
11682                   struct Row8;
11683                   struct Row9;
11684                   «ˇ// something on bottom»
11685                   struct Row10;"#},
11686        vec![
11687            DiffHunkStatus::Added,
11688            DiffHunkStatus::Added,
11689            DiffHunkStatus::Added,
11690            DiffHunkStatus::Added,
11691            DiffHunkStatus::Added,
11692        ],
11693        indoc! {r#"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        base_text,
11705        &mut cx,
11706    );
11707}
11708
11709#[gpui::test]
11710async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
11711    init_test(cx, |_| {});
11712    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11713    let base_text = indoc! {r#"
11714        struct Row;
11715        struct Row1;
11716        struct Row2;
11717
11718        struct Row4;
11719        struct Row5;
11720        struct Row6;
11721
11722        struct Row8;
11723        struct Row9;
11724        struct Row10;"#};
11725
11726    // Modification hunks behave the same as the addition ones.
11727    assert_hunk_revert(
11728        indoc! {r#"struct Row;
11729                   struct Row1;
11730                   struct Row33;
11731                   ˇ
11732                   struct Row4;
11733                   struct Row5;
11734                   struct Row6;
11735                   ˇ
11736                   struct Row99;
11737                   struct Row9;
11738                   struct Row10;"#},
11739        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11740        indoc! {r#"struct Row;
11741                   struct Row1;
11742                   struct Row33;
11743                   ˇ
11744                   struct Row4;
11745                   struct Row5;
11746                   struct Row6;
11747                   ˇ
11748                   struct Row99;
11749                   struct Row9;
11750                   struct Row10;"#},
11751        base_text,
11752        &mut cx,
11753    );
11754    assert_hunk_revert(
11755        indoc! {r#"struct Row;
11756                   struct Row1;
11757                   struct Row33;
11758                   «ˇ
11759                   struct Row4;
11760                   struct» Row5;
11761                   «struct Row6;
11762                   ˇ»
11763                   struct Row99;
11764                   struct Row9;
11765                   struct Row10;"#},
11766        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
11767        indoc! {r#"struct Row;
11768                   struct Row1;
11769                   struct Row33;
11770                   «ˇ
11771                   struct Row4;
11772                   struct» Row5;
11773                   «struct Row6;
11774                   ˇ»
11775                   struct Row99;
11776                   struct Row9;
11777                   struct Row10;"#},
11778        base_text,
11779        &mut cx,
11780    );
11781
11782    assert_hunk_revert(
11783        indoc! {r#"ˇstruct Row1.1;
11784                   struct Row1;
11785                   «ˇstr»uct Row22;
11786
11787                   struct ˇRow44;
11788                   struct Row5;
11789                   struct «Rˇ»ow66;ˇ
11790
11791                   «struˇ»ct Row88;
11792                   struct Row9;
11793                   struct Row1011;ˇ"#},
11794        vec![
11795            DiffHunkStatus::Modified,
11796            DiffHunkStatus::Modified,
11797            DiffHunkStatus::Modified,
11798            DiffHunkStatus::Modified,
11799            DiffHunkStatus::Modified,
11800            DiffHunkStatus::Modified,
11801        ],
11802        indoc! {r#"struct Row;
11803                   ˇstruct Row1;
11804                   struct Row2;
11805                   ˇ
11806                   struct Row4;
11807                   ˇstruct Row5;
11808                   struct Row6;
11809                   ˇ
11810                   struct Row8;
11811                   ˇstruct Row9;
11812                   struct Row10;ˇ"#},
11813        base_text,
11814        &mut cx,
11815    );
11816}
11817
11818#[gpui::test]
11819async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
11820    init_test(cx, |_| {});
11821    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11822    let base_text = indoc! {r#"struct Row;
11823struct Row1;
11824struct Row2;
11825
11826struct Row4;
11827struct Row5;
11828struct Row6;
11829
11830struct Row8;
11831struct Row9;
11832struct Row10;"#};
11833
11834    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
11835    assert_hunk_revert(
11836        indoc! {r#"struct Row;
11837                   struct Row2;
11838
11839                   ˇstruct Row4;
11840                   struct Row5;
11841                   struct Row6;
11842                   ˇ
11843                   struct Row8;
11844                   struct Row10;"#},
11845        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11846        indoc! {r#"struct Row;
11847                   struct Row2;
11848
11849                   ˇstruct Row4;
11850                   struct Row5;
11851                   struct Row6;
11852                   ˇ
11853                   struct Row8;
11854                   struct Row10;"#},
11855        base_text,
11856        &mut cx,
11857    );
11858    assert_hunk_revert(
11859        indoc! {r#"struct Row;
11860                   struct Row2;
11861
11862                   «ˇstruct Row4;
11863                   struct» Row5;
11864                   «struct Row6;
11865                   ˇ»
11866                   struct Row8;
11867                   struct Row10;"#},
11868        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11869        indoc! {r#"struct Row;
11870                   struct Row2;
11871
11872                   «ˇstruct Row4;
11873                   struct» Row5;
11874                   «struct Row6;
11875                   ˇ»
11876                   struct Row8;
11877                   struct Row10;"#},
11878        base_text,
11879        &mut cx,
11880    );
11881
11882    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
11883    assert_hunk_revert(
11884        indoc! {r#"struct Row;
11885                   ˇstruct Row2;
11886
11887                   struct Row4;
11888                   struct Row5;
11889                   struct Row6;
11890
11891                   struct Row8;ˇ
11892                   struct Row10;"#},
11893        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
11894        indoc! {r#"struct Row;
11895                   struct Row1;
11896                   ˇstruct Row2;
11897
11898                   struct Row4;
11899                   struct Row5;
11900                   struct Row6;
11901
11902                   struct Row8;ˇ
11903                   struct Row9;
11904                   struct Row10;"#},
11905        base_text,
11906        &mut cx,
11907    );
11908    assert_hunk_revert(
11909        indoc! {r#"struct Row;
11910                   struct Row2«ˇ;
11911                   struct Row4;
11912                   struct» Row5;
11913                   «struct Row6;
11914
11915                   struct Row8;ˇ»
11916                   struct Row10;"#},
11917        vec![
11918            DiffHunkStatus::Removed,
11919            DiffHunkStatus::Removed,
11920            DiffHunkStatus::Removed,
11921        ],
11922        indoc! {r#"struct Row;
11923                   struct Row1;
11924                   struct Row2«ˇ;
11925
11926                   struct Row4;
11927                   struct» Row5;
11928                   «struct Row6;
11929
11930                   struct Row8;ˇ»
11931                   struct Row9;
11932                   struct Row10;"#},
11933        base_text,
11934        &mut cx,
11935    );
11936}
11937
11938#[gpui::test]
11939async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
11940    init_test(cx, |_| {});
11941
11942    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
11943    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
11944    let base_text_3 =
11945        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
11946
11947    let text_1 = edit_first_char_of_every_line(base_text_1);
11948    let text_2 = edit_first_char_of_every_line(base_text_2);
11949    let text_3 = edit_first_char_of_every_line(base_text_3);
11950
11951    let buffer_1 = cx.new_model(|cx| Buffer::local(text_1.clone(), cx));
11952    let buffer_2 = cx.new_model(|cx| Buffer::local(text_2.clone(), cx));
11953    let buffer_3 = cx.new_model(|cx| Buffer::local(text_3.clone(), cx));
11954
11955    let multibuffer = cx.new_model(|cx| {
11956        let mut multibuffer = MultiBuffer::new(ReadWrite);
11957        multibuffer.push_excerpts(
11958            buffer_1.clone(),
11959            [
11960                ExcerptRange {
11961                    context: Point::new(0, 0)..Point::new(3, 0),
11962                    primary: None,
11963                },
11964                ExcerptRange {
11965                    context: Point::new(5, 0)..Point::new(7, 0),
11966                    primary: None,
11967                },
11968                ExcerptRange {
11969                    context: Point::new(9, 0)..Point::new(10, 4),
11970                    primary: None,
11971                },
11972            ],
11973            cx,
11974        );
11975        multibuffer.push_excerpts(
11976            buffer_2.clone(),
11977            [
11978                ExcerptRange {
11979                    context: Point::new(0, 0)..Point::new(3, 0),
11980                    primary: None,
11981                },
11982                ExcerptRange {
11983                    context: Point::new(5, 0)..Point::new(7, 0),
11984                    primary: None,
11985                },
11986                ExcerptRange {
11987                    context: Point::new(9, 0)..Point::new(10, 4),
11988                    primary: None,
11989                },
11990            ],
11991            cx,
11992        );
11993        multibuffer.push_excerpts(
11994            buffer_3.clone(),
11995            [
11996                ExcerptRange {
11997                    context: Point::new(0, 0)..Point::new(3, 0),
11998                    primary: None,
11999                },
12000                ExcerptRange {
12001                    context: Point::new(5, 0)..Point::new(7, 0),
12002                    primary: None,
12003                },
12004                ExcerptRange {
12005                    context: Point::new(9, 0)..Point::new(10, 4),
12006                    primary: None,
12007                },
12008            ],
12009            cx,
12010        );
12011        multibuffer
12012    });
12013
12014    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
12015    editor.update(cx, |editor, cx| {
12016        for (buffer, diff_base) in [
12017            (buffer_1.clone(), base_text_1),
12018            (buffer_2.clone(), base_text_2),
12019            (buffer_3.clone(), base_text_3),
12020        ] {
12021            let change_set = cx.new_model(|cx| {
12022                BufferChangeSet::new_with_base_text(
12023                    diff_base.to_string(),
12024                    buffer.read(cx).text_snapshot(),
12025                    cx,
12026                )
12027            });
12028            editor.diff_map.add_change_set(change_set, cx)
12029        }
12030    });
12031    cx.executor().run_until_parked();
12032
12033    editor.update(cx, |editor, cx| {
12034        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}");
12035        editor.select_all(&SelectAll, cx);
12036        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12037    });
12038    cx.executor().run_until_parked();
12039
12040    // When all ranges are selected, all buffer hunks are reverted.
12041    editor.update(cx, |editor, cx| {
12042        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");
12043    });
12044    buffer_1.update(cx, |buffer, _| {
12045        assert_eq!(buffer.text(), base_text_1);
12046    });
12047    buffer_2.update(cx, |buffer, _| {
12048        assert_eq!(buffer.text(), base_text_2);
12049    });
12050    buffer_3.update(cx, |buffer, _| {
12051        assert_eq!(buffer.text(), base_text_3);
12052    });
12053
12054    editor.update(cx, |editor, cx| {
12055        editor.undo(&Default::default(), cx);
12056    });
12057
12058    editor.update(cx, |editor, cx| {
12059        editor.change_selections(None, cx, |s| {
12060            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12061        });
12062        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
12063    });
12064
12065    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12066    // but not affect buffer_2 and its related excerpts.
12067    editor.update(cx, |editor, cx| {
12068        assert_eq!(
12069            editor.text(cx),
12070            "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}"
12071        );
12072    });
12073    buffer_1.update(cx, |buffer, _| {
12074        assert_eq!(buffer.text(), base_text_1);
12075    });
12076    buffer_2.update(cx, |buffer, _| {
12077        assert_eq!(
12078            buffer.text(),
12079            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12080        );
12081    });
12082    buffer_3.update(cx, |buffer, _| {
12083        assert_eq!(
12084            buffer.text(),
12085            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12086        );
12087    });
12088
12089    fn edit_first_char_of_every_line(text: &str) -> String {
12090        text.split('\n')
12091            .map(|line| format!("X{}", &line[1..]))
12092            .collect::<Vec<_>>()
12093            .join("\n")
12094    }
12095}
12096
12097#[gpui::test]
12098async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12099    init_test(cx, |_| {});
12100
12101    let cols = 4;
12102    let rows = 10;
12103    let sample_text_1 = sample_text(rows, cols, 'a');
12104    assert_eq!(
12105        sample_text_1,
12106        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12107    );
12108    let sample_text_2 = sample_text(rows, cols, 'l');
12109    assert_eq!(
12110        sample_text_2,
12111        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12112    );
12113    let sample_text_3 = sample_text(rows, cols, 'v');
12114    assert_eq!(
12115        sample_text_3,
12116        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12117    );
12118
12119    let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
12120    let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
12121    let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
12122
12123    let multi_buffer = cx.new_model(|cx| {
12124        let mut multibuffer = MultiBuffer::new(ReadWrite);
12125        multibuffer.push_excerpts(
12126            buffer_1.clone(),
12127            [
12128                ExcerptRange {
12129                    context: Point::new(0, 0)..Point::new(3, 0),
12130                    primary: None,
12131                },
12132                ExcerptRange {
12133                    context: Point::new(5, 0)..Point::new(7, 0),
12134                    primary: None,
12135                },
12136                ExcerptRange {
12137                    context: Point::new(9, 0)..Point::new(10, 4),
12138                    primary: None,
12139                },
12140            ],
12141            cx,
12142        );
12143        multibuffer.push_excerpts(
12144            buffer_2.clone(),
12145            [
12146                ExcerptRange {
12147                    context: Point::new(0, 0)..Point::new(3, 0),
12148                    primary: None,
12149                },
12150                ExcerptRange {
12151                    context: Point::new(5, 0)..Point::new(7, 0),
12152                    primary: None,
12153                },
12154                ExcerptRange {
12155                    context: Point::new(9, 0)..Point::new(10, 4),
12156                    primary: None,
12157                },
12158            ],
12159            cx,
12160        );
12161        multibuffer.push_excerpts(
12162            buffer_3.clone(),
12163            [
12164                ExcerptRange {
12165                    context: Point::new(0, 0)..Point::new(3, 0),
12166                    primary: None,
12167                },
12168                ExcerptRange {
12169                    context: Point::new(5, 0)..Point::new(7, 0),
12170                    primary: None,
12171                },
12172                ExcerptRange {
12173                    context: Point::new(9, 0)..Point::new(10, 4),
12174                    primary: None,
12175                },
12176            ],
12177            cx,
12178        );
12179        multibuffer
12180    });
12181
12182    let fs = FakeFs::new(cx.executor());
12183    fs.insert_tree(
12184        "/a",
12185        json!({
12186            "main.rs": sample_text_1,
12187            "other.rs": sample_text_2,
12188            "lib.rs": sample_text_3,
12189        }),
12190    )
12191    .await;
12192    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12193    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
12194    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12195    let multi_buffer_editor = cx.new_view(|cx| {
12196        Editor::new(
12197            EditorMode::Full,
12198            multi_buffer,
12199            Some(project.clone()),
12200            true,
12201            cx,
12202        )
12203    });
12204    let multibuffer_item_id = workspace
12205        .update(cx, |workspace, cx| {
12206            assert!(
12207                workspace.active_item(cx).is_none(),
12208                "active item should be None before the first item is added"
12209            );
12210            workspace.add_item_to_active_pane(
12211                Box::new(multi_buffer_editor.clone()),
12212                None,
12213                true,
12214                cx,
12215            );
12216            let active_item = workspace
12217                .active_item(cx)
12218                .expect("should have an active item after adding the multi buffer");
12219            assert!(
12220                !active_item.is_singleton(cx),
12221                "A multi buffer was expected to active after adding"
12222            );
12223            active_item.item_id()
12224        })
12225        .unwrap();
12226    cx.executor().run_until_parked();
12227
12228    multi_buffer_editor.update(cx, |editor, cx| {
12229        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
12230        editor.open_excerpts(&OpenExcerpts, cx);
12231    });
12232    cx.executor().run_until_parked();
12233    let first_item_id = workspace
12234        .update(cx, |workspace, cx| {
12235            let active_item = workspace
12236                .active_item(cx)
12237                .expect("should have an active item after navigating into the 1st buffer");
12238            let first_item_id = active_item.item_id();
12239            assert_ne!(
12240                first_item_id, multibuffer_item_id,
12241                "Should navigate into the 1st buffer and activate it"
12242            );
12243            assert!(
12244                active_item.is_singleton(cx),
12245                "New active item should be a singleton buffer"
12246            );
12247            assert_eq!(
12248                active_item
12249                    .act_as::<Editor>(cx)
12250                    .expect("should have navigated into an editor for the 1st buffer")
12251                    .read(cx)
12252                    .text(cx),
12253                sample_text_1
12254            );
12255
12256            workspace
12257                .go_back(workspace.active_pane().downgrade(), cx)
12258                .detach_and_log_err(cx);
12259
12260            first_item_id
12261        })
12262        .unwrap();
12263    cx.executor().run_until_parked();
12264    workspace
12265        .update(cx, |workspace, cx| {
12266            let active_item = workspace
12267                .active_item(cx)
12268                .expect("should have an active item after navigating back");
12269            assert_eq!(
12270                active_item.item_id(),
12271                multibuffer_item_id,
12272                "Should navigate back to the multi buffer"
12273            );
12274            assert!(!active_item.is_singleton(cx));
12275        })
12276        .unwrap();
12277
12278    multi_buffer_editor.update(cx, |editor, cx| {
12279        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12280            s.select_ranges(Some(39..40))
12281        });
12282        editor.open_excerpts(&OpenExcerpts, cx);
12283    });
12284    cx.executor().run_until_parked();
12285    let second_item_id = workspace
12286        .update(cx, |workspace, cx| {
12287            let active_item = workspace
12288                .active_item(cx)
12289                .expect("should have an active item after navigating into the 2nd buffer");
12290            let second_item_id = active_item.item_id();
12291            assert_ne!(
12292                second_item_id, multibuffer_item_id,
12293                "Should navigate away from the multibuffer"
12294            );
12295            assert_ne!(
12296                second_item_id, first_item_id,
12297                "Should navigate into the 2nd buffer and activate it"
12298            );
12299            assert!(
12300                active_item.is_singleton(cx),
12301                "New active item should be a singleton buffer"
12302            );
12303            assert_eq!(
12304                active_item
12305                    .act_as::<Editor>(cx)
12306                    .expect("should have navigated into an editor")
12307                    .read(cx)
12308                    .text(cx),
12309                sample_text_2
12310            );
12311
12312            workspace
12313                .go_back(workspace.active_pane().downgrade(), cx)
12314                .detach_and_log_err(cx);
12315
12316            second_item_id
12317        })
12318        .unwrap();
12319    cx.executor().run_until_parked();
12320    workspace
12321        .update(cx, |workspace, cx| {
12322            let active_item = workspace
12323                .active_item(cx)
12324                .expect("should have an active item after navigating back from the 2nd buffer");
12325            assert_eq!(
12326                active_item.item_id(),
12327                multibuffer_item_id,
12328                "Should navigate back from the 2nd buffer to the multi buffer"
12329            );
12330            assert!(!active_item.is_singleton(cx));
12331        })
12332        .unwrap();
12333
12334    multi_buffer_editor.update(cx, |editor, cx| {
12335        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
12336            s.select_ranges(Some(70..70))
12337        });
12338        editor.open_excerpts(&OpenExcerpts, cx);
12339    });
12340    cx.executor().run_until_parked();
12341    workspace
12342        .update(cx, |workspace, cx| {
12343            let active_item = workspace
12344                .active_item(cx)
12345                .expect("should have an active item after navigating into the 3rd buffer");
12346            let third_item_id = active_item.item_id();
12347            assert_ne!(
12348                third_item_id, multibuffer_item_id,
12349                "Should navigate into the 3rd buffer and activate it"
12350            );
12351            assert_ne!(third_item_id, first_item_id);
12352            assert_ne!(third_item_id, second_item_id);
12353            assert!(
12354                active_item.is_singleton(cx),
12355                "New active item should be a singleton buffer"
12356            );
12357            assert_eq!(
12358                active_item
12359                    .act_as::<Editor>(cx)
12360                    .expect("should have navigated into an editor")
12361                    .read(cx)
12362                    .text(cx),
12363                sample_text_3
12364            );
12365
12366            workspace
12367                .go_back(workspace.active_pane().downgrade(), cx)
12368                .detach_and_log_err(cx);
12369        })
12370        .unwrap();
12371    cx.executor().run_until_parked();
12372    workspace
12373        .update(cx, |workspace, cx| {
12374            let active_item = workspace
12375                .active_item(cx)
12376                .expect("should have an active item after navigating back from the 3rd buffer");
12377            assert_eq!(
12378                active_item.item_id(),
12379                multibuffer_item_id,
12380                "Should navigate back from the 3rd buffer to the multi buffer"
12381            );
12382            assert!(!active_item.is_singleton(cx));
12383        })
12384        .unwrap();
12385}
12386
12387#[gpui::test]
12388async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12389    init_test(cx, |_| {});
12390
12391    let mut cx = EditorTestContext::new(cx).await;
12392
12393    let diff_base = r#"
12394        use some::mod;
12395
12396        const A: u32 = 42;
12397
12398        fn main() {
12399            println!("hello");
12400
12401            println!("world");
12402        }
12403        "#
12404    .unindent();
12405
12406    cx.set_state(
12407        &r#"
12408        use some::modified;
12409
12410        ˇ
12411        fn main() {
12412            println!("hello there");
12413
12414            println!("around the");
12415            println!("world");
12416        }
12417        "#
12418        .unindent(),
12419    );
12420
12421    cx.set_diff_base(&diff_base);
12422    executor.run_until_parked();
12423
12424    cx.update_editor(|editor, cx| {
12425        editor.go_to_next_hunk(&GoToHunk, cx);
12426        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12427    });
12428    executor.run_until_parked();
12429    cx.assert_state_with_diff(
12430        r#"
12431          use some::modified;
12432
12433
12434          fn main() {
12435        -     println!("hello");
12436        + ˇ    println!("hello there");
12437
12438              println!("around the");
12439              println!("world");
12440          }
12441        "#
12442        .unindent(),
12443    );
12444
12445    cx.update_editor(|editor, cx| {
12446        for _ in 0..3 {
12447            editor.go_to_next_hunk(&GoToHunk, cx);
12448            editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12449        }
12450    });
12451    executor.run_until_parked();
12452    cx.assert_state_with_diff(
12453        r#"
12454        - use some::mod;
12455        + use some::modified;
12456
12457        - const A: u32 = 42;
12458          ˇ
12459          fn main() {
12460        -     println!("hello");
12461        +     println!("hello there");
12462
12463        +     println!("around the");
12464              println!("world");
12465          }
12466        "#
12467        .unindent(),
12468    );
12469
12470    cx.update_editor(|editor, cx| {
12471        editor.cancel(&Cancel, cx);
12472    });
12473
12474    cx.assert_state_with_diff(
12475        r#"
12476          use some::modified;
12477
12478          ˇ
12479          fn main() {
12480              println!("hello there");
12481
12482              println!("around the");
12483              println!("world");
12484          }
12485        "#
12486        .unindent(),
12487    );
12488}
12489
12490#[gpui::test]
12491async fn test_diff_base_change_with_expanded_diff_hunks(
12492    executor: BackgroundExecutor,
12493    cx: &mut gpui::TestAppContext,
12494) {
12495    init_test(cx, |_| {});
12496
12497    let mut cx = EditorTestContext::new(cx).await;
12498
12499    let diff_base = r#"
12500        use some::mod1;
12501        use some::mod2;
12502
12503        const A: u32 = 42;
12504        const B: u32 = 42;
12505        const C: u32 = 42;
12506
12507        fn main() {
12508            println!("hello");
12509
12510            println!("world");
12511        }
12512        "#
12513    .unindent();
12514
12515    cx.set_state(
12516        &r#"
12517        use some::mod2;
12518
12519        const A: u32 = 42;
12520        const C: u32 = 42;
12521
12522        fn main(ˇ) {
12523            //println!("hello");
12524
12525            println!("world");
12526            //
12527            //
12528        }
12529        "#
12530        .unindent(),
12531    );
12532
12533    cx.set_diff_base(&diff_base);
12534    executor.run_until_parked();
12535
12536    cx.update_editor(|editor, cx| {
12537        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12538    });
12539    executor.run_until_parked();
12540    cx.assert_state_with_diff(
12541        r#"
12542        - use some::mod1;
12543          use some::mod2;
12544
12545          const A: u32 = 42;
12546        - const B: u32 = 42;
12547          const C: u32 = 42;
12548
12549          fn main(ˇ) {
12550        -     println!("hello");
12551        +     //println!("hello");
12552
12553              println!("world");
12554        +     //
12555        +     //
12556          }
12557        "#
12558        .unindent(),
12559    );
12560
12561    cx.set_diff_base("new diff base!");
12562    executor.run_until_parked();
12563    cx.assert_state_with_diff(
12564        r#"
12565          use some::mod2;
12566
12567          const A: u32 = 42;
12568          const C: u32 = 42;
12569
12570          fn main(ˇ) {
12571              //println!("hello");
12572
12573              println!("world");
12574              //
12575              //
12576          }
12577        "#
12578        .unindent(),
12579    );
12580
12581    cx.update_editor(|editor, cx| {
12582        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12583    });
12584    executor.run_until_parked();
12585    cx.assert_state_with_diff(
12586        r#"
12587        - new diff base!
12588        + use some::mod2;
12589        +
12590        + const A: u32 = 42;
12591        + const C: u32 = 42;
12592        +
12593        + fn main(ˇ) {
12594        +     //println!("hello");
12595        +
12596        +     println!("world");
12597        +     //
12598        +     //
12599        + }
12600        "#
12601        .unindent(),
12602    );
12603}
12604
12605#[gpui::test]
12606async fn test_fold_unfold_diff_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
12607    init_test(cx, |_| {});
12608
12609    let mut cx = EditorTestContext::new(cx).await;
12610
12611    let diff_base = r#"
12612        use some::mod1;
12613        use some::mod2;
12614
12615        const A: u32 = 42;
12616        const B: u32 = 42;
12617        const C: u32 = 42;
12618
12619        fn main() {
12620            println!("hello");
12621
12622            println!("world");
12623        }
12624
12625        fn another() {
12626            println!("another");
12627        }
12628
12629        fn another2() {
12630            println!("another2");
12631        }
12632        "#
12633    .unindent();
12634
12635    cx.set_state(
12636        &r#"
12637        «use some::mod2;
12638
12639        const A: u32 = 42;
12640        const C: u32 = 42;
12641
12642        fn main() {
12643            //println!("hello");
12644
12645            println!("world");
12646            //
12647            //ˇ»
12648        }
12649
12650        fn another() {
12651            println!("another");
12652            println!("another");
12653        }
12654
12655            println!("another2");
12656        }
12657        "#
12658        .unindent(),
12659    );
12660
12661    cx.set_diff_base(&diff_base);
12662    executor.run_until_parked();
12663
12664    cx.update_editor(|editor, cx| {
12665        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
12666    });
12667    executor.run_until_parked();
12668
12669    cx.assert_state_with_diff(
12670        r#"
12671        - use some::mod1;
12672          «use some::mod2;
12673
12674          const A: u32 = 42;
12675        - const B: u32 = 42;
12676          const C: u32 = 42;
12677
12678          fn main() {
12679        -     println!("hello");
12680        +     //println!("hello");
12681
12682              println!("world");
12683        +     //
12684        +     //ˇ»
12685          }
12686
12687          fn another() {
12688              println!("another");
12689        +     println!("another");
12690          }
12691
12692        - fn another2() {
12693              println!("another2");
12694          }
12695        "#
12696        .unindent(),
12697    );
12698
12699    // Fold across some of the diff hunks. They should no longer appear expanded.
12700    cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
12701    cx.executor().run_until_parked();
12702
12703    // Hunks are not shown if their position is within a fold
12704    cx.assert_state_with_diff(
12705        r#"
12706          «use some::mod2;
12707
12708          const A: u32 = 42;
12709          const C: u32 = 42;
12710
12711          fn main() {
12712              //println!("hello");
12713
12714              println!("world");
12715              //
12716              //ˇ»
12717          }
12718
12719          fn another() {
12720              println!("another");
12721        +     println!("another");
12722          }
12723
12724        - fn another2() {
12725              println!("another2");
12726          }
12727        "#
12728        .unindent(),
12729    );
12730
12731    cx.update_editor(|editor, cx| {
12732        editor.select_all(&SelectAll, cx);
12733        editor.unfold_lines(&UnfoldLines, cx);
12734    });
12735    cx.executor().run_until_parked();
12736
12737    // The deletions reappear when unfolding.
12738    cx.assert_state_with_diff(
12739        r#"
12740        - use some::mod1;
12741          «use some::mod2;
12742
12743          const A: u32 = 42;
12744        - const B: u32 = 42;
12745          const C: u32 = 42;
12746
12747          fn main() {
12748        -     println!("hello");
12749        +     //println!("hello");
12750
12751              println!("world");
12752        +     //
12753        +     //
12754          }
12755
12756          fn another() {
12757              println!("another");
12758        +     println!("another");
12759          }
12760
12761        - fn another2() {
12762              println!("another2");
12763          }
12764          ˇ»"#
12765        .unindent(),
12766    );
12767}
12768
12769#[gpui::test]
12770async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
12771    init_test(cx, |_| {});
12772
12773    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12774    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
12775    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12776    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
12777    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
12778    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
12779
12780    let buffer_1 = cx.new_model(|cx| Buffer::local(file_1_new.to_string(), cx));
12781    let buffer_2 = cx.new_model(|cx| Buffer::local(file_2_new.to_string(), cx));
12782    let buffer_3 = cx.new_model(|cx| Buffer::local(file_3_new.to_string(), cx));
12783
12784    let multi_buffer = cx.new_model(|cx| {
12785        let mut multibuffer = MultiBuffer::new(ReadWrite);
12786        multibuffer.push_excerpts(
12787            buffer_1.clone(),
12788            [
12789                ExcerptRange {
12790                    context: Point::new(0, 0)..Point::new(3, 0),
12791                    primary: None,
12792                },
12793                ExcerptRange {
12794                    context: Point::new(5, 0)..Point::new(7, 0),
12795                    primary: None,
12796                },
12797                ExcerptRange {
12798                    context: Point::new(9, 0)..Point::new(10, 3),
12799                    primary: None,
12800                },
12801            ],
12802            cx,
12803        );
12804        multibuffer.push_excerpts(
12805            buffer_2.clone(),
12806            [
12807                ExcerptRange {
12808                    context: Point::new(0, 0)..Point::new(3, 0),
12809                    primary: None,
12810                },
12811                ExcerptRange {
12812                    context: Point::new(5, 0)..Point::new(7, 0),
12813                    primary: None,
12814                },
12815                ExcerptRange {
12816                    context: Point::new(9, 0)..Point::new(10, 3),
12817                    primary: None,
12818                },
12819            ],
12820            cx,
12821        );
12822        multibuffer.push_excerpts(
12823            buffer_3.clone(),
12824            [
12825                ExcerptRange {
12826                    context: Point::new(0, 0)..Point::new(3, 0),
12827                    primary: None,
12828                },
12829                ExcerptRange {
12830                    context: Point::new(5, 0)..Point::new(7, 0),
12831                    primary: None,
12832                },
12833                ExcerptRange {
12834                    context: Point::new(9, 0)..Point::new(10, 3),
12835                    primary: None,
12836                },
12837            ],
12838            cx,
12839        );
12840        multibuffer
12841    });
12842
12843    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12844    editor
12845        .update(cx, |editor, cx| {
12846            for (buffer, diff_base) in [
12847                (buffer_1.clone(), file_1_old),
12848                (buffer_2.clone(), file_2_old),
12849                (buffer_3.clone(), file_3_old),
12850            ] {
12851                let change_set = cx.new_model(|cx| {
12852                    BufferChangeSet::new_with_base_text(
12853                        diff_base.to_string(),
12854                        buffer.read(cx).text_snapshot(),
12855                        cx,
12856                    )
12857                });
12858                editor.diff_map.add_change_set(change_set, cx)
12859            }
12860        })
12861        .unwrap();
12862
12863    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12864    cx.run_until_parked();
12865
12866    cx.assert_editor_state(
12867        &"
12868            ˇaaa
12869            ccc
12870            ddd
12871
12872            ggg
12873            hhh
12874
12875
12876            lll
12877            mmm
12878            NNN
12879
12880            qqq
12881            rrr
12882
12883            uuu
12884            111
12885            222
12886            333
12887
12888            666
12889            777
12890
12891            000
12892            !!!"
12893        .unindent(),
12894    );
12895
12896    cx.update_editor(|editor, cx| {
12897        editor.select_all(&SelectAll, cx);
12898        editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
12899    });
12900    cx.executor().run_until_parked();
12901
12902    cx.assert_state_with_diff(
12903        "
12904            «aaa
12905          - bbb
12906            ccc
12907            ddd
12908
12909            ggg
12910            hhh
12911
12912
12913            lll
12914            mmm
12915          - nnn
12916          + NNN
12917
12918            qqq
12919            rrr
12920
12921            uuu
12922            111
12923            222
12924            333
12925
12926          + 666
12927            777
12928
12929            000
12930            !!!ˇ»"
12931            .unindent(),
12932    );
12933}
12934
12935#[gpui::test]
12936async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
12937    init_test(cx, |_| {});
12938
12939    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
12940    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
12941
12942    let buffer = cx.new_model(|cx| Buffer::local(text.to_string(), cx));
12943    let multi_buffer = cx.new_model(|cx| {
12944        let mut multibuffer = MultiBuffer::new(ReadWrite);
12945        multibuffer.push_excerpts(
12946            buffer.clone(),
12947            [
12948                ExcerptRange {
12949                    context: Point::new(0, 0)..Point::new(2, 0),
12950                    primary: None,
12951                },
12952                ExcerptRange {
12953                    context: Point::new(5, 0)..Point::new(7, 0),
12954                    primary: None,
12955                },
12956            ],
12957            cx,
12958        );
12959        multibuffer
12960    });
12961
12962    let editor = cx.add_window(|cx| Editor::new(EditorMode::Full, multi_buffer, None, true, cx));
12963    editor
12964        .update(cx, |editor, cx| {
12965            let buffer = buffer.read(cx).text_snapshot();
12966            let change_set = cx
12967                .new_model(|cx| BufferChangeSet::new_with_base_text(base.to_string(), buffer, cx));
12968            editor.diff_map.add_change_set(change_set, cx)
12969        })
12970        .unwrap();
12971
12972    let mut cx = EditorTestContext::for_editor(editor, cx).await;
12973    cx.run_until_parked();
12974
12975    cx.update_editor(|editor, cx| editor.expand_all_hunk_diffs(&Default::default(), cx));
12976    cx.executor().run_until_parked();
12977
12978    cx.assert_state_with_diff(
12979        "
12980            ˇaaa
12981          - bbb
12982          + BBB
12983
12984          - ddd
12985          - eee
12986          + EEE
12987            fff
12988        "
12989        .unindent(),
12990    );
12991}
12992
12993#[gpui::test]
12994async fn test_edits_around_expanded_insertion_hunks(
12995    executor: BackgroundExecutor,
12996    cx: &mut gpui::TestAppContext,
12997) {
12998    init_test(cx, |_| {});
12999
13000    let mut cx = EditorTestContext::new(cx).await;
13001
13002    let diff_base = r#"
13003        use some::mod1;
13004        use some::mod2;
13005
13006        const A: u32 = 42;
13007
13008        fn main() {
13009            println!("hello");
13010
13011            println!("world");
13012        }
13013        "#
13014    .unindent();
13015    executor.run_until_parked();
13016    cx.set_state(
13017        &r#"
13018        use some::mod1;
13019        use some::mod2;
13020
13021        const A: u32 = 42;
13022        const B: u32 = 42;
13023        const C: u32 = 42;
13024        ˇ
13025
13026        fn main() {
13027            println!("hello");
13028
13029            println!("world");
13030        }
13031        "#
13032        .unindent(),
13033    );
13034
13035    cx.set_diff_base(&diff_base);
13036    executor.run_until_parked();
13037
13038    cx.update_editor(|editor, cx| {
13039        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13040    });
13041    executor.run_until_parked();
13042
13043    cx.assert_state_with_diff(
13044        r#"
13045        use some::mod1;
13046        use some::mod2;
13047
13048        const A: u32 = 42;
13049      + const B: u32 = 42;
13050      + const C: u32 = 42;
13051      + ˇ
13052
13053        fn main() {
13054            println!("hello");
13055
13056            println!("world");
13057        }
13058        "#
13059        .unindent(),
13060    );
13061
13062    cx.update_editor(|editor, cx| editor.handle_input("const D: u32 = 42;\n", cx));
13063    executor.run_until_parked();
13064
13065    cx.assert_state_with_diff(
13066        r#"
13067        use some::mod1;
13068        use some::mod2;
13069
13070        const A: u32 = 42;
13071      + const B: u32 = 42;
13072      + const C: u32 = 42;
13073      + const D: u32 = 42;
13074      + ˇ
13075
13076        fn main() {
13077            println!("hello");
13078
13079            println!("world");
13080        }
13081        "#
13082        .unindent(),
13083    );
13084
13085    cx.update_editor(|editor, cx| editor.handle_input("const E: u32 = 42;\n", cx));
13086    executor.run_until_parked();
13087
13088    cx.assert_state_with_diff(
13089        r#"
13090        use some::mod1;
13091        use some::mod2;
13092
13093        const A: u32 = 42;
13094      + const B: u32 = 42;
13095      + const C: u32 = 42;
13096      + const D: u32 = 42;
13097      + const E: u32 = 42;
13098      + ˇ
13099
13100        fn main() {
13101            println!("hello");
13102
13103            println!("world");
13104        }
13105        "#
13106        .unindent(),
13107    );
13108
13109    cx.update_editor(|editor, cx| {
13110        editor.delete_line(&DeleteLine, cx);
13111    });
13112    executor.run_until_parked();
13113
13114    cx.assert_state_with_diff(
13115        r#"
13116        use some::mod1;
13117        use some::mod2;
13118
13119        const A: u32 = 42;
13120      + const B: u32 = 42;
13121      + const C: u32 = 42;
13122      + const D: u32 = 42;
13123      + const E: u32 = 42;
13124        ˇ
13125        fn main() {
13126            println!("hello");
13127
13128            println!("world");
13129        }
13130        "#
13131        .unindent(),
13132    );
13133
13134    cx.update_editor(|editor, cx| {
13135        editor.move_up(&MoveUp, cx);
13136        editor.delete_line(&DeleteLine, cx);
13137        editor.move_up(&MoveUp, cx);
13138        editor.delete_line(&DeleteLine, cx);
13139        editor.move_up(&MoveUp, cx);
13140        editor.delete_line(&DeleteLine, cx);
13141    });
13142    executor.run_until_parked();
13143    cx.assert_state_with_diff(
13144        r#"
13145        use some::mod1;
13146        use some::mod2;
13147
13148        const A: u32 = 42;
13149      + const B: u32 = 42;
13150        ˇ
13151        fn main() {
13152            println!("hello");
13153
13154            println!("world");
13155        }
13156        "#
13157        .unindent(),
13158    );
13159
13160    cx.update_editor(|editor, cx| {
13161        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, cx);
13162        editor.delete_line(&DeleteLine, cx);
13163    });
13164    executor.run_until_parked();
13165    cx.assert_state_with_diff(
13166        r#"
13167        use some::mod1;
13168      - use some::mod2;
13169      -
13170      - const A: u32 = 42;
13171        ˇ
13172        fn main() {
13173            println!("hello");
13174
13175            println!("world");
13176        }
13177        "#
13178        .unindent(),
13179    );
13180}
13181
13182#[gpui::test]
13183async fn test_edits_around_expanded_deletion_hunks(
13184    executor: BackgroundExecutor,
13185    cx: &mut gpui::TestAppContext,
13186) {
13187    init_test(cx, |_| {});
13188
13189    let mut cx = EditorTestContext::new(cx).await;
13190
13191    let diff_base = r#"
13192        use some::mod1;
13193        use some::mod2;
13194
13195        const A: u32 = 42;
13196        const B: u32 = 42;
13197        const C: u32 = 42;
13198
13199
13200        fn main() {
13201            println!("hello");
13202
13203            println!("world");
13204        }
13205    "#
13206    .unindent();
13207    executor.run_until_parked();
13208    cx.set_state(
13209        &r#"
13210        use some::mod1;
13211        use some::mod2;
13212
13213        ˇconst B: u32 = 42;
13214        const C: u32 = 42;
13215
13216
13217        fn main() {
13218            println!("hello");
13219
13220            println!("world");
13221        }
13222        "#
13223        .unindent(),
13224    );
13225
13226    cx.set_diff_base(&diff_base);
13227    executor.run_until_parked();
13228
13229    cx.update_editor(|editor, cx| {
13230        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13231    });
13232    executor.run_until_parked();
13233
13234    cx.assert_state_with_diff(
13235        r#"
13236        use some::mod1;
13237        use some::mod2;
13238
13239      - const A: u32 = 42;
13240        ˇconst B: u32 = 42;
13241        const C: u32 = 42;
13242
13243
13244        fn main() {
13245            println!("hello");
13246
13247            println!("world");
13248        }
13249        "#
13250        .unindent(),
13251    );
13252
13253    cx.update_editor(|editor, cx| {
13254        editor.delete_line(&DeleteLine, cx);
13255    });
13256    executor.run_until_parked();
13257    cx.assert_state_with_diff(
13258        r#"
13259        use some::mod1;
13260        use some::mod2;
13261
13262      - const A: u32 = 42;
13263      - const B: u32 = 42;
13264        ˇconst C: u32 = 42;
13265
13266
13267        fn main() {
13268            println!("hello");
13269
13270            println!("world");
13271        }
13272        "#
13273        .unindent(),
13274    );
13275
13276    cx.update_editor(|editor, cx| {
13277        editor.delete_line(&DeleteLine, cx);
13278    });
13279    executor.run_until_parked();
13280    cx.assert_state_with_diff(
13281        r#"
13282        use some::mod1;
13283        use some::mod2;
13284
13285      - const A: u32 = 42;
13286      - const B: u32 = 42;
13287      - const C: u32 = 42;
13288        ˇ
13289
13290        fn main() {
13291            println!("hello");
13292
13293            println!("world");
13294        }
13295        "#
13296        .unindent(),
13297    );
13298
13299    cx.update_editor(|editor, cx| {
13300        editor.handle_input("replacement", cx);
13301    });
13302    executor.run_until_parked();
13303    cx.assert_state_with_diff(
13304        r#"
13305        use some::mod1;
13306        use some::mod2;
13307
13308      - const A: u32 = 42;
13309      - const B: u32 = 42;
13310      - const C: u32 = 42;
13311      -
13312      + replacementˇ
13313
13314        fn main() {
13315            println!("hello");
13316
13317            println!("world");
13318        }
13319        "#
13320        .unindent(),
13321    );
13322}
13323
13324#[gpui::test]
13325async fn test_edit_after_expanded_modification_hunk(
13326    executor: BackgroundExecutor,
13327    cx: &mut gpui::TestAppContext,
13328) {
13329    init_test(cx, |_| {});
13330
13331    let mut cx = EditorTestContext::new(cx).await;
13332
13333    let diff_base = 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        const D: u32 = 42;
13341
13342
13343        fn main() {
13344            println!("hello");
13345
13346            println!("world");
13347        }"#
13348    .unindent();
13349
13350    cx.set_state(
13351        &r#"
13352        use some::mod1;
13353        use some::mod2;
13354
13355        const A: u32 = 42;
13356        const B: u32 = 42;
13357        const C: u32 = 43ˇ
13358        const D: u32 = 42;
13359
13360
13361        fn main() {
13362            println!("hello");
13363
13364            println!("world");
13365        }"#
13366        .unindent(),
13367    );
13368
13369    cx.set_diff_base(&diff_base);
13370    executor.run_until_parked();
13371    cx.update_editor(|editor, cx| {
13372        editor.expand_all_hunk_diffs(&ExpandAllHunkDiffs, cx);
13373    });
13374    executor.run_until_parked();
13375
13376    cx.assert_state_with_diff(
13377        r#"
13378        use some::mod1;
13379        use some::mod2;
13380
13381        const A: u32 = 42;
13382        const B: u32 = 42;
13383      - const C: u32 = 42;
13384      + const C: u32 = 43ˇ
13385        const D: u32 = 42;
13386
13387
13388        fn main() {
13389            println!("hello");
13390
13391            println!("world");
13392        }"#
13393        .unindent(),
13394    );
13395
13396    cx.update_editor(|editor, cx| {
13397        editor.handle_input("\nnew_line\n", cx);
13398    });
13399    executor.run_until_parked();
13400
13401    cx.assert_state_with_diff(
13402        r#"
13403        use some::mod1;
13404        use some::mod2;
13405
13406        const A: u32 = 42;
13407        const B: u32 = 42;
13408      - const C: u32 = 42;
13409      + const C: u32 = 43
13410      + new_line
13411      + ˇ
13412        const D: u32 = 42;
13413
13414
13415        fn main() {
13416            println!("hello");
13417
13418            println!("world");
13419        }"#
13420        .unindent(),
13421    );
13422}
13423
13424async fn setup_indent_guides_editor(
13425    text: &str,
13426    cx: &mut gpui::TestAppContext,
13427) -> (BufferId, EditorTestContext) {
13428    init_test(cx, |_| {});
13429
13430    let mut cx = EditorTestContext::new(cx).await;
13431
13432    let buffer_id = cx.update_editor(|editor, cx| {
13433        editor.set_text(text, cx);
13434        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13435
13436        buffer_ids[0]
13437    });
13438
13439    (buffer_id, cx)
13440}
13441
13442fn assert_indent_guides(
13443    range: Range<u32>,
13444    expected: Vec<IndentGuide>,
13445    active_indices: Option<Vec<usize>>,
13446    cx: &mut EditorTestContext,
13447) {
13448    let indent_guides = cx.update_editor(|editor, cx| {
13449        let snapshot = editor.snapshot(cx).display_snapshot;
13450        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13451            editor,
13452            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13453            true,
13454            &snapshot,
13455            cx,
13456        );
13457
13458        indent_guides.sort_by(|a, b| {
13459            a.depth.cmp(&b.depth).then(
13460                a.start_row
13461                    .cmp(&b.start_row)
13462                    .then(a.end_row.cmp(&b.end_row)),
13463            )
13464        });
13465        indent_guides
13466    });
13467
13468    if let Some(expected) = active_indices {
13469        let active_indices = cx.update_editor(|editor, cx| {
13470            let snapshot = editor.snapshot(cx).display_snapshot;
13471            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, cx)
13472        });
13473
13474        assert_eq!(
13475            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13476            expected,
13477            "Active indent guide indices do not match"
13478        );
13479    }
13480
13481    let expected: Vec<_> = expected
13482        .into_iter()
13483        .map(|guide| MultiBufferIndentGuide {
13484            multibuffer_row_range: MultiBufferRow(guide.start_row)..MultiBufferRow(guide.end_row),
13485            buffer: guide,
13486        })
13487        .collect();
13488
13489    assert_eq!(indent_guides, expected, "Indent guides do not match");
13490}
13491
13492fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13493    IndentGuide {
13494        buffer_id,
13495        start_row,
13496        end_row,
13497        depth,
13498        tab_size: 4,
13499        settings: IndentGuideSettings {
13500            enabled: true,
13501            line_width: 1,
13502            active_line_width: 1,
13503            ..Default::default()
13504        },
13505    }
13506}
13507
13508#[gpui::test]
13509async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13510    let (buffer_id, mut cx) = setup_indent_guides_editor(
13511        &"
13512    fn main() {
13513        let a = 1;
13514    }"
13515        .unindent(),
13516        cx,
13517    )
13518    .await;
13519
13520    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13521}
13522
13523#[gpui::test]
13524async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13525    let (buffer_id, mut cx) = setup_indent_guides_editor(
13526        &"
13527    fn main() {
13528        let a = 1;
13529        let b = 2;
13530    }"
13531        .unindent(),
13532        cx,
13533    )
13534    .await;
13535
13536    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13537}
13538
13539#[gpui::test]
13540async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13541    let (buffer_id, mut cx) = setup_indent_guides_editor(
13542        &"
13543    fn main() {
13544        let a = 1;
13545        if a == 3 {
13546            let b = 2;
13547        } else {
13548            let c = 3;
13549        }
13550    }"
13551        .unindent(),
13552        cx,
13553    )
13554    .await;
13555
13556    assert_indent_guides(
13557        0..8,
13558        vec![
13559            indent_guide(buffer_id, 1, 6, 0),
13560            indent_guide(buffer_id, 3, 3, 1),
13561            indent_guide(buffer_id, 5, 5, 1),
13562        ],
13563        None,
13564        &mut cx,
13565    );
13566}
13567
13568#[gpui::test]
13569async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
13570    let (buffer_id, mut cx) = setup_indent_guides_editor(
13571        &"
13572    fn main() {
13573        let a = 1;
13574            let b = 2;
13575        let c = 3;
13576    }"
13577        .unindent(),
13578        cx,
13579    )
13580    .await;
13581
13582    assert_indent_guides(
13583        0..5,
13584        vec![
13585            indent_guide(buffer_id, 1, 3, 0),
13586            indent_guide(buffer_id, 2, 2, 1),
13587        ],
13588        None,
13589        &mut cx,
13590    );
13591}
13592
13593#[gpui::test]
13594async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
13595    let (buffer_id, mut cx) = setup_indent_guides_editor(
13596        &"
13597        fn main() {
13598            let a = 1;
13599
13600            let c = 3;
13601        }"
13602        .unindent(),
13603        cx,
13604    )
13605    .await;
13606
13607    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
13608}
13609
13610#[gpui::test]
13611async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
13612    let (buffer_id, mut cx) = setup_indent_guides_editor(
13613        &"
13614        fn main() {
13615            let a = 1;
13616
13617            let c = 3;
13618
13619            if a == 3 {
13620                let b = 2;
13621            } else {
13622                let c = 3;
13623            }
13624        }"
13625        .unindent(),
13626        cx,
13627    )
13628    .await;
13629
13630    assert_indent_guides(
13631        0..11,
13632        vec![
13633            indent_guide(buffer_id, 1, 9, 0),
13634            indent_guide(buffer_id, 6, 6, 1),
13635            indent_guide(buffer_id, 8, 8, 1),
13636        ],
13637        None,
13638        &mut cx,
13639    );
13640}
13641
13642#[gpui::test]
13643async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
13644    let (buffer_id, mut cx) = setup_indent_guides_editor(
13645        &"
13646        fn main() {
13647            let a = 1;
13648
13649            let c = 3;
13650
13651            if a == 3 {
13652                let b = 2;
13653            } else {
13654                let c = 3;
13655            }
13656        }"
13657        .unindent(),
13658        cx,
13659    )
13660    .await;
13661
13662    assert_indent_guides(
13663        1..11,
13664        vec![
13665            indent_guide(buffer_id, 1, 9, 0),
13666            indent_guide(buffer_id, 6, 6, 1),
13667            indent_guide(buffer_id, 8, 8, 1),
13668        ],
13669        None,
13670        &mut cx,
13671    );
13672}
13673
13674#[gpui::test]
13675async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
13676    let (buffer_id, mut cx) = setup_indent_guides_editor(
13677        &"
13678        fn main() {
13679            let a = 1;
13680
13681            let c = 3;
13682
13683            if a == 3 {
13684                let b = 2;
13685            } else {
13686                let c = 3;
13687            }
13688        }"
13689        .unindent(),
13690        cx,
13691    )
13692    .await;
13693
13694    assert_indent_guides(
13695        1..10,
13696        vec![
13697            indent_guide(buffer_id, 1, 9, 0),
13698            indent_guide(buffer_id, 6, 6, 1),
13699            indent_guide(buffer_id, 8, 8, 1),
13700        ],
13701        None,
13702        &mut cx,
13703    );
13704}
13705
13706#[gpui::test]
13707async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
13708    let (buffer_id, mut cx) = setup_indent_guides_editor(
13709        &"
13710        block1
13711            block2
13712                block3
13713                    block4
13714            block2
13715        block1
13716        block1"
13717            .unindent(),
13718        cx,
13719    )
13720    .await;
13721
13722    assert_indent_guides(
13723        1..10,
13724        vec![
13725            indent_guide(buffer_id, 1, 4, 0),
13726            indent_guide(buffer_id, 2, 3, 1),
13727            indent_guide(buffer_id, 3, 3, 2),
13728        ],
13729        None,
13730        &mut cx,
13731    );
13732}
13733
13734#[gpui::test]
13735async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
13736    let (buffer_id, mut cx) = setup_indent_guides_editor(
13737        &"
13738        block1
13739            block2
13740                block3
13741
13742        block1
13743        block1"
13744            .unindent(),
13745        cx,
13746    )
13747    .await;
13748
13749    assert_indent_guides(
13750        0..6,
13751        vec![
13752            indent_guide(buffer_id, 1, 2, 0),
13753            indent_guide(buffer_id, 2, 2, 1),
13754        ],
13755        None,
13756        &mut cx,
13757    );
13758}
13759
13760#[gpui::test]
13761async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
13762    let (buffer_id, mut cx) = setup_indent_guides_editor(
13763        &"
13764        block1
13765
13766
13767
13768            block2
13769        "
13770        .unindent(),
13771        cx,
13772    )
13773    .await;
13774
13775    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13776}
13777
13778#[gpui::test]
13779async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
13780    let (buffer_id, mut cx) = setup_indent_guides_editor(
13781        &"
13782        def a:
13783        \tb = 3
13784        \tif True:
13785        \t\tc = 4
13786        \t\td = 5
13787        \tprint(b)
13788        "
13789        .unindent(),
13790        cx,
13791    )
13792    .await;
13793
13794    assert_indent_guides(
13795        0..6,
13796        vec![
13797            indent_guide(buffer_id, 1, 6, 0),
13798            indent_guide(buffer_id, 3, 4, 1),
13799        ],
13800        None,
13801        &mut cx,
13802    );
13803}
13804
13805#[gpui::test]
13806async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13807    let (buffer_id, mut cx) = setup_indent_guides_editor(
13808        &"
13809    fn main() {
13810        let a = 1;
13811    }"
13812        .unindent(),
13813        cx,
13814    )
13815    .await;
13816
13817    cx.update_editor(|editor, cx| {
13818        editor.change_selections(None, cx, |s| {
13819            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13820        });
13821    });
13822
13823    assert_indent_guides(
13824        0..3,
13825        vec![indent_guide(buffer_id, 1, 1, 0)],
13826        Some(vec![0]),
13827        &mut cx,
13828    );
13829}
13830
13831#[gpui::test]
13832async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
13833    let (buffer_id, mut cx) = setup_indent_guides_editor(
13834        &"
13835    fn main() {
13836        if 1 == 2 {
13837            let a = 1;
13838        }
13839    }"
13840        .unindent(),
13841        cx,
13842    )
13843    .await;
13844
13845    cx.update_editor(|editor, cx| {
13846        editor.change_selections(None, cx, |s| {
13847            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13848        });
13849    });
13850
13851    assert_indent_guides(
13852        0..4,
13853        vec![
13854            indent_guide(buffer_id, 1, 3, 0),
13855            indent_guide(buffer_id, 2, 2, 1),
13856        ],
13857        Some(vec![1]),
13858        &mut cx,
13859    );
13860
13861    cx.update_editor(|editor, cx| {
13862        editor.change_selections(None, cx, |s| {
13863            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13864        });
13865    });
13866
13867    assert_indent_guides(
13868        0..4,
13869        vec![
13870            indent_guide(buffer_id, 1, 3, 0),
13871            indent_guide(buffer_id, 2, 2, 1),
13872        ],
13873        Some(vec![1]),
13874        &mut cx,
13875    );
13876
13877    cx.update_editor(|editor, cx| {
13878        editor.change_selections(None, cx, |s| {
13879            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
13880        });
13881    });
13882
13883    assert_indent_guides(
13884        0..4,
13885        vec![
13886            indent_guide(buffer_id, 1, 3, 0),
13887            indent_guide(buffer_id, 2, 2, 1),
13888        ],
13889        Some(vec![0]),
13890        &mut cx,
13891    );
13892}
13893
13894#[gpui::test]
13895async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
13896    let (buffer_id, mut cx) = setup_indent_guides_editor(
13897        &"
13898    fn main() {
13899        let a = 1;
13900
13901        let b = 2;
13902    }"
13903        .unindent(),
13904        cx,
13905    )
13906    .await;
13907
13908    cx.update_editor(|editor, cx| {
13909        editor.change_selections(None, cx, |s| {
13910            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
13911        });
13912    });
13913
13914    assert_indent_guides(
13915        0..5,
13916        vec![indent_guide(buffer_id, 1, 3, 0)],
13917        Some(vec![0]),
13918        &mut cx,
13919    );
13920}
13921
13922#[gpui::test]
13923async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
13924    let (buffer_id, mut cx) = setup_indent_guides_editor(
13925        &"
13926    def m:
13927        a = 1
13928        pass"
13929            .unindent(),
13930        cx,
13931    )
13932    .await;
13933
13934    cx.update_editor(|editor, cx| {
13935        editor.change_selections(None, cx, |s| {
13936            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
13937        });
13938    });
13939
13940    assert_indent_guides(
13941        0..3,
13942        vec![indent_guide(buffer_id, 1, 2, 0)],
13943        Some(vec![0]),
13944        &mut cx,
13945    );
13946}
13947
13948#[gpui::test]
13949fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
13950    init_test(cx, |_| {});
13951
13952    let editor = cx.add_window(|cx| {
13953        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
13954        build_editor(buffer, cx)
13955    });
13956
13957    let render_args = Arc::new(Mutex::new(None));
13958    let snapshot = editor
13959        .update(cx, |editor, cx| {
13960            let snapshot = editor.buffer().read(cx).snapshot(cx);
13961            let range =
13962                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
13963
13964            struct RenderArgs {
13965                row: MultiBufferRow,
13966                folded: bool,
13967                callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
13968            }
13969
13970            let crease = Crease::inline(
13971                range,
13972                FoldPlaceholder::test(),
13973                {
13974                    let toggle_callback = render_args.clone();
13975                    move |row, folded, callback, _cx| {
13976                        *toggle_callback.lock() = Some(RenderArgs {
13977                            row,
13978                            folded,
13979                            callback,
13980                        });
13981                        div()
13982                    }
13983                },
13984                |_row, _folded, _cx| div(),
13985            );
13986
13987            editor.insert_creases(Some(crease), cx);
13988            let snapshot = editor.snapshot(cx);
13989            let _div =
13990                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
13991            snapshot
13992        })
13993        .unwrap();
13994
13995    let render_args = render_args.lock().take().unwrap();
13996    assert_eq!(render_args.row, MultiBufferRow(1));
13997    assert!(!render_args.folded);
13998    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
13999
14000    cx.update_window(*editor, |_, cx| (render_args.callback)(true, cx))
14001        .unwrap();
14002    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
14003    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14004
14005    cx.update_window(*editor, |_, cx| (render_args.callback)(false, cx))
14006        .unwrap();
14007    let snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)).unwrap();
14008    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14009}
14010
14011#[gpui::test]
14012async fn test_input_text(cx: &mut gpui::TestAppContext) {
14013    init_test(cx, |_| {});
14014    let mut cx = EditorTestContext::new(cx).await;
14015
14016    cx.set_state(
14017        &r#"ˇone
14018        two
14019
14020        three
14021        fourˇ
14022        five
14023
14024        siˇx"#
14025            .unindent(),
14026    );
14027
14028    cx.dispatch_action(HandleInput(String::new()));
14029    cx.assert_editor_state(
14030        &r#"ˇone
14031        two
14032
14033        three
14034        fourˇ
14035        five
14036
14037        siˇx"#
14038            .unindent(),
14039    );
14040
14041    cx.dispatch_action(HandleInput("AAAA".to_string()));
14042    cx.assert_editor_state(
14043        &r#"AAAAˇone
14044        two
14045
14046        three
14047        fourAAAAˇ
14048        five
14049
14050        siAAAAˇx"#
14051            .unindent(),
14052    );
14053}
14054
14055#[gpui::test]
14056async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14057    init_test(cx, |_| {});
14058
14059    let mut cx = EditorTestContext::new(cx).await;
14060    cx.set_state(
14061        r#"let foo = 1;
14062let foo = 2;
14063let foo = 3;
14064let fooˇ = 4;
14065let foo = 5;
14066let foo = 6;
14067let foo = 7;
14068let foo = 8;
14069let foo = 9;
14070let foo = 10;
14071let foo = 11;
14072let foo = 12;
14073let foo = 13;
14074let foo = 14;
14075let foo = 15;"#,
14076    );
14077
14078    cx.update_editor(|e, cx| {
14079        assert_eq!(
14080            e.next_scroll_position,
14081            NextScrollCursorCenterTopBottom::Center,
14082            "Default next scroll direction is center",
14083        );
14084
14085        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14086        assert_eq!(
14087            e.next_scroll_position,
14088            NextScrollCursorCenterTopBottom::Top,
14089            "After center, next scroll direction should be top",
14090        );
14091
14092        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14093        assert_eq!(
14094            e.next_scroll_position,
14095            NextScrollCursorCenterTopBottom::Bottom,
14096            "After top, next scroll direction should be bottom",
14097        );
14098
14099        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14100        assert_eq!(
14101            e.next_scroll_position,
14102            NextScrollCursorCenterTopBottom::Center,
14103            "After bottom, scrolling should start over",
14104        );
14105
14106        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, cx);
14107        assert_eq!(
14108            e.next_scroll_position,
14109            NextScrollCursorCenterTopBottom::Top,
14110            "Scrolling continues if retriggered fast enough"
14111        );
14112    });
14113
14114    cx.executor()
14115        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14116    cx.executor().run_until_parked();
14117    cx.update_editor(|e, _| {
14118        assert_eq!(
14119            e.next_scroll_position,
14120            NextScrollCursorCenterTopBottom::Center,
14121            "If scrolling is not triggered fast enough, it should reset"
14122        );
14123    });
14124}
14125
14126#[gpui::test]
14127async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14128    init_test(cx, |_| {});
14129    let mut cx = EditorLspTestContext::new_rust(
14130        lsp::ServerCapabilities {
14131            definition_provider: Some(lsp::OneOf::Left(true)),
14132            references_provider: Some(lsp::OneOf::Left(true)),
14133            ..lsp::ServerCapabilities::default()
14134        },
14135        cx,
14136    )
14137    .await;
14138
14139    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14140        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14141            move |params, _| async move {
14142                if empty_go_to_definition {
14143                    Ok(None)
14144                } else {
14145                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14146                        uri: params.text_document_position_params.text_document.uri,
14147                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14148                    })))
14149                }
14150            },
14151        );
14152        let references =
14153            cx.lsp
14154                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14155                    Ok(Some(vec![lsp::Location {
14156                        uri: params.text_document_position.text_document.uri,
14157                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14158                    }]))
14159                });
14160        (go_to_definition, references)
14161    };
14162
14163    cx.set_state(
14164        &r#"fn one() {
14165            let mut a = ˇtwo();
14166        }
14167
14168        fn two() {}"#
14169            .unindent(),
14170    );
14171    set_up_lsp_handlers(false, &mut cx);
14172    let navigated = cx
14173        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
14174        .await
14175        .expect("Failed to navigate to definition");
14176    assert_eq!(
14177        navigated,
14178        Navigated::Yes,
14179        "Should have navigated to definition from the GetDefinition response"
14180    );
14181    cx.assert_editor_state(
14182        &r#"fn one() {
14183            let mut a = two();
14184        }
14185
14186        fn «twoˇ»() {}"#
14187            .unindent(),
14188    );
14189
14190    let editors = cx.update_workspace(|workspace, cx| {
14191        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14192    });
14193    cx.update_editor(|_, test_editor_cx| {
14194        assert_eq!(
14195            editors.len(),
14196            1,
14197            "Initially, only one, test, editor should be open in the workspace"
14198        );
14199        assert_eq!(
14200            test_editor_cx.view(),
14201            editors.last().expect("Asserted len is 1")
14202        );
14203    });
14204
14205    set_up_lsp_handlers(true, &mut cx);
14206    let navigated = cx
14207        .update_editor(|editor, cx| editor.go_to_definition(&GoToDefinition, cx))
14208        .await
14209        .expect("Failed to navigate to lookup references");
14210    assert_eq!(
14211        navigated,
14212        Navigated::Yes,
14213        "Should have navigated to references as a fallback after empty GoToDefinition response"
14214    );
14215    // We should not change the selections in the existing file,
14216    // if opening another milti buffer with the references
14217    cx.assert_editor_state(
14218        &r#"fn one() {
14219            let mut a = two();
14220        }
14221
14222        fn «twoˇ»() {}"#
14223            .unindent(),
14224    );
14225    let editors = cx.update_workspace(|workspace, cx| {
14226        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14227    });
14228    cx.update_editor(|_, test_editor_cx| {
14229        assert_eq!(
14230            editors.len(),
14231            2,
14232            "After falling back to references search, we open a new editor with the results"
14233        );
14234        let references_fallback_text = editors
14235            .into_iter()
14236            .find(|new_editor| new_editor != test_editor_cx.view())
14237            .expect("Should have one non-test editor now")
14238            .read(test_editor_cx)
14239            .text(test_editor_cx);
14240        assert_eq!(
14241            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14242            "Should use the range from the references response and not the GoToDefinition one"
14243        );
14244    });
14245}
14246
14247#[gpui::test]
14248async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14249    init_test(cx, |_| {});
14250
14251    let language = Arc::new(Language::new(
14252        LanguageConfig::default(),
14253        Some(tree_sitter_rust::LANGUAGE.into()),
14254    ));
14255
14256    let text = r#"
14257        #[cfg(test)]
14258        mod tests() {
14259            #[test]
14260            fn runnable_1() {
14261                let a = 1;
14262            }
14263
14264            #[test]
14265            fn runnable_2() {
14266                let a = 1;
14267                let b = 2;
14268            }
14269        }
14270    "#
14271    .unindent();
14272
14273    let fs = FakeFs::new(cx.executor());
14274    fs.insert_file("/file.rs", Default::default()).await;
14275
14276    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14277    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14278    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14279    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
14280    let multi_buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14281
14282    let editor = cx.new_view(|cx| {
14283        Editor::new(
14284            EditorMode::Full,
14285            multi_buffer,
14286            Some(project.clone()),
14287            true,
14288            cx,
14289        )
14290    });
14291
14292    editor.update(cx, |editor, cx| {
14293        editor.tasks.insert(
14294            (buffer.read(cx).remote_id(), 3),
14295            RunnableTasks {
14296                templates: vec![],
14297                offset: MultiBufferOffset(43),
14298                column: 0,
14299                extra_variables: HashMap::default(),
14300                context_range: BufferOffset(43)..BufferOffset(85),
14301            },
14302        );
14303        editor.tasks.insert(
14304            (buffer.read(cx).remote_id(), 8),
14305            RunnableTasks {
14306                templates: vec![],
14307                offset: MultiBufferOffset(86),
14308                column: 0,
14309                extra_variables: HashMap::default(),
14310                context_range: BufferOffset(86)..BufferOffset(191),
14311            },
14312        );
14313
14314        // Test finding task when cursor is inside function body
14315        editor.change_selections(None, cx, |s| {
14316            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14317        });
14318        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14319        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14320
14321        // Test finding task when cursor is on function name
14322        editor.change_selections(None, cx, |s| {
14323            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14324        });
14325        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14326        assert_eq!(row, 8, "Should find task when cursor is on function name");
14327    });
14328}
14329
14330#[gpui::test]
14331async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14332    init_test(cx, |_| {});
14333
14334    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14335    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14336    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14337
14338    let fs = FakeFs::new(cx.executor());
14339    fs.insert_tree(
14340        "/a",
14341        json!({
14342            "first.rs": sample_text_1,
14343            "second.rs": sample_text_2,
14344            "third.rs": sample_text_3,
14345        }),
14346    )
14347    .await;
14348    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14349    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14350    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14351    let worktree = project.update(cx, |project, cx| {
14352        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14353        assert_eq!(worktrees.len(), 1);
14354        worktrees.pop().unwrap()
14355    });
14356    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14357
14358    let buffer_1 = project
14359        .update(cx, |project, cx| {
14360            project.open_buffer((worktree_id, "first.rs"), cx)
14361        })
14362        .await
14363        .unwrap();
14364    let buffer_2 = project
14365        .update(cx, |project, cx| {
14366            project.open_buffer((worktree_id, "second.rs"), cx)
14367        })
14368        .await
14369        .unwrap();
14370    let buffer_3 = project
14371        .update(cx, |project, cx| {
14372            project.open_buffer((worktree_id, "third.rs"), cx)
14373        })
14374        .await
14375        .unwrap();
14376
14377    let multi_buffer = cx.new_model(|cx| {
14378        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14379        multi_buffer.push_excerpts(
14380            buffer_1.clone(),
14381            [
14382                ExcerptRange {
14383                    context: Point::new(0, 0)..Point::new(3, 0),
14384                    primary: None,
14385                },
14386                ExcerptRange {
14387                    context: Point::new(5, 0)..Point::new(7, 0),
14388                    primary: None,
14389                },
14390                ExcerptRange {
14391                    context: Point::new(9, 0)..Point::new(10, 4),
14392                    primary: None,
14393                },
14394            ],
14395            cx,
14396        );
14397        multi_buffer.push_excerpts(
14398            buffer_2.clone(),
14399            [
14400                ExcerptRange {
14401                    context: Point::new(0, 0)..Point::new(3, 0),
14402                    primary: None,
14403                },
14404                ExcerptRange {
14405                    context: Point::new(5, 0)..Point::new(7, 0),
14406                    primary: None,
14407                },
14408                ExcerptRange {
14409                    context: Point::new(9, 0)..Point::new(10, 4),
14410                    primary: None,
14411                },
14412            ],
14413            cx,
14414        );
14415        multi_buffer.push_excerpts(
14416            buffer_3.clone(),
14417            [
14418                ExcerptRange {
14419                    context: Point::new(0, 0)..Point::new(3, 0),
14420                    primary: None,
14421                },
14422                ExcerptRange {
14423                    context: Point::new(5, 0)..Point::new(7, 0),
14424                    primary: None,
14425                },
14426                ExcerptRange {
14427                    context: Point::new(9, 0)..Point::new(10, 4),
14428                    primary: None,
14429                },
14430            ],
14431            cx,
14432        );
14433        multi_buffer
14434    });
14435    let multi_buffer_editor = cx.new_view(|cx| {
14436        Editor::new(
14437            EditorMode::Full,
14438            multi_buffer,
14439            Some(project.clone()),
14440            true,
14441            cx,
14442        )
14443    });
14444
14445    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";
14446    assert_eq!(
14447        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14448        full_text,
14449    );
14450
14451    multi_buffer_editor.update(cx, |editor, cx| {
14452        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14453    });
14454    assert_eq!(
14455        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14456        "\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",
14457        "After folding the first buffer, its text should not be displayed"
14458    );
14459
14460    multi_buffer_editor.update(cx, |editor, cx| {
14461        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14462    });
14463    assert_eq!(
14464        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14465        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
14466        "After folding the second buffer, its text should not be displayed"
14467    );
14468
14469    multi_buffer_editor.update(cx, |editor, cx| {
14470        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14471    });
14472    assert_eq!(
14473        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14474        "\n\n\n\n\n",
14475        "After folding the third buffer, its text should not be displayed"
14476    );
14477
14478    // Emulate selection inside the fold logic, that should work
14479    multi_buffer_editor.update(cx, |editor, cx| {
14480        editor.snapshot(cx).next_line_boundary(Point::new(0, 4));
14481    });
14482
14483    multi_buffer_editor.update(cx, |editor, cx| {
14484        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14485    });
14486    assert_eq!(
14487        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14488        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
14489        "After unfolding the second buffer, its text should be displayed"
14490    );
14491
14492    multi_buffer_editor.update(cx, |editor, cx| {
14493        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14494    });
14495    assert_eq!(
14496        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14497        "\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",
14498        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
14499    );
14500
14501    multi_buffer_editor.update(cx, |editor, cx| {
14502        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14503    });
14504    assert_eq!(
14505        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14506        full_text,
14507        "After unfolding the all buffers, all original text should be displayed"
14508    );
14509}
14510
14511#[gpui::test]
14512async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
14513    init_test(cx, |_| {});
14514
14515    let sample_text_1 = "1111\n2222\n3333".to_string();
14516    let sample_text_2 = "4444\n5555\n6666".to_string();
14517    let sample_text_3 = "7777\n8888\n9999".to_string();
14518
14519    let fs = FakeFs::new(cx.executor());
14520    fs.insert_tree(
14521        "/a",
14522        json!({
14523            "first.rs": sample_text_1,
14524            "second.rs": sample_text_2,
14525            "third.rs": sample_text_3,
14526        }),
14527    )
14528    .await;
14529    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14530    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14531    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14532    let worktree = project.update(cx, |project, cx| {
14533        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14534        assert_eq!(worktrees.len(), 1);
14535        worktrees.pop().unwrap()
14536    });
14537    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14538
14539    let buffer_1 = project
14540        .update(cx, |project, cx| {
14541            project.open_buffer((worktree_id, "first.rs"), cx)
14542        })
14543        .await
14544        .unwrap();
14545    let buffer_2 = project
14546        .update(cx, |project, cx| {
14547            project.open_buffer((worktree_id, "second.rs"), cx)
14548        })
14549        .await
14550        .unwrap();
14551    let buffer_3 = project
14552        .update(cx, |project, cx| {
14553            project.open_buffer((worktree_id, "third.rs"), cx)
14554        })
14555        .await
14556        .unwrap();
14557
14558    let multi_buffer = cx.new_model(|cx| {
14559        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14560        multi_buffer.push_excerpts(
14561            buffer_1.clone(),
14562            [ExcerptRange {
14563                context: Point::new(0, 0)..Point::new(3, 0),
14564                primary: None,
14565            }],
14566            cx,
14567        );
14568        multi_buffer.push_excerpts(
14569            buffer_2.clone(),
14570            [ExcerptRange {
14571                context: Point::new(0, 0)..Point::new(3, 0),
14572                primary: None,
14573            }],
14574            cx,
14575        );
14576        multi_buffer.push_excerpts(
14577            buffer_3.clone(),
14578            [ExcerptRange {
14579                context: Point::new(0, 0)..Point::new(3, 0),
14580                primary: None,
14581            }],
14582            cx,
14583        );
14584        multi_buffer
14585    });
14586
14587    let multi_buffer_editor = cx.new_view(|cx| {
14588        Editor::new(
14589            EditorMode::Full,
14590            multi_buffer,
14591            Some(project.clone()),
14592            true,
14593            cx,
14594        )
14595    });
14596
14597    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
14598    assert_eq!(
14599        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14600        full_text,
14601    );
14602
14603    multi_buffer_editor.update(cx, |editor, cx| {
14604        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14605    });
14606    assert_eq!(
14607        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14608        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
14609        "After folding the first buffer, its text should not be displayed"
14610    );
14611
14612    multi_buffer_editor.update(cx, |editor, cx| {
14613        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
14614    });
14615
14616    assert_eq!(
14617        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14618        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
14619        "After folding the second buffer, its text should not be displayed"
14620    );
14621
14622    multi_buffer_editor.update(cx, |editor, cx| {
14623        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
14624    });
14625    assert_eq!(
14626        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14627        "\n\n\n\n\n",
14628        "After folding the third buffer, its text should not be displayed"
14629    );
14630
14631    multi_buffer_editor.update(cx, |editor, cx| {
14632        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
14633    });
14634    assert_eq!(
14635        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14636        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
14637        "After unfolding the second buffer, its text should be displayed"
14638    );
14639
14640    multi_buffer_editor.update(cx, |editor, cx| {
14641        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
14642    });
14643    assert_eq!(
14644        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14645        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
14646        "After unfolding the first buffer, its text should be displayed"
14647    );
14648
14649    multi_buffer_editor.update(cx, |editor, cx| {
14650        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
14651    });
14652    assert_eq!(
14653        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14654        full_text,
14655        "After unfolding all buffers, all original text should be displayed"
14656    );
14657}
14658
14659#[gpui::test]
14660async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
14661    init_test(cx, |_| {});
14662
14663    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14664
14665    let fs = FakeFs::new(cx.executor());
14666    fs.insert_tree(
14667        "/a",
14668        json!({
14669            "main.rs": sample_text,
14670        }),
14671    )
14672    .await;
14673    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14674    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
14675    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14676    let worktree = project.update(cx, |project, cx| {
14677        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14678        assert_eq!(worktrees.len(), 1);
14679        worktrees.pop().unwrap()
14680    });
14681    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14682
14683    let buffer_1 = project
14684        .update(cx, |project, cx| {
14685            project.open_buffer((worktree_id, "main.rs"), cx)
14686        })
14687        .await
14688        .unwrap();
14689
14690    let multi_buffer = cx.new_model(|cx| {
14691        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14692        multi_buffer.push_excerpts(
14693            buffer_1.clone(),
14694            [ExcerptRange {
14695                context: Point::new(0, 0)
14696                    ..Point::new(
14697                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
14698                        0,
14699                    ),
14700                primary: None,
14701            }],
14702            cx,
14703        );
14704        multi_buffer
14705    });
14706    let multi_buffer_editor = cx.new_view(|cx| {
14707        Editor::new(
14708            EditorMode::Full,
14709            multi_buffer,
14710            Some(project.clone()),
14711            true,
14712            cx,
14713        )
14714    });
14715
14716    let selection_range = Point::new(1, 0)..Point::new(2, 0);
14717    multi_buffer_editor.update(cx, |editor, cx| {
14718        enum TestHighlight {}
14719        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
14720        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
14721        editor.highlight_text::<TestHighlight>(
14722            vec![highlight_range.clone()],
14723            HighlightStyle::color(Hsla::green()),
14724            cx,
14725        );
14726        editor.change_selections(None, cx, |s| s.select_ranges(Some(highlight_range)));
14727    });
14728
14729    let full_text = format!("\n\n\n{sample_text}\n");
14730    assert_eq!(
14731        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14732        full_text,
14733    );
14734}
14735
14736#[gpui::test]
14737fn test_inline_completion_text(cx: &mut TestAppContext) {
14738    init_test(cx, |_| {});
14739
14740    // Simple insertion
14741    {
14742        let window = cx.add_window(|cx| {
14743            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14744            Editor::new(EditorMode::Full, buffer, None, true, cx)
14745        });
14746        let cx = &mut VisualTestContext::from_window(*window, cx);
14747
14748        window
14749            .update(cx, |editor, cx| {
14750                let snapshot = editor.snapshot(cx);
14751                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14752                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14753                let edits = vec![(edit_range, " beautiful".to_string())];
14754
14755                let InlineCompletionText::Edit { text, highlights } =
14756                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14757                else {
14758                    panic!("Failed to generate inline completion text");
14759                };
14760
14761                assert_eq!(text, "Hello, beautiful world!");
14762                assert_eq!(highlights.len(), 1);
14763                assert_eq!(highlights[0].0, 6..16);
14764                assert_eq!(
14765                    highlights[0].1.background_color,
14766                    Some(cx.theme().status().created_background)
14767                );
14768            })
14769            .unwrap();
14770    }
14771
14772    // Replacement
14773    {
14774        let window = cx.add_window(|cx| {
14775            let buffer = MultiBuffer::build_simple("This is a test.", cx);
14776            Editor::new(EditorMode::Full, buffer, None, true, cx)
14777        });
14778        let cx = &mut VisualTestContext::from_window(*window, cx);
14779
14780        window
14781            .update(cx, |editor, cx| {
14782                let snapshot = editor.snapshot(cx);
14783                let edits = vec![(
14784                    snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14785                        ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 4)),
14786                    "That".to_string(),
14787                )];
14788
14789                let InlineCompletionText::Edit { text, highlights } =
14790                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14791                else {
14792                    panic!("Failed to generate inline completion text");
14793                };
14794
14795                assert_eq!(text, "That is a test.");
14796                assert_eq!(highlights.len(), 1);
14797                assert_eq!(highlights[0].0, 0..4);
14798                assert_eq!(
14799                    highlights[0].1.background_color,
14800                    Some(cx.theme().status().created_background)
14801                );
14802            })
14803            .unwrap();
14804    }
14805
14806    // Multiple edits
14807    {
14808        let window = cx.add_window(|cx| {
14809            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14810            Editor::new(EditorMode::Full, buffer, None, true, cx)
14811        });
14812        let cx = &mut VisualTestContext::from_window(*window, cx);
14813
14814        window
14815            .update(cx, |editor, cx| {
14816                let snapshot = editor.snapshot(cx);
14817                let edits = vec![
14818                    (
14819                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 0))
14820                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 5)),
14821                        "Greetings".into(),
14822                    ),
14823                    (
14824                        snapshot.buffer_snapshot.anchor_after(Point::new(0, 12))
14825                            ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 12)),
14826                        " and universe".into(),
14827                    ),
14828                ];
14829
14830                let InlineCompletionText::Edit { text, highlights } =
14831                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14832                else {
14833                    panic!("Failed to generate inline completion text");
14834                };
14835
14836                assert_eq!(text, "Greetings, world and universe!");
14837                assert_eq!(highlights.len(), 2);
14838                assert_eq!(highlights[0].0, 0..9);
14839                assert_eq!(highlights[1].0, 16..29);
14840                assert_eq!(
14841                    highlights[0].1.background_color,
14842                    Some(cx.theme().status().created_background)
14843                );
14844                assert_eq!(
14845                    highlights[1].1.background_color,
14846                    Some(cx.theme().status().created_background)
14847                );
14848            })
14849            .unwrap();
14850    }
14851
14852    // Multiple lines with edits
14853    {
14854        let window = cx.add_window(|cx| {
14855            let buffer =
14856                MultiBuffer::build_simple("First line\nSecond line\nThird line\nFourth line", cx);
14857            Editor::new(EditorMode::Full, buffer, None, true, cx)
14858        });
14859        let cx = &mut VisualTestContext::from_window(*window, cx);
14860
14861        window
14862            .update(cx, |editor, cx| {
14863                let snapshot = editor.snapshot(cx);
14864                let edits = vec![
14865                    (
14866                        snapshot.buffer_snapshot.anchor_before(Point::new(1, 7))
14867                            ..snapshot.buffer_snapshot.anchor_before(Point::new(1, 11)),
14868                        "modified".to_string(),
14869                    ),
14870                    (
14871                        snapshot.buffer_snapshot.anchor_before(Point::new(2, 0))
14872                            ..snapshot.buffer_snapshot.anchor_before(Point::new(2, 10)),
14873                        "New third line".to_string(),
14874                    ),
14875                    (
14876                        snapshot.buffer_snapshot.anchor_before(Point::new(3, 6))
14877                            ..snapshot.buffer_snapshot.anchor_before(Point::new(3, 6)),
14878                        " updated".to_string(),
14879                    ),
14880                ];
14881
14882                let InlineCompletionText::Edit { text, highlights } =
14883                    inline_completion_edit_text(&snapshot, &edits, false, cx)
14884                else {
14885                    panic!("Failed to generate inline completion text");
14886                };
14887
14888                assert_eq!(text, "Second modified\nNew third line\nFourth updated line");
14889                assert_eq!(highlights.len(), 3);
14890                assert_eq!(highlights[0].0, 7..15); // "modified"
14891                assert_eq!(highlights[1].0, 16..30); // "New third line"
14892                assert_eq!(highlights[2].0, 37..45); // " updated"
14893
14894                for highlight in &highlights {
14895                    assert_eq!(
14896                        highlight.1.background_color,
14897                        Some(cx.theme().status().created_background)
14898                    );
14899                }
14900            })
14901            .unwrap();
14902    }
14903}
14904
14905#[gpui::test]
14906fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
14907    init_test(cx, |_| {});
14908
14909    // Deletion
14910    {
14911        let window = cx.add_window(|cx| {
14912            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14913            Editor::new(EditorMode::Full, buffer, None, true, cx)
14914        });
14915        let cx = &mut VisualTestContext::from_window(*window, cx);
14916
14917        window
14918            .update(cx, |editor, cx| {
14919                let snapshot = editor.snapshot(cx);
14920                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5))
14921                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11));
14922                let edits = vec![(edit_range, "".to_string())];
14923
14924                let InlineCompletionText::Edit { text, highlights } =
14925                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14926                else {
14927                    panic!("Failed to generate inline completion text");
14928                };
14929
14930                assert_eq!(text, "Hello, world!");
14931                assert_eq!(highlights.len(), 1);
14932                assert_eq!(highlights[0].0, 5..11);
14933                assert_eq!(
14934                    highlights[0].1.background_color,
14935                    Some(cx.theme().status().deleted_background)
14936                );
14937            })
14938            .unwrap();
14939    }
14940
14941    // Insertion
14942    {
14943        let window = cx.add_window(|cx| {
14944            let buffer = MultiBuffer::build_simple("Hello, world!", cx);
14945            Editor::new(EditorMode::Full, buffer, None, true, cx)
14946        });
14947        let cx = &mut VisualTestContext::from_window(*window, cx);
14948
14949        window
14950            .update(cx, |editor, cx| {
14951                let snapshot = editor.snapshot(cx);
14952                let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6))
14953                    ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6));
14954                let edits = vec![(edit_range, " digital".to_string())];
14955
14956                let InlineCompletionText::Edit { text, highlights } =
14957                    inline_completion_edit_text(&snapshot, &edits, true, cx)
14958                else {
14959                    panic!("Failed to generate inline completion text");
14960                };
14961
14962                assert_eq!(text, "Hello, digital world!");
14963                assert_eq!(highlights.len(), 1);
14964                assert_eq!(highlights[0].0, 6..14);
14965                assert_eq!(
14966                    highlights[0].1.background_color,
14967                    Some(cx.theme().status().created_background)
14968                );
14969            })
14970            .unwrap();
14971    }
14972}
14973
14974#[gpui::test]
14975async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
14976    init_test(cx, |_| {});
14977    let capabilities = lsp::ServerCapabilities {
14978        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
14979            prepare_provider: Some(true),
14980            work_done_progress_options: Default::default(),
14981        })),
14982        ..Default::default()
14983    };
14984    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
14985
14986    cx.set_state(indoc! {"
14987        struct Fˇoo {}
14988    "});
14989
14990    cx.update_editor(|editor, cx| {
14991        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
14992        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
14993        editor.highlight_background::<DocumentHighlightRead>(
14994            &[highlight_range],
14995            |c| c.editor_document_highlight_read_background,
14996            cx,
14997        );
14998    });
14999
15000    let mut prepare_rename_handler =
15001        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15002            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15003                start: lsp::Position {
15004                    line: 0,
15005                    character: 7,
15006                },
15007                end: lsp::Position {
15008                    line: 0,
15009                    character: 10,
15010                },
15011            })))
15012        });
15013    let prepare_rename_task = cx
15014        .update_editor(|e, cx| e.rename(&Rename, cx))
15015        .expect("Prepare rename was not started");
15016    prepare_rename_handler.next().await.unwrap();
15017    prepare_rename_task.await.expect("Prepare rename failed");
15018
15019    let mut rename_handler =
15020        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15021            let edit = lsp::TextEdit {
15022                range: lsp::Range {
15023                    start: lsp::Position {
15024                        line: 0,
15025                        character: 7,
15026                    },
15027                    end: lsp::Position {
15028                        line: 0,
15029                        character: 10,
15030                    },
15031                },
15032                new_text: "FooRenamed".to_string(),
15033            };
15034            Ok(Some(lsp::WorkspaceEdit::new(
15035                // Specify the same edit twice
15036                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15037            )))
15038        });
15039    let rename_task = cx
15040        .update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
15041        .expect("Confirm rename was not started");
15042    rename_handler.next().await.unwrap();
15043    rename_task.await.expect("Confirm rename failed");
15044    cx.run_until_parked();
15045
15046    // Despite two edits, only one is actually applied as those are identical
15047    cx.assert_editor_state(indoc! {"
15048        struct FooRenamedˇ {}
15049    "});
15050}
15051
15052#[gpui::test]
15053async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15054    init_test(cx, |_| {});
15055    // These capabilities indicate that the server does not support prepare rename.
15056    let capabilities = lsp::ServerCapabilities {
15057        rename_provider: Some(lsp::OneOf::Left(true)),
15058        ..Default::default()
15059    };
15060    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15061
15062    cx.set_state(indoc! {"
15063        struct Fˇoo {}
15064    "});
15065
15066    cx.update_editor(|editor, cx| {
15067        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15068        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15069        editor.highlight_background::<DocumentHighlightRead>(
15070            &[highlight_range],
15071            |c| c.editor_document_highlight_read_background,
15072            cx,
15073        );
15074    });
15075
15076    cx.update_editor(|e, cx| e.rename(&Rename, cx))
15077        .expect("Prepare rename was not started")
15078        .await
15079        .expect("Prepare rename failed");
15080
15081    let mut rename_handler =
15082        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15083            let edit = lsp::TextEdit {
15084                range: lsp::Range {
15085                    start: lsp::Position {
15086                        line: 0,
15087                        character: 7,
15088                    },
15089                    end: lsp::Position {
15090                        line: 0,
15091                        character: 10,
15092                    },
15093                },
15094                new_text: "FooRenamed".to_string(),
15095            };
15096            Ok(Some(lsp::WorkspaceEdit::new(
15097                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15098            )))
15099        });
15100    let rename_task = cx
15101        .update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
15102        .expect("Confirm rename was not started");
15103    rename_handler.next().await.unwrap();
15104    rename_task.await.expect("Confirm rename failed");
15105    cx.run_until_parked();
15106
15107    // Correct range is renamed, as `surrounding_word` is used to find it.
15108    cx.assert_editor_state(indoc! {"
15109        struct FooRenamedˇ {}
15110    "});
15111}
15112
15113fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15114    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15115    point..point
15116}
15117
15118fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
15119    let (text, ranges) = marked_text_ranges(marked_text, true);
15120    assert_eq!(view.text(cx), text);
15121    assert_eq!(
15122        view.selections.ranges(cx),
15123        ranges,
15124        "Assert selections are {}",
15125        marked_text
15126    );
15127}
15128
15129pub fn handle_signature_help_request(
15130    cx: &mut EditorLspTestContext,
15131    mocked_response: lsp::SignatureHelp,
15132) -> impl Future<Output = ()> {
15133    let mut request =
15134        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15135            let mocked_response = mocked_response.clone();
15136            async move { Ok(Some(mocked_response)) }
15137        });
15138
15139    async move {
15140        request.next().await;
15141    }
15142}
15143
15144/// Handle completion request passing a marked string specifying where the completion
15145/// should be triggered from using '|' character, what range should be replaced, and what completions
15146/// should be returned using '<' and '>' to delimit the range
15147pub fn handle_completion_request(
15148    cx: &mut EditorLspTestContext,
15149    marked_string: &str,
15150    completions: Vec<&'static str>,
15151    counter: Arc<AtomicUsize>,
15152) -> impl Future<Output = ()> {
15153    let complete_from_marker: TextRangeMarker = '|'.into();
15154    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15155    let (_, mut marked_ranges) = marked_text_ranges_by(
15156        marked_string,
15157        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15158    );
15159
15160    let complete_from_position =
15161        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15162    let replace_range =
15163        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15164
15165    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15166        let completions = completions.clone();
15167        counter.fetch_add(1, atomic::Ordering::Release);
15168        async move {
15169            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15170            assert_eq!(
15171                params.text_document_position.position,
15172                complete_from_position
15173            );
15174            Ok(Some(lsp::CompletionResponse::Array(
15175                completions
15176                    .iter()
15177                    .map(|completion_text| lsp::CompletionItem {
15178                        label: completion_text.to_string(),
15179                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15180                            range: replace_range,
15181                            new_text: completion_text.to_string(),
15182                        })),
15183                        ..Default::default()
15184                    })
15185                    .collect(),
15186            )))
15187        }
15188    });
15189
15190    async move {
15191        request.next().await;
15192    }
15193}
15194
15195fn handle_resolve_completion_request(
15196    cx: &mut EditorLspTestContext,
15197    edits: Option<Vec<(&'static str, &'static str)>>,
15198) -> impl Future<Output = ()> {
15199    let edits = edits.map(|edits| {
15200        edits
15201            .iter()
15202            .map(|(marked_string, new_text)| {
15203                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15204                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15205                lsp::TextEdit::new(replace_range, new_text.to_string())
15206            })
15207            .collect::<Vec<_>>()
15208    });
15209
15210    let mut request =
15211        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15212            let edits = edits.clone();
15213            async move {
15214                Ok(lsp::CompletionItem {
15215                    additional_text_edits: edits,
15216                    ..Default::default()
15217                })
15218            }
15219        });
15220
15221    async move {
15222        request.next().await;
15223    }
15224}
15225
15226pub(crate) fn update_test_language_settings(
15227    cx: &mut TestAppContext,
15228    f: impl Fn(&mut AllLanguageSettingsContent),
15229) {
15230    cx.update(|cx| {
15231        SettingsStore::update_global(cx, |store, cx| {
15232            store.update_user_settings::<AllLanguageSettings>(cx, f);
15233        });
15234    });
15235}
15236
15237pub(crate) fn update_test_project_settings(
15238    cx: &mut TestAppContext,
15239    f: impl Fn(&mut ProjectSettings),
15240) {
15241    cx.update(|cx| {
15242        SettingsStore::update_global(cx, |store, cx| {
15243            store.update_user_settings::<ProjectSettings>(cx, f);
15244        });
15245    });
15246}
15247
15248pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15249    cx.update(|cx| {
15250        assets::Assets.load_test_fonts(cx);
15251        let store = SettingsStore::test(cx);
15252        cx.set_global(store);
15253        theme::init(theme::LoadThemes::JustBase, cx);
15254        release_channel::init(SemanticVersion::default(), cx);
15255        client::init_settings(cx);
15256        language::init(cx);
15257        Project::init_settings(cx);
15258        workspace::init_settings(cx);
15259        crate::init(cx);
15260    });
15261
15262    update_test_language_settings(cx, f);
15263}
15264
15265#[track_caller]
15266fn assert_hunk_revert(
15267    not_reverted_text_with_selections: &str,
15268    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
15269    expected_reverted_text_with_selections: &str,
15270    base_text: &str,
15271    cx: &mut EditorLspTestContext,
15272) {
15273    cx.set_state(not_reverted_text_with_selections);
15274    cx.set_diff_base(base_text);
15275    cx.executor().run_until_parked();
15276
15277    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
15278        let snapshot = editor.snapshot(cx);
15279        let reverted_hunk_statuses = snapshot
15280            .diff_map
15281            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len(), &snapshot.buffer_snapshot)
15282            .map(|hunk| hunk_status(&hunk))
15283            .collect::<Vec<_>>();
15284
15285        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
15286        reverted_hunk_statuses
15287    });
15288    cx.executor().run_until_parked();
15289    cx.assert_editor_state(expected_reverted_text_with_selections);
15290    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
15291}