editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use futures::StreamExt;
   11use gpui::{
   12    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   13    WindowBounds, WindowOptions,
   14};
   15use indoc::indoc;
   16use language::{
   17    language_settings::{
   18        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   19    },
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, ParsedMarkdown, Point,
   24};
   25use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   26use multi_buffer::IndentGuide;
   27use parking_lot::Mutex;
   28use pretty_assertions::{assert_eq, assert_ne};
   29use project::{buffer_store::BufferChangeSet, FakeFs};
   30use project::{
   31    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   32    project_settings::{LspSettings, ProjectSettings},
   33};
   34use serde_json::{self, json};
   35use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   36use std::{
   37    iter,
   38    sync::atomic::{self, AtomicUsize},
   39};
   40use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq, path,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45    uri,
   46};
   47use workspace::{
   48    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   49    NavigationEntry, ViewId,
   50};
   51
   52#[gpui::test]
   53fn test_edit_events(cx: &mut TestAppContext) {
   54    init_test(cx, |_| {});
   55
   56    let buffer = cx.new(|cx| {
   57        let mut buffer = language::Buffer::local("123456", cx);
   58        buffer.set_group_interval(Duration::from_secs(1));
   59        buffer
   60    });
   61
   62    let events = Rc::new(RefCell::new(Vec::new()));
   63    let editor1 = cx.add_window({
   64        let events = events.clone();
   65        |window, cx| {
   66            let entity = cx.entity().clone();
   67            cx.subscribe_in(
   68                &entity,
   69                window,
   70                move |_, _, event: &EditorEvent, _, _| match event {
   71                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   72                    EditorEvent::BufferEdited => {
   73                        events.borrow_mut().push(("editor1", "buffer edited"))
   74                    }
   75                    _ => {}
   76                },
   77            )
   78            .detach();
   79            Editor::for_buffer(buffer.clone(), None, window, cx)
   80        }
   81    });
   82
   83    let editor2 = cx.add_window({
   84        let events = events.clone();
   85        |window, cx| {
   86            cx.subscribe_in(
   87                &cx.entity().clone(),
   88                window,
   89                move |_, _, event: &EditorEvent, _, _| match event {
   90                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   91                    EditorEvent::BufferEdited => {
   92                        events.borrow_mut().push(("editor2", "buffer edited"))
   93                    }
   94                    _ => {}
   95                },
   96            )
   97            .detach();
   98            Editor::for_buffer(buffer.clone(), None, window, cx)
   99        }
  100    });
  101
  102    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  103
  104    // Mutating editor 1 will emit an `Edited` event only for that editor.
  105    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor1", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Mutating editor 2 will emit an `Edited` event only for that editor.
  116    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor2", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  138    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor1", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  160    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  161    assert_eq!(
  162        mem::take(&mut *events.borrow_mut()),
  163        [
  164            ("editor2", "edited"),
  165            ("editor1", "buffer edited"),
  166            ("editor2", "buffer edited"),
  167        ]
  168    );
  169
  170    // No event is emitted when the mutation is a no-op.
  171    _ = editor2.update(cx, |editor, window, cx| {
  172        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  173
  174        editor.backspace(&Backspace, window, cx);
  175    });
  176    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  177}
  178
  179#[gpui::test]
  180fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  181    init_test(cx, |_| {});
  182
  183    let mut now = Instant::now();
  184    let group_interval = Duration::from_millis(1);
  185    let buffer = cx.new(|cx| {
  186        let mut buf = language::Buffer::local("123456", cx);
  187        buf.set_group_interval(group_interval);
  188        buf
  189    });
  190    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  191    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  192
  193    _ = editor.update(cx, |editor, window, cx| {
  194        editor.start_transaction_at(now, window, cx);
  195        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  196
  197        editor.insert("cd", window, cx);
  198        editor.end_transaction_at(now, cx);
  199        assert_eq!(editor.text(cx), "12cd56");
  200        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  201
  202        editor.start_transaction_at(now, window, cx);
  203        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  204        editor.insert("e", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cde6");
  207        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  208
  209        now += group_interval + Duration::from_millis(1);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  211
  212        // Simulate an edit in another editor
  213        buffer.update(cx, |buffer, cx| {
  214            buffer.start_transaction_at(now, cx);
  215            buffer.edit([(0..1, "a")], None, cx);
  216            buffer.edit([(1..1, "b")], None, cx);
  217            buffer.end_transaction_at(now, cx);
  218        });
  219
  220        assert_eq!(editor.text(cx), "ab2cde6");
  221        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  222
  223        // Last transaction happened past the group interval in a different editor.
  224        // Undo it individually and don't restore selections.
  225        editor.undo(&Undo, window, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  228
  229        // First two transactions happened within the group interval in this editor.
  230        // Undo them together and restore selections.
  231        editor.undo(&Undo, window, cx);
  232        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  233        assert_eq!(editor.text(cx), "123456");
  234        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  235
  236        // Redo the first two transactions together.
  237        editor.redo(&Redo, window, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  240
  241        // Redo the last transaction on its own.
  242        editor.redo(&Redo, window, cx);
  243        assert_eq!(editor.text(cx), "ab2cde6");
  244        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  245
  246        // Test empty transactions.
  247        editor.start_transaction_at(now, window, cx);
  248        editor.end_transaction_at(now, cx);
  249        editor.undo(&Undo, window, cx);
  250        assert_eq!(editor.text(cx), "12cde6");
  251    });
  252}
  253
  254#[gpui::test]
  255fn test_ime_composition(cx: &mut TestAppContext) {
  256    init_test(cx, |_| {});
  257
  258    let buffer = cx.new(|cx| {
  259        let mut buffer = language::Buffer::local("abcde", cx);
  260        // Ensure automatic grouping doesn't occur.
  261        buffer.set_group_interval(Duration::ZERO);
  262        buffer
  263    });
  264
  265    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  266    cx.add_window(|window, cx| {
  267        let mut editor = build_editor(buffer.clone(), window, cx);
  268
  269        // Start a new IME composition.
  270        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  271        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  272        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  273        assert_eq!(editor.text(cx), "äbcde");
  274        assert_eq!(
  275            editor.marked_text_ranges(cx),
  276            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  277        );
  278
  279        // Finalize IME composition.
  280        editor.replace_text_in_range(None, "ā", window, cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // IME composition edits are grouped and are undone/redone at once.
  285        editor.undo(&Default::default(), window, cx);
  286        assert_eq!(editor.text(cx), "abcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288        editor.redo(&Default::default(), window, cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition.
  293        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  294        assert_eq!(
  295            editor.marked_text_ranges(cx),
  296            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  297        );
  298
  299        // Undoing during an IME composition cancels it.
  300        editor.undo(&Default::default(), window, cx);
  301        assert_eq!(editor.text(cx), "ābcde");
  302        assert_eq!(editor.marked_text_ranges(cx), None);
  303
  304        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  305        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  306        assert_eq!(editor.text(cx), "ābcdè");
  307        assert_eq!(
  308            editor.marked_text_ranges(cx),
  309            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  310        );
  311
  312        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  313        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  314        assert_eq!(editor.text(cx), "ābcdę");
  315        assert_eq!(editor.marked_text_ranges(cx), None);
  316
  317        // Start a new IME composition with multiple cursors.
  318        editor.change_selections(None, window, cx, |s| {
  319            s.select_ranges([
  320                OffsetUtf16(1)..OffsetUtf16(1),
  321                OffsetUtf16(3)..OffsetUtf16(3),
  322                OffsetUtf16(5)..OffsetUtf16(5),
  323            ])
  324        });
  325        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  326        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(0)..OffsetUtf16(3),
  331                OffsetUtf16(4)..OffsetUtf16(7),
  332                OffsetUtf16(8)..OffsetUtf16(11)
  333            ])
  334        );
  335
  336        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  337        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  338        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  339        assert_eq!(
  340            editor.marked_text_ranges(cx),
  341            Some(vec![
  342                OffsetUtf16(1)..OffsetUtf16(2),
  343                OffsetUtf16(5)..OffsetUtf16(6),
  344                OffsetUtf16(9)..OffsetUtf16(10)
  345            ])
  346        );
  347
  348        // Finalize IME composition with multiple cursors.
  349        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  350        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  351        assert_eq!(editor.marked_text_ranges(cx), None);
  352
  353        editor
  354    });
  355}
  356
  357#[gpui::test]
  358fn test_selection_with_mouse(cx: &mut TestAppContext) {
  359    init_test(cx, |_| {});
  360
  361    let editor = cx.add_window(|window, cx| {
  362        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  363        build_editor(buffer, window, cx)
  364    });
  365
  366    _ = editor.update(cx, |editor, window, cx| {
  367        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  368    });
  369    assert_eq!(
  370        editor
  371            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  374    );
  375
  376    _ = editor.update(cx, |editor, window, cx| {
  377        editor.update_selection(
  378            DisplayPoint::new(DisplayRow(3), 3),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            window,
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  391    );
  392
  393    _ = editor.update(cx, |editor, window, cx| {
  394        editor.update_selection(
  395            DisplayPoint::new(DisplayRow(1), 1),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            window,
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |editor, window, cx| {
  411        editor.end_selection(window, cx);
  412        editor.update_selection(
  413            DisplayPoint::new(DisplayRow(3), 3),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            window,
  417            cx,
  418        );
  419    });
  420
  421    assert_eq!(
  422        editor
  423            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  424            .unwrap(),
  425        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  426    );
  427
  428    _ = editor.update(cx, |editor, window, cx| {
  429        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  430        editor.update_selection(
  431            DisplayPoint::new(DisplayRow(0), 0),
  432            0,
  433            gpui::Point::<f32>::default(),
  434            window,
  435            cx,
  436        );
  437    });
  438
  439    assert_eq!(
  440        editor
  441            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  442            .unwrap(),
  443        [
  444            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  445            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  446        ]
  447    );
  448
  449    _ = editor.update(cx, |editor, window, cx| {
  450        editor.end_selection(window, cx);
  451    });
  452
  453    assert_eq!(
  454        editor
  455            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  456            .unwrap(),
  457        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  458    );
  459}
  460
  461#[gpui::test]
  462fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  463    init_test(cx, |_| {});
  464
  465    let editor = cx.add_window(|window, cx| {
  466        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  467        build_editor(buffer, window, cx)
  468    });
  469
  470    _ = editor.update(cx, |editor, window, cx| {
  471        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  472    });
  473
  474    _ = editor.update(cx, |editor, window, cx| {
  475        editor.end_selection(window, cx);
  476    });
  477
  478    _ = editor.update(cx, |editor, window, cx| {
  479        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  480    });
  481
  482    _ = editor.update(cx, |editor, window, cx| {
  483        editor.end_selection(window, cx);
  484    });
  485
  486    assert_eq!(
  487        editor
  488            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  489            .unwrap(),
  490        [
  491            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  492            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  493        ]
  494    );
  495
  496    _ = editor.update(cx, |editor, window, cx| {
  497        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  498    });
  499
  500    _ = editor.update(cx, |editor, window, cx| {
  501        editor.end_selection(window, cx);
  502    });
  503
  504    assert_eq!(
  505        editor
  506            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  507            .unwrap(),
  508        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  509    );
  510}
  511
  512#[gpui::test]
  513fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  514    init_test(cx, |_| {});
  515
  516    let editor = cx.add_window(|window, cx| {
  517        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  518        build_editor(buffer, window, cx)
  519    });
  520
  521    _ = editor.update(cx, |editor, window, cx| {
  522        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  523        assert_eq!(
  524            editor.selections.display_ranges(cx),
  525            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  526        );
  527    });
  528
  529    _ = editor.update(cx, |editor, window, cx| {
  530        editor.update_selection(
  531            DisplayPoint::new(DisplayRow(3), 3),
  532            0,
  533            gpui::Point::<f32>::default(),
  534            window,
  535            cx,
  536        );
  537        assert_eq!(
  538            editor.selections.display_ranges(cx),
  539            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  540        );
  541    });
  542
  543    _ = editor.update(cx, |editor, window, cx| {
  544        editor.cancel(&Cancel, window, cx);
  545        editor.update_selection(
  546            DisplayPoint::new(DisplayRow(1), 1),
  547            0,
  548            gpui::Point::<f32>::default(),
  549            window,
  550            cx,
  551        );
  552        assert_eq!(
  553            editor.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  555        );
  556    });
  557}
  558
  559#[gpui::test]
  560fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  561    init_test(cx, |_| {});
  562
  563    let editor = cx.add_window(|window, cx| {
  564        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  565        build_editor(buffer, window, cx)
  566    });
  567
  568    _ = editor.update(cx, |editor, window, cx| {
  569        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  570        assert_eq!(
  571            editor.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  573        );
  574
  575        editor.move_down(&Default::default(), window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  579        );
  580
  581        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  585        );
  586
  587        editor.move_up(&Default::default(), window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  591        );
  592    });
  593}
  594
  595#[gpui::test]
  596fn test_clone(cx: &mut TestAppContext) {
  597    init_test(cx, |_| {});
  598
  599    let (text, selection_ranges) = marked_text_ranges(
  600        indoc! {"
  601            one
  602            two
  603            threeˇ
  604            four
  605            fiveˇ
  606        "},
  607        true,
  608    );
  609
  610    let editor = cx.add_window(|window, cx| {
  611        let buffer = MultiBuffer::build_simple(&text, cx);
  612        build_editor(buffer, window, cx)
  613    });
  614
  615    _ = editor.update(cx, |editor, window, cx| {
  616        editor.change_selections(None, window, cx, |s| {
  617            s.select_ranges(selection_ranges.clone())
  618        });
  619        editor.fold_creases(
  620            vec![
  621                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  622                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  623            ],
  624            true,
  625            window,
  626            cx,
  627        );
  628    });
  629
  630    let cloned_editor = editor
  631        .update(cx, |editor, _, cx| {
  632            cx.open_window(Default::default(), |window, cx| {
  633                cx.new(|cx| editor.clone(window, cx))
  634            })
  635        })
  636        .unwrap()
  637        .unwrap();
  638
  639    let snapshot = editor
  640        .update(cx, |e, window, cx| e.snapshot(window, cx))
  641        .unwrap();
  642    let cloned_snapshot = cloned_editor
  643        .update(cx, |e, window, cx| e.snapshot(window, cx))
  644        .unwrap();
  645
  646    assert_eq!(
  647        cloned_editor
  648            .update(cx, |e, _, cx| e.display_text(cx))
  649            .unwrap(),
  650        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  651    );
  652    assert_eq!(
  653        cloned_snapshot
  654            .folds_in_range(0..text.len())
  655            .collect::<Vec<_>>(),
  656        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  657    );
  658    assert_set_eq!(
  659        cloned_editor
  660            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  661            .unwrap(),
  662        editor
  663            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  664            .unwrap()
  665    );
  666    assert_set_eq!(
  667        cloned_editor
  668            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  669            .unwrap(),
  670        editor
  671            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  672            .unwrap()
  673    );
  674}
  675
  676#[gpui::test]
  677async fn test_navigation_history(cx: &mut TestAppContext) {
  678    init_test(cx, |_| {});
  679
  680    use workspace::item::Item;
  681
  682    let fs = FakeFs::new(cx.executor());
  683    let project = Project::test(fs, [], cx).await;
  684    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  685    let pane = workspace
  686        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  687        .unwrap();
  688
  689    _ = workspace.update(cx, |_v, window, cx| {
  690        cx.new(|cx| {
  691            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  692            let mut editor = build_editor(buffer.clone(), window, cx);
  693            let handle = cx.entity();
  694            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  695
  696            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  697                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  698            }
  699
  700            // Move the cursor a small distance.
  701            // Nothing is added to the navigation history.
  702            editor.change_selections(None, window, cx, |s| {
  703                s.select_display_ranges([
  704                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  705                ])
  706            });
  707            editor.change_selections(None, window, cx, |s| {
  708                s.select_display_ranges([
  709                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  710                ])
  711            });
  712            assert!(pop_history(&mut editor, cx).is_none());
  713
  714            // Move the cursor a large distance.
  715            // The history can jump back to the previous position.
  716            editor.change_selections(None, window, cx, |s| {
  717                s.select_display_ranges([
  718                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  719                ])
  720            });
  721            let nav_entry = pop_history(&mut editor, cx).unwrap();
  722            editor.navigate(nav_entry.data.unwrap(), window, cx);
  723            assert_eq!(nav_entry.item.id(), cx.entity_id());
  724            assert_eq!(
  725                editor.selections.display_ranges(cx),
  726                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  727            );
  728            assert!(pop_history(&mut editor, cx).is_none());
  729
  730            // Move the cursor a small distance via the mouse.
  731            // Nothing is added to the navigation history.
  732            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  733            editor.end_selection(window, cx);
  734            assert_eq!(
  735                editor.selections.display_ranges(cx),
  736                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  737            );
  738            assert!(pop_history(&mut editor, cx).is_none());
  739
  740            // Move the cursor a large distance via the mouse.
  741            // The history can jump back to the previous position.
  742            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  743            editor.end_selection(window, cx);
  744            assert_eq!(
  745                editor.selections.display_ranges(cx),
  746                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  747            );
  748            let nav_entry = pop_history(&mut editor, cx).unwrap();
  749            editor.navigate(nav_entry.data.unwrap(), window, cx);
  750            assert_eq!(nav_entry.item.id(), cx.entity_id());
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  754            );
  755            assert!(pop_history(&mut editor, cx).is_none());
  756
  757            // Set scroll position to check later
  758            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  759            let original_scroll_position = editor.scroll_manager.anchor();
  760
  761            // Jump to the end of the document and adjust scroll
  762            editor.move_to_end(&MoveToEnd, window, cx);
  763            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  764            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  765
  766            let nav_entry = pop_history(&mut editor, cx).unwrap();
  767            editor.navigate(nav_entry.data.unwrap(), window, cx);
  768            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  769
  770            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  771            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  772            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  773            let invalid_point = Point::new(9999, 0);
  774            editor.navigate(
  775                Box::new(NavigationData {
  776                    cursor_anchor: invalid_anchor,
  777                    cursor_position: invalid_point,
  778                    scroll_anchor: ScrollAnchor {
  779                        anchor: invalid_anchor,
  780                        offset: Default::default(),
  781                    },
  782                    scroll_top_row: invalid_point.row,
  783                }),
  784                window,
  785                cx,
  786            );
  787            assert_eq!(
  788                editor.selections.display_ranges(cx),
  789                &[editor.max_point(cx)..editor.max_point(cx)]
  790            );
  791            assert_eq!(
  792                editor.scroll_position(cx),
  793                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  794            );
  795
  796            editor
  797        })
  798    });
  799}
  800
  801#[gpui::test]
  802fn test_cancel(cx: &mut TestAppContext) {
  803    init_test(cx, |_| {});
  804
  805    let editor = cx.add_window(|window, cx| {
  806        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  807        build_editor(buffer, window, cx)
  808    });
  809
  810    _ = editor.update(cx, |editor, window, cx| {
  811        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  812        editor.update_selection(
  813            DisplayPoint::new(DisplayRow(1), 1),
  814            0,
  815            gpui::Point::<f32>::default(),
  816            window,
  817            cx,
  818        );
  819        editor.end_selection(window, cx);
  820
  821        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  822        editor.update_selection(
  823            DisplayPoint::new(DisplayRow(0), 3),
  824            0,
  825            gpui::Point::<f32>::default(),
  826            window,
  827            cx,
  828        );
  829        editor.end_selection(window, cx);
  830        assert_eq!(
  831            editor.selections.display_ranges(cx),
  832            [
  833                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  834                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  835            ]
  836        );
  837    });
  838
  839    _ = editor.update(cx, |editor, window, cx| {
  840        editor.cancel(&Cancel, window, cx);
  841        assert_eq!(
  842            editor.selections.display_ranges(cx),
  843            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  844        );
  845    });
  846
  847    _ = editor.update(cx, |editor, window, cx| {
  848        editor.cancel(&Cancel, window, cx);
  849        assert_eq!(
  850            editor.selections.display_ranges(cx),
  851            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  852        );
  853    });
  854}
  855
  856#[gpui::test]
  857fn test_fold_action(cx: &mut TestAppContext) {
  858    init_test(cx, |_| {});
  859
  860    let editor = cx.add_window(|window, cx| {
  861        let buffer = MultiBuffer::build_simple(
  862            &"
  863                impl Foo {
  864                    // Hello!
  865
  866                    fn a() {
  867                        1
  868                    }
  869
  870                    fn b() {
  871                        2
  872                    }
  873
  874                    fn c() {
  875                        3
  876                    }
  877                }
  878            "
  879            .unindent(),
  880            cx,
  881        );
  882        build_editor(buffer.clone(), window, cx)
  883    });
  884
  885    _ = editor.update(cx, |editor, window, cx| {
  886        editor.change_selections(None, window, cx, |s| {
  887            s.select_display_ranges([
  888                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  889            ]);
  890        });
  891        editor.fold(&Fold, window, cx);
  892        assert_eq!(
  893            editor.display_text(cx),
  894            "
  895                impl Foo {
  896                    // Hello!
  897
  898                    fn a() {
  899                        1
  900                    }
  901
  902                    fn b() {⋯
  903                    }
  904
  905                    fn c() {⋯
  906                    }
  907                }
  908            "
  909            .unindent(),
  910        );
  911
  912        editor.fold(&Fold, window, cx);
  913        assert_eq!(
  914            editor.display_text(cx),
  915            "
  916                impl Foo {⋯
  917                }
  918            "
  919            .unindent(),
  920        );
  921
  922        editor.unfold_lines(&UnfoldLines, window, cx);
  923        assert_eq!(
  924            editor.display_text(cx),
  925            "
  926                impl Foo {
  927                    // Hello!
  928
  929                    fn a() {
  930                        1
  931                    }
  932
  933                    fn b() {⋯
  934                    }
  935
  936                    fn c() {⋯
  937                    }
  938                }
  939            "
  940            .unindent(),
  941        );
  942
  943        editor.unfold_lines(&UnfoldLines, window, cx);
  944        assert_eq!(
  945            editor.display_text(cx),
  946            editor.buffer.read(cx).read(cx).text()
  947        );
  948    });
  949}
  950
  951#[gpui::test]
  952fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  953    init_test(cx, |_| {});
  954
  955    let editor = cx.add_window(|window, cx| {
  956        let buffer = MultiBuffer::build_simple(
  957            &"
  958                class Foo:
  959                    # Hello!
  960
  961                    def a():
  962                        print(1)
  963
  964                    def b():
  965                        print(2)
  966
  967                    def c():
  968                        print(3)
  969            "
  970            .unindent(),
  971            cx,
  972        );
  973        build_editor(buffer.clone(), window, cx)
  974    });
  975
  976    _ = editor.update(cx, |editor, window, cx| {
  977        editor.change_selections(None, window, cx, |s| {
  978            s.select_display_ranges([
  979                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  980            ]);
  981        });
  982        editor.fold(&Fold, window, cx);
  983        assert_eq!(
  984            editor.display_text(cx),
  985            "
  986                class Foo:
  987                    # Hello!
  988
  989                    def a():
  990                        print(1)
  991
  992                    def b():⋯
  993
  994                    def c():⋯
  995            "
  996            .unindent(),
  997        );
  998
  999        editor.fold(&Fold, window, cx);
 1000        assert_eq!(
 1001            editor.display_text(cx),
 1002            "
 1003                class Foo:⋯
 1004            "
 1005            .unindent(),
 1006        );
 1007
 1008        editor.unfold_lines(&UnfoldLines, window, cx);
 1009        assert_eq!(
 1010            editor.display_text(cx),
 1011            "
 1012                class Foo:
 1013                    # Hello!
 1014
 1015                    def a():
 1016                        print(1)
 1017
 1018                    def b():⋯
 1019
 1020                    def c():⋯
 1021            "
 1022            .unindent(),
 1023        );
 1024
 1025        editor.unfold_lines(&UnfoldLines, window, cx);
 1026        assert_eq!(
 1027            editor.display_text(cx),
 1028            editor.buffer.read(cx).read(cx).text()
 1029        );
 1030    });
 1031}
 1032
 1033#[gpui::test]
 1034fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1035    init_test(cx, |_| {});
 1036
 1037    let editor = cx.add_window(|window, cx| {
 1038        let buffer = MultiBuffer::build_simple(
 1039            &"
 1040                class Foo:
 1041                    # Hello!
 1042
 1043                    def a():
 1044                        print(1)
 1045
 1046                    def b():
 1047                        print(2)
 1048
 1049
 1050                    def c():
 1051                        print(3)
 1052
 1053
 1054            "
 1055            .unindent(),
 1056            cx,
 1057        );
 1058        build_editor(buffer.clone(), window, cx)
 1059    });
 1060
 1061    _ = editor.update(cx, |editor, window, cx| {
 1062        editor.change_selections(None, window, cx, |s| {
 1063            s.select_display_ranges([
 1064                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1065            ]);
 1066        });
 1067        editor.fold(&Fold, window, cx);
 1068        assert_eq!(
 1069            editor.display_text(cx),
 1070            "
 1071                class Foo:
 1072                    # Hello!
 1073
 1074                    def a():
 1075                        print(1)
 1076
 1077                    def b():⋯
 1078
 1079
 1080                    def c():⋯
 1081
 1082
 1083            "
 1084            .unindent(),
 1085        );
 1086
 1087        editor.fold(&Fold, window, cx);
 1088        assert_eq!(
 1089            editor.display_text(cx),
 1090            "
 1091                class Foo:⋯
 1092
 1093
 1094            "
 1095            .unindent(),
 1096        );
 1097
 1098        editor.unfold_lines(&UnfoldLines, window, cx);
 1099        assert_eq!(
 1100            editor.display_text(cx),
 1101            "
 1102                class Foo:
 1103                    # Hello!
 1104
 1105                    def a():
 1106                        print(1)
 1107
 1108                    def b():⋯
 1109
 1110
 1111                    def c():⋯
 1112
 1113
 1114            "
 1115            .unindent(),
 1116        );
 1117
 1118        editor.unfold_lines(&UnfoldLines, window, cx);
 1119        assert_eq!(
 1120            editor.display_text(cx),
 1121            editor.buffer.read(cx).read(cx).text()
 1122        );
 1123    });
 1124}
 1125
 1126#[gpui::test]
 1127fn test_fold_at_level(cx: &mut TestAppContext) {
 1128    init_test(cx, |_| {});
 1129
 1130    let editor = cx.add_window(|window, cx| {
 1131        let buffer = MultiBuffer::build_simple(
 1132            &"
 1133                class Foo:
 1134                    # Hello!
 1135
 1136                    def a():
 1137                        print(1)
 1138
 1139                    def b():
 1140                        print(2)
 1141
 1142
 1143                class Bar:
 1144                    # World!
 1145
 1146                    def a():
 1147                        print(1)
 1148
 1149                    def b():
 1150                        print(2)
 1151
 1152
 1153            "
 1154            .unindent(),
 1155            cx,
 1156        );
 1157        build_editor(buffer.clone(), window, cx)
 1158    });
 1159
 1160    _ = editor.update(cx, |editor, window, cx| {
 1161        editor.fold_at_level(&FoldAtLevel { level: 2 }, window, cx);
 1162        assert_eq!(
 1163            editor.display_text(cx),
 1164            "
 1165                class Foo:
 1166                    # Hello!
 1167
 1168                    def a():⋯
 1169
 1170                    def b():⋯
 1171
 1172
 1173                class Bar:
 1174                    # World!
 1175
 1176                    def a():⋯
 1177
 1178                    def b():⋯
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        editor.fold_at_level(&FoldAtLevel { level: 1 }, window, cx);
 1186        assert_eq!(
 1187            editor.display_text(cx),
 1188            "
 1189                class Foo:⋯
 1190
 1191
 1192                class Bar:⋯
 1193
 1194
 1195            "
 1196            .unindent(),
 1197        );
 1198
 1199        editor.unfold_all(&UnfoldAll, window, cx);
 1200        editor.fold_at_level(&FoldAtLevel { level: 0 }, window, cx);
 1201        assert_eq!(
 1202            editor.display_text(cx),
 1203            "
 1204                class Foo:
 1205                    # Hello!
 1206
 1207                    def a():
 1208                        print(1)
 1209
 1210                    def b():
 1211                        print(2)
 1212
 1213
 1214                class Bar:
 1215                    # World!
 1216
 1217                    def a():
 1218                        print(1)
 1219
 1220                    def b():
 1221                        print(2)
 1222
 1223
 1224            "
 1225            .unindent(),
 1226        );
 1227
 1228        assert_eq!(
 1229            editor.display_text(cx),
 1230            editor.buffer.read(cx).read(cx).text()
 1231        );
 1232    });
 1233}
 1234
 1235#[gpui::test]
 1236fn test_move_cursor(cx: &mut TestAppContext) {
 1237    init_test(cx, |_| {});
 1238
 1239    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1240    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1241
 1242    buffer.update(cx, |buffer, cx| {
 1243        buffer.edit(
 1244            vec![
 1245                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1246                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1247            ],
 1248            None,
 1249            cx,
 1250        );
 1251    });
 1252    _ = editor.update(cx, |editor, window, cx| {
 1253        assert_eq!(
 1254            editor.selections.display_ranges(cx),
 1255            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1256        );
 1257
 1258        editor.move_down(&MoveDown, window, cx);
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1262        );
 1263
 1264        editor.move_right(&MoveRight, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1268        );
 1269
 1270        editor.move_left(&MoveLeft, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1274        );
 1275
 1276        editor.move_up(&MoveUp, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1280        );
 1281
 1282        editor.move_to_end(&MoveToEnd, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1286        );
 1287
 1288        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1292        );
 1293
 1294        editor.change_selections(None, window, cx, |s| {
 1295            s.select_display_ranges([
 1296                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1297            ]);
 1298        });
 1299        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1300        assert_eq!(
 1301            editor.selections.display_ranges(cx),
 1302            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1303        );
 1304
 1305        editor.select_to_end(&SelectToEnd, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1309        );
 1310    });
 1311}
 1312
 1313// TODO: Re-enable this test
 1314#[cfg(target_os = "macos")]
 1315#[gpui::test]
 1316fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1317    init_test(cx, |_| {});
 1318
 1319    let editor = cx.add_window(|window, cx| {
 1320        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1321        build_editor(buffer.clone(), window, cx)
 1322    });
 1323
 1324    assert_eq!('🟥'.len_utf8(), 4);
 1325    assert_eq!('α'.len_utf8(), 2);
 1326
 1327    _ = editor.update(cx, |editor, window, cx| {
 1328        editor.fold_creases(
 1329            vec![
 1330                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1331                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1332                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1333            ],
 1334            true,
 1335            window,
 1336            cx,
 1337        );
 1338        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1339
 1340        editor.move_right(&MoveRight, window, cx);
 1341        assert_eq!(
 1342            editor.selections.display_ranges(cx),
 1343            &[empty_range(0, "🟥".len())]
 1344        );
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥🟧".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧⋯".len())]
 1354        );
 1355
 1356        editor.move_down(&MoveDown, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(1, "ab⋯e".len())]
 1360        );
 1361        editor.move_left(&MoveLeft, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "a".len())]
 1375        );
 1376
 1377        editor.move_down(&MoveDown, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(2, "α".len())]
 1381        );
 1382        editor.move_right(&MoveRight, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "αβ".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ⋯".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯ε".len())]
 1396        );
 1397
 1398        editor.move_up(&MoveUp, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(1, "ab⋯e".len())]
 1402        );
 1403        editor.move_down(&MoveDown, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβ⋯ε".len())]
 1407        );
 1408        editor.move_up(&MoveUp, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(1, "ab⋯e".len())]
 1412        );
 1413
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(0, "🟥🟧".len())]
 1418        );
 1419        editor.move_left(&MoveLeft, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "".len())]
 1428        );
 1429    });
 1430}
 1431
 1432#[gpui::test]
 1433fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1434    init_test(cx, |_| {});
 1435
 1436    let editor = cx.add_window(|window, cx| {
 1437        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1438        build_editor(buffer.clone(), window, cx)
 1439    });
 1440    _ = editor.update(cx, |editor, window, cx| {
 1441        editor.change_selections(None, window, cx, |s| {
 1442            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1443        });
 1444
 1445        // moving above start of document should move selection to start of document,
 1446        // but the next move down should still be at the original goal_x
 1447        editor.move_up(&MoveUp, window, cx);
 1448        assert_eq!(
 1449            editor.selections.display_ranges(cx),
 1450            &[empty_range(0, "".len())]
 1451        );
 1452
 1453        editor.move_down(&MoveDown, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(1, "abcd".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(2, "αβγ".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(3, "abcd".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1475        );
 1476
 1477        // moving past end of document should not change goal_x
 1478        editor.move_down(&MoveDown, window, cx);
 1479        assert_eq!(
 1480            editor.selections.display_ranges(cx),
 1481            &[empty_range(5, "".len())]
 1482        );
 1483
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_up(&MoveUp, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(3, "abcd".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(2, "αβγ".len())]
 1506        );
 1507    });
 1508}
 1509
 1510#[gpui::test]
 1511fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1512    init_test(cx, |_| {});
 1513    let move_to_beg = MoveToBeginningOfLine {
 1514        stop_at_soft_wraps: true,
 1515    };
 1516
 1517    let move_to_end = MoveToEndOfLine {
 1518        stop_at_soft_wraps: true,
 1519    };
 1520
 1521    let editor = cx.add_window(|window, cx| {
 1522        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1523        build_editor(buffer, window, cx)
 1524    });
 1525    _ = editor.update(cx, |editor, window, cx| {
 1526        editor.change_selections(None, window, cx, |s| {
 1527            s.select_display_ranges([
 1528                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1529                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1530            ]);
 1531        });
 1532    });
 1533
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1536        assert_eq!(
 1537            editor.selections.display_ranges(cx),
 1538            &[
 1539                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1540                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1541            ]
 1542        );
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_end_of_line(&move_to_end, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1573                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1574            ]
 1575        );
 1576    });
 1577
 1578    // Moving to the end of line again is a no-op.
 1579    _ = editor.update(cx, |editor, window, cx| {
 1580        editor.move_to_end_of_line(&move_to_end, window, cx);
 1581        assert_eq!(
 1582            editor.selections.display_ranges(cx),
 1583            &[
 1584                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1585                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1586            ]
 1587        );
 1588    });
 1589
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_left(&MoveLeft, window, cx);
 1592        editor.select_to_beginning_of_line(
 1593            &SelectToBeginningOfLine {
 1594                stop_at_soft_wraps: true,
 1595            },
 1596            window,
 1597            cx,
 1598        );
 1599        assert_eq!(
 1600            editor.selections.display_ranges(cx),
 1601            &[
 1602                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1603                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1604            ]
 1605        );
 1606    });
 1607
 1608    _ = editor.update(cx, |editor, window, cx| {
 1609        editor.select_to_beginning_of_line(
 1610            &SelectToBeginningOfLine {
 1611                stop_at_soft_wraps: true,
 1612            },
 1613            window,
 1614            cx,
 1615        );
 1616        assert_eq!(
 1617            editor.selections.display_ranges(cx),
 1618            &[
 1619                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1620                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1621            ]
 1622        );
 1623    });
 1624
 1625    _ = editor.update(cx, |editor, window, cx| {
 1626        editor.select_to_beginning_of_line(
 1627            &SelectToBeginningOfLine {
 1628                stop_at_soft_wraps: true,
 1629            },
 1630            window,
 1631            cx,
 1632        );
 1633        assert_eq!(
 1634            editor.selections.display_ranges(cx),
 1635            &[
 1636                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1637                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1638            ]
 1639        );
 1640    });
 1641
 1642    _ = editor.update(cx, |editor, window, cx| {
 1643        editor.select_to_end_of_line(
 1644            &SelectToEndOfLine {
 1645                stop_at_soft_wraps: true,
 1646            },
 1647            window,
 1648            cx,
 1649        );
 1650        assert_eq!(
 1651            editor.selections.display_ranges(cx),
 1652            &[
 1653                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1654                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1655            ]
 1656        );
 1657    });
 1658
 1659    _ = editor.update(cx, |editor, window, cx| {
 1660        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1661        assert_eq!(editor.display_text(cx), "ab\n  de");
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "\n");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1678                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1679            ]
 1680        );
 1681    });
 1682}
 1683
 1684#[gpui::test]
 1685fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1686    init_test(cx, |_| {});
 1687    let move_to_beg = MoveToBeginningOfLine {
 1688        stop_at_soft_wraps: false,
 1689    };
 1690
 1691    let move_to_end = MoveToEndOfLine {
 1692        stop_at_soft_wraps: false,
 1693    };
 1694
 1695    let editor = cx.add_window(|window, cx| {
 1696        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1697        build_editor(buffer, window, cx)
 1698    });
 1699
 1700    _ = editor.update(cx, |editor, window, cx| {
 1701        editor.set_wrap_width(Some(140.0.into()), cx);
 1702
 1703        // We expect the following lines after wrapping
 1704        // ```
 1705        // thequickbrownfox
 1706        // jumpedoverthelazydo
 1707        // gs
 1708        // ```
 1709        // The final `gs` was soft-wrapped onto a new line.
 1710        assert_eq!(
 1711            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1712            editor.display_text(cx),
 1713        );
 1714
 1715        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1716        // Start the cursor at the `k` on the first line
 1717        editor.change_selections(None, window, cx, |s| {
 1718            s.select_display_ranges([
 1719                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1720            ]);
 1721        });
 1722
 1723        // Moving to the beginning of the line should put us at the beginning of the line.
 1724        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1725        assert_eq!(
 1726            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1727            editor.selections.display_ranges(cx)
 1728        );
 1729
 1730        // Moving to the end of the line should put us at the end of the line.
 1731        editor.move_to_end_of_line(&move_to_end, window, cx);
 1732        assert_eq!(
 1733            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1734            editor.selections.display_ranges(cx)
 1735        );
 1736
 1737        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1738        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1739        editor.change_selections(None, window, cx, |s| {
 1740            s.select_display_ranges([
 1741                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1742            ]);
 1743        });
 1744
 1745        // Moving to the beginning of the line should put us at the start of the second line of
 1746        // display text, i.e., the `j`.
 1747        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1748        assert_eq!(
 1749            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1750            editor.selections.display_ranges(cx)
 1751        );
 1752
 1753        // Moving to the beginning of the line again should be a no-op.
 1754        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1755        assert_eq!(
 1756            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1757            editor.selections.display_ranges(cx)
 1758        );
 1759
 1760        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1761        // next display line.
 1762        editor.move_to_end_of_line(&move_to_end, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the end of the line again should be a no-op.
 1769        editor.move_to_end_of_line(&move_to_end, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774    });
 1775}
 1776
 1777#[gpui::test]
 1778fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1779    init_test(cx, |_| {});
 1780
 1781    let editor = cx.add_window(|window, cx| {
 1782        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1783        build_editor(buffer, window, cx)
 1784    });
 1785    _ = editor.update(cx, |editor, window, cx| {
 1786        editor.change_selections(None, window, cx, |s| {
 1787            s.select_display_ranges([
 1788                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1789                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1790            ])
 1791        });
 1792
 1793        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1794        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1795
 1796        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1797        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1798
 1799        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1800        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1801
 1802        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1803        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1804
 1805        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1806        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1807
 1808        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1809        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1810
 1811        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1812        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1813
 1814        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1815        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1816
 1817        editor.move_right(&MoveRight, window, cx);
 1818        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1819        assert_selection_ranges(
 1820            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1821            editor,
 1822            cx,
 1823        );
 1824
 1825        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1826        assert_selection_ranges(
 1827            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1828            editor,
 1829            cx,
 1830        );
 1831
 1832        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1833        assert_selection_ranges(
 1834            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1835            editor,
 1836            cx,
 1837        );
 1838    });
 1839}
 1840
 1841#[gpui::test]
 1842fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1843    init_test(cx, |_| {});
 1844
 1845    let editor = cx.add_window(|window, cx| {
 1846        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1847        build_editor(buffer, window, cx)
 1848    });
 1849
 1850    _ = editor.update(cx, |editor, window, cx| {
 1851        editor.set_wrap_width(Some(140.0.into()), cx);
 1852        assert_eq!(
 1853            editor.display_text(cx),
 1854            "use one::{\n    two::three::\n    four::five\n};"
 1855        );
 1856
 1857        editor.change_selections(None, window, cx, |s| {
 1858            s.select_display_ranges([
 1859                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1860            ]);
 1861        });
 1862
 1863        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1864        assert_eq!(
 1865            editor.selections.display_ranges(cx),
 1866            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1867        );
 1868
 1869        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1870        assert_eq!(
 1871            editor.selections.display_ranges(cx),
 1872            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1873        );
 1874
 1875        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1879        );
 1880
 1881        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1882        assert_eq!(
 1883            editor.selections.display_ranges(cx),
 1884            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1885        );
 1886
 1887        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1888        assert_eq!(
 1889            editor.selections.display_ranges(cx),
 1890            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1891        );
 1892
 1893        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1894        assert_eq!(
 1895            editor.selections.display_ranges(cx),
 1896            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1897        );
 1898    });
 1899}
 1900
 1901#[gpui::test]
 1902async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1903    init_test(cx, |_| {});
 1904    let mut cx = EditorTestContext::new(cx).await;
 1905
 1906    let line_height = cx.editor(|editor, window, _| {
 1907        editor
 1908            .style()
 1909            .unwrap()
 1910            .text
 1911            .line_height_in_pixels(window.rem_size())
 1912    });
 1913    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1914
 1915    cx.set_state(
 1916        &r#"ˇone
 1917        two
 1918
 1919        three
 1920        fourˇ
 1921        five
 1922
 1923        six"#
 1924            .unindent(),
 1925    );
 1926
 1927    cx.update_editor(|editor, window, cx| {
 1928        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1929    });
 1930    cx.assert_editor_state(
 1931        &r#"one
 1932        two
 1933        ˇ
 1934        three
 1935        four
 1936        five
 1937        ˇ
 1938        six"#
 1939            .unindent(),
 1940    );
 1941
 1942    cx.update_editor(|editor, window, cx| {
 1943        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1944    });
 1945    cx.assert_editor_state(
 1946        &r#"one
 1947        two
 1948
 1949        three
 1950        four
 1951        five
 1952        ˇ
 1953        sixˇ"#
 1954            .unindent(),
 1955    );
 1956
 1957    cx.update_editor(|editor, window, cx| {
 1958        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1959    });
 1960    cx.assert_editor_state(
 1961        &r#"one
 1962        two
 1963
 1964        three
 1965        four
 1966        five
 1967
 1968        sixˇ"#
 1969            .unindent(),
 1970    );
 1971
 1972    cx.update_editor(|editor, window, cx| {
 1973        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1974    });
 1975    cx.assert_editor_state(
 1976        &r#"one
 1977        two
 1978
 1979        three
 1980        four
 1981        five
 1982        ˇ
 1983        six"#
 1984            .unindent(),
 1985    );
 1986
 1987    cx.update_editor(|editor, window, cx| {
 1988        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1989    });
 1990    cx.assert_editor_state(
 1991        &r#"one
 1992        two
 1993        ˇ
 1994        three
 1995        four
 1996        five
 1997
 1998        six"#
 1999            .unindent(),
 2000    );
 2001
 2002    cx.update_editor(|editor, window, cx| {
 2003        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2004    });
 2005    cx.assert_editor_state(
 2006        &r#"ˇone
 2007        two
 2008
 2009        three
 2010        four
 2011        five
 2012
 2013        six"#
 2014            .unindent(),
 2015    );
 2016}
 2017
 2018#[gpui::test]
 2019async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2020    init_test(cx, |_| {});
 2021    let mut cx = EditorTestContext::new(cx).await;
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    let window = cx.window;
 2030    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2031
 2032    cx.set_state(
 2033        r#"ˇone
 2034        two
 2035        three
 2036        four
 2037        five
 2038        six
 2039        seven
 2040        eight
 2041        nine
 2042        ten
 2043        "#,
 2044    );
 2045
 2046    cx.update_editor(|editor, window, cx| {
 2047        assert_eq!(
 2048            editor.snapshot(window, cx).scroll_position(),
 2049            gpui::Point::new(0., 0.)
 2050        );
 2051        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2052        assert_eq!(
 2053            editor.snapshot(window, cx).scroll_position(),
 2054            gpui::Point::new(0., 3.)
 2055        );
 2056        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2057        assert_eq!(
 2058            editor.snapshot(window, cx).scroll_position(),
 2059            gpui::Point::new(0., 6.)
 2060        );
 2061        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2062        assert_eq!(
 2063            editor.snapshot(window, cx).scroll_position(),
 2064            gpui::Point::new(0., 3.)
 2065        );
 2066
 2067        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2068        assert_eq!(
 2069            editor.snapshot(window, cx).scroll_position(),
 2070            gpui::Point::new(0., 1.)
 2071        );
 2072        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2073        assert_eq!(
 2074            editor.snapshot(window, cx).scroll_position(),
 2075            gpui::Point::new(0., 3.)
 2076        );
 2077    });
 2078}
 2079
 2080#[gpui::test]
 2081async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2082    init_test(cx, |_| {});
 2083    let mut cx = EditorTestContext::new(cx).await;
 2084
 2085    let line_height = cx.update_editor(|editor, window, cx| {
 2086        editor.set_vertical_scroll_margin(2, cx);
 2087        editor
 2088            .style()
 2089            .unwrap()
 2090            .text
 2091            .line_height_in_pixels(window.rem_size())
 2092    });
 2093    let window = cx.window;
 2094    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2095
 2096    cx.set_state(
 2097        r#"ˇone
 2098            two
 2099            three
 2100            four
 2101            five
 2102            six
 2103            seven
 2104            eight
 2105            nine
 2106            ten
 2107        "#,
 2108    );
 2109    cx.update_editor(|editor, window, cx| {
 2110        assert_eq!(
 2111            editor.snapshot(window, cx).scroll_position(),
 2112            gpui::Point::new(0., 0.0)
 2113        );
 2114    });
 2115
 2116    // Add a cursor below the visible area. Since both cursors cannot fit
 2117    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2118    // allows the vertical scroll margin below that cursor.
 2119    cx.update_editor(|editor, window, cx| {
 2120        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2121            selections.select_ranges([
 2122                Point::new(0, 0)..Point::new(0, 0),
 2123                Point::new(6, 0)..Point::new(6, 0),
 2124            ]);
 2125        })
 2126    });
 2127    cx.update_editor(|editor, window, cx| {
 2128        assert_eq!(
 2129            editor.snapshot(window, cx).scroll_position(),
 2130            gpui::Point::new(0., 3.0)
 2131        );
 2132    });
 2133
 2134    // Move down. The editor cursor scrolls down to track the newest cursor.
 2135    cx.update_editor(|editor, window, cx| {
 2136        editor.move_down(&Default::default(), window, cx);
 2137    });
 2138    cx.update_editor(|editor, window, cx| {
 2139        assert_eq!(
 2140            editor.snapshot(window, cx).scroll_position(),
 2141            gpui::Point::new(0., 4.0)
 2142        );
 2143    });
 2144
 2145    // Add a cursor above the visible area. Since both cursors fit on screen,
 2146    // the editor scrolls to show both.
 2147    cx.update_editor(|editor, window, cx| {
 2148        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2149            selections.select_ranges([
 2150                Point::new(1, 0)..Point::new(1, 0),
 2151                Point::new(6, 0)..Point::new(6, 0),
 2152            ]);
 2153        })
 2154    });
 2155    cx.update_editor(|editor, window, cx| {
 2156        assert_eq!(
 2157            editor.snapshot(window, cx).scroll_position(),
 2158            gpui::Point::new(0., 1.0)
 2159        );
 2160    });
 2161}
 2162
 2163#[gpui::test]
 2164async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2165    init_test(cx, |_| {});
 2166    let mut cx = EditorTestContext::new(cx).await;
 2167
 2168    let line_height = cx.editor(|editor, window, _cx| {
 2169        editor
 2170            .style()
 2171            .unwrap()
 2172            .text
 2173            .line_height_in_pixels(window.rem_size())
 2174    });
 2175    let window = cx.window;
 2176    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2177    cx.set_state(
 2178        &r#"
 2179        ˇone
 2180        two
 2181        threeˇ
 2182        four
 2183        five
 2184        six
 2185        seven
 2186        eight
 2187        nine
 2188        ten
 2189        "#
 2190        .unindent(),
 2191    );
 2192
 2193    cx.update_editor(|editor, window, cx| {
 2194        editor.move_page_down(&MovePageDown::default(), window, cx)
 2195    });
 2196    cx.assert_editor_state(
 2197        &r#"
 2198        one
 2199        two
 2200        three
 2201        ˇfour
 2202        five
 2203        sixˇ
 2204        seven
 2205        eight
 2206        nine
 2207        ten
 2208        "#
 2209        .unindent(),
 2210    );
 2211
 2212    cx.update_editor(|editor, window, cx| {
 2213        editor.move_page_down(&MovePageDown::default(), window, cx)
 2214    });
 2215    cx.assert_editor_state(
 2216        &r#"
 2217        one
 2218        two
 2219        three
 2220        four
 2221        five
 2222        six
 2223        ˇseven
 2224        eight
 2225        nineˇ
 2226        ten
 2227        "#
 2228        .unindent(),
 2229    );
 2230
 2231    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2232    cx.assert_editor_state(
 2233        &r#"
 2234        one
 2235        two
 2236        three
 2237        ˇfour
 2238        five
 2239        sixˇ
 2240        seven
 2241        eight
 2242        nine
 2243        ten
 2244        "#
 2245        .unindent(),
 2246    );
 2247
 2248    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2249    cx.assert_editor_state(
 2250        &r#"
 2251        ˇone
 2252        two
 2253        threeˇ
 2254        four
 2255        five
 2256        six
 2257        seven
 2258        eight
 2259        nine
 2260        ten
 2261        "#
 2262        .unindent(),
 2263    );
 2264
 2265    // Test select collapsing
 2266    cx.update_editor(|editor, window, cx| {
 2267        editor.move_page_down(&MovePageDown::default(), window, cx);
 2268        editor.move_page_down(&MovePageDown::default(), window, cx);
 2269        editor.move_page_down(&MovePageDown::default(), window, cx);
 2270    });
 2271    cx.assert_editor_state(
 2272        &r#"
 2273        one
 2274        two
 2275        three
 2276        four
 2277        five
 2278        six
 2279        seven
 2280        eight
 2281        nine
 2282        ˇten
 2283        ˇ"#
 2284        .unindent(),
 2285    );
 2286}
 2287
 2288#[gpui::test]
 2289async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2290    init_test(cx, |_| {});
 2291    let mut cx = EditorTestContext::new(cx).await;
 2292    cx.set_state("one «two threeˇ» four");
 2293    cx.update_editor(|editor, window, cx| {
 2294        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2295        assert_eq!(editor.text(cx), " four");
 2296    });
 2297}
 2298
 2299#[gpui::test]
 2300fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2301    init_test(cx, |_| {});
 2302
 2303    let editor = cx.add_window(|window, cx| {
 2304        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2305        build_editor(buffer.clone(), window, cx)
 2306    });
 2307
 2308    _ = editor.update(cx, |editor, window, cx| {
 2309        editor.change_selections(None, window, cx, |s| {
 2310            s.select_display_ranges([
 2311                // an empty selection - the preceding word fragment is deleted
 2312                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2313                // characters selected - they are deleted
 2314                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2315            ])
 2316        });
 2317        editor.delete_to_previous_word_start(
 2318            &DeleteToPreviousWordStart {
 2319                ignore_newlines: false,
 2320            },
 2321            window,
 2322            cx,
 2323        );
 2324        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2325    });
 2326
 2327    _ = editor.update(cx, |editor, window, cx| {
 2328        editor.change_selections(None, window, cx, |s| {
 2329            s.select_display_ranges([
 2330                // an empty selection - the following word fragment is deleted
 2331                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2332                // characters selected - they are deleted
 2333                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2334            ])
 2335        });
 2336        editor.delete_to_next_word_end(
 2337            &DeleteToNextWordEnd {
 2338                ignore_newlines: false,
 2339            },
 2340            window,
 2341            cx,
 2342        );
 2343        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2344    });
 2345}
 2346
 2347#[gpui::test]
 2348fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2349    init_test(cx, |_| {});
 2350
 2351    let editor = cx.add_window(|window, cx| {
 2352        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2353        build_editor(buffer.clone(), window, cx)
 2354    });
 2355    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2356        ignore_newlines: false,
 2357    };
 2358    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2359        ignore_newlines: true,
 2360    };
 2361
 2362    _ = editor.update(cx, |editor, window, cx| {
 2363        editor.change_selections(None, window, cx, |s| {
 2364            s.select_display_ranges([
 2365                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2366            ])
 2367        });
 2368        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2369        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2370        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2371        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2372        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2373        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2374        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2375        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2376        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2377        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2378        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2379        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2380    });
 2381}
 2382
 2383#[gpui::test]
 2384fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2385    init_test(cx, |_| {});
 2386
 2387    let editor = cx.add_window(|window, cx| {
 2388        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2389        build_editor(buffer.clone(), window, cx)
 2390    });
 2391    let del_to_next_word_end = DeleteToNextWordEnd {
 2392        ignore_newlines: false,
 2393    };
 2394    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2395        ignore_newlines: true,
 2396    };
 2397
 2398    _ = editor.update(cx, |editor, window, cx| {
 2399        editor.change_selections(None, window, cx, |s| {
 2400            s.select_display_ranges([
 2401                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2402            ])
 2403        });
 2404        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2405        assert_eq!(
 2406            editor.buffer.read(cx).read(cx).text(),
 2407            "one\n   two\nthree\n   four"
 2408        );
 2409        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2410        assert_eq!(
 2411            editor.buffer.read(cx).read(cx).text(),
 2412            "\n   two\nthree\n   four"
 2413        );
 2414        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2415        assert_eq!(
 2416            editor.buffer.read(cx).read(cx).text(),
 2417            "two\nthree\n   four"
 2418        );
 2419        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2420        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2421        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2422        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2423        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2424        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2425    });
 2426}
 2427
 2428#[gpui::test]
 2429fn test_newline(cx: &mut TestAppContext) {
 2430    init_test(cx, |_| {});
 2431
 2432    let editor = cx.add_window(|window, cx| {
 2433        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2434        build_editor(buffer.clone(), window, cx)
 2435    });
 2436
 2437    _ = editor.update(cx, |editor, window, cx| {
 2438        editor.change_selections(None, window, cx, |s| {
 2439            s.select_display_ranges([
 2440                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2441                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2442                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2443            ])
 2444        });
 2445
 2446        editor.newline(&Newline, window, cx);
 2447        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2448    });
 2449}
 2450
 2451#[gpui::test]
 2452fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2453    init_test(cx, |_| {});
 2454
 2455    let editor = cx.add_window(|window, cx| {
 2456        let buffer = MultiBuffer::build_simple(
 2457            "
 2458                a
 2459                b(
 2460                    X
 2461                )
 2462                c(
 2463                    X
 2464                )
 2465            "
 2466            .unindent()
 2467            .as_str(),
 2468            cx,
 2469        );
 2470        let mut editor = build_editor(buffer.clone(), window, cx);
 2471        editor.change_selections(None, window, cx, |s| {
 2472            s.select_ranges([
 2473                Point::new(2, 4)..Point::new(2, 5),
 2474                Point::new(5, 4)..Point::new(5, 5),
 2475            ])
 2476        });
 2477        editor
 2478    });
 2479
 2480    _ = editor.update(cx, |editor, window, cx| {
 2481        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2482        editor.buffer.update(cx, |buffer, cx| {
 2483            buffer.edit(
 2484                [
 2485                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2486                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2487                ],
 2488                None,
 2489                cx,
 2490            );
 2491            assert_eq!(
 2492                buffer.read(cx).text(),
 2493                "
 2494                    a
 2495                    b()
 2496                    c()
 2497                "
 2498                .unindent()
 2499            );
 2500        });
 2501        assert_eq!(
 2502            editor.selections.ranges(cx),
 2503            &[
 2504                Point::new(1, 2)..Point::new(1, 2),
 2505                Point::new(2, 2)..Point::new(2, 2),
 2506            ],
 2507        );
 2508
 2509        editor.newline(&Newline, window, cx);
 2510        assert_eq!(
 2511            editor.text(cx),
 2512            "
 2513                a
 2514                b(
 2515                )
 2516                c(
 2517                )
 2518            "
 2519            .unindent()
 2520        );
 2521
 2522        // The selections are moved after the inserted newlines
 2523        assert_eq!(
 2524            editor.selections.ranges(cx),
 2525            &[
 2526                Point::new(2, 0)..Point::new(2, 0),
 2527                Point::new(4, 0)..Point::new(4, 0),
 2528            ],
 2529        );
 2530    });
 2531}
 2532
 2533#[gpui::test]
 2534async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2535    init_test(cx, |settings| {
 2536        settings.defaults.tab_size = NonZeroU32::new(4)
 2537    });
 2538
 2539    let language = Arc::new(
 2540        Language::new(
 2541            LanguageConfig::default(),
 2542            Some(tree_sitter_rust::LANGUAGE.into()),
 2543        )
 2544        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2545        .unwrap(),
 2546    );
 2547
 2548    let mut cx = EditorTestContext::new(cx).await;
 2549    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2550    cx.set_state(indoc! {"
 2551        const a: ˇA = (
 2552 2553                «const_functionˇ»(ˇ),
 2554                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2555 2556        ˇ);ˇ
 2557    "});
 2558
 2559    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2560    cx.assert_editor_state(indoc! {"
 2561        ˇ
 2562        const a: A = (
 2563            ˇ
 2564            (
 2565                ˇ
 2566                ˇ
 2567                const_function(),
 2568                ˇ
 2569                ˇ
 2570                ˇ
 2571                ˇ
 2572                something_else,
 2573                ˇ
 2574            )
 2575            ˇ
 2576            ˇ
 2577        );
 2578    "});
 2579}
 2580
 2581#[gpui::test]
 2582async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2583    init_test(cx, |settings| {
 2584        settings.defaults.tab_size = NonZeroU32::new(4)
 2585    });
 2586
 2587    let language = Arc::new(
 2588        Language::new(
 2589            LanguageConfig::default(),
 2590            Some(tree_sitter_rust::LANGUAGE.into()),
 2591        )
 2592        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2593        .unwrap(),
 2594    );
 2595
 2596    let mut cx = EditorTestContext::new(cx).await;
 2597    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2598    cx.set_state(indoc! {"
 2599        const a: ˇA = (
 2600 2601                «const_functionˇ»(ˇ),
 2602                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2603 2604        ˇ);ˇ
 2605    "});
 2606
 2607    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2608    cx.assert_editor_state(indoc! {"
 2609        const a: A = (
 2610            ˇ
 2611            (
 2612                ˇ
 2613                const_function(),
 2614                ˇ
 2615                ˇ
 2616                something_else,
 2617                ˇ
 2618                ˇ
 2619                ˇ
 2620                ˇ
 2621            )
 2622            ˇ
 2623        );
 2624        ˇ
 2625        ˇ
 2626    "});
 2627}
 2628
 2629#[gpui::test]
 2630async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2631    init_test(cx, |settings| {
 2632        settings.defaults.tab_size = NonZeroU32::new(4)
 2633    });
 2634
 2635    let language = Arc::new(Language::new(
 2636        LanguageConfig {
 2637            line_comments: vec!["//".into()],
 2638            ..LanguageConfig::default()
 2639        },
 2640        None,
 2641    ));
 2642    {
 2643        let mut cx = EditorTestContext::new(cx).await;
 2644        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2645        cx.set_state(indoc! {"
 2646        // Fooˇ
 2647    "});
 2648
 2649        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2650        cx.assert_editor_state(indoc! {"
 2651        // Foo
 2652        //ˇ
 2653    "});
 2654        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2655        cx.set_state(indoc! {"
 2656        ˇ// Foo
 2657    "});
 2658        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2659        cx.assert_editor_state(indoc! {"
 2660
 2661        ˇ// Foo
 2662    "});
 2663    }
 2664    // Ensure that comment continuations can be disabled.
 2665    update_test_language_settings(cx, |settings| {
 2666        settings.defaults.extend_comment_on_newline = Some(false);
 2667    });
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.set_state(indoc! {"
 2670        // Fooˇ
 2671    "});
 2672    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2673    cx.assert_editor_state(indoc! {"
 2674        // Foo
 2675        ˇ
 2676    "});
 2677}
 2678
 2679#[gpui::test]
 2680fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2681    init_test(cx, |_| {});
 2682
 2683    let editor = cx.add_window(|window, cx| {
 2684        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2685        let mut editor = build_editor(buffer.clone(), window, cx);
 2686        editor.change_selections(None, window, cx, |s| {
 2687            s.select_ranges([3..4, 11..12, 19..20])
 2688        });
 2689        editor
 2690    });
 2691
 2692    _ = editor.update(cx, |editor, window, cx| {
 2693        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2694        editor.buffer.update(cx, |buffer, cx| {
 2695            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2696            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2697        });
 2698        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2699
 2700        editor.insert("Z", window, cx);
 2701        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2702
 2703        // The selections are moved after the inserted characters
 2704        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2705    });
 2706}
 2707
 2708#[gpui::test]
 2709async fn test_tab(cx: &mut gpui::TestAppContext) {
 2710    init_test(cx, |settings| {
 2711        settings.defaults.tab_size = NonZeroU32::new(3)
 2712    });
 2713
 2714    let mut cx = EditorTestContext::new(cx).await;
 2715    cx.set_state(indoc! {"
 2716        ˇabˇc
 2717        ˇ🏀ˇ🏀ˇefg
 2718 2719    "});
 2720    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2721    cx.assert_editor_state(indoc! {"
 2722           ˇab ˇc
 2723           ˇ🏀  ˇ🏀  ˇefg
 2724        d  ˇ
 2725    "});
 2726
 2727    cx.set_state(indoc! {"
 2728        a
 2729        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2730    "});
 2731    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2732    cx.assert_editor_state(indoc! {"
 2733        a
 2734           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2735    "});
 2736}
 2737
 2738#[gpui::test]
 2739async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2740    init_test(cx, |_| {});
 2741
 2742    let mut cx = EditorTestContext::new(cx).await;
 2743    let language = Arc::new(
 2744        Language::new(
 2745            LanguageConfig::default(),
 2746            Some(tree_sitter_rust::LANGUAGE.into()),
 2747        )
 2748        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2749        .unwrap(),
 2750    );
 2751    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2752
 2753    // cursors that are already at the suggested indent level insert
 2754    // a soft tab. cursors that are to the left of the suggested indent
 2755    // auto-indent their line.
 2756    cx.set_state(indoc! {"
 2757        ˇ
 2758        const a: B = (
 2759            c(
 2760                d(
 2761        ˇ
 2762                )
 2763        ˇ
 2764        ˇ    )
 2765        );
 2766    "});
 2767    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2768    cx.assert_editor_state(indoc! {"
 2769            ˇ
 2770        const a: B = (
 2771            c(
 2772                d(
 2773                    ˇ
 2774                )
 2775                ˇ
 2776            ˇ)
 2777        );
 2778    "});
 2779
 2780    // handle auto-indent when there are multiple cursors on the same line
 2781    cx.set_state(indoc! {"
 2782        const a: B = (
 2783            c(
 2784        ˇ    ˇ
 2785        ˇ    )
 2786        );
 2787    "});
 2788    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        const a: B = (
 2791            c(
 2792                ˇ
 2793            ˇ)
 2794        );
 2795    "});
 2796}
 2797
 2798#[gpui::test]
 2799async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2800    init_test(cx, |settings| {
 2801        settings.defaults.tab_size = NonZeroU32::new(4)
 2802    });
 2803
 2804    let language = Arc::new(
 2805        Language::new(
 2806            LanguageConfig::default(),
 2807            Some(tree_sitter_rust::LANGUAGE.into()),
 2808        )
 2809        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2810        .unwrap(),
 2811    );
 2812
 2813    let mut cx = EditorTestContext::new(cx).await;
 2814    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2815    cx.set_state(indoc! {"
 2816        fn a() {
 2817            if b {
 2818        \t ˇc
 2819            }
 2820        }
 2821    "});
 2822
 2823    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2824    cx.assert_editor_state(indoc! {"
 2825        fn a() {
 2826            if b {
 2827                ˇc
 2828            }
 2829        }
 2830    "});
 2831}
 2832
 2833#[gpui::test]
 2834async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2835    init_test(cx, |settings| {
 2836        settings.defaults.tab_size = NonZeroU32::new(4);
 2837    });
 2838
 2839    let mut cx = EditorTestContext::new(cx).await;
 2840
 2841    cx.set_state(indoc! {"
 2842          «oneˇ» «twoˇ»
 2843        three
 2844         four
 2845    "});
 2846    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2847    cx.assert_editor_state(indoc! {"
 2848            «oneˇ» «twoˇ»
 2849        three
 2850         four
 2851    "});
 2852
 2853    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        «oneˇ» «twoˇ»
 2856        three
 2857         four
 2858    "});
 2859
 2860    // select across line ending
 2861    cx.set_state(indoc! {"
 2862        one two
 2863        t«hree
 2864        ˇ» four
 2865    "});
 2866    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2867    cx.assert_editor_state(indoc! {"
 2868        one two
 2869            t«hree
 2870        ˇ» four
 2871    "});
 2872
 2873    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2874    cx.assert_editor_state(indoc! {"
 2875        one two
 2876        t«hree
 2877        ˇ» four
 2878    "});
 2879
 2880    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2881    cx.set_state(indoc! {"
 2882        one two
 2883        ˇthree
 2884            four
 2885    "});
 2886    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2887    cx.assert_editor_state(indoc! {"
 2888        one two
 2889            ˇthree
 2890            four
 2891    "});
 2892
 2893    cx.set_state(indoc! {"
 2894        one two
 2895        ˇ    three
 2896            four
 2897    "});
 2898    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2899    cx.assert_editor_state(indoc! {"
 2900        one two
 2901        ˇthree
 2902            four
 2903    "});
 2904}
 2905
 2906#[gpui::test]
 2907async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2908    init_test(cx, |settings| {
 2909        settings.defaults.hard_tabs = Some(true);
 2910    });
 2911
 2912    let mut cx = EditorTestContext::new(cx).await;
 2913
 2914    // select two ranges on one line
 2915    cx.set_state(indoc! {"
 2916        «oneˇ» «twoˇ»
 2917        three
 2918        four
 2919    "});
 2920    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2921    cx.assert_editor_state(indoc! {"
 2922        \t«oneˇ» «twoˇ»
 2923        three
 2924        four
 2925    "});
 2926    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2927    cx.assert_editor_state(indoc! {"
 2928        \t\t«oneˇ» «twoˇ»
 2929        three
 2930        four
 2931    "});
 2932    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2933    cx.assert_editor_state(indoc! {"
 2934        \t«oneˇ» «twoˇ»
 2935        three
 2936        four
 2937    "});
 2938    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2939    cx.assert_editor_state(indoc! {"
 2940        «oneˇ» «twoˇ»
 2941        three
 2942        four
 2943    "});
 2944
 2945    // select across a line ending
 2946    cx.set_state(indoc! {"
 2947        one two
 2948        t«hree
 2949        ˇ»four
 2950    "});
 2951    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2952    cx.assert_editor_state(indoc! {"
 2953        one two
 2954        \tt«hree
 2955        ˇ»four
 2956    "});
 2957    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2958    cx.assert_editor_state(indoc! {"
 2959        one two
 2960        \t\tt«hree
 2961        ˇ»four
 2962    "});
 2963    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2964    cx.assert_editor_state(indoc! {"
 2965        one two
 2966        \tt«hree
 2967        ˇ»four
 2968    "});
 2969    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2970    cx.assert_editor_state(indoc! {"
 2971        one two
 2972        t«hree
 2973        ˇ»four
 2974    "});
 2975
 2976    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2977    cx.set_state(indoc! {"
 2978        one two
 2979        ˇthree
 2980        four
 2981    "});
 2982    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2983    cx.assert_editor_state(indoc! {"
 2984        one two
 2985        ˇthree
 2986        four
 2987    "});
 2988    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2989    cx.assert_editor_state(indoc! {"
 2990        one two
 2991        \tˇthree
 2992        four
 2993    "});
 2994    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2995    cx.assert_editor_state(indoc! {"
 2996        one two
 2997        ˇthree
 2998        four
 2999    "});
 3000}
 3001
 3002#[gpui::test]
 3003fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3004    init_test(cx, |settings| {
 3005        settings.languages.extend([
 3006            (
 3007                "TOML".into(),
 3008                LanguageSettingsContent {
 3009                    tab_size: NonZeroU32::new(2),
 3010                    ..Default::default()
 3011                },
 3012            ),
 3013            (
 3014                "Rust".into(),
 3015                LanguageSettingsContent {
 3016                    tab_size: NonZeroU32::new(4),
 3017                    ..Default::default()
 3018                },
 3019            ),
 3020        ]);
 3021    });
 3022
 3023    let toml_language = Arc::new(Language::new(
 3024        LanguageConfig {
 3025            name: "TOML".into(),
 3026            ..Default::default()
 3027        },
 3028        None,
 3029    ));
 3030    let rust_language = Arc::new(Language::new(
 3031        LanguageConfig {
 3032            name: "Rust".into(),
 3033            ..Default::default()
 3034        },
 3035        None,
 3036    ));
 3037
 3038    let toml_buffer =
 3039        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3040    let rust_buffer =
 3041        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3042    let multibuffer = cx.new(|cx| {
 3043        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3044        multibuffer.push_excerpts(
 3045            toml_buffer.clone(),
 3046            [ExcerptRange {
 3047                context: Point::new(0, 0)..Point::new(2, 0),
 3048                primary: None,
 3049            }],
 3050            cx,
 3051        );
 3052        multibuffer.push_excerpts(
 3053            rust_buffer.clone(),
 3054            [ExcerptRange {
 3055                context: Point::new(0, 0)..Point::new(1, 0),
 3056                primary: None,
 3057            }],
 3058            cx,
 3059        );
 3060        multibuffer
 3061    });
 3062
 3063    cx.add_window(|window, cx| {
 3064        let mut editor = build_editor(multibuffer, window, cx);
 3065
 3066        assert_eq!(
 3067            editor.text(cx),
 3068            indoc! {"
 3069                a = 1
 3070                b = 2
 3071
 3072                const c: usize = 3;
 3073            "}
 3074        );
 3075
 3076        select_ranges(
 3077            &mut editor,
 3078            indoc! {"
 3079                «aˇ» = 1
 3080                b = 2
 3081
 3082                «const c:ˇ» usize = 3;
 3083            "},
 3084            window,
 3085            cx,
 3086        );
 3087
 3088        editor.tab(&Tab, window, cx);
 3089        assert_text_with_selections(
 3090            &mut editor,
 3091            indoc! {"
 3092                  «aˇ» = 1
 3093                b = 2
 3094
 3095                    «const c:ˇ» usize = 3;
 3096            "},
 3097            cx,
 3098        );
 3099        editor.tab_prev(&TabPrev, window, cx);
 3100        assert_text_with_selections(
 3101            &mut editor,
 3102            indoc! {"
 3103                «aˇ» = 1
 3104                b = 2
 3105
 3106                «const c:ˇ» usize = 3;
 3107            "},
 3108            cx,
 3109        );
 3110
 3111        editor
 3112    });
 3113}
 3114
 3115#[gpui::test]
 3116async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3117    init_test(cx, |_| {});
 3118
 3119    let mut cx = EditorTestContext::new(cx).await;
 3120
 3121    // Basic backspace
 3122    cx.set_state(indoc! {"
 3123        onˇe two three
 3124        fou«rˇ» five six
 3125        seven «ˇeight nine
 3126        »ten
 3127    "});
 3128    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3129    cx.assert_editor_state(indoc! {"
 3130        oˇe two three
 3131        fouˇ five six
 3132        seven ˇten
 3133    "});
 3134
 3135    // Test backspace inside and around indents
 3136    cx.set_state(indoc! {"
 3137        zero
 3138            ˇone
 3139                ˇtwo
 3140            ˇ ˇ ˇ  three
 3141        ˇ  ˇ  four
 3142    "});
 3143    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3144    cx.assert_editor_state(indoc! {"
 3145        zero
 3146        ˇone
 3147            ˇtwo
 3148        ˇ  threeˇ  four
 3149    "});
 3150
 3151    // Test backspace with line_mode set to true
 3152    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3153    cx.set_state(indoc! {"
 3154        The ˇquick ˇbrown
 3155        fox jumps over
 3156        the lazy dog
 3157        ˇThe qu«ick bˇ»rown"});
 3158    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3159    cx.assert_editor_state(indoc! {"
 3160        ˇfox jumps over
 3161        the lazy dogˇ"});
 3162}
 3163
 3164#[gpui::test]
 3165async fn test_delete(cx: &mut gpui::TestAppContext) {
 3166    init_test(cx, |_| {});
 3167
 3168    let mut cx = EditorTestContext::new(cx).await;
 3169    cx.set_state(indoc! {"
 3170        onˇe two three
 3171        fou«rˇ» five six
 3172        seven «ˇeight nine
 3173        »ten
 3174    "});
 3175    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3176    cx.assert_editor_state(indoc! {"
 3177        onˇ two three
 3178        fouˇ five six
 3179        seven ˇten
 3180    "});
 3181
 3182    // Test backspace with line_mode set to true
 3183    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3184    cx.set_state(indoc! {"
 3185        The ˇquick ˇbrown
 3186        fox «ˇjum»ps over
 3187        the lazy dog
 3188        ˇThe qu«ick bˇ»rown"});
 3189    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3190    cx.assert_editor_state("ˇthe lazy dogˇ");
 3191}
 3192
 3193#[gpui::test]
 3194fn test_delete_line(cx: &mut TestAppContext) {
 3195    init_test(cx, |_| {});
 3196
 3197    let editor = cx.add_window(|window, cx| {
 3198        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3199        build_editor(buffer, window, cx)
 3200    });
 3201    _ = editor.update(cx, |editor, window, cx| {
 3202        editor.change_selections(None, window, cx, |s| {
 3203            s.select_display_ranges([
 3204                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3205                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3206                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3207            ])
 3208        });
 3209        editor.delete_line(&DeleteLine, window, cx);
 3210        assert_eq!(editor.display_text(cx), "ghi");
 3211        assert_eq!(
 3212            editor.selections.display_ranges(cx),
 3213            vec![
 3214                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3215                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3216            ]
 3217        );
 3218    });
 3219
 3220    let editor = cx.add_window(|window, cx| {
 3221        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3222        build_editor(buffer, window, cx)
 3223    });
 3224    _ = editor.update(cx, |editor, window, cx| {
 3225        editor.change_selections(None, window, cx, |s| {
 3226            s.select_display_ranges([
 3227                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3228            ])
 3229        });
 3230        editor.delete_line(&DeleteLine, window, cx);
 3231        assert_eq!(editor.display_text(cx), "ghi\n");
 3232        assert_eq!(
 3233            editor.selections.display_ranges(cx),
 3234            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3235        );
 3236    });
 3237}
 3238
 3239#[gpui::test]
 3240fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3241    init_test(cx, |_| {});
 3242
 3243    cx.add_window(|window, cx| {
 3244        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3245        let mut editor = build_editor(buffer.clone(), window, cx);
 3246        let buffer = buffer.read(cx).as_singleton().unwrap();
 3247
 3248        assert_eq!(
 3249            editor.selections.ranges::<Point>(cx),
 3250            &[Point::new(0, 0)..Point::new(0, 0)]
 3251        );
 3252
 3253        // When on single line, replace newline at end by space
 3254        editor.join_lines(&JoinLines, window, cx);
 3255        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3256        assert_eq!(
 3257            editor.selections.ranges::<Point>(cx),
 3258            &[Point::new(0, 3)..Point::new(0, 3)]
 3259        );
 3260
 3261        // When multiple lines are selected, remove newlines that are spanned by the selection
 3262        editor.change_selections(None, window, cx, |s| {
 3263            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3264        });
 3265        editor.join_lines(&JoinLines, window, cx);
 3266        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3267        assert_eq!(
 3268            editor.selections.ranges::<Point>(cx),
 3269            &[Point::new(0, 11)..Point::new(0, 11)]
 3270        );
 3271
 3272        // Undo should be transactional
 3273        editor.undo(&Undo, window, cx);
 3274        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3275        assert_eq!(
 3276            editor.selections.ranges::<Point>(cx),
 3277            &[Point::new(0, 5)..Point::new(2, 2)]
 3278        );
 3279
 3280        // When joining an empty line don't insert a space
 3281        editor.change_selections(None, window, cx, |s| {
 3282            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3283        });
 3284        editor.join_lines(&JoinLines, window, cx);
 3285        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3286        assert_eq!(
 3287            editor.selections.ranges::<Point>(cx),
 3288            [Point::new(2, 3)..Point::new(2, 3)]
 3289        );
 3290
 3291        // We can remove trailing newlines
 3292        editor.join_lines(&JoinLines, window, cx);
 3293        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3294        assert_eq!(
 3295            editor.selections.ranges::<Point>(cx),
 3296            [Point::new(2, 3)..Point::new(2, 3)]
 3297        );
 3298
 3299        // We don't blow up on the last line
 3300        editor.join_lines(&JoinLines, window, cx);
 3301        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3302        assert_eq!(
 3303            editor.selections.ranges::<Point>(cx),
 3304            [Point::new(2, 3)..Point::new(2, 3)]
 3305        );
 3306
 3307        // reset to test indentation
 3308        editor.buffer.update(cx, |buffer, cx| {
 3309            buffer.edit(
 3310                [
 3311                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3312                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3313                ],
 3314                None,
 3315                cx,
 3316            )
 3317        });
 3318
 3319        // We remove any leading spaces
 3320        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3321        editor.change_selections(None, window, cx, |s| {
 3322            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3323        });
 3324        editor.join_lines(&JoinLines, window, cx);
 3325        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3326
 3327        // We don't insert a space for a line containing only spaces
 3328        editor.join_lines(&JoinLines, window, cx);
 3329        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3330
 3331        // We ignore any leading tabs
 3332        editor.join_lines(&JoinLines, window, cx);
 3333        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3334
 3335        editor
 3336    });
 3337}
 3338
 3339#[gpui::test]
 3340fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3341    init_test(cx, |_| {});
 3342
 3343    cx.add_window(|window, cx| {
 3344        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3345        let mut editor = build_editor(buffer.clone(), window, cx);
 3346        let buffer = buffer.read(cx).as_singleton().unwrap();
 3347
 3348        editor.change_selections(None, window, cx, |s| {
 3349            s.select_ranges([
 3350                Point::new(0, 2)..Point::new(1, 1),
 3351                Point::new(1, 2)..Point::new(1, 2),
 3352                Point::new(3, 1)..Point::new(3, 2),
 3353            ])
 3354        });
 3355
 3356        editor.join_lines(&JoinLines, window, cx);
 3357        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3358
 3359        assert_eq!(
 3360            editor.selections.ranges::<Point>(cx),
 3361            [
 3362                Point::new(0, 7)..Point::new(0, 7),
 3363                Point::new(1, 3)..Point::new(1, 3)
 3364            ]
 3365        );
 3366        editor
 3367    });
 3368}
 3369
 3370#[gpui::test]
 3371async fn test_join_lines_with_git_diff_base(
 3372    executor: BackgroundExecutor,
 3373    cx: &mut gpui::TestAppContext,
 3374) {
 3375    init_test(cx, |_| {});
 3376
 3377    let mut cx = EditorTestContext::new(cx).await;
 3378
 3379    let diff_base = r#"
 3380        Line 0
 3381        Line 1
 3382        Line 2
 3383        Line 3
 3384        "#
 3385    .unindent();
 3386
 3387    cx.set_state(
 3388        &r#"
 3389        ˇLine 0
 3390        Line 1
 3391        Line 2
 3392        Line 3
 3393        "#
 3394        .unindent(),
 3395    );
 3396
 3397    cx.set_diff_base(&diff_base);
 3398    executor.run_until_parked();
 3399
 3400    // Join lines
 3401    cx.update_editor(|editor, window, cx| {
 3402        editor.join_lines(&JoinLines, window, cx);
 3403    });
 3404    executor.run_until_parked();
 3405
 3406    cx.assert_editor_state(
 3407        &r#"
 3408        Line 0ˇ Line 1
 3409        Line 2
 3410        Line 3
 3411        "#
 3412        .unindent(),
 3413    );
 3414    // Join again
 3415    cx.update_editor(|editor, window, cx| {
 3416        editor.join_lines(&JoinLines, window, cx);
 3417    });
 3418    executor.run_until_parked();
 3419
 3420    cx.assert_editor_state(
 3421        &r#"
 3422        Line 0 Line 1ˇ Line 2
 3423        Line 3
 3424        "#
 3425        .unindent(),
 3426    );
 3427}
 3428
 3429#[gpui::test]
 3430async fn test_custom_newlines_cause_no_false_positive_diffs(
 3431    executor: BackgroundExecutor,
 3432    cx: &mut gpui::TestAppContext,
 3433) {
 3434    init_test(cx, |_| {});
 3435    let mut cx = EditorTestContext::new(cx).await;
 3436    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3437    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3438    executor.run_until_parked();
 3439
 3440    cx.update_editor(|editor, window, cx| {
 3441        let snapshot = editor.snapshot(window, cx);
 3442        assert_eq!(
 3443            snapshot
 3444                .buffer_snapshot
 3445                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3446                .collect::<Vec<_>>(),
 3447            Vec::new(),
 3448            "Should not have any diffs for files with custom newlines"
 3449        );
 3450    });
 3451}
 3452
 3453#[gpui::test]
 3454async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3455    init_test(cx, |_| {});
 3456
 3457    let mut cx = EditorTestContext::new(cx).await;
 3458
 3459    // Test sort_lines_case_insensitive()
 3460    cx.set_state(indoc! {"
 3461        «z
 3462        y
 3463        x
 3464        Z
 3465        Y
 3466        Xˇ»
 3467    "});
 3468    cx.update_editor(|e, window, cx| {
 3469        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3470    });
 3471    cx.assert_editor_state(indoc! {"
 3472        «x
 3473        X
 3474        y
 3475        Y
 3476        z
 3477        Zˇ»
 3478    "});
 3479
 3480    // Test reverse_lines()
 3481    cx.set_state(indoc! {"
 3482        «5
 3483        4
 3484        3
 3485        2
 3486        1ˇ»
 3487    "});
 3488    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3489    cx.assert_editor_state(indoc! {"
 3490        «1
 3491        2
 3492        3
 3493        4
 3494        5ˇ»
 3495    "});
 3496
 3497    // Skip testing shuffle_line()
 3498
 3499    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3500    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3501
 3502    // Don't manipulate when cursor is on single line, but expand the selection
 3503    cx.set_state(indoc! {"
 3504        ddˇdd
 3505        ccc
 3506        bb
 3507        a
 3508    "});
 3509    cx.update_editor(|e, window, cx| {
 3510        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3511    });
 3512    cx.assert_editor_state(indoc! {"
 3513        «ddddˇ»
 3514        ccc
 3515        bb
 3516        a
 3517    "});
 3518
 3519    // Basic manipulate case
 3520    // Start selection moves to column 0
 3521    // End of selection shrinks to fit shorter line
 3522    cx.set_state(indoc! {"
 3523        dd«d
 3524        ccc
 3525        bb
 3526        aaaaaˇ»
 3527    "});
 3528    cx.update_editor(|e, window, cx| {
 3529        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3530    });
 3531    cx.assert_editor_state(indoc! {"
 3532        «aaaaa
 3533        bb
 3534        ccc
 3535        dddˇ»
 3536    "});
 3537
 3538    // Manipulate case with newlines
 3539    cx.set_state(indoc! {"
 3540        dd«d
 3541        ccc
 3542
 3543        bb
 3544        aaaaa
 3545
 3546        ˇ»
 3547    "});
 3548    cx.update_editor(|e, window, cx| {
 3549        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3550    });
 3551    cx.assert_editor_state(indoc! {"
 3552        «
 3553
 3554        aaaaa
 3555        bb
 3556        ccc
 3557        dddˇ»
 3558
 3559    "});
 3560
 3561    // Adding new line
 3562    cx.set_state(indoc! {"
 3563        aa«a
 3564        bbˇ»b
 3565    "});
 3566    cx.update_editor(|e, window, cx| {
 3567        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3568    });
 3569    cx.assert_editor_state(indoc! {"
 3570        «aaa
 3571        bbb
 3572        added_lineˇ»
 3573    "});
 3574
 3575    // Removing line
 3576    cx.set_state(indoc! {"
 3577        aa«a
 3578        bbbˇ»
 3579    "});
 3580    cx.update_editor(|e, window, cx| {
 3581        e.manipulate_lines(window, cx, |lines| {
 3582            lines.pop();
 3583        })
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «aaaˇ»
 3587    "});
 3588
 3589    // Removing all lines
 3590    cx.set_state(indoc! {"
 3591        aa«a
 3592        bbbˇ»
 3593    "});
 3594    cx.update_editor(|e, window, cx| {
 3595        e.manipulate_lines(window, cx, |lines| {
 3596            lines.drain(..);
 3597        })
 3598    });
 3599    cx.assert_editor_state(indoc! {"
 3600        ˇ
 3601    "});
 3602}
 3603
 3604#[gpui::test]
 3605async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3606    init_test(cx, |_| {});
 3607
 3608    let mut cx = EditorTestContext::new(cx).await;
 3609
 3610    // Consider continuous selection as single selection
 3611    cx.set_state(indoc! {"
 3612        Aaa«aa
 3613        cˇ»c«c
 3614        bb
 3615        aaaˇ»aa
 3616    "});
 3617    cx.update_editor(|e, window, cx| {
 3618        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3619    });
 3620    cx.assert_editor_state(indoc! {"
 3621        «Aaaaa
 3622        ccc
 3623        bb
 3624        aaaaaˇ»
 3625    "});
 3626
 3627    cx.set_state(indoc! {"
 3628        Aaa«aa
 3629        cˇ»c«c
 3630        bb
 3631        aaaˇ»aa
 3632    "});
 3633    cx.update_editor(|e, window, cx| {
 3634        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3635    });
 3636    cx.assert_editor_state(indoc! {"
 3637        «Aaaaa
 3638        ccc
 3639        bbˇ»
 3640    "});
 3641
 3642    // Consider non continuous selection as distinct dedup operations
 3643    cx.set_state(indoc! {"
 3644        «aaaaa
 3645        bb
 3646        aaaaa
 3647        aaaaaˇ»
 3648
 3649        aaa«aaˇ»
 3650    "});
 3651    cx.update_editor(|e, window, cx| {
 3652        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3653    });
 3654    cx.assert_editor_state(indoc! {"
 3655        «aaaaa
 3656        bbˇ»
 3657
 3658        «aaaaaˇ»
 3659    "});
 3660}
 3661
 3662#[gpui::test]
 3663async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3664    init_test(cx, |_| {});
 3665
 3666    let mut cx = EditorTestContext::new(cx).await;
 3667
 3668    cx.set_state(indoc! {"
 3669        «Aaa
 3670        aAa
 3671        Aaaˇ»
 3672    "});
 3673    cx.update_editor(|e, window, cx| {
 3674        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «Aaa
 3678        aAaˇ»
 3679    "});
 3680
 3681    cx.set_state(indoc! {"
 3682        «Aaa
 3683        aAa
 3684        aaAˇ»
 3685    "});
 3686    cx.update_editor(|e, window, cx| {
 3687        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3688    });
 3689    cx.assert_editor_state(indoc! {"
 3690        «Aaaˇ»
 3691    "});
 3692}
 3693
 3694#[gpui::test]
 3695async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3696    init_test(cx, |_| {});
 3697
 3698    let mut cx = EditorTestContext::new(cx).await;
 3699
 3700    // Manipulate with multiple selections on a single line
 3701    cx.set_state(indoc! {"
 3702        dd«dd
 3703        cˇ»c«c
 3704        bb
 3705        aaaˇ»aa
 3706    "});
 3707    cx.update_editor(|e, window, cx| {
 3708        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3709    });
 3710    cx.assert_editor_state(indoc! {"
 3711        «aaaaa
 3712        bb
 3713        ccc
 3714        ddddˇ»
 3715    "});
 3716
 3717    // Manipulate with multiple disjoin selections
 3718    cx.set_state(indoc! {"
 3719 3720        4
 3721        3
 3722        2
 3723        1ˇ»
 3724
 3725        dd«dd
 3726        ccc
 3727        bb
 3728        aaaˇ»aa
 3729    "});
 3730    cx.update_editor(|e, window, cx| {
 3731        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3732    });
 3733    cx.assert_editor_state(indoc! {"
 3734        «1
 3735        2
 3736        3
 3737        4
 3738        5ˇ»
 3739
 3740        «aaaaa
 3741        bb
 3742        ccc
 3743        ddddˇ»
 3744    "});
 3745
 3746    // Adding lines on each selection
 3747    cx.set_state(indoc! {"
 3748 3749        1ˇ»
 3750
 3751        bb«bb
 3752        aaaˇ»aa
 3753    "});
 3754    cx.update_editor(|e, window, cx| {
 3755        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3756    });
 3757    cx.assert_editor_state(indoc! {"
 3758        «2
 3759        1
 3760        added lineˇ»
 3761
 3762        «bbbb
 3763        aaaaa
 3764        added lineˇ»
 3765    "});
 3766
 3767    // Removing lines on each selection
 3768    cx.set_state(indoc! {"
 3769 3770        1ˇ»
 3771
 3772        bb«bb
 3773        aaaˇ»aa
 3774    "});
 3775    cx.update_editor(|e, window, cx| {
 3776        e.manipulate_lines(window, cx, |lines| {
 3777            lines.pop();
 3778        })
 3779    });
 3780    cx.assert_editor_state(indoc! {"
 3781        «2ˇ»
 3782
 3783        «bbbbˇ»
 3784    "});
 3785}
 3786
 3787#[gpui::test]
 3788async fn test_manipulate_text(cx: &mut TestAppContext) {
 3789    init_test(cx, |_| {});
 3790
 3791    let mut cx = EditorTestContext::new(cx).await;
 3792
 3793    // Test convert_to_upper_case()
 3794    cx.set_state(indoc! {"
 3795        «hello worldˇ»
 3796    "});
 3797    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3798    cx.assert_editor_state(indoc! {"
 3799        «HELLO WORLDˇ»
 3800    "});
 3801
 3802    // Test convert_to_lower_case()
 3803    cx.set_state(indoc! {"
 3804        «HELLO WORLDˇ»
 3805    "});
 3806    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3807    cx.assert_editor_state(indoc! {"
 3808        «hello worldˇ»
 3809    "});
 3810
 3811    // Test multiple line, single selection case
 3812    cx.set_state(indoc! {"
 3813        «The quick brown
 3814        fox jumps over
 3815        the lazy dogˇ»
 3816    "});
 3817    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3818    cx.assert_editor_state(indoc! {"
 3819        «The Quick Brown
 3820        Fox Jumps Over
 3821        The Lazy Dogˇ»
 3822    "});
 3823
 3824    // Test multiple line, single selection case
 3825    cx.set_state(indoc! {"
 3826        «The quick brown
 3827        fox jumps over
 3828        the lazy dogˇ»
 3829    "});
 3830    cx.update_editor(|e, window, cx| {
 3831        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3832    });
 3833    cx.assert_editor_state(indoc! {"
 3834        «TheQuickBrown
 3835        FoxJumpsOver
 3836        TheLazyDogˇ»
 3837    "});
 3838
 3839    // From here on out, test more complex cases of manipulate_text()
 3840
 3841    // Test no selection case - should affect words cursors are in
 3842    // Cursor at beginning, middle, and end of word
 3843    cx.set_state(indoc! {"
 3844        ˇhello big beauˇtiful worldˇ
 3845    "});
 3846    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3847    cx.assert_editor_state(indoc! {"
 3848        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3849    "});
 3850
 3851    // Test multiple selections on a single line and across multiple lines
 3852    cx.set_state(indoc! {"
 3853        «Theˇ» quick «brown
 3854        foxˇ» jumps «overˇ»
 3855        the «lazyˇ» dog
 3856    "});
 3857    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3858    cx.assert_editor_state(indoc! {"
 3859        «THEˇ» quick «BROWN
 3860        FOXˇ» jumps «OVERˇ»
 3861        the «LAZYˇ» dog
 3862    "});
 3863
 3864    // Test case where text length grows
 3865    cx.set_state(indoc! {"
 3866        «tschüߡ»
 3867    "});
 3868    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3869    cx.assert_editor_state(indoc! {"
 3870        «TSCHÜSSˇ»
 3871    "});
 3872
 3873    // Test to make sure we don't crash when text shrinks
 3874    cx.set_state(indoc! {"
 3875        aaa_bbbˇ
 3876    "});
 3877    cx.update_editor(|e, window, cx| {
 3878        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3879    });
 3880    cx.assert_editor_state(indoc! {"
 3881        «aaaBbbˇ»
 3882    "});
 3883
 3884    // Test to make sure we all aware of the fact that each word can grow and shrink
 3885    // Final selections should be aware of this fact
 3886    cx.set_state(indoc! {"
 3887        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3891    });
 3892    cx.assert_editor_state(indoc! {"
 3893        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3894    "});
 3895
 3896    cx.set_state(indoc! {"
 3897        «hElLo, WoRld!ˇ»
 3898    "});
 3899    cx.update_editor(|e, window, cx| {
 3900        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3901    });
 3902    cx.assert_editor_state(indoc! {"
 3903        «HeLlO, wOrLD!ˇ»
 3904    "});
 3905}
 3906
 3907#[gpui::test]
 3908fn test_duplicate_line(cx: &mut TestAppContext) {
 3909    init_test(cx, |_| {});
 3910
 3911    let editor = cx.add_window(|window, cx| {
 3912        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3913        build_editor(buffer, window, cx)
 3914    });
 3915    _ = editor.update(cx, |editor, window, cx| {
 3916        editor.change_selections(None, window, cx, |s| {
 3917            s.select_display_ranges([
 3918                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3919                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3920                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3921                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3922            ])
 3923        });
 3924        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3925        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3926        assert_eq!(
 3927            editor.selections.display_ranges(cx),
 3928            vec![
 3929                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3930                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3931                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3932                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3933            ]
 3934        );
 3935    });
 3936
 3937    let editor = cx.add_window(|window, cx| {
 3938        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3939        build_editor(buffer, window, cx)
 3940    });
 3941    _ = editor.update(cx, |editor, window, cx| {
 3942        editor.change_selections(None, window, cx, |s| {
 3943            s.select_display_ranges([
 3944                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3945                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3946            ])
 3947        });
 3948        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3949        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3950        assert_eq!(
 3951            editor.selections.display_ranges(cx),
 3952            vec![
 3953                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3954                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3955            ]
 3956        );
 3957    });
 3958
 3959    // With `move_upwards` the selections stay in place, except for
 3960    // the lines inserted above them
 3961    let editor = cx.add_window(|window, cx| {
 3962        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3963        build_editor(buffer, window, cx)
 3964    });
 3965    _ = editor.update(cx, |editor, window, cx| {
 3966        editor.change_selections(None, window, cx, |s| {
 3967            s.select_display_ranges([
 3968                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3969                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3970                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3971                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3972            ])
 3973        });
 3974        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3975        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3976        assert_eq!(
 3977            editor.selections.display_ranges(cx),
 3978            vec![
 3979                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3980                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3981                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3982                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3983            ]
 3984        );
 3985    });
 3986
 3987    let editor = cx.add_window(|window, cx| {
 3988        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3989        build_editor(buffer, window, cx)
 3990    });
 3991    _ = editor.update(cx, |editor, window, cx| {
 3992        editor.change_selections(None, window, cx, |s| {
 3993            s.select_display_ranges([
 3994                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3995                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3996            ])
 3997        });
 3998        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3999        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4000        assert_eq!(
 4001            editor.selections.display_ranges(cx),
 4002            vec![
 4003                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4004                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4005            ]
 4006        );
 4007    });
 4008
 4009    let editor = cx.add_window(|window, cx| {
 4010        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4011        build_editor(buffer, window, cx)
 4012    });
 4013    _ = editor.update(cx, |editor, window, cx| {
 4014        editor.change_selections(None, window, cx, |s| {
 4015            s.select_display_ranges([
 4016                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4017                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4018            ])
 4019        });
 4020        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4021        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4022        assert_eq!(
 4023            editor.selections.display_ranges(cx),
 4024            vec![
 4025                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4026                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4027            ]
 4028        );
 4029    });
 4030}
 4031
 4032#[gpui::test]
 4033fn test_move_line_up_down(cx: &mut TestAppContext) {
 4034    init_test(cx, |_| {});
 4035
 4036    let editor = cx.add_window(|window, cx| {
 4037        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4038        build_editor(buffer, window, cx)
 4039    });
 4040    _ = editor.update(cx, |editor, window, cx| {
 4041        editor.fold_creases(
 4042            vec![
 4043                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4044                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4045                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4046            ],
 4047            true,
 4048            window,
 4049            cx,
 4050        );
 4051        editor.change_selections(None, window, cx, |s| {
 4052            s.select_display_ranges([
 4053                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4054                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4055                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4056                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4057            ])
 4058        });
 4059        assert_eq!(
 4060            editor.display_text(cx),
 4061            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4062        );
 4063
 4064        editor.move_line_up(&MoveLineUp, window, cx);
 4065        assert_eq!(
 4066            editor.display_text(cx),
 4067            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4068        );
 4069        assert_eq!(
 4070            editor.selections.display_ranges(cx),
 4071            vec![
 4072                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4073                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4074                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4075                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4076            ]
 4077        );
 4078    });
 4079
 4080    _ = editor.update(cx, |editor, window, cx| {
 4081        editor.move_line_down(&MoveLineDown, window, cx);
 4082        assert_eq!(
 4083            editor.display_text(cx),
 4084            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4085        );
 4086        assert_eq!(
 4087            editor.selections.display_ranges(cx),
 4088            vec![
 4089                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4090                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4091                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4092                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4093            ]
 4094        );
 4095    });
 4096
 4097    _ = editor.update(cx, |editor, window, cx| {
 4098        editor.move_line_down(&MoveLineDown, window, cx);
 4099        assert_eq!(
 4100            editor.display_text(cx),
 4101            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4102        );
 4103        assert_eq!(
 4104            editor.selections.display_ranges(cx),
 4105            vec![
 4106                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4107                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4108                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4109                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4110            ]
 4111        );
 4112    });
 4113
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.move_line_up(&MoveLineUp, window, cx);
 4116        assert_eq!(
 4117            editor.display_text(cx),
 4118            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4119        );
 4120        assert_eq!(
 4121            editor.selections.display_ranges(cx),
 4122            vec![
 4123                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4124                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4125                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4126                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4127            ]
 4128        );
 4129    });
 4130}
 4131
 4132#[gpui::test]
 4133fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4134    init_test(cx, |_| {});
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4142        editor.insert_blocks(
 4143            [BlockProperties {
 4144                style: BlockStyle::Fixed,
 4145                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4146                height: 1,
 4147                render: Arc::new(|_| div().into_any()),
 4148                priority: 0,
 4149            }],
 4150            Some(Autoscroll::fit()),
 4151            cx,
 4152        );
 4153        editor.change_selections(None, window, cx, |s| {
 4154            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4155        });
 4156        editor.move_line_down(&MoveLineDown, window, cx);
 4157    });
 4158}
 4159
 4160#[gpui::test]
 4161async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4162    init_test(cx, |_| {});
 4163
 4164    let mut cx = EditorTestContext::new(cx).await;
 4165    cx.set_state(
 4166        &"
 4167            ˇzero
 4168            one
 4169            two
 4170            three
 4171            four
 4172            five
 4173        "
 4174        .unindent(),
 4175    );
 4176
 4177    // Create a four-line block that replaces three lines of text.
 4178    cx.update_editor(|editor, window, cx| {
 4179        let snapshot = editor.snapshot(window, cx);
 4180        let snapshot = &snapshot.buffer_snapshot;
 4181        let placement = BlockPlacement::Replace(
 4182            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4183        );
 4184        editor.insert_blocks(
 4185            [BlockProperties {
 4186                placement,
 4187                height: 4,
 4188                style: BlockStyle::Sticky,
 4189                render: Arc::new(|_| gpui::div().into_any_element()),
 4190                priority: 0,
 4191            }],
 4192            None,
 4193            cx,
 4194        );
 4195    });
 4196
 4197    // Move down so that the cursor touches the block.
 4198    cx.update_editor(|editor, window, cx| {
 4199        editor.move_down(&Default::default(), window, cx);
 4200    });
 4201    cx.assert_editor_state(
 4202        &"
 4203            zero
 4204            «one
 4205            two
 4206            threeˇ»
 4207            four
 4208            five
 4209        "
 4210        .unindent(),
 4211    );
 4212
 4213    // Move down past the block.
 4214    cx.update_editor(|editor, window, cx| {
 4215        editor.move_down(&Default::default(), window, cx);
 4216    });
 4217    cx.assert_editor_state(
 4218        &"
 4219            zero
 4220            one
 4221            two
 4222            three
 4223            ˇfour
 4224            five
 4225        "
 4226        .unindent(),
 4227    );
 4228}
 4229
 4230#[gpui::test]
 4231fn test_transpose(cx: &mut TestAppContext) {
 4232    init_test(cx, |_| {});
 4233
 4234    _ = cx.add_window(|window, cx| {
 4235        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4236        editor.set_style(EditorStyle::default(), window, cx);
 4237        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4238        editor.transpose(&Default::default(), window, cx);
 4239        assert_eq!(editor.text(cx), "bac");
 4240        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4241
 4242        editor.transpose(&Default::default(), window, cx);
 4243        assert_eq!(editor.text(cx), "bca");
 4244        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4245
 4246        editor.transpose(&Default::default(), window, cx);
 4247        assert_eq!(editor.text(cx), "bac");
 4248        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4249
 4250        editor
 4251    });
 4252
 4253    _ = cx.add_window(|window, cx| {
 4254        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4255        editor.set_style(EditorStyle::default(), window, cx);
 4256        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4257        editor.transpose(&Default::default(), window, cx);
 4258        assert_eq!(editor.text(cx), "acb\nde");
 4259        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4260
 4261        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4262        editor.transpose(&Default::default(), window, cx);
 4263        assert_eq!(editor.text(cx), "acbd\ne");
 4264        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4265
 4266        editor.transpose(&Default::default(), window, cx);
 4267        assert_eq!(editor.text(cx), "acbde\n");
 4268        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4269
 4270        editor.transpose(&Default::default(), window, cx);
 4271        assert_eq!(editor.text(cx), "acbd\ne");
 4272        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4273
 4274        editor
 4275    });
 4276
 4277    _ = cx.add_window(|window, cx| {
 4278        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4279        editor.set_style(EditorStyle::default(), window, cx);
 4280        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4281        editor.transpose(&Default::default(), window, cx);
 4282        assert_eq!(editor.text(cx), "bacd\ne");
 4283        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4284
 4285        editor.transpose(&Default::default(), window, cx);
 4286        assert_eq!(editor.text(cx), "bcade\n");
 4287        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4288
 4289        editor.transpose(&Default::default(), window, cx);
 4290        assert_eq!(editor.text(cx), "bcda\ne");
 4291        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4292
 4293        editor.transpose(&Default::default(), window, cx);
 4294        assert_eq!(editor.text(cx), "bcade\n");
 4295        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4296
 4297        editor.transpose(&Default::default(), window, cx);
 4298        assert_eq!(editor.text(cx), "bcaed\n");
 4299        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4300
 4301        editor
 4302    });
 4303
 4304    _ = cx.add_window(|window, cx| {
 4305        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4306        editor.set_style(EditorStyle::default(), window, cx);
 4307        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4308        editor.transpose(&Default::default(), window, cx);
 4309        assert_eq!(editor.text(cx), "🏀🍐✋");
 4310        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4311
 4312        editor.transpose(&Default::default(), window, cx);
 4313        assert_eq!(editor.text(cx), "🏀✋🍐");
 4314        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4315
 4316        editor.transpose(&Default::default(), window, cx);
 4317        assert_eq!(editor.text(cx), "🏀🍐✋");
 4318        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4319
 4320        editor
 4321    });
 4322}
 4323
 4324#[gpui::test]
 4325async fn test_rewrap(cx: &mut TestAppContext) {
 4326    init_test(cx, |_| {});
 4327
 4328    let mut cx = EditorTestContext::new(cx).await;
 4329
 4330    let language_with_c_comments = Arc::new(Language::new(
 4331        LanguageConfig {
 4332            line_comments: vec!["// ".into()],
 4333            ..LanguageConfig::default()
 4334        },
 4335        None,
 4336    ));
 4337    let language_with_pound_comments = Arc::new(Language::new(
 4338        LanguageConfig {
 4339            line_comments: vec!["# ".into()],
 4340            ..LanguageConfig::default()
 4341        },
 4342        None,
 4343    ));
 4344    let markdown_language = Arc::new(Language::new(
 4345        LanguageConfig {
 4346            name: "Markdown".into(),
 4347            ..LanguageConfig::default()
 4348        },
 4349        None,
 4350    ));
 4351    let language_with_doc_comments = Arc::new(Language::new(
 4352        LanguageConfig {
 4353            line_comments: vec!["// ".into(), "/// ".into()],
 4354            ..LanguageConfig::default()
 4355        },
 4356        Some(tree_sitter_rust::LANGUAGE.into()),
 4357    ));
 4358
 4359    let plaintext_language = Arc::new(Language::new(
 4360        LanguageConfig {
 4361            name: "Plain Text".into(),
 4362            ..LanguageConfig::default()
 4363        },
 4364        None,
 4365    ));
 4366
 4367    assert_rewrap(
 4368        indoc! {"
 4369            // ˇ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.
 4370        "},
 4371        indoc! {"
 4372            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4373            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4374            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4375            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4376            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4377            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4378            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4379            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4380            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4381            // porttitor id. Aliquam id accumsan eros.
 4382        "},
 4383        language_with_c_comments.clone(),
 4384        &mut cx,
 4385    );
 4386
 4387    // Test that rewrapping works inside of a selection
 4388    assert_rewrap(
 4389        indoc! {"
 4390            «// 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.ˇ»
 4391        "},
 4392        indoc! {"
 4393            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4394            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4395            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4396            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4397            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4398            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4399            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4400            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4401            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4402            // porttitor id. Aliquam id accumsan eros.ˇ»
 4403        "},
 4404        language_with_c_comments.clone(),
 4405        &mut cx,
 4406    );
 4407
 4408    // Test that cursors that expand to the same region are collapsed.
 4409    assert_rewrap(
 4410        indoc! {"
 4411            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4412            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4413            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4414            // ˇ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.
 4415        "},
 4416        indoc! {"
 4417            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4418            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4419            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4420            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4421            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4422            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4423            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4424            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4425            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4426            // porttitor id. Aliquam id accumsan eros.
 4427        "},
 4428        language_with_c_comments.clone(),
 4429        &mut cx,
 4430    );
 4431
 4432    // Test that non-contiguous selections are treated separately.
 4433    assert_rewrap(
 4434        indoc! {"
 4435            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4436            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4437            //
 4438            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4439            // ˇ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.
 4440        "},
 4441        indoc! {"
 4442            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4443            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4444            // auctor, eu lacinia sapien scelerisque.
 4445            //
 4446            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4447            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4448            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4449            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4450            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4451            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4452            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4453        "},
 4454        language_with_c_comments.clone(),
 4455        &mut cx,
 4456    );
 4457
 4458    // Test that different comment prefixes are supported.
 4459    assert_rewrap(
 4460        indoc! {"
 4461            # ˇ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.
 4462        "},
 4463        indoc! {"
 4464            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4465            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4466            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4467            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4468            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4469            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4470            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4471            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4472            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4473            # accumsan eros.
 4474        "},
 4475        language_with_pound_comments.clone(),
 4476        &mut cx,
 4477    );
 4478
 4479    // Test that rewrapping is ignored outside of comments in most languages.
 4480    assert_rewrap(
 4481        indoc! {"
 4482            /// Adds two numbers.
 4483            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4484            fn add(a: u32, b: u32) -> u32 {
 4485                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ˇ
 4486            }
 4487        "},
 4488        indoc! {"
 4489            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4490            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4491            fn add(a: u32, b: u32) -> u32 {
 4492                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ˇ
 4493            }
 4494        "},
 4495        language_with_doc_comments.clone(),
 4496        &mut cx,
 4497    );
 4498
 4499    // Test that rewrapping works in Markdown and Plain Text languages.
 4500    assert_rewrap(
 4501        indoc! {"
 4502            # Hello
 4503
 4504            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4505        "},
 4506        indoc! {"
 4507            # Hello
 4508
 4509            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4510            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4511            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4512            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4513            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4514            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4515            Integer sit amet scelerisque nisi.
 4516        "},
 4517        markdown_language,
 4518        &mut cx,
 4519    );
 4520
 4521    assert_rewrap(
 4522        indoc! {"
 4523            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.
 4524        "},
 4525        indoc! {"
 4526            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4527            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4528            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4529            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4530            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4531            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4532            Integer sit amet scelerisque nisi.
 4533        "},
 4534        plaintext_language,
 4535        &mut cx,
 4536    );
 4537
 4538    // Test rewrapping unaligned comments in a selection.
 4539    assert_rewrap(
 4540        indoc! {"
 4541            fn foo() {
 4542                if true {
 4543            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4544            // Praesent semper egestas tellus id dignissim.ˇ»
 4545                    do_something();
 4546                } else {
 4547                    //
 4548                }
 4549            }
 4550        "},
 4551        indoc! {"
 4552            fn foo() {
 4553                if true {
 4554            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4555                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4556                    // egestas tellus id dignissim.ˇ»
 4557                    do_something();
 4558                } else {
 4559                    //
 4560                }
 4561            }
 4562        "},
 4563        language_with_doc_comments.clone(),
 4564        &mut cx,
 4565    );
 4566
 4567    assert_rewrap(
 4568        indoc! {"
 4569            fn foo() {
 4570                if true {
 4571            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4572            // Praesent semper egestas tellus id dignissim.»
 4573                    do_something();
 4574                } else {
 4575                    //
 4576                }
 4577
 4578            }
 4579        "},
 4580        indoc! {"
 4581            fn foo() {
 4582                if true {
 4583            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4584                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4585                    // egestas tellus id dignissim.»
 4586                    do_something();
 4587                } else {
 4588                    //
 4589                }
 4590
 4591            }
 4592        "},
 4593        language_with_doc_comments.clone(),
 4594        &mut cx,
 4595    );
 4596
 4597    #[track_caller]
 4598    fn assert_rewrap(
 4599        unwrapped_text: &str,
 4600        wrapped_text: &str,
 4601        language: Arc<Language>,
 4602        cx: &mut EditorTestContext,
 4603    ) {
 4604        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4605        cx.set_state(unwrapped_text);
 4606        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4607        cx.assert_editor_state(wrapped_text);
 4608    }
 4609}
 4610
 4611#[gpui::test]
 4612async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4613    init_test(cx, |_| {});
 4614
 4615    let mut cx = EditorTestContext::new(cx).await;
 4616
 4617    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4618    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4619    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4620
 4621    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4622    cx.set_state("two ˇfour ˇsix ˇ");
 4623    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4624    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4625
 4626    // Paste again but with only two cursors. Since the number of cursors doesn't
 4627    // match the number of slices in the clipboard, the entire clipboard text
 4628    // is pasted at each cursor.
 4629    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4630    cx.update_editor(|e, window, cx| {
 4631        e.handle_input("( ", window, cx);
 4632        e.paste(&Paste, window, cx);
 4633        e.handle_input(") ", window, cx);
 4634    });
 4635    cx.assert_editor_state(
 4636        &([
 4637            "( one✅ ",
 4638            "three ",
 4639            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4640            "three ",
 4641            "five ) ˇ",
 4642        ]
 4643        .join("\n")),
 4644    );
 4645
 4646    // Cut with three selections, one of which is full-line.
 4647    cx.set_state(indoc! {"
 4648        1«2ˇ»3
 4649        4ˇ567
 4650        «8ˇ»9"});
 4651    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4652    cx.assert_editor_state(indoc! {"
 4653        1ˇ3
 4654        ˇ9"});
 4655
 4656    // Paste with three selections, noticing how the copied selection that was full-line
 4657    // gets inserted before the second cursor.
 4658    cx.set_state(indoc! {"
 4659        1ˇ3
 4660 4661        «oˇ»ne"});
 4662    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4663    cx.assert_editor_state(indoc! {"
 4664        12ˇ3
 4665        4567
 4666 4667        8ˇne"});
 4668
 4669    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4670    cx.set_state(indoc! {"
 4671        The quick brown
 4672        fox juˇmps over
 4673        the lazy dog"});
 4674    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4675    assert_eq!(
 4676        cx.read_from_clipboard()
 4677            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4678        Some("fox jumps over\n".to_string())
 4679    );
 4680
 4681    // Paste with three selections, noticing how the copied full-line selection is inserted
 4682    // before the empty selections but replaces the selection that is non-empty.
 4683    cx.set_state(indoc! {"
 4684        Tˇhe quick brown
 4685        «foˇ»x jumps over
 4686        tˇhe lazy dog"});
 4687    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4688    cx.assert_editor_state(indoc! {"
 4689        fox jumps over
 4690        Tˇhe quick brown
 4691        fox jumps over
 4692        ˇx jumps over
 4693        fox jumps over
 4694        tˇhe lazy dog"});
 4695}
 4696
 4697#[gpui::test]
 4698async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4699    init_test(cx, |_| {});
 4700
 4701    let mut cx = EditorTestContext::new(cx).await;
 4702    let language = Arc::new(Language::new(
 4703        LanguageConfig::default(),
 4704        Some(tree_sitter_rust::LANGUAGE.into()),
 4705    ));
 4706    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4707
 4708    // Cut an indented block, without the leading whitespace.
 4709    cx.set_state(indoc! {"
 4710        const a: B = (
 4711            c(),
 4712            «d(
 4713                e,
 4714                f
 4715            )ˇ»
 4716        );
 4717    "});
 4718    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4719    cx.assert_editor_state(indoc! {"
 4720        const a: B = (
 4721            c(),
 4722            ˇ
 4723        );
 4724    "});
 4725
 4726    // Paste it at the same position.
 4727    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4728    cx.assert_editor_state(indoc! {"
 4729        const a: B = (
 4730            c(),
 4731            d(
 4732                e,
 4733                f
 4734 4735        );
 4736    "});
 4737
 4738    // Paste it at a line with a lower indent level.
 4739    cx.set_state(indoc! {"
 4740        ˇ
 4741        const a: B = (
 4742            c(),
 4743        );
 4744    "});
 4745    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4746    cx.assert_editor_state(indoc! {"
 4747        d(
 4748            e,
 4749            f
 4750 4751        const a: B = (
 4752            c(),
 4753        );
 4754    "});
 4755
 4756    // Cut an indented block, with the leading whitespace.
 4757    cx.set_state(indoc! {"
 4758        const a: B = (
 4759            c(),
 4760        «    d(
 4761                e,
 4762                f
 4763            )
 4764        ˇ»);
 4765    "});
 4766    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4767    cx.assert_editor_state(indoc! {"
 4768        const a: B = (
 4769            c(),
 4770        ˇ);
 4771    "});
 4772
 4773    // Paste it at the same position.
 4774    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4775    cx.assert_editor_state(indoc! {"
 4776        const a: B = (
 4777            c(),
 4778            d(
 4779                e,
 4780                f
 4781            )
 4782        ˇ);
 4783    "});
 4784
 4785    // Paste it at a line with a higher indent level.
 4786    cx.set_state(indoc! {"
 4787        const a: B = (
 4788            c(),
 4789            d(
 4790                e,
 4791 4792            )
 4793        );
 4794    "});
 4795    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4796    cx.assert_editor_state(indoc! {"
 4797        const a: B = (
 4798            c(),
 4799            d(
 4800                e,
 4801                f    d(
 4802                    e,
 4803                    f
 4804                )
 4805        ˇ
 4806            )
 4807        );
 4808    "});
 4809}
 4810
 4811#[gpui::test]
 4812fn test_select_all(cx: &mut TestAppContext) {
 4813    init_test(cx, |_| {});
 4814
 4815    let editor = cx.add_window(|window, cx| {
 4816        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4817        build_editor(buffer, window, cx)
 4818    });
 4819    _ = editor.update(cx, |editor, window, cx| {
 4820        editor.select_all(&SelectAll, window, cx);
 4821        assert_eq!(
 4822            editor.selections.display_ranges(cx),
 4823            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4824        );
 4825    });
 4826}
 4827
 4828#[gpui::test]
 4829fn test_select_line(cx: &mut TestAppContext) {
 4830    init_test(cx, |_| {});
 4831
 4832    let editor = cx.add_window(|window, cx| {
 4833        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4834        build_editor(buffer, window, cx)
 4835    });
 4836    _ = editor.update(cx, |editor, window, cx| {
 4837        editor.change_selections(None, window, cx, |s| {
 4838            s.select_display_ranges([
 4839                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4840                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4841                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4842                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4843            ])
 4844        });
 4845        editor.select_line(&SelectLine, window, cx);
 4846        assert_eq!(
 4847            editor.selections.display_ranges(cx),
 4848            vec![
 4849                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4850                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4851            ]
 4852        );
 4853    });
 4854
 4855    _ = editor.update(cx, |editor, window, cx| {
 4856        editor.select_line(&SelectLine, window, cx);
 4857        assert_eq!(
 4858            editor.selections.display_ranges(cx),
 4859            vec![
 4860                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4861                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4862            ]
 4863        );
 4864    });
 4865
 4866    _ = editor.update(cx, |editor, window, cx| {
 4867        editor.select_line(&SelectLine, window, cx);
 4868        assert_eq!(
 4869            editor.selections.display_ranges(cx),
 4870            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4871        );
 4872    });
 4873}
 4874
 4875#[gpui::test]
 4876fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4877    init_test(cx, |_| {});
 4878
 4879    let editor = cx.add_window(|window, cx| {
 4880        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4881        build_editor(buffer, window, cx)
 4882    });
 4883    _ = editor.update(cx, |editor, window, cx| {
 4884        editor.fold_creases(
 4885            vec![
 4886                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4887                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4888                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4889            ],
 4890            true,
 4891            window,
 4892            cx,
 4893        );
 4894        editor.change_selections(None, window, cx, |s| {
 4895            s.select_display_ranges([
 4896                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4897                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4898                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4899                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4900            ])
 4901        });
 4902        assert_eq!(
 4903            editor.display_text(cx),
 4904            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4905        );
 4906    });
 4907
 4908    _ = editor.update(cx, |editor, window, cx| {
 4909        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4910        assert_eq!(
 4911            editor.display_text(cx),
 4912            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4913        );
 4914        assert_eq!(
 4915            editor.selections.display_ranges(cx),
 4916            [
 4917                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4918                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4919                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4920                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4921            ]
 4922        );
 4923    });
 4924
 4925    _ = editor.update(cx, |editor, window, cx| {
 4926        editor.change_selections(None, window, cx, |s| {
 4927            s.select_display_ranges([
 4928                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4929            ])
 4930        });
 4931        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4932        assert_eq!(
 4933            editor.display_text(cx),
 4934            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4935        );
 4936        assert_eq!(
 4937            editor.selections.display_ranges(cx),
 4938            [
 4939                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4940                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4941                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4942                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4943                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4944                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4945                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4946                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4947            ]
 4948        );
 4949    });
 4950}
 4951
 4952#[gpui::test]
 4953async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4954    init_test(cx, |_| {});
 4955
 4956    let mut cx = EditorTestContext::new(cx).await;
 4957
 4958    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4959    cx.set_state(indoc!(
 4960        r#"abc
 4961           defˇghi
 4962
 4963           jk
 4964           nlmo
 4965           "#
 4966    ));
 4967
 4968    cx.update_editor(|editor, window, cx| {
 4969        editor.add_selection_above(&Default::default(), window, cx);
 4970    });
 4971
 4972    cx.assert_editor_state(indoc!(
 4973        r#"abcˇ
 4974           defˇghi
 4975
 4976           jk
 4977           nlmo
 4978           "#
 4979    ));
 4980
 4981    cx.update_editor(|editor, window, cx| {
 4982        editor.add_selection_above(&Default::default(), window, cx);
 4983    });
 4984
 4985    cx.assert_editor_state(indoc!(
 4986        r#"abcˇ
 4987            defˇghi
 4988
 4989            jk
 4990            nlmo
 4991            "#
 4992    ));
 4993
 4994    cx.update_editor(|editor, window, cx| {
 4995        editor.add_selection_below(&Default::default(), window, cx);
 4996    });
 4997
 4998    cx.assert_editor_state(indoc!(
 4999        r#"abc
 5000           defˇghi
 5001
 5002           jk
 5003           nlmo
 5004           "#
 5005    ));
 5006
 5007    cx.update_editor(|editor, window, cx| {
 5008        editor.undo_selection(&Default::default(), window, cx);
 5009    });
 5010
 5011    cx.assert_editor_state(indoc!(
 5012        r#"abcˇ
 5013           defˇghi
 5014
 5015           jk
 5016           nlmo
 5017           "#
 5018    ));
 5019
 5020    cx.update_editor(|editor, window, cx| {
 5021        editor.redo_selection(&Default::default(), window, cx);
 5022    });
 5023
 5024    cx.assert_editor_state(indoc!(
 5025        r#"abc
 5026           defˇghi
 5027
 5028           jk
 5029           nlmo
 5030           "#
 5031    ));
 5032
 5033    cx.update_editor(|editor, window, cx| {
 5034        editor.add_selection_below(&Default::default(), window, cx);
 5035    });
 5036
 5037    cx.assert_editor_state(indoc!(
 5038        r#"abc
 5039           defˇghi
 5040
 5041           jk
 5042           nlmˇo
 5043           "#
 5044    ));
 5045
 5046    cx.update_editor(|editor, window, cx| {
 5047        editor.add_selection_below(&Default::default(), window, cx);
 5048    });
 5049
 5050    cx.assert_editor_state(indoc!(
 5051        r#"abc
 5052           defˇghi
 5053
 5054           jk
 5055           nlmˇo
 5056           "#
 5057    ));
 5058
 5059    // change selections
 5060    cx.set_state(indoc!(
 5061        r#"abc
 5062           def«ˇg»hi
 5063
 5064           jk
 5065           nlmo
 5066           "#
 5067    ));
 5068
 5069    cx.update_editor(|editor, window, cx| {
 5070        editor.add_selection_below(&Default::default(), window, cx);
 5071    });
 5072
 5073    cx.assert_editor_state(indoc!(
 5074        r#"abc
 5075           def«ˇg»hi
 5076
 5077           jk
 5078           nlm«ˇo»
 5079           "#
 5080    ));
 5081
 5082    cx.update_editor(|editor, window, cx| {
 5083        editor.add_selection_below(&Default::default(), window, cx);
 5084    });
 5085
 5086    cx.assert_editor_state(indoc!(
 5087        r#"abc
 5088           def«ˇg»hi
 5089
 5090           jk
 5091           nlm«ˇo»
 5092           "#
 5093    ));
 5094
 5095    cx.update_editor(|editor, window, cx| {
 5096        editor.add_selection_above(&Default::default(), window, cx);
 5097    });
 5098
 5099    cx.assert_editor_state(indoc!(
 5100        r#"abc
 5101           def«ˇg»hi
 5102
 5103           jk
 5104           nlmo
 5105           "#
 5106    ));
 5107
 5108    cx.update_editor(|editor, window, cx| {
 5109        editor.add_selection_above(&Default::default(), window, cx);
 5110    });
 5111
 5112    cx.assert_editor_state(indoc!(
 5113        r#"abc
 5114           def«ˇg»hi
 5115
 5116           jk
 5117           nlmo
 5118           "#
 5119    ));
 5120
 5121    // Change selections again
 5122    cx.set_state(indoc!(
 5123        r#"a«bc
 5124           defgˇ»hi
 5125
 5126           jk
 5127           nlmo
 5128           "#
 5129    ));
 5130
 5131    cx.update_editor(|editor, window, cx| {
 5132        editor.add_selection_below(&Default::default(), window, cx);
 5133    });
 5134
 5135    cx.assert_editor_state(indoc!(
 5136        r#"a«bcˇ»
 5137           d«efgˇ»hi
 5138
 5139           j«kˇ»
 5140           nlmo
 5141           "#
 5142    ));
 5143
 5144    cx.update_editor(|editor, window, cx| {
 5145        editor.add_selection_below(&Default::default(), window, cx);
 5146    });
 5147    cx.assert_editor_state(indoc!(
 5148        r#"a«bcˇ»
 5149           d«efgˇ»hi
 5150
 5151           j«kˇ»
 5152           n«lmoˇ»
 5153           "#
 5154    ));
 5155    cx.update_editor(|editor, window, cx| {
 5156        editor.add_selection_above(&Default::default(), window, cx);
 5157    });
 5158
 5159    cx.assert_editor_state(indoc!(
 5160        r#"a«bcˇ»
 5161           d«efgˇ»hi
 5162
 5163           j«kˇ»
 5164           nlmo
 5165           "#
 5166    ));
 5167
 5168    // Change selections again
 5169    cx.set_state(indoc!(
 5170        r#"abc
 5171           d«ˇefghi
 5172
 5173           jk
 5174           nlm»o
 5175           "#
 5176    ));
 5177
 5178    cx.update_editor(|editor, window, cx| {
 5179        editor.add_selection_above(&Default::default(), window, cx);
 5180    });
 5181
 5182    cx.assert_editor_state(indoc!(
 5183        r#"a«ˇbc»
 5184           d«ˇef»ghi
 5185
 5186           j«ˇk»
 5187           n«ˇlm»o
 5188           "#
 5189    ));
 5190
 5191    cx.update_editor(|editor, window, cx| {
 5192        editor.add_selection_below(&Default::default(), window, cx);
 5193    });
 5194
 5195    cx.assert_editor_state(indoc!(
 5196        r#"abc
 5197           d«ˇef»ghi
 5198
 5199           j«ˇk»
 5200           n«ˇlm»o
 5201           "#
 5202    ));
 5203}
 5204
 5205#[gpui::test]
 5206async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5207    init_test(cx, |_| {});
 5208
 5209    let mut cx = EditorTestContext::new(cx).await;
 5210    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5211
 5212    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5213        .unwrap();
 5214    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5215
 5216    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5217        .unwrap();
 5218    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5219
 5220    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5221    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5222
 5223    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5224    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5225
 5226    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5227        .unwrap();
 5228    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5229
 5230    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5231        .unwrap();
 5232    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5233}
 5234
 5235#[gpui::test]
 5236async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5237    init_test(cx, |_| {});
 5238
 5239    let mut cx = EditorTestContext::new(cx).await;
 5240
 5241    // Test caret-only selections
 5242    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5243
 5244    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5245        .unwrap();
 5246    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5247
 5248    // Test left-to-right selections
 5249    cx.set_state("abc\n«abcˇ»\nabc");
 5250
 5251    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5252        .unwrap();
 5253    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5254
 5255    // Test right-to-left selections
 5256    cx.set_state("abc\n«ˇabc»\nabc");
 5257
 5258    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5259        .unwrap();
 5260    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5261}
 5262
 5263#[gpui::test]
 5264async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5265    init_test(cx, |_| {});
 5266
 5267    let mut cx = EditorTestContext::new(cx).await;
 5268    cx.set_state(
 5269        r#"let foo = 2;
 5270lˇet foo = 2;
 5271let fooˇ = 2;
 5272let foo = 2;
 5273let foo = ˇ2;"#,
 5274    );
 5275
 5276    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5277        .unwrap();
 5278    cx.assert_editor_state(
 5279        r#"let foo = 2;
 5280«letˇ» foo = 2;
 5281let «fooˇ» = 2;
 5282let foo = 2;
 5283let foo = «2ˇ»;"#,
 5284    );
 5285
 5286    // noop for multiple selections with different contents
 5287    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5288        .unwrap();
 5289    cx.assert_editor_state(
 5290        r#"let foo = 2;
 5291«letˇ» foo = 2;
 5292let «fooˇ» = 2;
 5293let foo = 2;
 5294let foo = «2ˇ»;"#,
 5295    );
 5296}
 5297
 5298#[gpui::test]
 5299async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5300    init_test(cx, |_| {});
 5301
 5302    let mut cx =
 5303        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5304
 5305    cx.assert_editor_state(indoc! {"
 5306        ˇbbb
 5307        ccc
 5308
 5309        bbb
 5310        ccc
 5311        "});
 5312    cx.dispatch_action(SelectPrevious::default());
 5313    cx.assert_editor_state(indoc! {"
 5314                «bbbˇ»
 5315                ccc
 5316
 5317                bbb
 5318                ccc
 5319                "});
 5320    cx.dispatch_action(SelectPrevious::default());
 5321    cx.assert_editor_state(indoc! {"
 5322                «bbbˇ»
 5323                ccc
 5324
 5325                «bbbˇ»
 5326                ccc
 5327                "});
 5328}
 5329
 5330#[gpui::test]
 5331async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5332    init_test(cx, |_| {});
 5333
 5334    let mut cx = EditorTestContext::new(cx).await;
 5335    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5336
 5337    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5338        .unwrap();
 5339    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5340
 5341    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5342        .unwrap();
 5343    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5344
 5345    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5346    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5347
 5348    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5349    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5350
 5351    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5352        .unwrap();
 5353    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5354
 5355    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5356        .unwrap();
 5357    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5358
 5359    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5360        .unwrap();
 5361    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5362}
 5363
 5364#[gpui::test]
 5365async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5366    init_test(cx, |_| {});
 5367
 5368    let mut cx = EditorTestContext::new(cx).await;
 5369    cx.set_state(
 5370        r#"let foo = 2;
 5371lˇet foo = 2;
 5372let fooˇ = 2;
 5373let foo = 2;
 5374let foo = ˇ2;"#,
 5375    );
 5376
 5377    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5378        .unwrap();
 5379    cx.assert_editor_state(
 5380        r#"let foo = 2;
 5381«letˇ» foo = 2;
 5382let «fooˇ» = 2;
 5383let foo = 2;
 5384let foo = «2ˇ»;"#,
 5385    );
 5386
 5387    // noop for multiple selections with different contents
 5388    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5389        .unwrap();
 5390    cx.assert_editor_state(
 5391        r#"let foo = 2;
 5392«letˇ» foo = 2;
 5393let «fooˇ» = 2;
 5394let foo = 2;
 5395let foo = «2ˇ»;"#,
 5396    );
 5397}
 5398
 5399#[gpui::test]
 5400async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5401    init_test(cx, |_| {});
 5402
 5403    let mut cx = EditorTestContext::new(cx).await;
 5404    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5405
 5406    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5407        .unwrap();
 5408    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5409
 5410    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5411        .unwrap();
 5412    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5413
 5414    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5415    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5416
 5417    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5418    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5419
 5420    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5421        .unwrap();
 5422    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5423
 5424    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5425        .unwrap();
 5426    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5427}
 5428
 5429#[gpui::test]
 5430async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5431    init_test(cx, |_| {});
 5432
 5433    let language = Arc::new(Language::new(
 5434        LanguageConfig::default(),
 5435        Some(tree_sitter_rust::LANGUAGE.into()),
 5436    ));
 5437
 5438    let text = r#"
 5439        use mod1::mod2::{mod3, mod4};
 5440
 5441        fn fn_1(param1: bool, param2: &str) {
 5442            let var1 = "text";
 5443        }
 5444    "#
 5445    .unindent();
 5446
 5447    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5448    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5449    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5450
 5451    editor
 5452        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5453        .await;
 5454
 5455    editor.update_in(cx, |editor, window, cx| {
 5456        editor.change_selections(None, window, cx, |s| {
 5457            s.select_display_ranges([
 5458                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5459                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5460                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5461            ]);
 5462        });
 5463        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5464    });
 5465    editor.update(cx, |editor, cx| {
 5466        assert_text_with_selections(
 5467            editor,
 5468            indoc! {r#"
 5469                use mod1::mod2::{mod3, «mod4ˇ»};
 5470
 5471                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5472                    let var1 = "«textˇ»";
 5473                }
 5474            "#},
 5475            cx,
 5476        );
 5477    });
 5478
 5479    editor.update_in(cx, |editor, window, cx| {
 5480        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5481    });
 5482    editor.update(cx, |editor, cx| {
 5483        assert_text_with_selections(
 5484            editor,
 5485            indoc! {r#"
 5486                use mod1::mod2::«{mod3, mod4}ˇ»;
 5487
 5488                «ˇfn fn_1(param1: bool, param2: &str) {
 5489                    let var1 = "text";
 5490 5491            "#},
 5492            cx,
 5493        );
 5494    });
 5495
 5496    editor.update_in(cx, |editor, window, cx| {
 5497        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5498    });
 5499    assert_eq!(
 5500        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5501        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5502    );
 5503
 5504    // Trying to expand the selected syntax node one more time has no effect.
 5505    editor.update_in(cx, |editor, window, cx| {
 5506        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5507    });
 5508    assert_eq!(
 5509        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5510        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5511    );
 5512
 5513    editor.update_in(cx, |editor, window, cx| {
 5514        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5515    });
 5516    editor.update(cx, |editor, cx| {
 5517        assert_text_with_selections(
 5518            editor,
 5519            indoc! {r#"
 5520                use mod1::mod2::«{mod3, mod4}ˇ»;
 5521
 5522                «ˇfn fn_1(param1: bool, param2: &str) {
 5523                    let var1 = "text";
 5524 5525            "#},
 5526            cx,
 5527        );
 5528    });
 5529
 5530    editor.update_in(cx, |editor, window, cx| {
 5531        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5532    });
 5533    editor.update(cx, |editor, cx| {
 5534        assert_text_with_selections(
 5535            editor,
 5536            indoc! {r#"
 5537                use mod1::mod2::{mod3, «mod4ˇ»};
 5538
 5539                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5540                    let var1 = "«textˇ»";
 5541                }
 5542            "#},
 5543            cx,
 5544        );
 5545    });
 5546
 5547    editor.update_in(cx, |editor, window, cx| {
 5548        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5549    });
 5550    editor.update(cx, |editor, cx| {
 5551        assert_text_with_selections(
 5552            editor,
 5553            indoc! {r#"
 5554                use mod1::mod2::{mod3, mo«ˇ»d4};
 5555
 5556                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5557                    let var1 = "te«ˇ»xt";
 5558                }
 5559            "#},
 5560            cx,
 5561        );
 5562    });
 5563
 5564    // Trying to shrink the selected syntax node one more time has no effect.
 5565    editor.update_in(cx, |editor, window, cx| {
 5566        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5567    });
 5568    editor.update_in(cx, |editor, _, cx| {
 5569        assert_text_with_selections(
 5570            editor,
 5571            indoc! {r#"
 5572                use mod1::mod2::{mod3, mo«ˇ»d4};
 5573
 5574                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5575                    let var1 = "te«ˇ»xt";
 5576                }
 5577            "#},
 5578            cx,
 5579        );
 5580    });
 5581
 5582    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5583    // a fold.
 5584    editor.update_in(cx, |editor, window, cx| {
 5585        editor.fold_creases(
 5586            vec![
 5587                Crease::simple(
 5588                    Point::new(0, 21)..Point::new(0, 24),
 5589                    FoldPlaceholder::test(),
 5590                ),
 5591                Crease::simple(
 5592                    Point::new(3, 20)..Point::new(3, 22),
 5593                    FoldPlaceholder::test(),
 5594                ),
 5595            ],
 5596            true,
 5597            window,
 5598            cx,
 5599        );
 5600        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5601    });
 5602    editor.update(cx, |editor, cx| {
 5603        assert_text_with_selections(
 5604            editor,
 5605            indoc! {r#"
 5606                use mod1::mod2::«{mod3, mod4}ˇ»;
 5607
 5608                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5609                    «let var1 = "text";ˇ»
 5610                }
 5611            "#},
 5612            cx,
 5613        );
 5614    });
 5615}
 5616
 5617#[gpui::test]
 5618async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5619    init_test(cx, |_| {});
 5620
 5621    let base_text = r#"
 5622        impl A {
 5623            // this is an uncommitted comment
 5624
 5625            fn b() {
 5626                c();
 5627            }
 5628
 5629            // this is another uncommitted comment
 5630
 5631            fn d() {
 5632                // e
 5633                // f
 5634            }
 5635        }
 5636
 5637        fn g() {
 5638            // h
 5639        }
 5640    "#
 5641    .unindent();
 5642
 5643    let text = r#"
 5644        ˇimpl A {
 5645
 5646            fn b() {
 5647                c();
 5648            }
 5649
 5650            fn d() {
 5651                // e
 5652                // f
 5653            }
 5654        }
 5655
 5656        fn g() {
 5657            // h
 5658        }
 5659    "#
 5660    .unindent();
 5661
 5662    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5663    cx.set_state(&text);
 5664    cx.set_diff_base(&base_text);
 5665    cx.update_editor(|editor, window, cx| {
 5666        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5667    });
 5668
 5669    cx.assert_state_with_diff(
 5670        "
 5671        ˇimpl A {
 5672      -     // this is an uncommitted comment
 5673
 5674            fn b() {
 5675                c();
 5676            }
 5677
 5678      -     // this is another uncommitted comment
 5679      -
 5680            fn d() {
 5681                // e
 5682                // f
 5683            }
 5684        }
 5685
 5686        fn g() {
 5687            // h
 5688        }
 5689    "
 5690        .unindent(),
 5691    );
 5692
 5693    let expected_display_text = "
 5694        impl A {
 5695            // this is an uncommitted comment
 5696
 5697            fn b() {
 5698 5699            }
 5700
 5701            // this is another uncommitted comment
 5702
 5703            fn d() {
 5704 5705            }
 5706        }
 5707
 5708        fn g() {
 5709 5710        }
 5711        "
 5712    .unindent();
 5713
 5714    cx.update_editor(|editor, window, cx| {
 5715        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5716        assert_eq!(editor.display_text(cx), expected_display_text);
 5717    });
 5718}
 5719
 5720#[gpui::test]
 5721async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5722    init_test(cx, |_| {});
 5723
 5724    let language = Arc::new(
 5725        Language::new(
 5726            LanguageConfig {
 5727                brackets: BracketPairConfig {
 5728                    pairs: vec![
 5729                        BracketPair {
 5730                            start: "{".to_string(),
 5731                            end: "}".to_string(),
 5732                            close: false,
 5733                            surround: false,
 5734                            newline: true,
 5735                        },
 5736                        BracketPair {
 5737                            start: "(".to_string(),
 5738                            end: ")".to_string(),
 5739                            close: false,
 5740                            surround: false,
 5741                            newline: true,
 5742                        },
 5743                    ],
 5744                    ..Default::default()
 5745                },
 5746                ..Default::default()
 5747            },
 5748            Some(tree_sitter_rust::LANGUAGE.into()),
 5749        )
 5750        .with_indents_query(
 5751            r#"
 5752                (_ "(" ")" @end) @indent
 5753                (_ "{" "}" @end) @indent
 5754            "#,
 5755        )
 5756        .unwrap(),
 5757    );
 5758
 5759    let text = "fn a() {}";
 5760
 5761    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5762    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5763    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5764    editor
 5765        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5766        .await;
 5767
 5768    editor.update_in(cx, |editor, window, cx| {
 5769        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5770        editor.newline(&Newline, window, cx);
 5771        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5772        assert_eq!(
 5773            editor.selections.ranges(cx),
 5774            &[
 5775                Point::new(1, 4)..Point::new(1, 4),
 5776                Point::new(3, 4)..Point::new(3, 4),
 5777                Point::new(5, 0)..Point::new(5, 0)
 5778            ]
 5779        );
 5780    });
 5781}
 5782
 5783#[gpui::test]
 5784async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5785    init_test(cx, |_| {});
 5786
 5787    {
 5788        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5789        cx.set_state(indoc! {"
 5790            impl A {
 5791
 5792                fn b() {}
 5793
 5794            «fn c() {
 5795
 5796            }ˇ»
 5797            }
 5798        "});
 5799
 5800        cx.update_editor(|editor, window, cx| {
 5801            editor.autoindent(&Default::default(), window, cx);
 5802        });
 5803
 5804        cx.assert_editor_state(indoc! {"
 5805            impl A {
 5806
 5807                fn b() {}
 5808
 5809                «fn c() {
 5810
 5811                }ˇ»
 5812            }
 5813        "});
 5814    }
 5815
 5816    {
 5817        let mut cx = EditorTestContext::new_multibuffer(
 5818            cx,
 5819            [indoc! { "
 5820                impl A {
 5821                «
 5822                // a
 5823                fn b(){}
 5824                »
 5825                «
 5826                    }
 5827                    fn c(){}
 5828                »
 5829            "}],
 5830        );
 5831
 5832        let buffer = cx.update_editor(|editor, _, cx| {
 5833            let buffer = editor.buffer().update(cx, |buffer, _| {
 5834                buffer.all_buffers().iter().next().unwrap().clone()
 5835            });
 5836            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5837            buffer
 5838        });
 5839
 5840        cx.run_until_parked();
 5841        cx.update_editor(|editor, window, cx| {
 5842            editor.select_all(&Default::default(), window, cx);
 5843            editor.autoindent(&Default::default(), window, cx)
 5844        });
 5845        cx.run_until_parked();
 5846
 5847        cx.update(|_, cx| {
 5848            pretty_assertions::assert_eq!(
 5849                buffer.read(cx).text(),
 5850                indoc! { "
 5851                    impl A {
 5852
 5853                        // a
 5854                        fn b(){}
 5855
 5856
 5857                    }
 5858                    fn c(){}
 5859
 5860                " }
 5861            )
 5862        });
 5863    }
 5864}
 5865
 5866#[gpui::test]
 5867async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5868    init_test(cx, |_| {});
 5869
 5870    let mut cx = EditorTestContext::new(cx).await;
 5871
 5872    let language = Arc::new(Language::new(
 5873        LanguageConfig {
 5874            brackets: BracketPairConfig {
 5875                pairs: vec![
 5876                    BracketPair {
 5877                        start: "{".to_string(),
 5878                        end: "}".to_string(),
 5879                        close: true,
 5880                        surround: true,
 5881                        newline: true,
 5882                    },
 5883                    BracketPair {
 5884                        start: "(".to_string(),
 5885                        end: ")".to_string(),
 5886                        close: true,
 5887                        surround: true,
 5888                        newline: true,
 5889                    },
 5890                    BracketPair {
 5891                        start: "/*".to_string(),
 5892                        end: " */".to_string(),
 5893                        close: true,
 5894                        surround: true,
 5895                        newline: true,
 5896                    },
 5897                    BracketPair {
 5898                        start: "[".to_string(),
 5899                        end: "]".to_string(),
 5900                        close: false,
 5901                        surround: false,
 5902                        newline: true,
 5903                    },
 5904                    BracketPair {
 5905                        start: "\"".to_string(),
 5906                        end: "\"".to_string(),
 5907                        close: true,
 5908                        surround: true,
 5909                        newline: false,
 5910                    },
 5911                    BracketPair {
 5912                        start: "<".to_string(),
 5913                        end: ">".to_string(),
 5914                        close: false,
 5915                        surround: true,
 5916                        newline: true,
 5917                    },
 5918                ],
 5919                ..Default::default()
 5920            },
 5921            autoclose_before: "})]".to_string(),
 5922            ..Default::default()
 5923        },
 5924        Some(tree_sitter_rust::LANGUAGE.into()),
 5925    ));
 5926
 5927    cx.language_registry().add(language.clone());
 5928    cx.update_buffer(|buffer, cx| {
 5929        buffer.set_language(Some(language), cx);
 5930    });
 5931
 5932    cx.set_state(
 5933        &r#"
 5934            🏀ˇ
 5935            εˇ
 5936            ❤️ˇ
 5937        "#
 5938        .unindent(),
 5939    );
 5940
 5941    // autoclose multiple nested brackets at multiple cursors
 5942    cx.update_editor(|editor, window, cx| {
 5943        editor.handle_input("{", window, cx);
 5944        editor.handle_input("{", window, cx);
 5945        editor.handle_input("{", window, cx);
 5946    });
 5947    cx.assert_editor_state(
 5948        &"
 5949            🏀{{{ˇ}}}
 5950            ε{{{ˇ}}}
 5951            ❤️{{{ˇ}}}
 5952        "
 5953        .unindent(),
 5954    );
 5955
 5956    // insert a different closing bracket
 5957    cx.update_editor(|editor, window, cx| {
 5958        editor.handle_input(")", window, cx);
 5959    });
 5960    cx.assert_editor_state(
 5961        &"
 5962            🏀{{{)ˇ}}}
 5963            ε{{{)ˇ}}}
 5964            ❤️{{{)ˇ}}}
 5965        "
 5966        .unindent(),
 5967    );
 5968
 5969    // skip over the auto-closed brackets when typing a closing bracket
 5970    cx.update_editor(|editor, window, cx| {
 5971        editor.move_right(&MoveRight, window, cx);
 5972        editor.handle_input("}", window, cx);
 5973        editor.handle_input("}", window, cx);
 5974        editor.handle_input("}", window, cx);
 5975    });
 5976    cx.assert_editor_state(
 5977        &"
 5978            🏀{{{)}}}}ˇ
 5979            ε{{{)}}}}ˇ
 5980            ❤️{{{)}}}}ˇ
 5981        "
 5982        .unindent(),
 5983    );
 5984
 5985    // autoclose multi-character pairs
 5986    cx.set_state(
 5987        &"
 5988            ˇ
 5989            ˇ
 5990        "
 5991        .unindent(),
 5992    );
 5993    cx.update_editor(|editor, window, cx| {
 5994        editor.handle_input("/", window, cx);
 5995        editor.handle_input("*", window, cx);
 5996    });
 5997    cx.assert_editor_state(
 5998        &"
 5999            /*ˇ */
 6000            /*ˇ */
 6001        "
 6002        .unindent(),
 6003    );
 6004
 6005    // one cursor autocloses a multi-character pair, one cursor
 6006    // does not autoclose.
 6007    cx.set_state(
 6008        &"
 6009 6010            ˇ
 6011        "
 6012        .unindent(),
 6013    );
 6014    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6015    cx.assert_editor_state(
 6016        &"
 6017            /*ˇ */
 6018 6019        "
 6020        .unindent(),
 6021    );
 6022
 6023    // Don't autoclose if the next character isn't whitespace and isn't
 6024    // listed in the language's "autoclose_before" section.
 6025    cx.set_state("ˇa b");
 6026    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6027    cx.assert_editor_state("{ˇa b");
 6028
 6029    // Don't autoclose if `close` is false for the bracket pair
 6030    cx.set_state("ˇ");
 6031    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6032    cx.assert_editor_state("");
 6033
 6034    // Surround with brackets if text is selected
 6035    cx.set_state("«aˇ» b");
 6036    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6037    cx.assert_editor_state("{«aˇ»} b");
 6038
 6039    // Autclose pair where the start and end characters are the same
 6040    cx.set_state("");
 6041    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6042    cx.assert_editor_state("a\"ˇ\"");
 6043    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6044    cx.assert_editor_state("a\"\"ˇ");
 6045
 6046    // Don't autoclose pair if autoclose is disabled
 6047    cx.set_state("ˇ");
 6048    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6049    cx.assert_editor_state("");
 6050
 6051    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6052    cx.set_state("«aˇ» b");
 6053    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6054    cx.assert_editor_state("<«aˇ»> b");
 6055}
 6056
 6057#[gpui::test]
 6058async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6059    init_test(cx, |settings| {
 6060        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6061    });
 6062
 6063    let mut cx = EditorTestContext::new(cx).await;
 6064
 6065    let language = Arc::new(Language::new(
 6066        LanguageConfig {
 6067            brackets: BracketPairConfig {
 6068                pairs: vec![
 6069                    BracketPair {
 6070                        start: "{".to_string(),
 6071                        end: "}".to_string(),
 6072                        close: true,
 6073                        surround: true,
 6074                        newline: true,
 6075                    },
 6076                    BracketPair {
 6077                        start: "(".to_string(),
 6078                        end: ")".to_string(),
 6079                        close: true,
 6080                        surround: true,
 6081                        newline: true,
 6082                    },
 6083                    BracketPair {
 6084                        start: "[".to_string(),
 6085                        end: "]".to_string(),
 6086                        close: false,
 6087                        surround: false,
 6088                        newline: true,
 6089                    },
 6090                ],
 6091                ..Default::default()
 6092            },
 6093            autoclose_before: "})]".to_string(),
 6094            ..Default::default()
 6095        },
 6096        Some(tree_sitter_rust::LANGUAGE.into()),
 6097    ));
 6098
 6099    cx.language_registry().add(language.clone());
 6100    cx.update_buffer(|buffer, cx| {
 6101        buffer.set_language(Some(language), cx);
 6102    });
 6103
 6104    cx.set_state(
 6105        &"
 6106            ˇ
 6107            ˇ
 6108            ˇ
 6109        "
 6110        .unindent(),
 6111    );
 6112
 6113    // ensure only matching closing brackets are skipped over
 6114    cx.update_editor(|editor, window, cx| {
 6115        editor.handle_input("}", window, cx);
 6116        editor.move_left(&MoveLeft, window, cx);
 6117        editor.handle_input(")", window, cx);
 6118        editor.move_left(&MoveLeft, window, cx);
 6119    });
 6120    cx.assert_editor_state(
 6121        &"
 6122            ˇ)}
 6123            ˇ)}
 6124            ˇ)}
 6125        "
 6126        .unindent(),
 6127    );
 6128
 6129    // skip-over closing brackets at multiple cursors
 6130    cx.update_editor(|editor, window, cx| {
 6131        editor.handle_input(")", window, cx);
 6132        editor.handle_input("}", window, cx);
 6133    });
 6134    cx.assert_editor_state(
 6135        &"
 6136            )}ˇ
 6137            )}ˇ
 6138            )}ˇ
 6139        "
 6140        .unindent(),
 6141    );
 6142
 6143    // ignore non-close brackets
 6144    cx.update_editor(|editor, window, cx| {
 6145        editor.handle_input("]", window, cx);
 6146        editor.move_left(&MoveLeft, window, cx);
 6147        editor.handle_input("]", window, cx);
 6148    });
 6149    cx.assert_editor_state(
 6150        &"
 6151            )}]ˇ]
 6152            )}]ˇ]
 6153            )}]ˇ]
 6154        "
 6155        .unindent(),
 6156    );
 6157}
 6158
 6159#[gpui::test]
 6160async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6161    init_test(cx, |_| {});
 6162
 6163    let mut cx = EditorTestContext::new(cx).await;
 6164
 6165    let html_language = Arc::new(
 6166        Language::new(
 6167            LanguageConfig {
 6168                name: "HTML".into(),
 6169                brackets: BracketPairConfig {
 6170                    pairs: vec![
 6171                        BracketPair {
 6172                            start: "<".into(),
 6173                            end: ">".into(),
 6174                            close: true,
 6175                            ..Default::default()
 6176                        },
 6177                        BracketPair {
 6178                            start: "{".into(),
 6179                            end: "}".into(),
 6180                            close: true,
 6181                            ..Default::default()
 6182                        },
 6183                        BracketPair {
 6184                            start: "(".into(),
 6185                            end: ")".into(),
 6186                            close: true,
 6187                            ..Default::default()
 6188                        },
 6189                    ],
 6190                    ..Default::default()
 6191                },
 6192                autoclose_before: "})]>".into(),
 6193                ..Default::default()
 6194            },
 6195            Some(tree_sitter_html::language()),
 6196        )
 6197        .with_injection_query(
 6198            r#"
 6199            (script_element
 6200                (raw_text) @injection.content
 6201                (#set! injection.language "javascript"))
 6202            "#,
 6203        )
 6204        .unwrap(),
 6205    );
 6206
 6207    let javascript_language = Arc::new(Language::new(
 6208        LanguageConfig {
 6209            name: "JavaScript".into(),
 6210            brackets: BracketPairConfig {
 6211                pairs: vec![
 6212                    BracketPair {
 6213                        start: "/*".into(),
 6214                        end: " */".into(),
 6215                        close: true,
 6216                        ..Default::default()
 6217                    },
 6218                    BracketPair {
 6219                        start: "{".into(),
 6220                        end: "}".into(),
 6221                        close: true,
 6222                        ..Default::default()
 6223                    },
 6224                    BracketPair {
 6225                        start: "(".into(),
 6226                        end: ")".into(),
 6227                        close: true,
 6228                        ..Default::default()
 6229                    },
 6230                ],
 6231                ..Default::default()
 6232            },
 6233            autoclose_before: "})]>".into(),
 6234            ..Default::default()
 6235        },
 6236        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6237    ));
 6238
 6239    cx.language_registry().add(html_language.clone());
 6240    cx.language_registry().add(javascript_language.clone());
 6241
 6242    cx.update_buffer(|buffer, cx| {
 6243        buffer.set_language(Some(html_language), cx);
 6244    });
 6245
 6246    cx.set_state(
 6247        &r#"
 6248            <body>ˇ
 6249                <script>
 6250                    var x = 1;ˇ
 6251                </script>
 6252            </body>ˇ
 6253        "#
 6254        .unindent(),
 6255    );
 6256
 6257    // Precondition: different languages are active at different locations.
 6258    cx.update_editor(|editor, window, cx| {
 6259        let snapshot = editor.snapshot(window, cx);
 6260        let cursors = editor.selections.ranges::<usize>(cx);
 6261        let languages = cursors
 6262            .iter()
 6263            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6264            .collect::<Vec<_>>();
 6265        assert_eq!(
 6266            languages,
 6267            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6268        );
 6269    });
 6270
 6271    // Angle brackets autoclose in HTML, but not JavaScript.
 6272    cx.update_editor(|editor, window, cx| {
 6273        editor.handle_input("<", window, cx);
 6274        editor.handle_input("a", window, cx);
 6275    });
 6276    cx.assert_editor_state(
 6277        &r#"
 6278            <body><aˇ>
 6279                <script>
 6280                    var x = 1;<aˇ
 6281                </script>
 6282            </body><aˇ>
 6283        "#
 6284        .unindent(),
 6285    );
 6286
 6287    // Curly braces and parens autoclose in both HTML and JavaScript.
 6288    cx.update_editor(|editor, window, cx| {
 6289        editor.handle_input(" b=", window, cx);
 6290        editor.handle_input("{", window, cx);
 6291        editor.handle_input("c", window, cx);
 6292        editor.handle_input("(", window, cx);
 6293    });
 6294    cx.assert_editor_state(
 6295        &r#"
 6296            <body><a b={c(ˇ)}>
 6297                <script>
 6298                    var x = 1;<a b={c(ˇ)}
 6299                </script>
 6300            </body><a b={c(ˇ)}>
 6301        "#
 6302        .unindent(),
 6303    );
 6304
 6305    // Brackets that were already autoclosed are skipped.
 6306    cx.update_editor(|editor, window, cx| {
 6307        editor.handle_input(")", window, cx);
 6308        editor.handle_input("d", window, cx);
 6309        editor.handle_input("}", window, cx);
 6310    });
 6311    cx.assert_editor_state(
 6312        &r#"
 6313            <body><a b={c()d}ˇ>
 6314                <script>
 6315                    var x = 1;<a b={c()d}ˇ
 6316                </script>
 6317            </body><a b={c()d}ˇ>
 6318        "#
 6319        .unindent(),
 6320    );
 6321    cx.update_editor(|editor, window, cx| {
 6322        editor.handle_input(">", window, cx);
 6323    });
 6324    cx.assert_editor_state(
 6325        &r#"
 6326            <body><a b={c()d}>ˇ
 6327                <script>
 6328                    var x = 1;<a b={c()d}>ˇ
 6329                </script>
 6330            </body><a b={c()d}>ˇ
 6331        "#
 6332        .unindent(),
 6333    );
 6334
 6335    // Reset
 6336    cx.set_state(
 6337        &r#"
 6338            <body>ˇ
 6339                <script>
 6340                    var x = 1;ˇ
 6341                </script>
 6342            </body>ˇ
 6343        "#
 6344        .unindent(),
 6345    );
 6346
 6347    cx.update_editor(|editor, window, cx| {
 6348        editor.handle_input("<", window, cx);
 6349    });
 6350    cx.assert_editor_state(
 6351        &r#"
 6352            <body><ˇ>
 6353                <script>
 6354                    var x = 1;<ˇ
 6355                </script>
 6356            </body><ˇ>
 6357        "#
 6358        .unindent(),
 6359    );
 6360
 6361    // When backspacing, the closing angle brackets are removed.
 6362    cx.update_editor(|editor, window, cx| {
 6363        editor.backspace(&Backspace, window, cx);
 6364    });
 6365    cx.assert_editor_state(
 6366        &r#"
 6367            <body>ˇ
 6368                <script>
 6369                    var x = 1;ˇ
 6370                </script>
 6371            </body>ˇ
 6372        "#
 6373        .unindent(),
 6374    );
 6375
 6376    // Block comments autoclose in JavaScript, but not HTML.
 6377    cx.update_editor(|editor, window, cx| {
 6378        editor.handle_input("/", window, cx);
 6379        editor.handle_input("*", window, cx);
 6380    });
 6381    cx.assert_editor_state(
 6382        &r#"
 6383            <body>/*ˇ
 6384                <script>
 6385                    var x = 1;/*ˇ */
 6386                </script>
 6387            </body>/*ˇ
 6388        "#
 6389        .unindent(),
 6390    );
 6391}
 6392
 6393#[gpui::test]
 6394async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6395    init_test(cx, |_| {});
 6396
 6397    let mut cx = EditorTestContext::new(cx).await;
 6398
 6399    let rust_language = Arc::new(
 6400        Language::new(
 6401            LanguageConfig {
 6402                name: "Rust".into(),
 6403                brackets: serde_json::from_value(json!([
 6404                    { "start": "{", "end": "}", "close": true, "newline": true },
 6405                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6406                ]))
 6407                .unwrap(),
 6408                autoclose_before: "})]>".into(),
 6409                ..Default::default()
 6410            },
 6411            Some(tree_sitter_rust::LANGUAGE.into()),
 6412        )
 6413        .with_override_query("(string_literal) @string")
 6414        .unwrap(),
 6415    );
 6416
 6417    cx.language_registry().add(rust_language.clone());
 6418    cx.update_buffer(|buffer, cx| {
 6419        buffer.set_language(Some(rust_language), cx);
 6420    });
 6421
 6422    cx.set_state(
 6423        &r#"
 6424            let x = ˇ
 6425        "#
 6426        .unindent(),
 6427    );
 6428
 6429    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6430    cx.update_editor(|editor, window, cx| {
 6431        editor.handle_input("\"", window, cx);
 6432    });
 6433    cx.assert_editor_state(
 6434        &r#"
 6435            let x = "ˇ"
 6436        "#
 6437        .unindent(),
 6438    );
 6439
 6440    // Inserting another quotation mark. The cursor moves across the existing
 6441    // automatically-inserted quotation mark.
 6442    cx.update_editor(|editor, window, cx| {
 6443        editor.handle_input("\"", window, cx);
 6444    });
 6445    cx.assert_editor_state(
 6446        &r#"
 6447            let x = ""ˇ
 6448        "#
 6449        .unindent(),
 6450    );
 6451
 6452    // Reset
 6453    cx.set_state(
 6454        &r#"
 6455            let x = ˇ
 6456        "#
 6457        .unindent(),
 6458    );
 6459
 6460    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6461    cx.update_editor(|editor, window, cx| {
 6462        editor.handle_input("\"", window, cx);
 6463        editor.handle_input(" ", window, cx);
 6464        editor.move_left(&Default::default(), window, cx);
 6465        editor.handle_input("\\", window, cx);
 6466        editor.handle_input("\"", window, cx);
 6467    });
 6468    cx.assert_editor_state(
 6469        &r#"
 6470            let x = "\"ˇ "
 6471        "#
 6472        .unindent(),
 6473    );
 6474
 6475    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6476    // mark. Nothing is inserted.
 6477    cx.update_editor(|editor, window, cx| {
 6478        editor.move_right(&Default::default(), window, cx);
 6479        editor.handle_input("\"", window, cx);
 6480    });
 6481    cx.assert_editor_state(
 6482        &r#"
 6483            let x = "\" "ˇ
 6484        "#
 6485        .unindent(),
 6486    );
 6487}
 6488
 6489#[gpui::test]
 6490async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6491    init_test(cx, |_| {});
 6492
 6493    let language = Arc::new(Language::new(
 6494        LanguageConfig {
 6495            brackets: BracketPairConfig {
 6496                pairs: vec![
 6497                    BracketPair {
 6498                        start: "{".to_string(),
 6499                        end: "}".to_string(),
 6500                        close: true,
 6501                        surround: true,
 6502                        newline: true,
 6503                    },
 6504                    BracketPair {
 6505                        start: "/* ".to_string(),
 6506                        end: "*/".to_string(),
 6507                        close: true,
 6508                        surround: true,
 6509                        ..Default::default()
 6510                    },
 6511                ],
 6512                ..Default::default()
 6513            },
 6514            ..Default::default()
 6515        },
 6516        Some(tree_sitter_rust::LANGUAGE.into()),
 6517    ));
 6518
 6519    let text = r#"
 6520        a
 6521        b
 6522        c
 6523    "#
 6524    .unindent();
 6525
 6526    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6527    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6528    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6529    editor
 6530        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6531        .await;
 6532
 6533    editor.update_in(cx, |editor, window, cx| {
 6534        editor.change_selections(None, window, cx, |s| {
 6535            s.select_display_ranges([
 6536                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6537                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6538                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6539            ])
 6540        });
 6541
 6542        editor.handle_input("{", window, cx);
 6543        editor.handle_input("{", window, cx);
 6544        editor.handle_input("{", window, cx);
 6545        assert_eq!(
 6546            editor.text(cx),
 6547            "
 6548                {{{a}}}
 6549                {{{b}}}
 6550                {{{c}}}
 6551            "
 6552            .unindent()
 6553        );
 6554        assert_eq!(
 6555            editor.selections.display_ranges(cx),
 6556            [
 6557                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6558                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6559                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6560            ]
 6561        );
 6562
 6563        editor.undo(&Undo, window, cx);
 6564        editor.undo(&Undo, window, cx);
 6565        editor.undo(&Undo, window, cx);
 6566        assert_eq!(
 6567            editor.text(cx),
 6568            "
 6569                a
 6570                b
 6571                c
 6572            "
 6573            .unindent()
 6574        );
 6575        assert_eq!(
 6576            editor.selections.display_ranges(cx),
 6577            [
 6578                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6579                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6580                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6581            ]
 6582        );
 6583
 6584        // Ensure inserting the first character of a multi-byte bracket pair
 6585        // doesn't surround the selections with the bracket.
 6586        editor.handle_input("/", window, cx);
 6587        assert_eq!(
 6588            editor.text(cx),
 6589            "
 6590                /
 6591                /
 6592                /
 6593            "
 6594            .unindent()
 6595        );
 6596        assert_eq!(
 6597            editor.selections.display_ranges(cx),
 6598            [
 6599                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6600                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6601                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6602            ]
 6603        );
 6604
 6605        editor.undo(&Undo, window, cx);
 6606        assert_eq!(
 6607            editor.text(cx),
 6608            "
 6609                a
 6610                b
 6611                c
 6612            "
 6613            .unindent()
 6614        );
 6615        assert_eq!(
 6616            editor.selections.display_ranges(cx),
 6617            [
 6618                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6619                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6620                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6621            ]
 6622        );
 6623
 6624        // Ensure inserting the last character of a multi-byte bracket pair
 6625        // doesn't surround the selections with the bracket.
 6626        editor.handle_input("*", window, cx);
 6627        assert_eq!(
 6628            editor.text(cx),
 6629            "
 6630                *
 6631                *
 6632                *
 6633            "
 6634            .unindent()
 6635        );
 6636        assert_eq!(
 6637            editor.selections.display_ranges(cx),
 6638            [
 6639                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6640                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6641                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6642            ]
 6643        );
 6644    });
 6645}
 6646
 6647#[gpui::test]
 6648async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6649    init_test(cx, |_| {});
 6650
 6651    let language = Arc::new(Language::new(
 6652        LanguageConfig {
 6653            brackets: BracketPairConfig {
 6654                pairs: vec![BracketPair {
 6655                    start: "{".to_string(),
 6656                    end: "}".to_string(),
 6657                    close: true,
 6658                    surround: true,
 6659                    newline: true,
 6660                }],
 6661                ..Default::default()
 6662            },
 6663            autoclose_before: "}".to_string(),
 6664            ..Default::default()
 6665        },
 6666        Some(tree_sitter_rust::LANGUAGE.into()),
 6667    ));
 6668
 6669    let text = r#"
 6670        a
 6671        b
 6672        c
 6673    "#
 6674    .unindent();
 6675
 6676    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6677    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6678    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6679    editor
 6680        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6681        .await;
 6682
 6683    editor.update_in(cx, |editor, window, cx| {
 6684        editor.change_selections(None, window, cx, |s| {
 6685            s.select_ranges([
 6686                Point::new(0, 1)..Point::new(0, 1),
 6687                Point::new(1, 1)..Point::new(1, 1),
 6688                Point::new(2, 1)..Point::new(2, 1),
 6689            ])
 6690        });
 6691
 6692        editor.handle_input("{", window, cx);
 6693        editor.handle_input("{", window, cx);
 6694        editor.handle_input("_", window, cx);
 6695        assert_eq!(
 6696            editor.text(cx),
 6697            "
 6698                a{{_}}
 6699                b{{_}}
 6700                c{{_}}
 6701            "
 6702            .unindent()
 6703        );
 6704        assert_eq!(
 6705            editor.selections.ranges::<Point>(cx),
 6706            [
 6707                Point::new(0, 4)..Point::new(0, 4),
 6708                Point::new(1, 4)..Point::new(1, 4),
 6709                Point::new(2, 4)..Point::new(2, 4)
 6710            ]
 6711        );
 6712
 6713        editor.backspace(&Default::default(), window, cx);
 6714        editor.backspace(&Default::default(), window, cx);
 6715        assert_eq!(
 6716            editor.text(cx),
 6717            "
 6718                a{}
 6719                b{}
 6720                c{}
 6721            "
 6722            .unindent()
 6723        );
 6724        assert_eq!(
 6725            editor.selections.ranges::<Point>(cx),
 6726            [
 6727                Point::new(0, 2)..Point::new(0, 2),
 6728                Point::new(1, 2)..Point::new(1, 2),
 6729                Point::new(2, 2)..Point::new(2, 2)
 6730            ]
 6731        );
 6732
 6733        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6734        assert_eq!(
 6735            editor.text(cx),
 6736            "
 6737                a
 6738                b
 6739                c
 6740            "
 6741            .unindent()
 6742        );
 6743        assert_eq!(
 6744            editor.selections.ranges::<Point>(cx),
 6745            [
 6746                Point::new(0, 1)..Point::new(0, 1),
 6747                Point::new(1, 1)..Point::new(1, 1),
 6748                Point::new(2, 1)..Point::new(2, 1)
 6749            ]
 6750        );
 6751    });
 6752}
 6753
 6754#[gpui::test]
 6755async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6756    init_test(cx, |settings| {
 6757        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6758    });
 6759
 6760    let mut cx = EditorTestContext::new(cx).await;
 6761
 6762    let language = Arc::new(Language::new(
 6763        LanguageConfig {
 6764            brackets: BracketPairConfig {
 6765                pairs: vec![
 6766                    BracketPair {
 6767                        start: "{".to_string(),
 6768                        end: "}".to_string(),
 6769                        close: true,
 6770                        surround: true,
 6771                        newline: true,
 6772                    },
 6773                    BracketPair {
 6774                        start: "(".to_string(),
 6775                        end: ")".to_string(),
 6776                        close: true,
 6777                        surround: true,
 6778                        newline: true,
 6779                    },
 6780                    BracketPair {
 6781                        start: "[".to_string(),
 6782                        end: "]".to_string(),
 6783                        close: false,
 6784                        surround: true,
 6785                        newline: true,
 6786                    },
 6787                ],
 6788                ..Default::default()
 6789            },
 6790            autoclose_before: "})]".to_string(),
 6791            ..Default::default()
 6792        },
 6793        Some(tree_sitter_rust::LANGUAGE.into()),
 6794    ));
 6795
 6796    cx.language_registry().add(language.clone());
 6797    cx.update_buffer(|buffer, cx| {
 6798        buffer.set_language(Some(language), cx);
 6799    });
 6800
 6801    cx.set_state(
 6802        &"
 6803            {(ˇ)}
 6804            [[ˇ]]
 6805            {(ˇ)}
 6806        "
 6807        .unindent(),
 6808    );
 6809
 6810    cx.update_editor(|editor, window, cx| {
 6811        editor.backspace(&Default::default(), window, cx);
 6812        editor.backspace(&Default::default(), window, cx);
 6813    });
 6814
 6815    cx.assert_editor_state(
 6816        &"
 6817            ˇ
 6818            ˇ]]
 6819            ˇ
 6820        "
 6821        .unindent(),
 6822    );
 6823
 6824    cx.update_editor(|editor, window, cx| {
 6825        editor.handle_input("{", window, cx);
 6826        editor.handle_input("{", window, cx);
 6827        editor.move_right(&MoveRight, window, cx);
 6828        editor.move_right(&MoveRight, window, cx);
 6829        editor.move_left(&MoveLeft, window, cx);
 6830        editor.move_left(&MoveLeft, window, cx);
 6831        editor.backspace(&Default::default(), window, cx);
 6832    });
 6833
 6834    cx.assert_editor_state(
 6835        &"
 6836            {ˇ}
 6837            {ˇ}]]
 6838            {ˇ}
 6839        "
 6840        .unindent(),
 6841    );
 6842
 6843    cx.update_editor(|editor, window, cx| {
 6844        editor.backspace(&Default::default(), window, cx);
 6845    });
 6846
 6847    cx.assert_editor_state(
 6848        &"
 6849            ˇ
 6850            ˇ]]
 6851            ˇ
 6852        "
 6853        .unindent(),
 6854    );
 6855}
 6856
 6857#[gpui::test]
 6858async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6859    init_test(cx, |_| {});
 6860
 6861    let language = Arc::new(Language::new(
 6862        LanguageConfig::default(),
 6863        Some(tree_sitter_rust::LANGUAGE.into()),
 6864    ));
 6865
 6866    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6867    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6868    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6869    editor
 6870        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6871        .await;
 6872
 6873    editor.update_in(cx, |editor, window, cx| {
 6874        editor.set_auto_replace_emoji_shortcode(true);
 6875
 6876        editor.handle_input("Hello ", window, cx);
 6877        editor.handle_input(":wave", window, cx);
 6878        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6879
 6880        editor.handle_input(":", window, cx);
 6881        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6882
 6883        editor.handle_input(" :smile", window, cx);
 6884        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6885
 6886        editor.handle_input(":", window, cx);
 6887        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6888
 6889        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6890        editor.handle_input(":wave", window, cx);
 6891        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6892
 6893        editor.handle_input(":", window, cx);
 6894        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6895
 6896        editor.handle_input(":1", window, cx);
 6897        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6898
 6899        editor.handle_input(":", window, cx);
 6900        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6901
 6902        // Ensure shortcode does not get replaced when it is part of a word
 6903        editor.handle_input(" Test:wave", window, cx);
 6904        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6905
 6906        editor.handle_input(":", window, cx);
 6907        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6908
 6909        editor.set_auto_replace_emoji_shortcode(false);
 6910
 6911        // Ensure shortcode does not get replaced when auto replace is off
 6912        editor.handle_input(" :wave", window, cx);
 6913        assert_eq!(
 6914            editor.text(cx),
 6915            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6916        );
 6917
 6918        editor.handle_input(":", window, cx);
 6919        assert_eq!(
 6920            editor.text(cx),
 6921            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6922        );
 6923    });
 6924}
 6925
 6926#[gpui::test]
 6927async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6928    init_test(cx, |_| {});
 6929
 6930    let (text, insertion_ranges) = marked_text_ranges(
 6931        indoc! {"
 6932            ˇ
 6933        "},
 6934        false,
 6935    );
 6936
 6937    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6938    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6939
 6940    _ = editor.update_in(cx, |editor, window, cx| {
 6941        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6942
 6943        editor
 6944            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6945            .unwrap();
 6946
 6947        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6948            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6949            assert_eq!(editor.text(cx), expected_text);
 6950            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6951        }
 6952
 6953        assert(
 6954            editor,
 6955            cx,
 6956            indoc! {"
 6957            type «» =•
 6958            "},
 6959        );
 6960
 6961        assert!(editor.context_menu_visible(), "There should be a matches");
 6962    });
 6963}
 6964
 6965#[gpui::test]
 6966async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6967    init_test(cx, |_| {});
 6968
 6969    let (text, insertion_ranges) = marked_text_ranges(
 6970        indoc! {"
 6971            a.ˇ b
 6972            a.ˇ b
 6973            a.ˇ b
 6974        "},
 6975        false,
 6976    );
 6977
 6978    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6979    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6980
 6981    editor.update_in(cx, |editor, window, cx| {
 6982        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6983
 6984        editor
 6985            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6986            .unwrap();
 6987
 6988        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6989            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6990            assert_eq!(editor.text(cx), expected_text);
 6991            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6992        }
 6993
 6994        assert(
 6995            editor,
 6996            cx,
 6997            indoc! {"
 6998                a.f(«one», two, «three») b
 6999                a.f(«one», two, «three») b
 7000                a.f(«one», two, «three») b
 7001            "},
 7002        );
 7003
 7004        // Can't move earlier than the first tab stop
 7005        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7006        assert(
 7007            editor,
 7008            cx,
 7009            indoc! {"
 7010                a.f(«one», two, «three») b
 7011                a.f(«one», two, «three») b
 7012                a.f(«one», two, «three») b
 7013            "},
 7014        );
 7015
 7016        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7017        assert(
 7018            editor,
 7019            cx,
 7020            indoc! {"
 7021                a.f(one, «two», three) b
 7022                a.f(one, «two», three) b
 7023                a.f(one, «two», three) b
 7024            "},
 7025        );
 7026
 7027        editor.move_to_prev_snippet_tabstop(window, cx);
 7028        assert(
 7029            editor,
 7030            cx,
 7031            indoc! {"
 7032                a.f(«one», two, «three») b
 7033                a.f(«one», two, «three») b
 7034                a.f(«one», two, «three») b
 7035            "},
 7036        );
 7037
 7038        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7039        assert(
 7040            editor,
 7041            cx,
 7042            indoc! {"
 7043                a.f(one, «two», three) b
 7044                a.f(one, «two», three) b
 7045                a.f(one, «two», three) b
 7046            "},
 7047        );
 7048        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7049        assert(
 7050            editor,
 7051            cx,
 7052            indoc! {"
 7053                a.f(one, two, three)ˇ b
 7054                a.f(one, two, three)ˇ b
 7055                a.f(one, two, three)ˇ b
 7056            "},
 7057        );
 7058
 7059        // As soon as the last tab stop is reached, snippet state is gone
 7060        editor.move_to_prev_snippet_tabstop(window, cx);
 7061        assert(
 7062            editor,
 7063            cx,
 7064            indoc! {"
 7065                a.f(one, two, three)ˇ b
 7066                a.f(one, two, three)ˇ b
 7067                a.f(one, two, three)ˇ b
 7068            "},
 7069        );
 7070    });
 7071}
 7072
 7073#[gpui::test]
 7074async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7075    init_test(cx, |_| {});
 7076
 7077    let fs = FakeFs::new(cx.executor());
 7078    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7079
 7080    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7081
 7082    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7083    language_registry.add(rust_lang());
 7084    let mut fake_servers = language_registry.register_fake_lsp(
 7085        "Rust",
 7086        FakeLspAdapter {
 7087            capabilities: lsp::ServerCapabilities {
 7088                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7089                ..Default::default()
 7090            },
 7091            ..Default::default()
 7092        },
 7093    );
 7094
 7095    let buffer = project
 7096        .update(cx, |project, cx| {
 7097            project.open_local_buffer(path!("/file.rs"), cx)
 7098        })
 7099        .await
 7100        .unwrap();
 7101
 7102    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7103    let (editor, cx) = cx.add_window_view(|window, cx| {
 7104        build_editor_with_project(project.clone(), buffer, window, cx)
 7105    });
 7106    editor.update_in(cx, |editor, window, cx| {
 7107        editor.set_text("one\ntwo\nthree\n", window, cx)
 7108    });
 7109    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7110
 7111    cx.executor().start_waiting();
 7112    let fake_server = fake_servers.next().await.unwrap();
 7113
 7114    let save = editor
 7115        .update_in(cx, |editor, window, cx| {
 7116            editor.save(true, project.clone(), window, cx)
 7117        })
 7118        .unwrap();
 7119    fake_server
 7120        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7121            assert_eq!(
 7122                params.text_document.uri,
 7123                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7124            );
 7125            assert_eq!(params.options.tab_size, 4);
 7126            Ok(Some(vec![lsp::TextEdit::new(
 7127                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7128                ", ".to_string(),
 7129            )]))
 7130        })
 7131        .next()
 7132        .await;
 7133    cx.executor().start_waiting();
 7134    save.await;
 7135
 7136    assert_eq!(
 7137        editor.update(cx, |editor, cx| editor.text(cx)),
 7138        "one, two\nthree\n"
 7139    );
 7140    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7141
 7142    editor.update_in(cx, |editor, window, cx| {
 7143        editor.set_text("one\ntwo\nthree\n", window, cx)
 7144    });
 7145    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7146
 7147    // Ensure we can still save even if formatting hangs.
 7148    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7149        assert_eq!(
 7150            params.text_document.uri,
 7151            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7152        );
 7153        futures::future::pending::<()>().await;
 7154        unreachable!()
 7155    });
 7156    let save = editor
 7157        .update_in(cx, |editor, window, cx| {
 7158            editor.save(true, project.clone(), window, cx)
 7159        })
 7160        .unwrap();
 7161    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7162    cx.executor().start_waiting();
 7163    save.await;
 7164    assert_eq!(
 7165        editor.update(cx, |editor, cx| editor.text(cx)),
 7166        "one\ntwo\nthree\n"
 7167    );
 7168    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7169
 7170    // For non-dirty buffer, no formatting request should be sent
 7171    let save = editor
 7172        .update_in(cx, |editor, window, cx| {
 7173            editor.save(true, project.clone(), window, cx)
 7174        })
 7175        .unwrap();
 7176    let _pending_format_request = fake_server
 7177        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7178            panic!("Should not be invoked on non-dirty buffer");
 7179        })
 7180        .next();
 7181    cx.executor().start_waiting();
 7182    save.await;
 7183
 7184    // Set rust language override and assert overridden tabsize is sent to language server
 7185    update_test_language_settings(cx, |settings| {
 7186        settings.languages.insert(
 7187            "Rust".into(),
 7188            LanguageSettingsContent {
 7189                tab_size: NonZeroU32::new(8),
 7190                ..Default::default()
 7191            },
 7192        );
 7193    });
 7194
 7195    editor.update_in(cx, |editor, window, cx| {
 7196        editor.set_text("somehting_new\n", window, cx)
 7197    });
 7198    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7199    let save = editor
 7200        .update_in(cx, |editor, window, cx| {
 7201            editor.save(true, project.clone(), window, cx)
 7202        })
 7203        .unwrap();
 7204    fake_server
 7205        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7206            assert_eq!(
 7207                params.text_document.uri,
 7208                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7209            );
 7210            assert_eq!(params.options.tab_size, 8);
 7211            Ok(Some(vec![]))
 7212        })
 7213        .next()
 7214        .await;
 7215    cx.executor().start_waiting();
 7216    save.await;
 7217}
 7218
 7219#[gpui::test]
 7220async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7221    init_test(cx, |_| {});
 7222
 7223    let cols = 4;
 7224    let rows = 10;
 7225    let sample_text_1 = sample_text(rows, cols, 'a');
 7226    assert_eq!(
 7227        sample_text_1,
 7228        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7229    );
 7230    let sample_text_2 = sample_text(rows, cols, 'l');
 7231    assert_eq!(
 7232        sample_text_2,
 7233        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7234    );
 7235    let sample_text_3 = sample_text(rows, cols, 'v');
 7236    assert_eq!(
 7237        sample_text_3,
 7238        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7239    );
 7240
 7241    let fs = FakeFs::new(cx.executor());
 7242    fs.insert_tree(
 7243        path!("/a"),
 7244        json!({
 7245            "main.rs": sample_text_1,
 7246            "other.rs": sample_text_2,
 7247            "lib.rs": sample_text_3,
 7248        }),
 7249    )
 7250    .await;
 7251
 7252    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7253    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7254    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7255
 7256    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7257    language_registry.add(rust_lang());
 7258    let mut fake_servers = language_registry.register_fake_lsp(
 7259        "Rust",
 7260        FakeLspAdapter {
 7261            capabilities: lsp::ServerCapabilities {
 7262                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7263                ..Default::default()
 7264            },
 7265            ..Default::default()
 7266        },
 7267    );
 7268
 7269    let worktree = project.update(cx, |project, cx| {
 7270        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7271        assert_eq!(worktrees.len(), 1);
 7272        worktrees.pop().unwrap()
 7273    });
 7274    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7275
 7276    let buffer_1 = project
 7277        .update(cx, |project, cx| {
 7278            project.open_buffer((worktree_id, "main.rs"), cx)
 7279        })
 7280        .await
 7281        .unwrap();
 7282    let buffer_2 = project
 7283        .update(cx, |project, cx| {
 7284            project.open_buffer((worktree_id, "other.rs"), cx)
 7285        })
 7286        .await
 7287        .unwrap();
 7288    let buffer_3 = project
 7289        .update(cx, |project, cx| {
 7290            project.open_buffer((worktree_id, "lib.rs"), cx)
 7291        })
 7292        .await
 7293        .unwrap();
 7294
 7295    let multi_buffer = cx.new(|cx| {
 7296        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7297        multi_buffer.push_excerpts(
 7298            buffer_1.clone(),
 7299            [
 7300                ExcerptRange {
 7301                    context: Point::new(0, 0)..Point::new(3, 0),
 7302                    primary: None,
 7303                },
 7304                ExcerptRange {
 7305                    context: Point::new(5, 0)..Point::new(7, 0),
 7306                    primary: None,
 7307                },
 7308                ExcerptRange {
 7309                    context: Point::new(9, 0)..Point::new(10, 4),
 7310                    primary: None,
 7311                },
 7312            ],
 7313            cx,
 7314        );
 7315        multi_buffer.push_excerpts(
 7316            buffer_2.clone(),
 7317            [
 7318                ExcerptRange {
 7319                    context: Point::new(0, 0)..Point::new(3, 0),
 7320                    primary: None,
 7321                },
 7322                ExcerptRange {
 7323                    context: Point::new(5, 0)..Point::new(7, 0),
 7324                    primary: None,
 7325                },
 7326                ExcerptRange {
 7327                    context: Point::new(9, 0)..Point::new(10, 4),
 7328                    primary: None,
 7329                },
 7330            ],
 7331            cx,
 7332        );
 7333        multi_buffer.push_excerpts(
 7334            buffer_3.clone(),
 7335            [
 7336                ExcerptRange {
 7337                    context: Point::new(0, 0)..Point::new(3, 0),
 7338                    primary: None,
 7339                },
 7340                ExcerptRange {
 7341                    context: Point::new(5, 0)..Point::new(7, 0),
 7342                    primary: None,
 7343                },
 7344                ExcerptRange {
 7345                    context: Point::new(9, 0)..Point::new(10, 4),
 7346                    primary: None,
 7347                },
 7348            ],
 7349            cx,
 7350        );
 7351        multi_buffer
 7352    });
 7353    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7354        Editor::new(
 7355            EditorMode::Full,
 7356            multi_buffer,
 7357            Some(project.clone()),
 7358            true,
 7359            window,
 7360            cx,
 7361        )
 7362    });
 7363
 7364    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7365        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7366            s.select_ranges(Some(1..2))
 7367        });
 7368        editor.insert("|one|two|three|", window, cx);
 7369    });
 7370    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7371    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7372        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7373            s.select_ranges(Some(60..70))
 7374        });
 7375        editor.insert("|four|five|six|", window, cx);
 7376    });
 7377    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7378
 7379    // First two buffers should be edited, but not the third one.
 7380    assert_eq!(
 7381        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7382        "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}",
 7383    );
 7384    buffer_1.update(cx, |buffer, _| {
 7385        assert!(buffer.is_dirty());
 7386        assert_eq!(
 7387            buffer.text(),
 7388            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7389        )
 7390    });
 7391    buffer_2.update(cx, |buffer, _| {
 7392        assert!(buffer.is_dirty());
 7393        assert_eq!(
 7394            buffer.text(),
 7395            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7396        )
 7397    });
 7398    buffer_3.update(cx, |buffer, _| {
 7399        assert!(!buffer.is_dirty());
 7400        assert_eq!(buffer.text(), sample_text_3,)
 7401    });
 7402    cx.executor().run_until_parked();
 7403
 7404    cx.executor().start_waiting();
 7405    let save = multi_buffer_editor
 7406        .update_in(cx, |editor, window, cx| {
 7407            editor.save(true, project.clone(), window, cx)
 7408        })
 7409        .unwrap();
 7410
 7411    let fake_server = fake_servers.next().await.unwrap();
 7412    fake_server
 7413        .server
 7414        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7415            Ok(Some(vec![lsp::TextEdit::new(
 7416                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7417                format!("[{} formatted]", params.text_document.uri),
 7418            )]))
 7419        })
 7420        .detach();
 7421    save.await;
 7422
 7423    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7424    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7425    assert_eq!(
 7426        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7427        uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"),
 7428    );
 7429    buffer_1.update(cx, |buffer, _| {
 7430        assert!(!buffer.is_dirty());
 7431        assert_eq!(
 7432            buffer.text(),
 7433            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7434        )
 7435    });
 7436    buffer_2.update(cx, |buffer, _| {
 7437        assert!(!buffer.is_dirty());
 7438        assert_eq!(
 7439            buffer.text(),
 7440            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7441        )
 7442    });
 7443    buffer_3.update(cx, |buffer, _| {
 7444        assert!(!buffer.is_dirty());
 7445        assert_eq!(buffer.text(), sample_text_3,)
 7446    });
 7447}
 7448
 7449#[gpui::test]
 7450async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7451    init_test(cx, |_| {});
 7452
 7453    let fs = FakeFs::new(cx.executor());
 7454    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7455
 7456    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7457
 7458    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7459    language_registry.add(rust_lang());
 7460    let mut fake_servers = language_registry.register_fake_lsp(
 7461        "Rust",
 7462        FakeLspAdapter {
 7463            capabilities: lsp::ServerCapabilities {
 7464                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7465                ..Default::default()
 7466            },
 7467            ..Default::default()
 7468        },
 7469    );
 7470
 7471    let buffer = project
 7472        .update(cx, |project, cx| {
 7473            project.open_local_buffer(path!("/file.rs"), cx)
 7474        })
 7475        .await
 7476        .unwrap();
 7477
 7478    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7479    let (editor, cx) = cx.add_window_view(|window, cx| {
 7480        build_editor_with_project(project.clone(), buffer, window, cx)
 7481    });
 7482    editor.update_in(cx, |editor, window, cx| {
 7483        editor.set_text("one\ntwo\nthree\n", window, cx)
 7484    });
 7485    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7486
 7487    cx.executor().start_waiting();
 7488    let fake_server = fake_servers.next().await.unwrap();
 7489
 7490    let save = editor
 7491        .update_in(cx, |editor, window, cx| {
 7492            editor.save(true, project.clone(), window, cx)
 7493        })
 7494        .unwrap();
 7495    fake_server
 7496        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7497            assert_eq!(
 7498                params.text_document.uri,
 7499                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7500            );
 7501            assert_eq!(params.options.tab_size, 4);
 7502            Ok(Some(vec![lsp::TextEdit::new(
 7503                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7504                ", ".to_string(),
 7505            )]))
 7506        })
 7507        .next()
 7508        .await;
 7509    cx.executor().start_waiting();
 7510    save.await;
 7511    assert_eq!(
 7512        editor.update(cx, |editor, cx| editor.text(cx)),
 7513        "one, two\nthree\n"
 7514    );
 7515    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7516
 7517    editor.update_in(cx, |editor, window, cx| {
 7518        editor.set_text("one\ntwo\nthree\n", window, cx)
 7519    });
 7520    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7521
 7522    // Ensure we can still save even if formatting hangs.
 7523    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7524        move |params, _| async move {
 7525            assert_eq!(
 7526                params.text_document.uri,
 7527                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7528            );
 7529            futures::future::pending::<()>().await;
 7530            unreachable!()
 7531        },
 7532    );
 7533    let save = editor
 7534        .update_in(cx, |editor, window, cx| {
 7535            editor.save(true, project.clone(), window, cx)
 7536        })
 7537        .unwrap();
 7538    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7539    cx.executor().start_waiting();
 7540    save.await;
 7541    assert_eq!(
 7542        editor.update(cx, |editor, cx| editor.text(cx)),
 7543        "one\ntwo\nthree\n"
 7544    );
 7545    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7546
 7547    // For non-dirty buffer, no formatting request should be sent
 7548    let save = editor
 7549        .update_in(cx, |editor, window, cx| {
 7550            editor.save(true, project.clone(), window, cx)
 7551        })
 7552        .unwrap();
 7553    let _pending_format_request = fake_server
 7554        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7555            panic!("Should not be invoked on non-dirty buffer");
 7556        })
 7557        .next();
 7558    cx.executor().start_waiting();
 7559    save.await;
 7560
 7561    // Set Rust language override and assert overridden tabsize is sent to language server
 7562    update_test_language_settings(cx, |settings| {
 7563        settings.languages.insert(
 7564            "Rust".into(),
 7565            LanguageSettingsContent {
 7566                tab_size: NonZeroU32::new(8),
 7567                ..Default::default()
 7568            },
 7569        );
 7570    });
 7571
 7572    editor.update_in(cx, |editor, window, cx| {
 7573        editor.set_text("somehting_new\n", window, cx)
 7574    });
 7575    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7576    let save = editor
 7577        .update_in(cx, |editor, window, cx| {
 7578            editor.save(true, project.clone(), window, cx)
 7579        })
 7580        .unwrap();
 7581    fake_server
 7582        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7583            assert_eq!(
 7584                params.text_document.uri,
 7585                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7586            );
 7587            assert_eq!(params.options.tab_size, 8);
 7588            Ok(Some(vec![]))
 7589        })
 7590        .next()
 7591        .await;
 7592    cx.executor().start_waiting();
 7593    save.await;
 7594}
 7595
 7596#[gpui::test]
 7597async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7598    init_test(cx, |settings| {
 7599        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7600            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7601        ))
 7602    });
 7603
 7604    let fs = FakeFs::new(cx.executor());
 7605    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7606
 7607    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7608
 7609    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7610    language_registry.add(Arc::new(Language::new(
 7611        LanguageConfig {
 7612            name: "Rust".into(),
 7613            matcher: LanguageMatcher {
 7614                path_suffixes: vec!["rs".to_string()],
 7615                ..Default::default()
 7616            },
 7617            ..LanguageConfig::default()
 7618        },
 7619        Some(tree_sitter_rust::LANGUAGE.into()),
 7620    )));
 7621    update_test_language_settings(cx, |settings| {
 7622        // Enable Prettier formatting for the same buffer, and ensure
 7623        // LSP is called instead of Prettier.
 7624        settings.defaults.prettier = Some(PrettierSettings {
 7625            allowed: true,
 7626            ..PrettierSettings::default()
 7627        });
 7628    });
 7629    let mut fake_servers = language_registry.register_fake_lsp(
 7630        "Rust",
 7631        FakeLspAdapter {
 7632            capabilities: lsp::ServerCapabilities {
 7633                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7634                ..Default::default()
 7635            },
 7636            ..Default::default()
 7637        },
 7638    );
 7639
 7640    let buffer = project
 7641        .update(cx, |project, cx| {
 7642            project.open_local_buffer(path!("/file.rs"), cx)
 7643        })
 7644        .await
 7645        .unwrap();
 7646
 7647    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7648    let (editor, cx) = cx.add_window_view(|window, cx| {
 7649        build_editor_with_project(project.clone(), buffer, window, cx)
 7650    });
 7651    editor.update_in(cx, |editor, window, cx| {
 7652        editor.set_text("one\ntwo\nthree\n", window, cx)
 7653    });
 7654
 7655    cx.executor().start_waiting();
 7656    let fake_server = fake_servers.next().await.unwrap();
 7657
 7658    let format = editor
 7659        .update_in(cx, |editor, window, cx| {
 7660            editor.perform_format(
 7661                project.clone(),
 7662                FormatTrigger::Manual,
 7663                FormatTarget::Buffers,
 7664                window,
 7665                cx,
 7666            )
 7667        })
 7668        .unwrap();
 7669    fake_server
 7670        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7671            assert_eq!(
 7672                params.text_document.uri,
 7673                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7674            );
 7675            assert_eq!(params.options.tab_size, 4);
 7676            Ok(Some(vec![lsp::TextEdit::new(
 7677                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7678                ", ".to_string(),
 7679            )]))
 7680        })
 7681        .next()
 7682        .await;
 7683    cx.executor().start_waiting();
 7684    format.await;
 7685    assert_eq!(
 7686        editor.update(cx, |editor, cx| editor.text(cx)),
 7687        "one, two\nthree\n"
 7688    );
 7689
 7690    editor.update_in(cx, |editor, window, cx| {
 7691        editor.set_text("one\ntwo\nthree\n", window, cx)
 7692    });
 7693    // Ensure we don't lock if formatting hangs.
 7694    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7695        assert_eq!(
 7696            params.text_document.uri,
 7697            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7698        );
 7699        futures::future::pending::<()>().await;
 7700        unreachable!()
 7701    });
 7702    let format = editor
 7703        .update_in(cx, |editor, window, cx| {
 7704            editor.perform_format(
 7705                project,
 7706                FormatTrigger::Manual,
 7707                FormatTarget::Buffers,
 7708                window,
 7709                cx,
 7710            )
 7711        })
 7712        .unwrap();
 7713    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7714    cx.executor().start_waiting();
 7715    format.await;
 7716    assert_eq!(
 7717        editor.update(cx, |editor, cx| editor.text(cx)),
 7718        "one\ntwo\nthree\n"
 7719    );
 7720}
 7721
 7722#[gpui::test]
 7723async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7724    init_test(cx, |_| {});
 7725
 7726    let mut cx = EditorLspTestContext::new_rust(
 7727        lsp::ServerCapabilities {
 7728            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7729            ..Default::default()
 7730        },
 7731        cx,
 7732    )
 7733    .await;
 7734
 7735    cx.set_state(indoc! {"
 7736        one.twoˇ
 7737    "});
 7738
 7739    // The format request takes a long time. When it completes, it inserts
 7740    // a newline and an indent before the `.`
 7741    cx.lsp
 7742        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7743            let executor = cx.background_executor().clone();
 7744            async move {
 7745                executor.timer(Duration::from_millis(100)).await;
 7746                Ok(Some(vec![lsp::TextEdit {
 7747                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7748                    new_text: "\n    ".into(),
 7749                }]))
 7750            }
 7751        });
 7752
 7753    // Submit a format request.
 7754    let format_1 = cx
 7755        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7756        .unwrap();
 7757    cx.executor().run_until_parked();
 7758
 7759    // Submit a second format request.
 7760    let format_2 = cx
 7761        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7762        .unwrap();
 7763    cx.executor().run_until_parked();
 7764
 7765    // Wait for both format requests to complete
 7766    cx.executor().advance_clock(Duration::from_millis(200));
 7767    cx.executor().start_waiting();
 7768    format_1.await.unwrap();
 7769    cx.executor().start_waiting();
 7770    format_2.await.unwrap();
 7771
 7772    // The formatting edits only happens once.
 7773    cx.assert_editor_state(indoc! {"
 7774        one
 7775            .twoˇ
 7776    "});
 7777}
 7778
 7779#[gpui::test]
 7780async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7781    init_test(cx, |settings| {
 7782        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7783    });
 7784
 7785    let mut cx = EditorLspTestContext::new_rust(
 7786        lsp::ServerCapabilities {
 7787            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7788            ..Default::default()
 7789        },
 7790        cx,
 7791    )
 7792    .await;
 7793
 7794    // Set up a buffer white some trailing whitespace and no trailing newline.
 7795    cx.set_state(
 7796        &[
 7797            "one ",   //
 7798            "twoˇ",   //
 7799            "three ", //
 7800            "four",   //
 7801        ]
 7802        .join("\n"),
 7803    );
 7804
 7805    // Submit a format request.
 7806    let format = cx
 7807        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7808        .unwrap();
 7809
 7810    // Record which buffer changes have been sent to the language server
 7811    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7812    cx.lsp
 7813        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7814            let buffer_changes = buffer_changes.clone();
 7815            move |params, _| {
 7816                buffer_changes.lock().extend(
 7817                    params
 7818                        .content_changes
 7819                        .into_iter()
 7820                        .map(|e| (e.range.unwrap(), e.text)),
 7821                );
 7822            }
 7823        });
 7824
 7825    // Handle formatting requests to the language server.
 7826    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7827        let buffer_changes = buffer_changes.clone();
 7828        move |_, _| {
 7829            // When formatting is requested, trailing whitespace has already been stripped,
 7830            // and the trailing newline has already been added.
 7831            assert_eq!(
 7832                &buffer_changes.lock()[1..],
 7833                &[
 7834                    (
 7835                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7836                        "".into()
 7837                    ),
 7838                    (
 7839                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7840                        "".into()
 7841                    ),
 7842                    (
 7843                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7844                        "\n".into()
 7845                    ),
 7846                ]
 7847            );
 7848
 7849            // Insert blank lines between each line of the buffer.
 7850            async move {
 7851                Ok(Some(vec![
 7852                    lsp::TextEdit {
 7853                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7854                        new_text: "\n".into(),
 7855                    },
 7856                    lsp::TextEdit {
 7857                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7858                        new_text: "\n".into(),
 7859                    },
 7860                ]))
 7861            }
 7862        }
 7863    });
 7864
 7865    // After formatting the buffer, the trailing whitespace is stripped,
 7866    // a newline is appended, and the edits provided by the language server
 7867    // have been applied.
 7868    format.await.unwrap();
 7869    cx.assert_editor_state(
 7870        &[
 7871            "one",   //
 7872            "",      //
 7873            "twoˇ",  //
 7874            "",      //
 7875            "three", //
 7876            "four",  //
 7877            "",      //
 7878        ]
 7879        .join("\n"),
 7880    );
 7881
 7882    // Undoing the formatting undoes the trailing whitespace removal, the
 7883    // trailing newline, and the LSP edits.
 7884    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7885    cx.assert_editor_state(
 7886        &[
 7887            "one ",   //
 7888            "twoˇ",   //
 7889            "three ", //
 7890            "four",   //
 7891        ]
 7892        .join("\n"),
 7893    );
 7894}
 7895
 7896#[gpui::test]
 7897async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7898    cx: &mut gpui::TestAppContext,
 7899) {
 7900    init_test(cx, |_| {});
 7901
 7902    cx.update(|cx| {
 7903        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7904            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7905                settings.auto_signature_help = Some(true);
 7906            });
 7907        });
 7908    });
 7909
 7910    let mut cx = EditorLspTestContext::new_rust(
 7911        lsp::ServerCapabilities {
 7912            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7913                ..Default::default()
 7914            }),
 7915            ..Default::default()
 7916        },
 7917        cx,
 7918    )
 7919    .await;
 7920
 7921    let language = Language::new(
 7922        LanguageConfig {
 7923            name: "Rust".into(),
 7924            brackets: BracketPairConfig {
 7925                pairs: vec![
 7926                    BracketPair {
 7927                        start: "{".to_string(),
 7928                        end: "}".to_string(),
 7929                        close: true,
 7930                        surround: true,
 7931                        newline: true,
 7932                    },
 7933                    BracketPair {
 7934                        start: "(".to_string(),
 7935                        end: ")".to_string(),
 7936                        close: true,
 7937                        surround: true,
 7938                        newline: true,
 7939                    },
 7940                    BracketPair {
 7941                        start: "/*".to_string(),
 7942                        end: " */".to_string(),
 7943                        close: true,
 7944                        surround: true,
 7945                        newline: true,
 7946                    },
 7947                    BracketPair {
 7948                        start: "[".to_string(),
 7949                        end: "]".to_string(),
 7950                        close: false,
 7951                        surround: false,
 7952                        newline: true,
 7953                    },
 7954                    BracketPair {
 7955                        start: "\"".to_string(),
 7956                        end: "\"".to_string(),
 7957                        close: true,
 7958                        surround: true,
 7959                        newline: false,
 7960                    },
 7961                    BracketPair {
 7962                        start: "<".to_string(),
 7963                        end: ">".to_string(),
 7964                        close: false,
 7965                        surround: true,
 7966                        newline: true,
 7967                    },
 7968                ],
 7969                ..Default::default()
 7970            },
 7971            autoclose_before: "})]".to_string(),
 7972            ..Default::default()
 7973        },
 7974        Some(tree_sitter_rust::LANGUAGE.into()),
 7975    );
 7976    let language = Arc::new(language);
 7977
 7978    cx.language_registry().add(language.clone());
 7979    cx.update_buffer(|buffer, cx| {
 7980        buffer.set_language(Some(language), cx);
 7981    });
 7982
 7983    cx.set_state(
 7984        &r#"
 7985            fn main() {
 7986                sampleˇ
 7987            }
 7988        "#
 7989        .unindent(),
 7990    );
 7991
 7992    cx.update_editor(|editor, window, cx| {
 7993        editor.handle_input("(", window, cx);
 7994    });
 7995    cx.assert_editor_state(
 7996        &"
 7997            fn main() {
 7998                sample(ˇ)
 7999            }
 8000        "
 8001        .unindent(),
 8002    );
 8003
 8004    let mocked_response = lsp::SignatureHelp {
 8005        signatures: vec![lsp::SignatureInformation {
 8006            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8007            documentation: None,
 8008            parameters: Some(vec![
 8009                lsp::ParameterInformation {
 8010                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8011                    documentation: None,
 8012                },
 8013                lsp::ParameterInformation {
 8014                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8015                    documentation: None,
 8016                },
 8017            ]),
 8018            active_parameter: None,
 8019        }],
 8020        active_signature: Some(0),
 8021        active_parameter: Some(0),
 8022    };
 8023    handle_signature_help_request(&mut cx, mocked_response).await;
 8024
 8025    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8026        .await;
 8027
 8028    cx.editor(|editor, _, _| {
 8029        let signature_help_state = editor.signature_help_state.popover().cloned();
 8030        assert!(signature_help_state.is_some());
 8031        let ParsedMarkdown {
 8032            text, highlights, ..
 8033        } = signature_help_state.unwrap().parsed_content;
 8034        assert_eq!(text, "param1: u8, param2: u8");
 8035        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8036    });
 8037}
 8038
 8039#[gpui::test]
 8040async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8041    init_test(cx, |_| {});
 8042
 8043    cx.update(|cx| {
 8044        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8045            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8046                settings.auto_signature_help = Some(false);
 8047                settings.show_signature_help_after_edits = Some(false);
 8048            });
 8049        });
 8050    });
 8051
 8052    let mut cx = EditorLspTestContext::new_rust(
 8053        lsp::ServerCapabilities {
 8054            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8055                ..Default::default()
 8056            }),
 8057            ..Default::default()
 8058        },
 8059        cx,
 8060    )
 8061    .await;
 8062
 8063    let language = Language::new(
 8064        LanguageConfig {
 8065            name: "Rust".into(),
 8066            brackets: BracketPairConfig {
 8067                pairs: vec![
 8068                    BracketPair {
 8069                        start: "{".to_string(),
 8070                        end: "}".to_string(),
 8071                        close: true,
 8072                        surround: true,
 8073                        newline: true,
 8074                    },
 8075                    BracketPair {
 8076                        start: "(".to_string(),
 8077                        end: ")".to_string(),
 8078                        close: true,
 8079                        surround: true,
 8080                        newline: true,
 8081                    },
 8082                    BracketPair {
 8083                        start: "/*".to_string(),
 8084                        end: " */".to_string(),
 8085                        close: true,
 8086                        surround: true,
 8087                        newline: true,
 8088                    },
 8089                    BracketPair {
 8090                        start: "[".to_string(),
 8091                        end: "]".to_string(),
 8092                        close: false,
 8093                        surround: false,
 8094                        newline: true,
 8095                    },
 8096                    BracketPair {
 8097                        start: "\"".to_string(),
 8098                        end: "\"".to_string(),
 8099                        close: true,
 8100                        surround: true,
 8101                        newline: false,
 8102                    },
 8103                    BracketPair {
 8104                        start: "<".to_string(),
 8105                        end: ">".to_string(),
 8106                        close: false,
 8107                        surround: true,
 8108                        newline: true,
 8109                    },
 8110                ],
 8111                ..Default::default()
 8112            },
 8113            autoclose_before: "})]".to_string(),
 8114            ..Default::default()
 8115        },
 8116        Some(tree_sitter_rust::LANGUAGE.into()),
 8117    );
 8118    let language = Arc::new(language);
 8119
 8120    cx.language_registry().add(language.clone());
 8121    cx.update_buffer(|buffer, cx| {
 8122        buffer.set_language(Some(language), cx);
 8123    });
 8124
 8125    // Ensure that signature_help is not called when no signature help is enabled.
 8126    cx.set_state(
 8127        &r#"
 8128            fn main() {
 8129                sampleˇ
 8130            }
 8131        "#
 8132        .unindent(),
 8133    );
 8134    cx.update_editor(|editor, window, cx| {
 8135        editor.handle_input("(", window, cx);
 8136    });
 8137    cx.assert_editor_state(
 8138        &"
 8139            fn main() {
 8140                sample(ˇ)
 8141            }
 8142        "
 8143        .unindent(),
 8144    );
 8145    cx.editor(|editor, _, _| {
 8146        assert!(editor.signature_help_state.task().is_none());
 8147    });
 8148
 8149    let mocked_response = lsp::SignatureHelp {
 8150        signatures: vec![lsp::SignatureInformation {
 8151            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8152            documentation: None,
 8153            parameters: Some(vec![
 8154                lsp::ParameterInformation {
 8155                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8156                    documentation: None,
 8157                },
 8158                lsp::ParameterInformation {
 8159                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8160                    documentation: None,
 8161                },
 8162            ]),
 8163            active_parameter: None,
 8164        }],
 8165        active_signature: Some(0),
 8166        active_parameter: Some(0),
 8167    };
 8168
 8169    // Ensure that signature_help is called when enabled afte edits
 8170    cx.update(|_, cx| {
 8171        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8172            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8173                settings.auto_signature_help = Some(false);
 8174                settings.show_signature_help_after_edits = Some(true);
 8175            });
 8176        });
 8177    });
 8178    cx.set_state(
 8179        &r#"
 8180            fn main() {
 8181                sampleˇ
 8182            }
 8183        "#
 8184        .unindent(),
 8185    );
 8186    cx.update_editor(|editor, window, cx| {
 8187        editor.handle_input("(", window, cx);
 8188    });
 8189    cx.assert_editor_state(
 8190        &"
 8191            fn main() {
 8192                sample(ˇ)
 8193            }
 8194        "
 8195        .unindent(),
 8196    );
 8197    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8198    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8199        .await;
 8200    cx.update_editor(|editor, _, _| {
 8201        let signature_help_state = editor.signature_help_state.popover().cloned();
 8202        assert!(signature_help_state.is_some());
 8203        let ParsedMarkdown {
 8204            text, highlights, ..
 8205        } = signature_help_state.unwrap().parsed_content;
 8206        assert_eq!(text, "param1: u8, param2: u8");
 8207        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8208        editor.signature_help_state = SignatureHelpState::default();
 8209    });
 8210
 8211    // Ensure that signature_help is called when auto signature help override is enabled
 8212    cx.update(|_, cx| {
 8213        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8214            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8215                settings.auto_signature_help = Some(true);
 8216                settings.show_signature_help_after_edits = Some(false);
 8217            });
 8218        });
 8219    });
 8220    cx.set_state(
 8221        &r#"
 8222            fn main() {
 8223                sampleˇ
 8224            }
 8225        "#
 8226        .unindent(),
 8227    );
 8228    cx.update_editor(|editor, window, cx| {
 8229        editor.handle_input("(", window, cx);
 8230    });
 8231    cx.assert_editor_state(
 8232        &"
 8233            fn main() {
 8234                sample(ˇ)
 8235            }
 8236        "
 8237        .unindent(),
 8238    );
 8239    handle_signature_help_request(&mut cx, mocked_response).await;
 8240    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8241        .await;
 8242    cx.editor(|editor, _, _| {
 8243        let signature_help_state = editor.signature_help_state.popover().cloned();
 8244        assert!(signature_help_state.is_some());
 8245        let ParsedMarkdown {
 8246            text, highlights, ..
 8247        } = signature_help_state.unwrap().parsed_content;
 8248        assert_eq!(text, "param1: u8, param2: u8");
 8249        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8250    });
 8251}
 8252
 8253#[gpui::test]
 8254async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8255    init_test(cx, |_| {});
 8256    cx.update(|cx| {
 8257        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8258            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8259                settings.auto_signature_help = Some(true);
 8260            });
 8261        });
 8262    });
 8263
 8264    let mut cx = EditorLspTestContext::new_rust(
 8265        lsp::ServerCapabilities {
 8266            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8267                ..Default::default()
 8268            }),
 8269            ..Default::default()
 8270        },
 8271        cx,
 8272    )
 8273    .await;
 8274
 8275    // A test that directly calls `show_signature_help`
 8276    cx.update_editor(|editor, window, cx| {
 8277        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8278    });
 8279
 8280    let mocked_response = lsp::SignatureHelp {
 8281        signatures: vec![lsp::SignatureInformation {
 8282            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8283            documentation: None,
 8284            parameters: Some(vec![
 8285                lsp::ParameterInformation {
 8286                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8287                    documentation: None,
 8288                },
 8289                lsp::ParameterInformation {
 8290                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8291                    documentation: None,
 8292                },
 8293            ]),
 8294            active_parameter: None,
 8295        }],
 8296        active_signature: Some(0),
 8297        active_parameter: Some(0),
 8298    };
 8299    handle_signature_help_request(&mut cx, mocked_response).await;
 8300
 8301    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8302        .await;
 8303
 8304    cx.editor(|editor, _, _| {
 8305        let signature_help_state = editor.signature_help_state.popover().cloned();
 8306        assert!(signature_help_state.is_some());
 8307        let ParsedMarkdown {
 8308            text, highlights, ..
 8309        } = signature_help_state.unwrap().parsed_content;
 8310        assert_eq!(text, "param1: u8, param2: u8");
 8311        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8312    });
 8313
 8314    // When exiting outside from inside the brackets, `signature_help` is closed.
 8315    cx.set_state(indoc! {"
 8316        fn main() {
 8317            sample(ˇ);
 8318        }
 8319
 8320        fn sample(param1: u8, param2: u8) {}
 8321    "});
 8322
 8323    cx.update_editor(|editor, window, cx| {
 8324        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8325    });
 8326
 8327    let mocked_response = lsp::SignatureHelp {
 8328        signatures: Vec::new(),
 8329        active_signature: None,
 8330        active_parameter: None,
 8331    };
 8332    handle_signature_help_request(&mut cx, mocked_response).await;
 8333
 8334    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8335        .await;
 8336
 8337    cx.editor(|editor, _, _| {
 8338        assert!(!editor.signature_help_state.is_shown());
 8339    });
 8340
 8341    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8342    cx.set_state(indoc! {"
 8343        fn main() {
 8344            sample(ˇ);
 8345        }
 8346
 8347        fn sample(param1: u8, param2: u8) {}
 8348    "});
 8349
 8350    let mocked_response = lsp::SignatureHelp {
 8351        signatures: vec![lsp::SignatureInformation {
 8352            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8353            documentation: None,
 8354            parameters: Some(vec![
 8355                lsp::ParameterInformation {
 8356                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8357                    documentation: None,
 8358                },
 8359                lsp::ParameterInformation {
 8360                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8361                    documentation: None,
 8362                },
 8363            ]),
 8364            active_parameter: None,
 8365        }],
 8366        active_signature: Some(0),
 8367        active_parameter: Some(0),
 8368    };
 8369    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8370    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8371        .await;
 8372    cx.editor(|editor, _, _| {
 8373        assert!(editor.signature_help_state.is_shown());
 8374    });
 8375
 8376    // Restore the popover with more parameter input
 8377    cx.set_state(indoc! {"
 8378        fn main() {
 8379            sample(param1, param2ˇ);
 8380        }
 8381
 8382        fn sample(param1: u8, param2: u8) {}
 8383    "});
 8384
 8385    let mocked_response = lsp::SignatureHelp {
 8386        signatures: vec![lsp::SignatureInformation {
 8387            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8388            documentation: None,
 8389            parameters: Some(vec![
 8390                lsp::ParameterInformation {
 8391                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8392                    documentation: None,
 8393                },
 8394                lsp::ParameterInformation {
 8395                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8396                    documentation: None,
 8397                },
 8398            ]),
 8399            active_parameter: None,
 8400        }],
 8401        active_signature: Some(0),
 8402        active_parameter: Some(1),
 8403    };
 8404    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8405    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8406        .await;
 8407
 8408    // When selecting a range, the popover is gone.
 8409    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8410    cx.update_editor(|editor, window, cx| {
 8411        editor.change_selections(None, window, cx, |s| {
 8412            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8413        })
 8414    });
 8415    cx.assert_editor_state(indoc! {"
 8416        fn main() {
 8417            sample(param1, «ˇparam2»);
 8418        }
 8419
 8420        fn sample(param1: u8, param2: u8) {}
 8421    "});
 8422    cx.editor(|editor, _, _| {
 8423        assert!(!editor.signature_help_state.is_shown());
 8424    });
 8425
 8426    // When unselecting again, the popover is back if within the brackets.
 8427    cx.update_editor(|editor, window, cx| {
 8428        editor.change_selections(None, window, cx, |s| {
 8429            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8430        })
 8431    });
 8432    cx.assert_editor_state(indoc! {"
 8433        fn main() {
 8434            sample(param1, ˇparam2);
 8435        }
 8436
 8437        fn sample(param1: u8, param2: u8) {}
 8438    "});
 8439    handle_signature_help_request(&mut cx, mocked_response).await;
 8440    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8441        .await;
 8442    cx.editor(|editor, _, _| {
 8443        assert!(editor.signature_help_state.is_shown());
 8444    });
 8445
 8446    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8447    cx.update_editor(|editor, window, cx| {
 8448        editor.change_selections(None, window, cx, |s| {
 8449            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8450            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8451        })
 8452    });
 8453    cx.assert_editor_state(indoc! {"
 8454        fn main() {
 8455            sample(param1, ˇparam2);
 8456        }
 8457
 8458        fn sample(param1: u8, param2: u8) {}
 8459    "});
 8460
 8461    let mocked_response = lsp::SignatureHelp {
 8462        signatures: vec![lsp::SignatureInformation {
 8463            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8464            documentation: None,
 8465            parameters: Some(vec![
 8466                lsp::ParameterInformation {
 8467                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8468                    documentation: None,
 8469                },
 8470                lsp::ParameterInformation {
 8471                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8472                    documentation: None,
 8473                },
 8474            ]),
 8475            active_parameter: None,
 8476        }],
 8477        active_signature: Some(0),
 8478        active_parameter: Some(1),
 8479    };
 8480    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8481    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8482        .await;
 8483    cx.update_editor(|editor, _, cx| {
 8484        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8485    });
 8486    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8487        .await;
 8488    cx.update_editor(|editor, window, cx| {
 8489        editor.change_selections(None, window, cx, |s| {
 8490            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8491        })
 8492    });
 8493    cx.assert_editor_state(indoc! {"
 8494        fn main() {
 8495            sample(param1, «ˇparam2»);
 8496        }
 8497
 8498        fn sample(param1: u8, param2: u8) {}
 8499    "});
 8500    cx.update_editor(|editor, window, cx| {
 8501        editor.change_selections(None, window, cx, |s| {
 8502            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8503        })
 8504    });
 8505    cx.assert_editor_state(indoc! {"
 8506        fn main() {
 8507            sample(param1, ˇparam2);
 8508        }
 8509
 8510        fn sample(param1: u8, param2: u8) {}
 8511    "});
 8512    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8513        .await;
 8514}
 8515
 8516#[gpui::test]
 8517async fn test_completion(cx: &mut gpui::TestAppContext) {
 8518    init_test(cx, |_| {});
 8519
 8520    let mut cx = EditorLspTestContext::new_rust(
 8521        lsp::ServerCapabilities {
 8522            completion_provider: Some(lsp::CompletionOptions {
 8523                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8524                resolve_provider: Some(true),
 8525                ..Default::default()
 8526            }),
 8527            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8528            ..Default::default()
 8529        },
 8530        cx,
 8531    )
 8532    .await;
 8533    let counter = Arc::new(AtomicUsize::new(0));
 8534
 8535    cx.set_state(indoc! {"
 8536        oneˇ
 8537        two
 8538        three
 8539    "});
 8540    cx.simulate_keystroke(".");
 8541    handle_completion_request(
 8542        &mut cx,
 8543        indoc! {"
 8544            one.|<>
 8545            two
 8546            three
 8547        "},
 8548        vec!["first_completion", "second_completion"],
 8549        counter.clone(),
 8550    )
 8551    .await;
 8552    cx.condition(|editor, _| editor.context_menu_visible())
 8553        .await;
 8554    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8555
 8556    let _handler = handle_signature_help_request(
 8557        &mut cx,
 8558        lsp::SignatureHelp {
 8559            signatures: vec![lsp::SignatureInformation {
 8560                label: "test signature".to_string(),
 8561                documentation: None,
 8562                parameters: Some(vec![lsp::ParameterInformation {
 8563                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8564                    documentation: None,
 8565                }]),
 8566                active_parameter: None,
 8567            }],
 8568            active_signature: None,
 8569            active_parameter: None,
 8570        },
 8571    );
 8572    cx.update_editor(|editor, window, cx| {
 8573        assert!(
 8574            !editor.signature_help_state.is_shown(),
 8575            "No signature help was called for"
 8576        );
 8577        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8578    });
 8579    cx.run_until_parked();
 8580    cx.update_editor(|editor, _, _| {
 8581        assert!(
 8582            !editor.signature_help_state.is_shown(),
 8583            "No signature help should be shown when completions menu is open"
 8584        );
 8585    });
 8586
 8587    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8588        editor.context_menu_next(&Default::default(), window, cx);
 8589        editor
 8590            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8591            .unwrap()
 8592    });
 8593    cx.assert_editor_state(indoc! {"
 8594        one.second_completionˇ
 8595        two
 8596        three
 8597    "});
 8598
 8599    handle_resolve_completion_request(
 8600        &mut cx,
 8601        Some(vec![
 8602            (
 8603                //This overlaps with the primary completion edit which is
 8604                //misbehavior from the LSP spec, test that we filter it out
 8605                indoc! {"
 8606                    one.second_ˇcompletion
 8607                    two
 8608                    threeˇ
 8609                "},
 8610                "overlapping additional edit",
 8611            ),
 8612            (
 8613                indoc! {"
 8614                    one.second_completion
 8615                    two
 8616                    threeˇ
 8617                "},
 8618                "\nadditional edit",
 8619            ),
 8620        ]),
 8621    )
 8622    .await;
 8623    apply_additional_edits.await.unwrap();
 8624    cx.assert_editor_state(indoc! {"
 8625        one.second_completionˇ
 8626        two
 8627        three
 8628        additional edit
 8629    "});
 8630
 8631    cx.set_state(indoc! {"
 8632        one.second_completion
 8633        twoˇ
 8634        threeˇ
 8635        additional edit
 8636    "});
 8637    cx.simulate_keystroke(" ");
 8638    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8639    cx.simulate_keystroke("s");
 8640    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8641
 8642    cx.assert_editor_state(indoc! {"
 8643        one.second_completion
 8644        two sˇ
 8645        three sˇ
 8646        additional edit
 8647    "});
 8648    handle_completion_request(
 8649        &mut cx,
 8650        indoc! {"
 8651            one.second_completion
 8652            two s
 8653            three <s|>
 8654            additional edit
 8655        "},
 8656        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8657        counter.clone(),
 8658    )
 8659    .await;
 8660    cx.condition(|editor, _| editor.context_menu_visible())
 8661        .await;
 8662    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8663
 8664    cx.simulate_keystroke("i");
 8665
 8666    handle_completion_request(
 8667        &mut cx,
 8668        indoc! {"
 8669            one.second_completion
 8670            two si
 8671            three <si|>
 8672            additional edit
 8673        "},
 8674        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8675        counter.clone(),
 8676    )
 8677    .await;
 8678    cx.condition(|editor, _| editor.context_menu_visible())
 8679        .await;
 8680    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8681
 8682    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8683        editor
 8684            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8685            .unwrap()
 8686    });
 8687    cx.assert_editor_state(indoc! {"
 8688        one.second_completion
 8689        two sixth_completionˇ
 8690        three sixth_completionˇ
 8691        additional edit
 8692    "});
 8693
 8694    apply_additional_edits.await.unwrap();
 8695
 8696    update_test_language_settings(&mut cx, |settings| {
 8697        settings.defaults.show_completions_on_input = Some(false);
 8698    });
 8699    cx.set_state("editorˇ");
 8700    cx.simulate_keystroke(".");
 8701    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8702    cx.simulate_keystroke("c");
 8703    cx.simulate_keystroke("l");
 8704    cx.simulate_keystroke("o");
 8705    cx.assert_editor_state("editor.cloˇ");
 8706    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8707    cx.update_editor(|editor, window, cx| {
 8708        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8709    });
 8710    handle_completion_request(
 8711        &mut cx,
 8712        "editor.<clo|>",
 8713        vec!["close", "clobber"],
 8714        counter.clone(),
 8715    )
 8716    .await;
 8717    cx.condition(|editor, _| editor.context_menu_visible())
 8718        .await;
 8719    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8720
 8721    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8722        editor
 8723            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8724            .unwrap()
 8725    });
 8726    cx.assert_editor_state("editor.closeˇ");
 8727    handle_resolve_completion_request(&mut cx, None).await;
 8728    apply_additional_edits.await.unwrap();
 8729}
 8730
 8731#[gpui::test]
 8732async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8733    init_test(cx, |_| {});
 8734
 8735    let fs = FakeFs::new(cx.executor());
 8736    fs.insert_tree(
 8737        path!("/a"),
 8738        json!({
 8739            "main.ts": "a",
 8740        }),
 8741    )
 8742    .await;
 8743
 8744    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8745    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8746    let typescript_language = Arc::new(Language::new(
 8747        LanguageConfig {
 8748            name: "TypeScript".into(),
 8749            matcher: LanguageMatcher {
 8750                path_suffixes: vec!["ts".to_string()],
 8751                ..LanguageMatcher::default()
 8752            },
 8753            line_comments: vec!["// ".into()],
 8754            ..LanguageConfig::default()
 8755        },
 8756        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8757    ));
 8758    language_registry.add(typescript_language.clone());
 8759    let mut fake_servers = language_registry.register_fake_lsp(
 8760        "TypeScript",
 8761        FakeLspAdapter {
 8762            capabilities: lsp::ServerCapabilities {
 8763                completion_provider: Some(lsp::CompletionOptions {
 8764                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8765                    ..lsp::CompletionOptions::default()
 8766                }),
 8767                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8768                ..lsp::ServerCapabilities::default()
 8769            },
 8770            // Emulate vtsls label generation
 8771            label_for_completion: Some(Box::new(|item, _| {
 8772                let text = if let Some(description) = item
 8773                    .label_details
 8774                    .as_ref()
 8775                    .and_then(|label_details| label_details.description.as_ref())
 8776                {
 8777                    format!("{} {}", item.label, description)
 8778                } else if let Some(detail) = &item.detail {
 8779                    format!("{} {}", item.label, detail)
 8780                } else {
 8781                    item.label.clone()
 8782                };
 8783                let len = text.len();
 8784                Some(language::CodeLabel {
 8785                    text,
 8786                    runs: Vec::new(),
 8787                    filter_range: 0..len,
 8788                })
 8789            })),
 8790            ..FakeLspAdapter::default()
 8791        },
 8792    );
 8793    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8794    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8795    let worktree_id = workspace
 8796        .update(cx, |workspace, _window, cx| {
 8797            workspace.project().update(cx, |project, cx| {
 8798                project.worktrees(cx).next().unwrap().read(cx).id()
 8799            })
 8800        })
 8801        .unwrap();
 8802    let _buffer = project
 8803        .update(cx, |project, cx| {
 8804            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8805        })
 8806        .await
 8807        .unwrap();
 8808    let editor = workspace
 8809        .update(cx, |workspace, window, cx| {
 8810            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8811        })
 8812        .unwrap()
 8813        .await
 8814        .unwrap()
 8815        .downcast::<Editor>()
 8816        .unwrap();
 8817    let fake_server = fake_servers.next().await.unwrap();
 8818
 8819    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8820    let multiline_label_2 = "a\nb\nc\n";
 8821    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8822    let multiline_description = "d\ne\nf\n";
 8823    let multiline_detail_2 = "g\nh\ni\n";
 8824
 8825    let mut completion_handle =
 8826        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8827            Ok(Some(lsp::CompletionResponse::Array(vec![
 8828                lsp::CompletionItem {
 8829                    label: multiline_label.to_string(),
 8830                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8831                        range: lsp::Range {
 8832                            start: lsp::Position {
 8833                                line: params.text_document_position.position.line,
 8834                                character: params.text_document_position.position.character,
 8835                            },
 8836                            end: lsp::Position {
 8837                                line: params.text_document_position.position.line,
 8838                                character: params.text_document_position.position.character,
 8839                            },
 8840                        },
 8841                        new_text: "new_text_1".to_string(),
 8842                    })),
 8843                    ..lsp::CompletionItem::default()
 8844                },
 8845                lsp::CompletionItem {
 8846                    label: "single line label 1".to_string(),
 8847                    detail: Some(multiline_detail.to_string()),
 8848                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8849                        range: lsp::Range {
 8850                            start: lsp::Position {
 8851                                line: params.text_document_position.position.line,
 8852                                character: params.text_document_position.position.character,
 8853                            },
 8854                            end: lsp::Position {
 8855                                line: params.text_document_position.position.line,
 8856                                character: params.text_document_position.position.character,
 8857                            },
 8858                        },
 8859                        new_text: "new_text_2".to_string(),
 8860                    })),
 8861                    ..lsp::CompletionItem::default()
 8862                },
 8863                lsp::CompletionItem {
 8864                    label: "single line label 2".to_string(),
 8865                    label_details: Some(lsp::CompletionItemLabelDetails {
 8866                        description: Some(multiline_description.to_string()),
 8867                        detail: None,
 8868                    }),
 8869                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8870                        range: lsp::Range {
 8871                            start: lsp::Position {
 8872                                line: params.text_document_position.position.line,
 8873                                character: params.text_document_position.position.character,
 8874                            },
 8875                            end: lsp::Position {
 8876                                line: params.text_document_position.position.line,
 8877                                character: params.text_document_position.position.character,
 8878                            },
 8879                        },
 8880                        new_text: "new_text_2".to_string(),
 8881                    })),
 8882                    ..lsp::CompletionItem::default()
 8883                },
 8884                lsp::CompletionItem {
 8885                    label: multiline_label_2.to_string(),
 8886                    detail: Some(multiline_detail_2.to_string()),
 8887                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8888                        range: lsp::Range {
 8889                            start: lsp::Position {
 8890                                line: params.text_document_position.position.line,
 8891                                character: params.text_document_position.position.character,
 8892                            },
 8893                            end: lsp::Position {
 8894                                line: params.text_document_position.position.line,
 8895                                character: params.text_document_position.position.character,
 8896                            },
 8897                        },
 8898                        new_text: "new_text_3".to_string(),
 8899                    })),
 8900                    ..lsp::CompletionItem::default()
 8901                },
 8902                lsp::CompletionItem {
 8903                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8904                    detail: Some(
 8905                        "Details with many     spaces and \t but without newlines".to_string(),
 8906                    ),
 8907                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8908                        range: lsp::Range {
 8909                            start: lsp::Position {
 8910                                line: params.text_document_position.position.line,
 8911                                character: params.text_document_position.position.character,
 8912                            },
 8913                            end: lsp::Position {
 8914                                line: params.text_document_position.position.line,
 8915                                character: params.text_document_position.position.character,
 8916                            },
 8917                        },
 8918                        new_text: "new_text_4".to_string(),
 8919                    })),
 8920                    ..lsp::CompletionItem::default()
 8921                },
 8922            ])))
 8923        });
 8924
 8925    editor.update_in(cx, |editor, window, cx| {
 8926        cx.focus_self(window);
 8927        editor.move_to_end(&MoveToEnd, window, cx);
 8928        editor.handle_input(".", window, cx);
 8929    });
 8930    cx.run_until_parked();
 8931    completion_handle.next().await.unwrap();
 8932
 8933    editor.update(cx, |editor, _| {
 8934        assert!(editor.context_menu_visible());
 8935        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8936        {
 8937            let completion_labels = menu
 8938                .completions
 8939                .borrow()
 8940                .iter()
 8941                .map(|c| c.label.text.clone())
 8942                .collect::<Vec<_>>();
 8943            assert_eq!(
 8944                completion_labels,
 8945                &[
 8946                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8947                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8948                    "single line label 2 d e f ",
 8949                    "a b c g h i ",
 8950                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8951                ],
 8952                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8953            );
 8954
 8955            for completion in menu
 8956                .completions
 8957                .borrow()
 8958                .iter() {
 8959                    assert_eq!(
 8960                        completion.label.filter_range,
 8961                        0..completion.label.text.len(),
 8962                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8963                    );
 8964                }
 8965
 8966        } else {
 8967            panic!("expected completion menu to be open");
 8968        }
 8969    });
 8970}
 8971
 8972#[gpui::test]
 8973async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8974    init_test(cx, |_| {});
 8975    let mut cx = EditorLspTestContext::new_rust(
 8976        lsp::ServerCapabilities {
 8977            completion_provider: Some(lsp::CompletionOptions {
 8978                trigger_characters: Some(vec![".".to_string()]),
 8979                ..Default::default()
 8980            }),
 8981            ..Default::default()
 8982        },
 8983        cx,
 8984    )
 8985    .await;
 8986    cx.lsp
 8987        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8988            Ok(Some(lsp::CompletionResponse::Array(vec![
 8989                lsp::CompletionItem {
 8990                    label: "first".into(),
 8991                    ..Default::default()
 8992                },
 8993                lsp::CompletionItem {
 8994                    label: "last".into(),
 8995                    ..Default::default()
 8996                },
 8997            ])))
 8998        });
 8999    cx.set_state("variableˇ");
 9000    cx.simulate_keystroke(".");
 9001    cx.executor().run_until_parked();
 9002
 9003    cx.update_editor(|editor, _, _| {
 9004        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9005        {
 9006            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9007        } else {
 9008            panic!("expected completion menu to be open");
 9009        }
 9010    });
 9011
 9012    cx.update_editor(|editor, window, cx| {
 9013        editor.move_page_down(&MovePageDown::default(), window, cx);
 9014        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9015        {
 9016            assert!(
 9017                menu.selected_item == 1,
 9018                "expected PageDown to select the last item from the context menu"
 9019            );
 9020        } else {
 9021            panic!("expected completion menu to stay open after PageDown");
 9022        }
 9023    });
 9024
 9025    cx.update_editor(|editor, window, cx| {
 9026        editor.move_page_up(&MovePageUp::default(), window, cx);
 9027        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9028        {
 9029            assert!(
 9030                menu.selected_item == 0,
 9031                "expected PageUp to select the first item from the context menu"
 9032            );
 9033        } else {
 9034            panic!("expected completion menu to stay open after PageUp");
 9035        }
 9036    });
 9037}
 9038
 9039#[gpui::test]
 9040async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9041    init_test(cx, |_| {});
 9042    let mut cx = EditorLspTestContext::new_rust(
 9043        lsp::ServerCapabilities {
 9044            completion_provider: Some(lsp::CompletionOptions {
 9045                trigger_characters: Some(vec![".".to_string()]),
 9046                ..Default::default()
 9047            }),
 9048            ..Default::default()
 9049        },
 9050        cx,
 9051    )
 9052    .await;
 9053    cx.lsp
 9054        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9055            Ok(Some(lsp::CompletionResponse::Array(vec![
 9056                lsp::CompletionItem {
 9057                    label: "Range".into(),
 9058                    sort_text: Some("a".into()),
 9059                    ..Default::default()
 9060                },
 9061                lsp::CompletionItem {
 9062                    label: "r".into(),
 9063                    sort_text: Some("b".into()),
 9064                    ..Default::default()
 9065                },
 9066                lsp::CompletionItem {
 9067                    label: "ret".into(),
 9068                    sort_text: Some("c".into()),
 9069                    ..Default::default()
 9070                },
 9071                lsp::CompletionItem {
 9072                    label: "return".into(),
 9073                    sort_text: Some("d".into()),
 9074                    ..Default::default()
 9075                },
 9076                lsp::CompletionItem {
 9077                    label: "slice".into(),
 9078                    sort_text: Some("d".into()),
 9079                    ..Default::default()
 9080                },
 9081            ])))
 9082        });
 9083    cx.set_state("");
 9084    cx.executor().run_until_parked();
 9085    cx.update_editor(|editor, window, cx| {
 9086        editor.show_completions(
 9087            &ShowCompletions {
 9088                trigger: Some("r".into()),
 9089            },
 9090            window,
 9091            cx,
 9092        );
 9093    });
 9094    cx.executor().run_until_parked();
 9095
 9096    cx.update_editor(|editor, _, _| {
 9097        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9098        {
 9099            assert_eq!(
 9100                completion_menu_entries(&menu),
 9101                &["r", "ret", "Range", "return"]
 9102            );
 9103        } else {
 9104            panic!("expected completion menu to be open");
 9105        }
 9106    });
 9107}
 9108
 9109#[gpui::test]
 9110async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9111    init_test(cx, |_| {});
 9112
 9113    let mut cx = EditorLspTestContext::new_rust(
 9114        lsp::ServerCapabilities {
 9115            completion_provider: Some(lsp::CompletionOptions {
 9116                trigger_characters: Some(vec![".".to_string()]),
 9117                resolve_provider: Some(true),
 9118                ..Default::default()
 9119            }),
 9120            ..Default::default()
 9121        },
 9122        cx,
 9123    )
 9124    .await;
 9125
 9126    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9127    cx.simulate_keystroke(".");
 9128    let completion_item = lsp::CompletionItem {
 9129        label: "Some".into(),
 9130        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9131        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9132        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9133            kind: lsp::MarkupKind::Markdown,
 9134            value: "```rust\nSome(2)\n```".to_string(),
 9135        })),
 9136        deprecated: Some(false),
 9137        sort_text: Some("Some".to_string()),
 9138        filter_text: Some("Some".to_string()),
 9139        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9140        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9141            range: lsp::Range {
 9142                start: lsp::Position {
 9143                    line: 0,
 9144                    character: 22,
 9145                },
 9146                end: lsp::Position {
 9147                    line: 0,
 9148                    character: 22,
 9149                },
 9150            },
 9151            new_text: "Some(2)".to_string(),
 9152        })),
 9153        additional_text_edits: Some(vec![lsp::TextEdit {
 9154            range: lsp::Range {
 9155                start: lsp::Position {
 9156                    line: 0,
 9157                    character: 20,
 9158                },
 9159                end: lsp::Position {
 9160                    line: 0,
 9161                    character: 22,
 9162                },
 9163            },
 9164            new_text: "".to_string(),
 9165        }]),
 9166        ..Default::default()
 9167    };
 9168
 9169    let closure_completion_item = completion_item.clone();
 9170    let counter = Arc::new(AtomicUsize::new(0));
 9171    let counter_clone = counter.clone();
 9172    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9173        let task_completion_item = closure_completion_item.clone();
 9174        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9175        async move {
 9176            Ok(Some(lsp::CompletionResponse::Array(vec![
 9177                task_completion_item,
 9178            ])))
 9179        }
 9180    });
 9181
 9182    cx.condition(|editor, _| editor.context_menu_visible())
 9183        .await;
 9184    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9185    assert!(request.next().await.is_some());
 9186    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9187
 9188    cx.simulate_keystroke("S");
 9189    cx.simulate_keystroke("o");
 9190    cx.simulate_keystroke("m");
 9191    cx.condition(|editor, _| editor.context_menu_visible())
 9192        .await;
 9193    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9194    assert!(request.next().await.is_some());
 9195    assert!(request.next().await.is_some());
 9196    assert!(request.next().await.is_some());
 9197    request.close();
 9198    assert!(request.next().await.is_none());
 9199    assert_eq!(
 9200        counter.load(atomic::Ordering::Acquire),
 9201        4,
 9202        "With the completions menu open, only one LSP request should happen per input"
 9203    );
 9204}
 9205
 9206#[gpui::test]
 9207async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9208    init_test(cx, |_| {});
 9209    let mut cx = EditorTestContext::new(cx).await;
 9210    let language = Arc::new(Language::new(
 9211        LanguageConfig {
 9212            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9213            ..Default::default()
 9214        },
 9215        Some(tree_sitter_rust::LANGUAGE.into()),
 9216    ));
 9217    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9218
 9219    // If multiple selections intersect a line, the line is only toggled once.
 9220    cx.set_state(indoc! {"
 9221        fn a() {
 9222            «//b();
 9223            ˇ»// «c();
 9224            //ˇ»  d();
 9225        }
 9226    "});
 9227
 9228    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9229
 9230    cx.assert_editor_state(indoc! {"
 9231        fn a() {
 9232            «b();
 9233            c();
 9234            ˇ» d();
 9235        }
 9236    "});
 9237
 9238    // The comment prefix is inserted at the same column for every line in a
 9239    // selection.
 9240    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9241
 9242    cx.assert_editor_state(indoc! {"
 9243        fn a() {
 9244            // «b();
 9245            // c();
 9246            ˇ»//  d();
 9247        }
 9248    "});
 9249
 9250    // If a selection ends at the beginning of a line, that line is not toggled.
 9251    cx.set_selections_state(indoc! {"
 9252        fn a() {
 9253            // b();
 9254            «// c();
 9255        ˇ»    //  d();
 9256        }
 9257    "});
 9258
 9259    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9260
 9261    cx.assert_editor_state(indoc! {"
 9262        fn a() {
 9263            // b();
 9264            «c();
 9265        ˇ»    //  d();
 9266        }
 9267    "});
 9268
 9269    // If a selection span a single line and is empty, the line is toggled.
 9270    cx.set_state(indoc! {"
 9271        fn a() {
 9272            a();
 9273            b();
 9274        ˇ
 9275        }
 9276    "});
 9277
 9278    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9279
 9280    cx.assert_editor_state(indoc! {"
 9281        fn a() {
 9282            a();
 9283            b();
 9284        //•ˇ
 9285        }
 9286    "});
 9287
 9288    // If a selection span multiple lines, empty lines are not toggled.
 9289    cx.set_state(indoc! {"
 9290        fn a() {
 9291            «a();
 9292
 9293            c();ˇ»
 9294        }
 9295    "});
 9296
 9297    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9298
 9299    cx.assert_editor_state(indoc! {"
 9300        fn a() {
 9301            // «a();
 9302
 9303            // c();ˇ»
 9304        }
 9305    "});
 9306
 9307    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9308    cx.set_state(indoc! {"
 9309        fn a() {
 9310            «// a();
 9311            /// b();
 9312            //! c();ˇ»
 9313        }
 9314    "});
 9315
 9316    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9317
 9318    cx.assert_editor_state(indoc! {"
 9319        fn a() {
 9320            «a();
 9321            b();
 9322            c();ˇ»
 9323        }
 9324    "});
 9325}
 9326
 9327#[gpui::test]
 9328async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9329    init_test(cx, |_| {});
 9330    let mut cx = EditorTestContext::new(cx).await;
 9331    let language = Arc::new(Language::new(
 9332        LanguageConfig {
 9333            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9334            ..Default::default()
 9335        },
 9336        Some(tree_sitter_rust::LANGUAGE.into()),
 9337    ));
 9338    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9339
 9340    let toggle_comments = &ToggleComments {
 9341        advance_downwards: false,
 9342        ignore_indent: true,
 9343    };
 9344
 9345    // If multiple selections intersect a line, the line is only toggled once.
 9346    cx.set_state(indoc! {"
 9347        fn a() {
 9348        //    «b();
 9349        //    c();
 9350        //    ˇ» d();
 9351        }
 9352    "});
 9353
 9354    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9355
 9356    cx.assert_editor_state(indoc! {"
 9357        fn a() {
 9358            «b();
 9359            c();
 9360            ˇ» d();
 9361        }
 9362    "});
 9363
 9364    // The comment prefix is inserted at the beginning of each line
 9365    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9366
 9367    cx.assert_editor_state(indoc! {"
 9368        fn a() {
 9369        //    «b();
 9370        //    c();
 9371        //    ˇ» d();
 9372        }
 9373    "});
 9374
 9375    // If a selection ends at the beginning of a line, that line is not toggled.
 9376    cx.set_selections_state(indoc! {"
 9377        fn a() {
 9378        //    b();
 9379        //    «c();
 9380        ˇ»//     d();
 9381        }
 9382    "});
 9383
 9384    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9385
 9386    cx.assert_editor_state(indoc! {"
 9387        fn a() {
 9388        //    b();
 9389            «c();
 9390        ˇ»//     d();
 9391        }
 9392    "});
 9393
 9394    // If a selection span a single line and is empty, the line is toggled.
 9395    cx.set_state(indoc! {"
 9396        fn a() {
 9397            a();
 9398            b();
 9399        ˇ
 9400        }
 9401    "});
 9402
 9403    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9404
 9405    cx.assert_editor_state(indoc! {"
 9406        fn a() {
 9407            a();
 9408            b();
 9409        //ˇ
 9410        }
 9411    "});
 9412
 9413    // If a selection span multiple lines, empty lines are not toggled.
 9414    cx.set_state(indoc! {"
 9415        fn a() {
 9416            «a();
 9417
 9418            c();ˇ»
 9419        }
 9420    "});
 9421
 9422    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9423
 9424    cx.assert_editor_state(indoc! {"
 9425        fn a() {
 9426        //    «a();
 9427
 9428        //    c();ˇ»
 9429        }
 9430    "});
 9431
 9432    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9433    cx.set_state(indoc! {"
 9434        fn a() {
 9435        //    «a();
 9436        ///    b();
 9437        //!    c();ˇ»
 9438        }
 9439    "});
 9440
 9441    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9442
 9443    cx.assert_editor_state(indoc! {"
 9444        fn a() {
 9445            «a();
 9446            b();
 9447            c();ˇ»
 9448        }
 9449    "});
 9450}
 9451
 9452#[gpui::test]
 9453async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9454    init_test(cx, |_| {});
 9455
 9456    let language = Arc::new(Language::new(
 9457        LanguageConfig {
 9458            line_comments: vec!["// ".into()],
 9459            ..Default::default()
 9460        },
 9461        Some(tree_sitter_rust::LANGUAGE.into()),
 9462    ));
 9463
 9464    let mut cx = EditorTestContext::new(cx).await;
 9465
 9466    cx.language_registry().add(language.clone());
 9467    cx.update_buffer(|buffer, cx| {
 9468        buffer.set_language(Some(language), cx);
 9469    });
 9470
 9471    let toggle_comments = &ToggleComments {
 9472        advance_downwards: true,
 9473        ignore_indent: false,
 9474    };
 9475
 9476    // Single cursor on one line -> advance
 9477    // Cursor moves horizontally 3 characters as well on non-blank line
 9478    cx.set_state(indoc!(
 9479        "fn a() {
 9480             ˇdog();
 9481             cat();
 9482        }"
 9483    ));
 9484    cx.update_editor(|editor, window, cx| {
 9485        editor.toggle_comments(toggle_comments, window, cx);
 9486    });
 9487    cx.assert_editor_state(indoc!(
 9488        "fn a() {
 9489             // dog();
 9490             catˇ();
 9491        }"
 9492    ));
 9493
 9494    // Single selection on one line -> don't advance
 9495    cx.set_state(indoc!(
 9496        "fn a() {
 9497             «dog()ˇ»;
 9498             cat();
 9499        }"
 9500    ));
 9501    cx.update_editor(|editor, window, cx| {
 9502        editor.toggle_comments(toggle_comments, window, cx);
 9503    });
 9504    cx.assert_editor_state(indoc!(
 9505        "fn a() {
 9506             // «dog()ˇ»;
 9507             cat();
 9508        }"
 9509    ));
 9510
 9511    // Multiple cursors on one line -> advance
 9512    cx.set_state(indoc!(
 9513        "fn a() {
 9514             ˇdˇog();
 9515             cat();
 9516        }"
 9517    ));
 9518    cx.update_editor(|editor, window, cx| {
 9519        editor.toggle_comments(toggle_comments, window, cx);
 9520    });
 9521    cx.assert_editor_state(indoc!(
 9522        "fn a() {
 9523             // dog();
 9524             catˇ(ˇ);
 9525        }"
 9526    ));
 9527
 9528    // Multiple cursors on one line, with selection -> don't advance
 9529    cx.set_state(indoc!(
 9530        "fn a() {
 9531             ˇdˇog«()ˇ»;
 9532             cat();
 9533        }"
 9534    ));
 9535    cx.update_editor(|editor, window, cx| {
 9536        editor.toggle_comments(toggle_comments, window, cx);
 9537    });
 9538    cx.assert_editor_state(indoc!(
 9539        "fn a() {
 9540             // ˇdˇog«()ˇ»;
 9541             cat();
 9542        }"
 9543    ));
 9544
 9545    // Single cursor on one line -> advance
 9546    // Cursor moves to column 0 on blank line
 9547    cx.set_state(indoc!(
 9548        "fn a() {
 9549             ˇdog();
 9550
 9551             cat();
 9552        }"
 9553    ));
 9554    cx.update_editor(|editor, window, cx| {
 9555        editor.toggle_comments(toggle_comments, window, cx);
 9556    });
 9557    cx.assert_editor_state(indoc!(
 9558        "fn a() {
 9559             // dog();
 9560        ˇ
 9561             cat();
 9562        }"
 9563    ));
 9564
 9565    // Single cursor on one line -> advance
 9566    // Cursor starts and ends at column 0
 9567    cx.set_state(indoc!(
 9568        "fn a() {
 9569         ˇ    dog();
 9570             cat();
 9571        }"
 9572    ));
 9573    cx.update_editor(|editor, window, cx| {
 9574        editor.toggle_comments(toggle_comments, window, cx);
 9575    });
 9576    cx.assert_editor_state(indoc!(
 9577        "fn a() {
 9578             // dog();
 9579         ˇ    cat();
 9580        }"
 9581    ));
 9582}
 9583
 9584#[gpui::test]
 9585async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9586    init_test(cx, |_| {});
 9587
 9588    let mut cx = EditorTestContext::new(cx).await;
 9589
 9590    let html_language = Arc::new(
 9591        Language::new(
 9592            LanguageConfig {
 9593                name: "HTML".into(),
 9594                block_comment: Some(("<!-- ".into(), " -->".into())),
 9595                ..Default::default()
 9596            },
 9597            Some(tree_sitter_html::language()),
 9598        )
 9599        .with_injection_query(
 9600            r#"
 9601            (script_element
 9602                (raw_text) @injection.content
 9603                (#set! injection.language "javascript"))
 9604            "#,
 9605        )
 9606        .unwrap(),
 9607    );
 9608
 9609    let javascript_language = Arc::new(Language::new(
 9610        LanguageConfig {
 9611            name: "JavaScript".into(),
 9612            line_comments: vec!["// ".into()],
 9613            ..Default::default()
 9614        },
 9615        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9616    ));
 9617
 9618    cx.language_registry().add(html_language.clone());
 9619    cx.language_registry().add(javascript_language.clone());
 9620    cx.update_buffer(|buffer, cx| {
 9621        buffer.set_language(Some(html_language), cx);
 9622    });
 9623
 9624    // Toggle comments for empty selections
 9625    cx.set_state(
 9626        &r#"
 9627            <p>A</p>ˇ
 9628            <p>B</p>ˇ
 9629            <p>C</p>ˇ
 9630        "#
 9631        .unindent(),
 9632    );
 9633    cx.update_editor(|editor, window, cx| {
 9634        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9635    });
 9636    cx.assert_editor_state(
 9637        &r#"
 9638            <!-- <p>A</p>ˇ -->
 9639            <!-- <p>B</p>ˇ -->
 9640            <!-- <p>C</p>ˇ -->
 9641        "#
 9642        .unindent(),
 9643    );
 9644    cx.update_editor(|editor, window, cx| {
 9645        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9646    });
 9647    cx.assert_editor_state(
 9648        &r#"
 9649            <p>A</p>ˇ
 9650            <p>B</p>ˇ
 9651            <p>C</p>ˇ
 9652        "#
 9653        .unindent(),
 9654    );
 9655
 9656    // Toggle comments for mixture of empty and non-empty selections, where
 9657    // multiple selections occupy a given line.
 9658    cx.set_state(
 9659        &r#"
 9660            <p>A«</p>
 9661            <p>ˇ»B</p>ˇ
 9662            <p>C«</p>
 9663            <p>ˇ»D</p>ˇ
 9664        "#
 9665        .unindent(),
 9666    );
 9667
 9668    cx.update_editor(|editor, window, cx| {
 9669        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9670    });
 9671    cx.assert_editor_state(
 9672        &r#"
 9673            <!-- <p>A«</p>
 9674            <p>ˇ»B</p>ˇ -->
 9675            <!-- <p>C«</p>
 9676            <p>ˇ»D</p>ˇ -->
 9677        "#
 9678        .unindent(),
 9679    );
 9680    cx.update_editor(|editor, window, cx| {
 9681        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9682    });
 9683    cx.assert_editor_state(
 9684        &r#"
 9685            <p>A«</p>
 9686            <p>ˇ»B</p>ˇ
 9687            <p>C«</p>
 9688            <p>ˇ»D</p>ˇ
 9689        "#
 9690        .unindent(),
 9691    );
 9692
 9693    // Toggle comments when different languages are active for different
 9694    // selections.
 9695    cx.set_state(
 9696        &r#"
 9697            ˇ<script>
 9698                ˇvar x = new Y();
 9699            ˇ</script>
 9700        "#
 9701        .unindent(),
 9702    );
 9703    cx.executor().run_until_parked();
 9704    cx.update_editor(|editor, window, cx| {
 9705        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9706    });
 9707    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9708    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9709    cx.assert_editor_state(
 9710        &r#"
 9711            <!-- ˇ<script> -->
 9712                // ˇvar x = new Y();
 9713            // ˇ</script>
 9714        "#
 9715        .unindent(),
 9716    );
 9717}
 9718
 9719#[gpui::test]
 9720fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9721    init_test(cx, |_| {});
 9722
 9723    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9724    let multibuffer = cx.new(|cx| {
 9725        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9726        multibuffer.push_excerpts(
 9727            buffer.clone(),
 9728            [
 9729                ExcerptRange {
 9730                    context: Point::new(0, 0)..Point::new(0, 4),
 9731                    primary: None,
 9732                },
 9733                ExcerptRange {
 9734                    context: Point::new(1, 0)..Point::new(1, 4),
 9735                    primary: None,
 9736                },
 9737            ],
 9738            cx,
 9739        );
 9740        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9741        multibuffer
 9742    });
 9743
 9744    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9745    editor.update_in(cx, |editor, window, cx| {
 9746        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9747        editor.change_selections(None, window, cx, |s| {
 9748            s.select_ranges([
 9749                Point::new(0, 0)..Point::new(0, 0),
 9750                Point::new(1, 0)..Point::new(1, 0),
 9751            ])
 9752        });
 9753
 9754        editor.handle_input("X", window, cx);
 9755        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9756        assert_eq!(
 9757            editor.selections.ranges(cx),
 9758            [
 9759                Point::new(0, 1)..Point::new(0, 1),
 9760                Point::new(1, 1)..Point::new(1, 1),
 9761            ]
 9762        );
 9763
 9764        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9765        editor.change_selections(None, window, cx, |s| {
 9766            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9767        });
 9768        editor.backspace(&Default::default(), window, cx);
 9769        assert_eq!(editor.text(cx), "Xa\nbbb");
 9770        assert_eq!(
 9771            editor.selections.ranges(cx),
 9772            [Point::new(1, 0)..Point::new(1, 0)]
 9773        );
 9774
 9775        editor.change_selections(None, window, cx, |s| {
 9776            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9777        });
 9778        editor.backspace(&Default::default(), window, cx);
 9779        assert_eq!(editor.text(cx), "X\nbb");
 9780        assert_eq!(
 9781            editor.selections.ranges(cx),
 9782            [Point::new(0, 1)..Point::new(0, 1)]
 9783        );
 9784    });
 9785}
 9786
 9787#[gpui::test]
 9788fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9789    init_test(cx, |_| {});
 9790
 9791    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9792    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9793        indoc! {"
 9794            [aaaa
 9795            (bbbb]
 9796            cccc)",
 9797        },
 9798        markers.clone(),
 9799    );
 9800    let excerpt_ranges = markers.into_iter().map(|marker| {
 9801        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9802        ExcerptRange {
 9803            context,
 9804            primary: None,
 9805        }
 9806    });
 9807    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9808    let multibuffer = cx.new(|cx| {
 9809        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9810        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9811        multibuffer
 9812    });
 9813
 9814    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9815    editor.update_in(cx, |editor, window, cx| {
 9816        let (expected_text, selection_ranges) = marked_text_ranges(
 9817            indoc! {"
 9818                aaaa
 9819                bˇbbb
 9820                bˇbbˇb
 9821                cccc"
 9822            },
 9823            true,
 9824        );
 9825        assert_eq!(editor.text(cx), expected_text);
 9826        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9827
 9828        editor.handle_input("X", window, cx);
 9829
 9830        let (expected_text, expected_selections) = marked_text_ranges(
 9831            indoc! {"
 9832                aaaa
 9833                bXˇbbXb
 9834                bXˇbbXˇb
 9835                cccc"
 9836            },
 9837            false,
 9838        );
 9839        assert_eq!(editor.text(cx), expected_text);
 9840        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9841
 9842        editor.newline(&Newline, window, cx);
 9843        let (expected_text, expected_selections) = marked_text_ranges(
 9844            indoc! {"
 9845                aaaa
 9846                bX
 9847                ˇbbX
 9848                b
 9849                bX
 9850                ˇbbX
 9851                ˇb
 9852                cccc"
 9853            },
 9854            false,
 9855        );
 9856        assert_eq!(editor.text(cx), expected_text);
 9857        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9858    });
 9859}
 9860
 9861#[gpui::test]
 9862fn test_refresh_selections(cx: &mut TestAppContext) {
 9863    init_test(cx, |_| {});
 9864
 9865    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9866    let mut excerpt1_id = None;
 9867    let multibuffer = cx.new(|cx| {
 9868        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9869        excerpt1_id = multibuffer
 9870            .push_excerpts(
 9871                buffer.clone(),
 9872                [
 9873                    ExcerptRange {
 9874                        context: Point::new(0, 0)..Point::new(1, 4),
 9875                        primary: None,
 9876                    },
 9877                    ExcerptRange {
 9878                        context: Point::new(1, 0)..Point::new(2, 4),
 9879                        primary: None,
 9880                    },
 9881                ],
 9882                cx,
 9883            )
 9884            .into_iter()
 9885            .next();
 9886        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9887        multibuffer
 9888    });
 9889
 9890    let editor = cx.add_window(|window, cx| {
 9891        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9892        let snapshot = editor.snapshot(window, cx);
 9893        editor.change_selections(None, window, cx, |s| {
 9894            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9895        });
 9896        editor.begin_selection(
 9897            Point::new(2, 1).to_display_point(&snapshot),
 9898            true,
 9899            1,
 9900            window,
 9901            cx,
 9902        );
 9903        assert_eq!(
 9904            editor.selections.ranges(cx),
 9905            [
 9906                Point::new(1, 3)..Point::new(1, 3),
 9907                Point::new(2, 1)..Point::new(2, 1),
 9908            ]
 9909        );
 9910        editor
 9911    });
 9912
 9913    // Refreshing selections is a no-op when excerpts haven't changed.
 9914    _ = editor.update(cx, |editor, window, cx| {
 9915        editor.change_selections(None, window, cx, |s| s.refresh());
 9916        assert_eq!(
 9917            editor.selections.ranges(cx),
 9918            [
 9919                Point::new(1, 3)..Point::new(1, 3),
 9920                Point::new(2, 1)..Point::new(2, 1),
 9921            ]
 9922        );
 9923    });
 9924
 9925    multibuffer.update(cx, |multibuffer, cx| {
 9926        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9927    });
 9928    _ = editor.update(cx, |editor, window, cx| {
 9929        // Removing an excerpt causes the first selection to become degenerate.
 9930        assert_eq!(
 9931            editor.selections.ranges(cx),
 9932            [
 9933                Point::new(0, 0)..Point::new(0, 0),
 9934                Point::new(0, 1)..Point::new(0, 1)
 9935            ]
 9936        );
 9937
 9938        // Refreshing selections will relocate the first selection to the original buffer
 9939        // location.
 9940        editor.change_selections(None, window, cx, |s| s.refresh());
 9941        assert_eq!(
 9942            editor.selections.ranges(cx),
 9943            [
 9944                Point::new(0, 1)..Point::new(0, 1),
 9945                Point::new(0, 3)..Point::new(0, 3)
 9946            ]
 9947        );
 9948        assert!(editor.selections.pending_anchor().is_some());
 9949    });
 9950}
 9951
 9952#[gpui::test]
 9953fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9954    init_test(cx, |_| {});
 9955
 9956    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9957    let mut excerpt1_id = None;
 9958    let multibuffer = cx.new(|cx| {
 9959        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9960        excerpt1_id = multibuffer
 9961            .push_excerpts(
 9962                buffer.clone(),
 9963                [
 9964                    ExcerptRange {
 9965                        context: Point::new(0, 0)..Point::new(1, 4),
 9966                        primary: None,
 9967                    },
 9968                    ExcerptRange {
 9969                        context: Point::new(1, 0)..Point::new(2, 4),
 9970                        primary: None,
 9971                    },
 9972                ],
 9973                cx,
 9974            )
 9975            .into_iter()
 9976            .next();
 9977        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9978        multibuffer
 9979    });
 9980
 9981    let editor = cx.add_window(|window, cx| {
 9982        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9983        let snapshot = editor.snapshot(window, cx);
 9984        editor.begin_selection(
 9985            Point::new(1, 3).to_display_point(&snapshot),
 9986            false,
 9987            1,
 9988            window,
 9989            cx,
 9990        );
 9991        assert_eq!(
 9992            editor.selections.ranges(cx),
 9993            [Point::new(1, 3)..Point::new(1, 3)]
 9994        );
 9995        editor
 9996    });
 9997
 9998    multibuffer.update(cx, |multibuffer, cx| {
 9999        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10000    });
10001    _ = editor.update(cx, |editor, window, cx| {
10002        assert_eq!(
10003            editor.selections.ranges(cx),
10004            [Point::new(0, 0)..Point::new(0, 0)]
10005        );
10006
10007        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10008        editor.change_selections(None, window, cx, |s| s.refresh());
10009        assert_eq!(
10010            editor.selections.ranges(cx),
10011            [Point::new(0, 3)..Point::new(0, 3)]
10012        );
10013        assert!(editor.selections.pending_anchor().is_some());
10014    });
10015}
10016
10017#[gpui::test]
10018async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10019    init_test(cx, |_| {});
10020
10021    let language = Arc::new(
10022        Language::new(
10023            LanguageConfig {
10024                brackets: BracketPairConfig {
10025                    pairs: vec![
10026                        BracketPair {
10027                            start: "{".to_string(),
10028                            end: "}".to_string(),
10029                            close: true,
10030                            surround: true,
10031                            newline: true,
10032                        },
10033                        BracketPair {
10034                            start: "/* ".to_string(),
10035                            end: " */".to_string(),
10036                            close: true,
10037                            surround: true,
10038                            newline: true,
10039                        },
10040                    ],
10041                    ..Default::default()
10042                },
10043                ..Default::default()
10044            },
10045            Some(tree_sitter_rust::LANGUAGE.into()),
10046        )
10047        .with_indents_query("")
10048        .unwrap(),
10049    );
10050
10051    let text = concat!(
10052        "{   }\n",     //
10053        "  x\n",       //
10054        "  /*   */\n", //
10055        "x\n",         //
10056        "{{} }\n",     //
10057    );
10058
10059    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10060    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10061    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10062    editor
10063        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10064        .await;
10065
10066    editor.update_in(cx, |editor, window, cx| {
10067        editor.change_selections(None, window, cx, |s| {
10068            s.select_display_ranges([
10069                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10070                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10071                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10072            ])
10073        });
10074        editor.newline(&Newline, window, cx);
10075
10076        assert_eq!(
10077            editor.buffer().read(cx).read(cx).text(),
10078            concat!(
10079                "{ \n",    // Suppress rustfmt
10080                "\n",      //
10081                "}\n",     //
10082                "  x\n",   //
10083                "  /* \n", //
10084                "  \n",    //
10085                "  */\n",  //
10086                "x\n",     //
10087                "{{} \n",  //
10088                "}\n",     //
10089            )
10090        );
10091    });
10092}
10093
10094#[gpui::test]
10095fn test_highlighted_ranges(cx: &mut TestAppContext) {
10096    init_test(cx, |_| {});
10097
10098    let editor = cx.add_window(|window, cx| {
10099        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10100        build_editor(buffer.clone(), window, cx)
10101    });
10102
10103    _ = editor.update(cx, |editor, window, cx| {
10104        struct Type1;
10105        struct Type2;
10106
10107        let buffer = editor.buffer.read(cx).snapshot(cx);
10108
10109        let anchor_range =
10110            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10111
10112        editor.highlight_background::<Type1>(
10113            &[
10114                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10115                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10116                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10117                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10118            ],
10119            |_| Hsla::red(),
10120            cx,
10121        );
10122        editor.highlight_background::<Type2>(
10123            &[
10124                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10125                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10126                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10127                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10128            ],
10129            |_| Hsla::green(),
10130            cx,
10131        );
10132
10133        let snapshot = editor.snapshot(window, cx);
10134        let mut highlighted_ranges = editor.background_highlights_in_range(
10135            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10136            &snapshot,
10137            cx.theme().colors(),
10138        );
10139        // Enforce a consistent ordering based on color without relying on the ordering of the
10140        // highlight's `TypeId` which is non-executor.
10141        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10142        assert_eq!(
10143            highlighted_ranges,
10144            &[
10145                (
10146                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10147                    Hsla::red(),
10148                ),
10149                (
10150                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10151                    Hsla::red(),
10152                ),
10153                (
10154                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10155                    Hsla::green(),
10156                ),
10157                (
10158                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10159                    Hsla::green(),
10160                ),
10161            ]
10162        );
10163        assert_eq!(
10164            editor.background_highlights_in_range(
10165                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10166                &snapshot,
10167                cx.theme().colors(),
10168            ),
10169            &[(
10170                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10171                Hsla::red(),
10172            )]
10173        );
10174    });
10175}
10176
10177#[gpui::test]
10178async fn test_following(cx: &mut gpui::TestAppContext) {
10179    init_test(cx, |_| {});
10180
10181    let fs = FakeFs::new(cx.executor());
10182    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10183
10184    let buffer = project.update(cx, |project, cx| {
10185        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10186        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10187    });
10188    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10189    let follower = cx.update(|cx| {
10190        cx.open_window(
10191            WindowOptions {
10192                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10193                    gpui::Point::new(px(0.), px(0.)),
10194                    gpui::Point::new(px(10.), px(80.)),
10195                ))),
10196                ..Default::default()
10197            },
10198            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10199        )
10200        .unwrap()
10201    });
10202
10203    let is_still_following = Rc::new(RefCell::new(true));
10204    let follower_edit_event_count = Rc::new(RefCell::new(0));
10205    let pending_update = Rc::new(RefCell::new(None));
10206    let leader_entity = leader.root(cx).unwrap();
10207    let follower_entity = follower.root(cx).unwrap();
10208    _ = follower.update(cx, {
10209        let update = pending_update.clone();
10210        let is_still_following = is_still_following.clone();
10211        let follower_edit_event_count = follower_edit_event_count.clone();
10212        |_, window, cx| {
10213            cx.subscribe_in(
10214                &leader_entity,
10215                window,
10216                move |_, leader, event, window, cx| {
10217                    leader.read(cx).add_event_to_update_proto(
10218                        event,
10219                        &mut update.borrow_mut(),
10220                        window,
10221                        cx,
10222                    );
10223                },
10224            )
10225            .detach();
10226
10227            cx.subscribe_in(
10228                &follower_entity,
10229                window,
10230                move |_, _, event: &EditorEvent, _window, _cx| {
10231                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10232                        *is_still_following.borrow_mut() = false;
10233                    }
10234
10235                    if let EditorEvent::BufferEdited = event {
10236                        *follower_edit_event_count.borrow_mut() += 1;
10237                    }
10238                },
10239            )
10240            .detach();
10241        }
10242    });
10243
10244    // Update the selections only
10245    _ = leader.update(cx, |leader, window, cx| {
10246        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10247    });
10248    follower
10249        .update(cx, |follower, window, cx| {
10250            follower.apply_update_proto(
10251                &project,
10252                pending_update.borrow_mut().take().unwrap(),
10253                window,
10254                cx,
10255            )
10256        })
10257        .unwrap()
10258        .await
10259        .unwrap();
10260    _ = follower.update(cx, |follower, _, cx| {
10261        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10262    });
10263    assert!(*is_still_following.borrow());
10264    assert_eq!(*follower_edit_event_count.borrow(), 0);
10265
10266    // Update the scroll position only
10267    _ = leader.update(cx, |leader, window, cx| {
10268        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10269    });
10270    follower
10271        .update(cx, |follower, window, cx| {
10272            follower.apply_update_proto(
10273                &project,
10274                pending_update.borrow_mut().take().unwrap(),
10275                window,
10276                cx,
10277            )
10278        })
10279        .unwrap()
10280        .await
10281        .unwrap();
10282    assert_eq!(
10283        follower
10284            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10285            .unwrap(),
10286        gpui::Point::new(1.5, 3.5)
10287    );
10288    assert!(*is_still_following.borrow());
10289    assert_eq!(*follower_edit_event_count.borrow(), 0);
10290
10291    // Update the selections and scroll position. The follower's scroll position is updated
10292    // via autoscroll, not via the leader's exact scroll position.
10293    _ = leader.update(cx, |leader, window, cx| {
10294        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10295        leader.request_autoscroll(Autoscroll::newest(), cx);
10296        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10297    });
10298    follower
10299        .update(cx, |follower, window, cx| {
10300            follower.apply_update_proto(
10301                &project,
10302                pending_update.borrow_mut().take().unwrap(),
10303                window,
10304                cx,
10305            )
10306        })
10307        .unwrap()
10308        .await
10309        .unwrap();
10310    _ = follower.update(cx, |follower, _, cx| {
10311        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10312        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10313    });
10314    assert!(*is_still_following.borrow());
10315
10316    // Creating a pending selection that precedes another selection
10317    _ = leader.update(cx, |leader, window, cx| {
10318        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10319        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10320    });
10321    follower
10322        .update(cx, |follower, window, cx| {
10323            follower.apply_update_proto(
10324                &project,
10325                pending_update.borrow_mut().take().unwrap(),
10326                window,
10327                cx,
10328            )
10329        })
10330        .unwrap()
10331        .await
10332        .unwrap();
10333    _ = follower.update(cx, |follower, _, cx| {
10334        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10335    });
10336    assert!(*is_still_following.borrow());
10337
10338    // Extend the pending selection so that it surrounds another selection
10339    _ = leader.update(cx, |leader, window, cx| {
10340        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10341    });
10342    follower
10343        .update(cx, |follower, window, cx| {
10344            follower.apply_update_proto(
10345                &project,
10346                pending_update.borrow_mut().take().unwrap(),
10347                window,
10348                cx,
10349            )
10350        })
10351        .unwrap()
10352        .await
10353        .unwrap();
10354    _ = follower.update(cx, |follower, _, cx| {
10355        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10356    });
10357
10358    // Scrolling locally breaks the follow
10359    _ = follower.update(cx, |follower, window, cx| {
10360        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10361        follower.set_scroll_anchor(
10362            ScrollAnchor {
10363                anchor: top_anchor,
10364                offset: gpui::Point::new(0.0, 0.5),
10365            },
10366            window,
10367            cx,
10368        );
10369    });
10370    assert!(!(*is_still_following.borrow()));
10371}
10372
10373#[gpui::test]
10374async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10375    init_test(cx, |_| {});
10376
10377    let fs = FakeFs::new(cx.executor());
10378    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10379    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10380    let pane = workspace
10381        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10382        .unwrap();
10383
10384    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10385
10386    let leader = pane.update_in(cx, |_, window, cx| {
10387        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10388        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10389    });
10390
10391    // Start following the editor when it has no excerpts.
10392    let mut state_message =
10393        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10394    let workspace_entity = workspace.root(cx).unwrap();
10395    let follower_1 = cx
10396        .update_window(*workspace.deref(), |_, window, cx| {
10397            Editor::from_state_proto(
10398                workspace_entity,
10399                ViewId {
10400                    creator: Default::default(),
10401                    id: 0,
10402                },
10403                &mut state_message,
10404                window,
10405                cx,
10406            )
10407        })
10408        .unwrap()
10409        .unwrap()
10410        .await
10411        .unwrap();
10412
10413    let update_message = Rc::new(RefCell::new(None));
10414    follower_1.update_in(cx, {
10415        let update = update_message.clone();
10416        |_, window, cx| {
10417            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10418                leader.read(cx).add_event_to_update_proto(
10419                    event,
10420                    &mut update.borrow_mut(),
10421                    window,
10422                    cx,
10423                );
10424            })
10425            .detach();
10426        }
10427    });
10428
10429    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10430        (
10431            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10432            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10433        )
10434    });
10435
10436    // Insert some excerpts.
10437    leader.update(cx, |leader, cx| {
10438        leader.buffer.update(cx, |multibuffer, cx| {
10439            let excerpt_ids = multibuffer.push_excerpts(
10440                buffer_1.clone(),
10441                [
10442                    ExcerptRange {
10443                        context: 1..6,
10444                        primary: None,
10445                    },
10446                    ExcerptRange {
10447                        context: 12..15,
10448                        primary: None,
10449                    },
10450                    ExcerptRange {
10451                        context: 0..3,
10452                        primary: None,
10453                    },
10454                ],
10455                cx,
10456            );
10457            multibuffer.insert_excerpts_after(
10458                excerpt_ids[0],
10459                buffer_2.clone(),
10460                [
10461                    ExcerptRange {
10462                        context: 8..12,
10463                        primary: None,
10464                    },
10465                    ExcerptRange {
10466                        context: 0..6,
10467                        primary: None,
10468                    },
10469                ],
10470                cx,
10471            );
10472        });
10473    });
10474
10475    // Apply the update of adding the excerpts.
10476    follower_1
10477        .update_in(cx, |follower, window, cx| {
10478            follower.apply_update_proto(
10479                &project,
10480                update_message.borrow().clone().unwrap(),
10481                window,
10482                cx,
10483            )
10484        })
10485        .await
10486        .unwrap();
10487    assert_eq!(
10488        follower_1.update(cx, |editor, cx| editor.text(cx)),
10489        leader.update(cx, |editor, cx| editor.text(cx))
10490    );
10491    update_message.borrow_mut().take();
10492
10493    // Start following separately after it already has excerpts.
10494    let mut state_message =
10495        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10496    let workspace_entity = workspace.root(cx).unwrap();
10497    let follower_2 = cx
10498        .update_window(*workspace.deref(), |_, window, cx| {
10499            Editor::from_state_proto(
10500                workspace_entity,
10501                ViewId {
10502                    creator: Default::default(),
10503                    id: 0,
10504                },
10505                &mut state_message,
10506                window,
10507                cx,
10508            )
10509        })
10510        .unwrap()
10511        .unwrap()
10512        .await
10513        .unwrap();
10514    assert_eq!(
10515        follower_2.update(cx, |editor, cx| editor.text(cx)),
10516        leader.update(cx, |editor, cx| editor.text(cx))
10517    );
10518
10519    // Remove some excerpts.
10520    leader.update(cx, |leader, cx| {
10521        leader.buffer.update(cx, |multibuffer, cx| {
10522            let excerpt_ids = multibuffer.excerpt_ids();
10523            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10524            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10525        });
10526    });
10527
10528    // Apply the update of removing the excerpts.
10529    follower_1
10530        .update_in(cx, |follower, window, cx| {
10531            follower.apply_update_proto(
10532                &project,
10533                update_message.borrow().clone().unwrap(),
10534                window,
10535                cx,
10536            )
10537        })
10538        .await
10539        .unwrap();
10540    follower_2
10541        .update_in(cx, |follower, window, cx| {
10542            follower.apply_update_proto(
10543                &project,
10544                update_message.borrow().clone().unwrap(),
10545                window,
10546                cx,
10547            )
10548        })
10549        .await
10550        .unwrap();
10551    update_message.borrow_mut().take();
10552    assert_eq!(
10553        follower_1.update(cx, |editor, cx| editor.text(cx)),
10554        leader.update(cx, |editor, cx| editor.text(cx))
10555    );
10556}
10557
10558#[gpui::test]
10559async fn go_to_prev_overlapping_diagnostic(
10560    executor: BackgroundExecutor,
10561    cx: &mut gpui::TestAppContext,
10562) {
10563    init_test(cx, |_| {});
10564
10565    let mut cx = EditorTestContext::new(cx).await;
10566    let lsp_store =
10567        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10568
10569    cx.set_state(indoc! {"
10570        ˇfn func(abc def: i32) -> u32 {
10571        }
10572    "});
10573
10574    cx.update(|_, cx| {
10575        lsp_store.update(cx, |lsp_store, cx| {
10576            lsp_store
10577                .update_diagnostics(
10578                    LanguageServerId(0),
10579                    lsp::PublishDiagnosticsParams {
10580                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10581                        version: None,
10582                        diagnostics: vec![
10583                            lsp::Diagnostic {
10584                                range: lsp::Range::new(
10585                                    lsp::Position::new(0, 11),
10586                                    lsp::Position::new(0, 12),
10587                                ),
10588                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10589                                ..Default::default()
10590                            },
10591                            lsp::Diagnostic {
10592                                range: lsp::Range::new(
10593                                    lsp::Position::new(0, 12),
10594                                    lsp::Position::new(0, 15),
10595                                ),
10596                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10597                                ..Default::default()
10598                            },
10599                            lsp::Diagnostic {
10600                                range: lsp::Range::new(
10601                                    lsp::Position::new(0, 25),
10602                                    lsp::Position::new(0, 28),
10603                                ),
10604                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10605                                ..Default::default()
10606                            },
10607                        ],
10608                    },
10609                    &[],
10610                    cx,
10611                )
10612                .unwrap()
10613        });
10614    });
10615
10616    executor.run_until_parked();
10617
10618    cx.update_editor(|editor, window, cx| {
10619        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10620    });
10621
10622    cx.assert_editor_state(indoc! {"
10623        fn func(abc def: i32) -> ˇu32 {
10624        }
10625    "});
10626
10627    cx.update_editor(|editor, window, cx| {
10628        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10629    });
10630
10631    cx.assert_editor_state(indoc! {"
10632        fn func(abc ˇdef: i32) -> u32 {
10633        }
10634    "});
10635
10636    cx.update_editor(|editor, window, cx| {
10637        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10638    });
10639
10640    cx.assert_editor_state(indoc! {"
10641        fn func(abcˇ def: i32) -> u32 {
10642        }
10643    "});
10644
10645    cx.update_editor(|editor, window, cx| {
10646        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10647    });
10648
10649    cx.assert_editor_state(indoc! {"
10650        fn func(abc def: i32) -> ˇu32 {
10651        }
10652    "});
10653}
10654
10655#[gpui::test]
10656async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10657    init_test(cx, |_| {});
10658
10659    let mut cx = EditorTestContext::new(cx).await;
10660
10661    cx.set_state(indoc! {"
10662        fn func(abˇc def: i32) -> u32 {
10663        }
10664    "});
10665    let lsp_store =
10666        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10667
10668    cx.update(|_, cx| {
10669        lsp_store.update(cx, |lsp_store, cx| {
10670            lsp_store.update_diagnostics(
10671                LanguageServerId(0),
10672                lsp::PublishDiagnosticsParams {
10673                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10674                    version: None,
10675                    diagnostics: vec![lsp::Diagnostic {
10676                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10677                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10678                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10679                        ..Default::default()
10680                    }],
10681                },
10682                &[],
10683                cx,
10684            )
10685        })
10686    }).unwrap();
10687    cx.run_until_parked();
10688    cx.update_editor(|editor, window, cx| {
10689        hover_popover::hover(editor, &Default::default(), window, cx)
10690    });
10691    cx.run_until_parked();
10692    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10693}
10694
10695#[gpui::test]
10696async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10697    init_test(cx, |_| {});
10698
10699    let mut cx = EditorTestContext::new(cx).await;
10700
10701    let diff_base = r#"
10702        use some::mod;
10703
10704        const A: u32 = 42;
10705
10706        fn main() {
10707            println!("hello");
10708
10709            println!("world");
10710        }
10711        "#
10712    .unindent();
10713
10714    // Edits are modified, removed, modified, added
10715    cx.set_state(
10716        &r#"
10717        use some::modified;
10718
10719        ˇ
10720        fn main() {
10721            println!("hello there");
10722
10723            println!("around the");
10724            println!("world");
10725        }
10726        "#
10727        .unindent(),
10728    );
10729
10730    cx.set_diff_base(&diff_base);
10731    executor.run_until_parked();
10732
10733    cx.update_editor(|editor, window, cx| {
10734        //Wrap around the bottom of the buffer
10735        for _ in 0..3 {
10736            editor.go_to_next_hunk(&GoToHunk, window, cx);
10737        }
10738    });
10739
10740    cx.assert_editor_state(
10741        &r#"
10742        ˇuse some::modified;
10743
10744
10745        fn main() {
10746            println!("hello there");
10747
10748            println!("around the");
10749            println!("world");
10750        }
10751        "#
10752        .unindent(),
10753    );
10754
10755    cx.update_editor(|editor, window, cx| {
10756        //Wrap around the top of the buffer
10757        for _ in 0..2 {
10758            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10759        }
10760    });
10761
10762    cx.assert_editor_state(
10763        &r#"
10764        use some::modified;
10765
10766
10767        fn main() {
10768        ˇ    println!("hello there");
10769
10770            println!("around the");
10771            println!("world");
10772        }
10773        "#
10774        .unindent(),
10775    );
10776
10777    cx.update_editor(|editor, window, cx| {
10778        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10779    });
10780
10781    cx.assert_editor_state(
10782        &r#"
10783        use some::modified;
10784
10785        ˇ
10786        fn main() {
10787            println!("hello there");
10788
10789            println!("around the");
10790            println!("world");
10791        }
10792        "#
10793        .unindent(),
10794    );
10795
10796    cx.update_editor(|editor, window, cx| {
10797        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10798    });
10799
10800    cx.assert_editor_state(
10801        &r#"
10802        ˇuse some::modified;
10803
10804
10805        fn main() {
10806            println!("hello there");
10807
10808            println!("around the");
10809            println!("world");
10810        }
10811        "#
10812        .unindent(),
10813    );
10814
10815    cx.update_editor(|editor, window, cx| {
10816        for _ in 0..2 {
10817            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10818        }
10819    });
10820
10821    cx.assert_editor_state(
10822        &r#"
10823        use some::modified;
10824
10825
10826        fn main() {
10827        ˇ    println!("hello there");
10828
10829            println!("around the");
10830            println!("world");
10831        }
10832        "#
10833        .unindent(),
10834    );
10835
10836    cx.update_editor(|editor, window, cx| {
10837        editor.fold(&Fold, window, cx);
10838    });
10839
10840    cx.update_editor(|editor, window, cx| {
10841        editor.go_to_next_hunk(&GoToHunk, window, cx);
10842    });
10843
10844    cx.assert_editor_state(
10845        &r#"
10846        ˇuse some::modified;
10847
10848
10849        fn main() {
10850            println!("hello there");
10851
10852            println!("around the");
10853            println!("world");
10854        }
10855        "#
10856        .unindent(),
10857    );
10858}
10859
10860#[test]
10861fn test_split_words() {
10862    fn split(text: &str) -> Vec<&str> {
10863        split_words(text).collect()
10864    }
10865
10866    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
10867    assert_eq!(split("hello_world"), &["hello_", "world"]);
10868    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
10869    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
10870    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
10871    assert_eq!(split("helloworld"), &["helloworld"]);
10872
10873    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
10874}
10875
10876#[gpui::test]
10877async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
10878    init_test(cx, |_| {});
10879
10880    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
10881    let mut assert = |before, after| {
10882        let _state_context = cx.set_state(before);
10883        cx.run_until_parked();
10884        cx.update_editor(|editor, window, cx| {
10885            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
10886        });
10887        cx.assert_editor_state(after);
10888    };
10889
10890    // Outside bracket jumps to outside of matching bracket
10891    assert("console.logˇ(var);", "console.log(var)ˇ;");
10892    assert("console.log(var)ˇ;", "console.logˇ(var);");
10893
10894    // Inside bracket jumps to inside of matching bracket
10895    assert("console.log(ˇvar);", "console.log(varˇ);");
10896    assert("console.log(varˇ);", "console.log(ˇvar);");
10897
10898    // When outside a bracket and inside, favor jumping to the inside bracket
10899    assert(
10900        "console.log('foo', [1, 2, 3]ˇ);",
10901        "console.log(ˇ'foo', [1, 2, 3]);",
10902    );
10903    assert(
10904        "console.log(ˇ'foo', [1, 2, 3]);",
10905        "console.log('foo', [1, 2, 3]ˇ);",
10906    );
10907
10908    // Bias forward if two options are equally likely
10909    assert(
10910        "let result = curried_fun()ˇ();",
10911        "let result = curried_fun()()ˇ;",
10912    );
10913
10914    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
10915    assert(
10916        indoc! {"
10917            function test() {
10918                console.log('test')ˇ
10919            }"},
10920        indoc! {"
10921            function test() {
10922                console.logˇ('test')
10923            }"},
10924    );
10925}
10926
10927#[gpui::test]
10928async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
10929    init_test(cx, |_| {});
10930
10931    let fs = FakeFs::new(cx.executor());
10932    fs.insert_tree(
10933        path!("/a"),
10934        json!({
10935            "main.rs": "fn main() { let a = 5; }",
10936            "other.rs": "// Test file",
10937        }),
10938    )
10939    .await;
10940    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10941
10942    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10943    language_registry.add(Arc::new(Language::new(
10944        LanguageConfig {
10945            name: "Rust".into(),
10946            matcher: LanguageMatcher {
10947                path_suffixes: vec!["rs".to_string()],
10948                ..Default::default()
10949            },
10950            brackets: BracketPairConfig {
10951                pairs: vec![BracketPair {
10952                    start: "{".to_string(),
10953                    end: "}".to_string(),
10954                    close: true,
10955                    surround: true,
10956                    newline: true,
10957                }],
10958                disabled_scopes_by_bracket_ix: Vec::new(),
10959            },
10960            ..Default::default()
10961        },
10962        Some(tree_sitter_rust::LANGUAGE.into()),
10963    )));
10964    let mut fake_servers = language_registry.register_fake_lsp(
10965        "Rust",
10966        FakeLspAdapter {
10967            capabilities: lsp::ServerCapabilities {
10968                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
10969                    first_trigger_character: "{".to_string(),
10970                    more_trigger_character: None,
10971                }),
10972                ..Default::default()
10973            },
10974            ..Default::default()
10975        },
10976    );
10977
10978    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10979
10980    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10981
10982    let worktree_id = workspace
10983        .update(cx, |workspace, _, cx| {
10984            workspace.project().update(cx, |project, cx| {
10985                project.worktrees(cx).next().unwrap().read(cx).id()
10986            })
10987        })
10988        .unwrap();
10989
10990    let buffer = project
10991        .update(cx, |project, cx| {
10992            project.open_local_buffer(path!("/a/main.rs"), cx)
10993        })
10994        .await
10995        .unwrap();
10996    let editor_handle = workspace
10997        .update(cx, |workspace, window, cx| {
10998            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
10999        })
11000        .unwrap()
11001        .await
11002        .unwrap()
11003        .downcast::<Editor>()
11004        .unwrap();
11005
11006    cx.executor().start_waiting();
11007    let fake_server = fake_servers.next().await.unwrap();
11008
11009    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11010        assert_eq!(
11011            params.text_document_position.text_document.uri,
11012            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11013        );
11014        assert_eq!(
11015            params.text_document_position.position,
11016            lsp::Position::new(0, 21),
11017        );
11018
11019        Ok(Some(vec![lsp::TextEdit {
11020            new_text: "]".to_string(),
11021            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11022        }]))
11023    });
11024
11025    editor_handle.update_in(cx, |editor, window, cx| {
11026        window.focus(&editor.focus_handle(cx));
11027        editor.change_selections(None, window, cx, |s| {
11028            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11029        });
11030        editor.handle_input("{", window, cx);
11031    });
11032
11033    cx.executor().run_until_parked();
11034
11035    buffer.update(cx, |buffer, _| {
11036        assert_eq!(
11037            buffer.text(),
11038            "fn main() { let a = {5}; }",
11039            "No extra braces from on type formatting should appear in the buffer"
11040        )
11041    });
11042}
11043
11044#[gpui::test]
11045async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11046    init_test(cx, |_| {});
11047
11048    let fs = FakeFs::new(cx.executor());
11049    fs.insert_tree(
11050        path!("/a"),
11051        json!({
11052            "main.rs": "fn main() { let a = 5; }",
11053            "other.rs": "// Test file",
11054        }),
11055    )
11056    .await;
11057
11058    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11059
11060    let server_restarts = Arc::new(AtomicUsize::new(0));
11061    let closure_restarts = Arc::clone(&server_restarts);
11062    let language_server_name = "test language server";
11063    let language_name: LanguageName = "Rust".into();
11064
11065    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11066    language_registry.add(Arc::new(Language::new(
11067        LanguageConfig {
11068            name: language_name.clone(),
11069            matcher: LanguageMatcher {
11070                path_suffixes: vec!["rs".to_string()],
11071                ..Default::default()
11072            },
11073            ..Default::default()
11074        },
11075        Some(tree_sitter_rust::LANGUAGE.into()),
11076    )));
11077    let mut fake_servers = language_registry.register_fake_lsp(
11078        "Rust",
11079        FakeLspAdapter {
11080            name: language_server_name,
11081            initialization_options: Some(json!({
11082                "testOptionValue": true
11083            })),
11084            initializer: Some(Box::new(move |fake_server| {
11085                let task_restarts = Arc::clone(&closure_restarts);
11086                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11087                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11088                    futures::future::ready(Ok(()))
11089                });
11090            })),
11091            ..Default::default()
11092        },
11093    );
11094
11095    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11096    let _buffer = project
11097        .update(cx, |project, cx| {
11098            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11099        })
11100        .await
11101        .unwrap();
11102    let _fake_server = fake_servers.next().await.unwrap();
11103    update_test_language_settings(cx, |language_settings| {
11104        language_settings.languages.insert(
11105            language_name.clone(),
11106            LanguageSettingsContent {
11107                tab_size: NonZeroU32::new(8),
11108                ..Default::default()
11109            },
11110        );
11111    });
11112    cx.executor().run_until_parked();
11113    assert_eq!(
11114        server_restarts.load(atomic::Ordering::Acquire),
11115        0,
11116        "Should not restart LSP server on an unrelated change"
11117    );
11118
11119    update_test_project_settings(cx, |project_settings| {
11120        project_settings.lsp.insert(
11121            "Some other server name".into(),
11122            LspSettings {
11123                binary: None,
11124                settings: None,
11125                initialization_options: Some(json!({
11126                    "some other init value": false
11127                })),
11128            },
11129        );
11130    });
11131    cx.executor().run_until_parked();
11132    assert_eq!(
11133        server_restarts.load(atomic::Ordering::Acquire),
11134        0,
11135        "Should not restart LSP server on an unrelated LSP settings change"
11136    );
11137
11138    update_test_project_settings(cx, |project_settings| {
11139        project_settings.lsp.insert(
11140            language_server_name.into(),
11141            LspSettings {
11142                binary: None,
11143                settings: None,
11144                initialization_options: Some(json!({
11145                    "anotherInitValue": false
11146                })),
11147            },
11148        );
11149    });
11150    cx.executor().run_until_parked();
11151    assert_eq!(
11152        server_restarts.load(atomic::Ordering::Acquire),
11153        1,
11154        "Should restart LSP server on a related LSP settings change"
11155    );
11156
11157    update_test_project_settings(cx, |project_settings| {
11158        project_settings.lsp.insert(
11159            language_server_name.into(),
11160            LspSettings {
11161                binary: None,
11162                settings: None,
11163                initialization_options: Some(json!({
11164                    "anotherInitValue": false
11165                })),
11166            },
11167        );
11168    });
11169    cx.executor().run_until_parked();
11170    assert_eq!(
11171        server_restarts.load(atomic::Ordering::Acquire),
11172        1,
11173        "Should not restart LSP server on a related LSP settings change that is the same"
11174    );
11175
11176    update_test_project_settings(cx, |project_settings| {
11177        project_settings.lsp.insert(
11178            language_server_name.into(),
11179            LspSettings {
11180                binary: None,
11181                settings: None,
11182                initialization_options: None,
11183            },
11184        );
11185    });
11186    cx.executor().run_until_parked();
11187    assert_eq!(
11188        server_restarts.load(atomic::Ordering::Acquire),
11189        2,
11190        "Should restart LSP server on another related LSP settings change"
11191    );
11192}
11193
11194#[gpui::test]
11195async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11196    init_test(cx, |_| {});
11197
11198    let mut cx = EditorLspTestContext::new_rust(
11199        lsp::ServerCapabilities {
11200            completion_provider: Some(lsp::CompletionOptions {
11201                trigger_characters: Some(vec![".".to_string()]),
11202                resolve_provider: Some(true),
11203                ..Default::default()
11204            }),
11205            ..Default::default()
11206        },
11207        cx,
11208    )
11209    .await;
11210
11211    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11212    cx.simulate_keystroke(".");
11213    let completion_item = lsp::CompletionItem {
11214        label: "some".into(),
11215        kind: Some(lsp::CompletionItemKind::SNIPPET),
11216        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11217        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11218            kind: lsp::MarkupKind::Markdown,
11219            value: "```rust\nSome(2)\n```".to_string(),
11220        })),
11221        deprecated: Some(false),
11222        sort_text: Some("fffffff2".to_string()),
11223        filter_text: Some("some".to_string()),
11224        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11225        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11226            range: lsp::Range {
11227                start: lsp::Position {
11228                    line: 0,
11229                    character: 22,
11230                },
11231                end: lsp::Position {
11232                    line: 0,
11233                    character: 22,
11234                },
11235            },
11236            new_text: "Some(2)".to_string(),
11237        })),
11238        additional_text_edits: Some(vec![lsp::TextEdit {
11239            range: lsp::Range {
11240                start: lsp::Position {
11241                    line: 0,
11242                    character: 20,
11243                },
11244                end: lsp::Position {
11245                    line: 0,
11246                    character: 22,
11247                },
11248            },
11249            new_text: "".to_string(),
11250        }]),
11251        ..Default::default()
11252    };
11253
11254    let closure_completion_item = completion_item.clone();
11255    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11256        let task_completion_item = closure_completion_item.clone();
11257        async move {
11258            Ok(Some(lsp::CompletionResponse::Array(vec![
11259                task_completion_item,
11260            ])))
11261        }
11262    });
11263
11264    request.next().await;
11265
11266    cx.condition(|editor, _| editor.context_menu_visible())
11267        .await;
11268    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11269        editor
11270            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11271            .unwrap()
11272    });
11273    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11274
11275    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11276        let task_completion_item = completion_item.clone();
11277        async move { Ok(task_completion_item) }
11278    })
11279    .next()
11280    .await
11281    .unwrap();
11282    apply_additional_edits.await.unwrap();
11283    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11284}
11285
11286#[gpui::test]
11287async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11288    cx: &mut gpui::TestAppContext,
11289) {
11290    init_test(cx, |_| {});
11291
11292    let mut cx = EditorLspTestContext::new_rust(
11293        lsp::ServerCapabilities {
11294            completion_provider: Some(lsp::CompletionOptions {
11295                trigger_characters: Some(vec![".".to_string()]),
11296                resolve_provider: Some(true),
11297                ..Default::default()
11298            }),
11299            ..Default::default()
11300        },
11301        cx,
11302    )
11303    .await;
11304
11305    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11306    cx.simulate_keystroke(".");
11307
11308    let item1 = lsp::CompletionItem {
11309        label: "method id()".to_string(),
11310        filter_text: Some("id".to_string()),
11311        detail: None,
11312        documentation: None,
11313        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11314            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11315            new_text: ".id".to_string(),
11316        })),
11317        ..lsp::CompletionItem::default()
11318    };
11319
11320    let item2 = lsp::CompletionItem {
11321        label: "other".to_string(),
11322        filter_text: Some("other".to_string()),
11323        detail: None,
11324        documentation: None,
11325        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11326            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11327            new_text: ".other".to_string(),
11328        })),
11329        ..lsp::CompletionItem::default()
11330    };
11331
11332    let item1 = item1.clone();
11333    cx.handle_request::<lsp::request::Completion, _, _>({
11334        let item1 = item1.clone();
11335        move |_, _, _| {
11336            let item1 = item1.clone();
11337            let item2 = item2.clone();
11338            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11339        }
11340    })
11341    .next()
11342    .await;
11343
11344    cx.condition(|editor, _| editor.context_menu_visible())
11345        .await;
11346    cx.update_editor(|editor, _, _| {
11347        let context_menu = editor.context_menu.borrow_mut();
11348        let context_menu = context_menu
11349            .as_ref()
11350            .expect("Should have the context menu deployed");
11351        match context_menu {
11352            CodeContextMenu::Completions(completions_menu) => {
11353                let completions = completions_menu.completions.borrow_mut();
11354                assert_eq!(
11355                    completions
11356                        .iter()
11357                        .map(|completion| &completion.label.text)
11358                        .collect::<Vec<_>>(),
11359                    vec!["method id()", "other"]
11360                )
11361            }
11362            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11363        }
11364    });
11365
11366    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11367        let item1 = item1.clone();
11368        move |_, item_to_resolve, _| {
11369            let item1 = item1.clone();
11370            async move {
11371                if item1 == item_to_resolve {
11372                    Ok(lsp::CompletionItem {
11373                        label: "method id()".to_string(),
11374                        filter_text: Some("id".to_string()),
11375                        detail: Some("Now resolved!".to_string()),
11376                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11377                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11378                            range: lsp::Range::new(
11379                                lsp::Position::new(0, 22),
11380                                lsp::Position::new(0, 22),
11381                            ),
11382                            new_text: ".id".to_string(),
11383                        })),
11384                        ..lsp::CompletionItem::default()
11385                    })
11386                } else {
11387                    Ok(item_to_resolve)
11388                }
11389            }
11390        }
11391    })
11392    .next()
11393    .await
11394    .unwrap();
11395    cx.run_until_parked();
11396
11397    cx.update_editor(|editor, window, cx| {
11398        editor.context_menu_next(&Default::default(), window, cx);
11399    });
11400
11401    cx.update_editor(|editor, _, _| {
11402        let context_menu = editor.context_menu.borrow_mut();
11403        let context_menu = context_menu
11404            .as_ref()
11405            .expect("Should have the context menu deployed");
11406        match context_menu {
11407            CodeContextMenu::Completions(completions_menu) => {
11408                let completions = completions_menu.completions.borrow_mut();
11409                assert_eq!(
11410                    completions
11411                        .iter()
11412                        .map(|completion| &completion.label.text)
11413                        .collect::<Vec<_>>(),
11414                    vec!["method id() Now resolved!", "other"],
11415                    "Should update first completion label, but not second as the filter text did not match."
11416                );
11417            }
11418            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11419        }
11420    });
11421}
11422
11423#[gpui::test]
11424async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11425    init_test(cx, |_| {});
11426
11427    let mut cx = EditorLspTestContext::new_rust(
11428        lsp::ServerCapabilities {
11429            completion_provider: Some(lsp::CompletionOptions {
11430                trigger_characters: Some(vec![".".to_string()]),
11431                resolve_provider: Some(true),
11432                ..Default::default()
11433            }),
11434            ..Default::default()
11435        },
11436        cx,
11437    )
11438    .await;
11439
11440    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11441    cx.simulate_keystroke(".");
11442
11443    let unresolved_item_1 = lsp::CompletionItem {
11444        label: "id".to_string(),
11445        filter_text: Some("id".to_string()),
11446        detail: None,
11447        documentation: None,
11448        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11449            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11450            new_text: ".id".to_string(),
11451        })),
11452        ..lsp::CompletionItem::default()
11453    };
11454    let resolved_item_1 = lsp::CompletionItem {
11455        additional_text_edits: Some(vec![lsp::TextEdit {
11456            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11457            new_text: "!!".to_string(),
11458        }]),
11459        ..unresolved_item_1.clone()
11460    };
11461    let unresolved_item_2 = lsp::CompletionItem {
11462        label: "other".to_string(),
11463        filter_text: Some("other".to_string()),
11464        detail: None,
11465        documentation: None,
11466        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11467            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11468            new_text: ".other".to_string(),
11469        })),
11470        ..lsp::CompletionItem::default()
11471    };
11472    let resolved_item_2 = lsp::CompletionItem {
11473        additional_text_edits: Some(vec![lsp::TextEdit {
11474            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11475            new_text: "??".to_string(),
11476        }]),
11477        ..unresolved_item_2.clone()
11478    };
11479
11480    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11481    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11482    cx.lsp
11483        .server
11484        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11485            let unresolved_item_1 = unresolved_item_1.clone();
11486            let resolved_item_1 = resolved_item_1.clone();
11487            let unresolved_item_2 = unresolved_item_2.clone();
11488            let resolved_item_2 = resolved_item_2.clone();
11489            let resolve_requests_1 = resolve_requests_1.clone();
11490            let resolve_requests_2 = resolve_requests_2.clone();
11491            move |unresolved_request, _| {
11492                let unresolved_item_1 = unresolved_item_1.clone();
11493                let resolved_item_1 = resolved_item_1.clone();
11494                let unresolved_item_2 = unresolved_item_2.clone();
11495                let resolved_item_2 = resolved_item_2.clone();
11496                let resolve_requests_1 = resolve_requests_1.clone();
11497                let resolve_requests_2 = resolve_requests_2.clone();
11498                async move {
11499                    if unresolved_request == unresolved_item_1 {
11500                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11501                        Ok(resolved_item_1.clone())
11502                    } else if unresolved_request == unresolved_item_2 {
11503                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11504                        Ok(resolved_item_2.clone())
11505                    } else {
11506                        panic!("Unexpected completion item {unresolved_request:?}")
11507                    }
11508                }
11509            }
11510        })
11511        .detach();
11512
11513    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11514        let unresolved_item_1 = unresolved_item_1.clone();
11515        let unresolved_item_2 = unresolved_item_2.clone();
11516        async move {
11517            Ok(Some(lsp::CompletionResponse::Array(vec![
11518                unresolved_item_1,
11519                unresolved_item_2,
11520            ])))
11521        }
11522    })
11523    .next()
11524    .await;
11525
11526    cx.condition(|editor, _| editor.context_menu_visible())
11527        .await;
11528    cx.update_editor(|editor, _, _| {
11529        let context_menu = editor.context_menu.borrow_mut();
11530        let context_menu = context_menu
11531            .as_ref()
11532            .expect("Should have the context menu deployed");
11533        match context_menu {
11534            CodeContextMenu::Completions(completions_menu) => {
11535                let completions = completions_menu.completions.borrow_mut();
11536                assert_eq!(
11537                    completions
11538                        .iter()
11539                        .map(|completion| &completion.label.text)
11540                        .collect::<Vec<_>>(),
11541                    vec!["id", "other"]
11542                )
11543            }
11544            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11545        }
11546    });
11547    cx.run_until_parked();
11548
11549    cx.update_editor(|editor, window, cx| {
11550        editor.context_menu_next(&ContextMenuNext, window, cx);
11551    });
11552    cx.run_until_parked();
11553    cx.update_editor(|editor, window, cx| {
11554        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11555    });
11556    cx.run_until_parked();
11557    cx.update_editor(|editor, window, cx| {
11558        editor.context_menu_next(&ContextMenuNext, window, cx);
11559    });
11560    cx.run_until_parked();
11561    cx.update_editor(|editor, window, cx| {
11562        editor
11563            .compose_completion(&ComposeCompletion::default(), window, cx)
11564            .expect("No task returned")
11565    })
11566    .await
11567    .expect("Completion failed");
11568    cx.run_until_parked();
11569
11570    cx.update_editor(|editor, _, cx| {
11571        assert_eq!(
11572            resolve_requests_1.load(atomic::Ordering::Acquire),
11573            1,
11574            "Should always resolve once despite multiple selections"
11575        );
11576        assert_eq!(
11577            resolve_requests_2.load(atomic::Ordering::Acquire),
11578            1,
11579            "Should always resolve once after multiple selections and applying the completion"
11580        );
11581        assert_eq!(
11582            editor.text(cx),
11583            "fn main() { let a = ??.other; }",
11584            "Should use resolved data when applying the completion"
11585        );
11586    });
11587}
11588
11589#[gpui::test]
11590async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11591    init_test(cx, |_| {});
11592
11593    let item_0 = lsp::CompletionItem {
11594        label: "abs".into(),
11595        insert_text: Some("abs".into()),
11596        data: Some(json!({ "very": "special"})),
11597        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11598        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11599            lsp::InsertReplaceEdit {
11600                new_text: "abs".to_string(),
11601                insert: lsp::Range::default(),
11602                replace: lsp::Range::default(),
11603            },
11604        )),
11605        ..lsp::CompletionItem::default()
11606    };
11607    let items = iter::once(item_0.clone())
11608        .chain((11..51).map(|i| lsp::CompletionItem {
11609            label: format!("item_{}", i),
11610            insert_text: Some(format!("item_{}", i)),
11611            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11612            ..lsp::CompletionItem::default()
11613        }))
11614        .collect::<Vec<_>>();
11615
11616    let default_commit_characters = vec!["?".to_string()];
11617    let default_data = json!({ "default": "data"});
11618    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11619    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11620    let default_edit_range = lsp::Range {
11621        start: lsp::Position {
11622            line: 0,
11623            character: 5,
11624        },
11625        end: lsp::Position {
11626            line: 0,
11627            character: 5,
11628        },
11629    };
11630
11631    let item_0_out = lsp::CompletionItem {
11632        commit_characters: Some(default_commit_characters.clone()),
11633        insert_text_format: Some(default_insert_text_format),
11634        ..item_0
11635    };
11636    let items_out = iter::once(item_0_out)
11637        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11638            commit_characters: Some(default_commit_characters.clone()),
11639            data: Some(default_data.clone()),
11640            insert_text_mode: Some(default_insert_text_mode),
11641            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11642                range: default_edit_range,
11643                new_text: item.label.clone(),
11644            })),
11645            ..item.clone()
11646        }))
11647        .collect::<Vec<lsp::CompletionItem>>();
11648
11649    let mut cx = EditorLspTestContext::new_rust(
11650        lsp::ServerCapabilities {
11651            completion_provider: Some(lsp::CompletionOptions {
11652                trigger_characters: Some(vec![".".to_string()]),
11653                resolve_provider: Some(true),
11654                ..Default::default()
11655            }),
11656            ..Default::default()
11657        },
11658        cx,
11659    )
11660    .await;
11661
11662    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11663    cx.simulate_keystroke(".");
11664
11665    let completion_data = default_data.clone();
11666    let completion_characters = default_commit_characters.clone();
11667    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11668        let default_data = completion_data.clone();
11669        let default_commit_characters = completion_characters.clone();
11670        let items = items.clone();
11671        async move {
11672            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11673                items,
11674                item_defaults: Some(lsp::CompletionListItemDefaults {
11675                    data: Some(default_data.clone()),
11676                    commit_characters: Some(default_commit_characters.clone()),
11677                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11678                        default_edit_range,
11679                    )),
11680                    insert_text_format: Some(default_insert_text_format),
11681                    insert_text_mode: Some(default_insert_text_mode),
11682                }),
11683                ..lsp::CompletionList::default()
11684            })))
11685        }
11686    })
11687    .next()
11688    .await;
11689
11690    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11691    cx.lsp
11692        .server
11693        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11694            let closure_resolved_items = resolved_items.clone();
11695            move |item_to_resolve, _| {
11696                let closure_resolved_items = closure_resolved_items.clone();
11697                async move {
11698                    closure_resolved_items.lock().push(item_to_resolve.clone());
11699                    Ok(item_to_resolve)
11700                }
11701            }
11702        })
11703        .detach();
11704
11705    cx.condition(|editor, _| editor.context_menu_visible())
11706        .await;
11707    cx.run_until_parked();
11708    cx.update_editor(|editor, _, _| {
11709        let menu = editor.context_menu.borrow_mut();
11710        match menu.as_ref().expect("should have the completions menu") {
11711            CodeContextMenu::Completions(completions_menu) => {
11712                assert_eq!(
11713                    completions_menu
11714                        .entries
11715                        .borrow()
11716                        .iter()
11717                        .map(|mat| mat.string.clone())
11718                        .collect::<Vec<String>>(),
11719                    items_out
11720                        .iter()
11721                        .map(|completion| completion.label.clone())
11722                        .collect::<Vec<String>>()
11723                );
11724            }
11725            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11726        }
11727    });
11728    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11729    // with 4 from the end.
11730    assert_eq!(
11731        *resolved_items.lock(),
11732        [
11733            &items_out[0..16],
11734            &items_out[items_out.len() - 4..items_out.len()]
11735        ]
11736        .concat()
11737        .iter()
11738        .cloned()
11739        .collect::<Vec<lsp::CompletionItem>>()
11740    );
11741    resolved_items.lock().clear();
11742
11743    cx.update_editor(|editor, window, cx| {
11744        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11745    });
11746    cx.run_until_parked();
11747    // Completions that have already been resolved are skipped.
11748    assert_eq!(
11749        *resolved_items.lock(),
11750        items_out[items_out.len() - 16..items_out.len() - 4]
11751            .iter()
11752            .cloned()
11753            .collect::<Vec<lsp::CompletionItem>>()
11754    );
11755    resolved_items.lock().clear();
11756}
11757
11758#[gpui::test]
11759async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11760    init_test(cx, |_| {});
11761
11762    let mut cx = EditorLspTestContext::new(
11763        Language::new(
11764            LanguageConfig {
11765                matcher: LanguageMatcher {
11766                    path_suffixes: vec!["jsx".into()],
11767                    ..Default::default()
11768                },
11769                overrides: [(
11770                    "element".into(),
11771                    LanguageConfigOverride {
11772                        word_characters: Override::Set(['-'].into_iter().collect()),
11773                        ..Default::default()
11774                    },
11775                )]
11776                .into_iter()
11777                .collect(),
11778                ..Default::default()
11779            },
11780            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11781        )
11782        .with_override_query("(jsx_self_closing_element) @element")
11783        .unwrap(),
11784        lsp::ServerCapabilities {
11785            completion_provider: Some(lsp::CompletionOptions {
11786                trigger_characters: Some(vec![":".to_string()]),
11787                ..Default::default()
11788            }),
11789            ..Default::default()
11790        },
11791        cx,
11792    )
11793    .await;
11794
11795    cx.lsp
11796        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11797            Ok(Some(lsp::CompletionResponse::Array(vec![
11798                lsp::CompletionItem {
11799                    label: "bg-blue".into(),
11800                    ..Default::default()
11801                },
11802                lsp::CompletionItem {
11803                    label: "bg-red".into(),
11804                    ..Default::default()
11805                },
11806                lsp::CompletionItem {
11807                    label: "bg-yellow".into(),
11808                    ..Default::default()
11809                },
11810            ])))
11811        });
11812
11813    cx.set_state(r#"<p class="bgˇ" />"#);
11814
11815    // Trigger completion when typing a dash, because the dash is an extra
11816    // word character in the 'element' scope, which contains the cursor.
11817    cx.simulate_keystroke("-");
11818    cx.executor().run_until_parked();
11819    cx.update_editor(|editor, _, _| {
11820        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11821        {
11822            assert_eq!(
11823                completion_menu_entries(&menu),
11824                &["bg-red", "bg-blue", "bg-yellow"]
11825            );
11826        } else {
11827            panic!("expected completion menu to be open");
11828        }
11829    });
11830
11831    cx.simulate_keystroke("l");
11832    cx.executor().run_until_parked();
11833    cx.update_editor(|editor, _, _| {
11834        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11835        {
11836            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
11837        } else {
11838            panic!("expected completion menu to be open");
11839        }
11840    });
11841
11842    // When filtering completions, consider the character after the '-' to
11843    // be the start of a subword.
11844    cx.set_state(r#"<p class="yelˇ" />"#);
11845    cx.simulate_keystroke("l");
11846    cx.executor().run_until_parked();
11847    cx.update_editor(|editor, _, _| {
11848        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11849        {
11850            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
11851        } else {
11852            panic!("expected completion menu to be open");
11853        }
11854    });
11855}
11856
11857fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
11858    let entries = menu.entries.borrow();
11859    entries.iter().map(|mat| mat.string.clone()).collect()
11860}
11861
11862#[gpui::test]
11863async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
11864    init_test(cx, |settings| {
11865        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
11866            FormatterList(vec![Formatter::Prettier].into()),
11867        ))
11868    });
11869
11870    let fs = FakeFs::new(cx.executor());
11871    fs.insert_file(path!("/file.ts"), Default::default()).await;
11872
11873    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
11874    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11875
11876    language_registry.add(Arc::new(Language::new(
11877        LanguageConfig {
11878            name: "TypeScript".into(),
11879            matcher: LanguageMatcher {
11880                path_suffixes: vec!["ts".to_string()],
11881                ..Default::default()
11882            },
11883            ..Default::default()
11884        },
11885        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11886    )));
11887    update_test_language_settings(cx, |settings| {
11888        settings.defaults.prettier = Some(PrettierSettings {
11889            allowed: true,
11890            ..PrettierSettings::default()
11891        });
11892    });
11893
11894    let test_plugin = "test_plugin";
11895    let _ = language_registry.register_fake_lsp(
11896        "TypeScript",
11897        FakeLspAdapter {
11898            prettier_plugins: vec![test_plugin],
11899            ..Default::default()
11900        },
11901    );
11902
11903    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
11904    let buffer = project
11905        .update(cx, |project, cx| {
11906            project.open_local_buffer(path!("/file.ts"), cx)
11907        })
11908        .await
11909        .unwrap();
11910
11911    let buffer_text = "one\ntwo\nthree\n";
11912    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11913    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11914    editor.update_in(cx, |editor, window, cx| {
11915        editor.set_text(buffer_text, window, cx)
11916    });
11917
11918    editor
11919        .update_in(cx, |editor, window, cx| {
11920            editor.perform_format(
11921                project.clone(),
11922                FormatTrigger::Manual,
11923                FormatTarget::Buffers,
11924                window,
11925                cx,
11926            )
11927        })
11928        .unwrap()
11929        .await;
11930    assert_eq!(
11931        editor.update(cx, |editor, cx| editor.text(cx)),
11932        buffer_text.to_string() + prettier_format_suffix,
11933        "Test prettier formatting was not applied to the original buffer text",
11934    );
11935
11936    update_test_language_settings(cx, |settings| {
11937        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
11938    });
11939    let format = editor.update_in(cx, |editor, window, cx| {
11940        editor.perform_format(
11941            project.clone(),
11942            FormatTrigger::Manual,
11943            FormatTarget::Buffers,
11944            window,
11945            cx,
11946        )
11947    });
11948    format.await.unwrap();
11949    assert_eq!(
11950        editor.update(cx, |editor, cx| editor.text(cx)),
11951        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
11952        "Autoformatting (via test prettier) was not applied to the original buffer text",
11953    );
11954}
11955
11956#[gpui::test]
11957async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
11958    init_test(cx, |_| {});
11959    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11960    let base_text = indoc! {r#"
11961        struct Row;
11962        struct Row1;
11963        struct Row2;
11964
11965        struct Row4;
11966        struct Row5;
11967        struct Row6;
11968
11969        struct Row8;
11970        struct Row9;
11971        struct Row10;"#};
11972
11973    // When addition hunks are not adjacent to carets, no hunk revert is performed
11974    assert_hunk_revert(
11975        indoc! {r#"struct Row;
11976                   struct Row1;
11977                   struct Row1.1;
11978                   struct Row1.2;
11979                   struct Row2;ˇ
11980
11981                   struct Row4;
11982                   struct Row5;
11983                   struct Row6;
11984
11985                   struct Row8;
11986                   ˇstruct Row9;
11987                   struct Row9.1;
11988                   struct Row9.2;
11989                   struct Row9.3;
11990                   struct Row10;"#},
11991        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
11992        indoc! {r#"struct Row;
11993                   struct Row1;
11994                   struct Row1.1;
11995                   struct Row1.2;
11996                   struct Row2;ˇ
11997
11998                   struct Row4;
11999                   struct Row5;
12000                   struct Row6;
12001
12002                   struct Row8;
12003                   ˇstruct Row9;
12004                   struct Row9.1;
12005                   struct Row9.2;
12006                   struct Row9.3;
12007                   struct Row10;"#},
12008        base_text,
12009        &mut cx,
12010    );
12011    // Same for selections
12012    assert_hunk_revert(
12013        indoc! {r#"struct Row;
12014                   struct Row1;
12015                   struct Row2;
12016                   struct Row2.1;
12017                   struct Row2.2;
12018                   «ˇ
12019                   struct Row4;
12020                   struct» Row5;
12021                   «struct Row6;
12022                   ˇ»
12023                   struct Row9.1;
12024                   struct Row9.2;
12025                   struct Row9.3;
12026                   struct Row8;
12027                   struct Row9;
12028                   struct Row10;"#},
12029        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12030        indoc! {r#"struct Row;
12031                   struct Row1;
12032                   struct Row2;
12033                   struct Row2.1;
12034                   struct Row2.2;
12035                   «ˇ
12036                   struct Row4;
12037                   struct» Row5;
12038                   «struct Row6;
12039                   ˇ»
12040                   struct Row9.1;
12041                   struct Row9.2;
12042                   struct Row9.3;
12043                   struct Row8;
12044                   struct Row9;
12045                   struct Row10;"#},
12046        base_text,
12047        &mut cx,
12048    );
12049
12050    // When carets and selections intersect the addition hunks, those are reverted.
12051    // Adjacent carets got merged.
12052    assert_hunk_revert(
12053        indoc! {r#"struct Row;
12054                   ˇ// something on the top
12055                   struct Row1;
12056                   struct Row2;
12057                   struct Roˇw3.1;
12058                   struct Row2.2;
12059                   struct Row2.3;ˇ
12060
12061                   struct Row4;
12062                   struct ˇRow5.1;
12063                   struct Row5.2;
12064                   struct «Rowˇ»5.3;
12065                   struct Row5;
12066                   struct Row6;
12067                   ˇ
12068                   struct Row9.1;
12069                   struct «Rowˇ»9.2;
12070                   struct «ˇRow»9.3;
12071                   struct Row8;
12072                   struct Row9;
12073                   «ˇ// something on bottom»
12074                   struct Row10;"#},
12075        vec![
12076            DiffHunkStatus::Added,
12077            DiffHunkStatus::Added,
12078            DiffHunkStatus::Added,
12079            DiffHunkStatus::Added,
12080            DiffHunkStatus::Added,
12081        ],
12082        indoc! {r#"struct Row;
12083                   ˇstruct Row1;
12084                   struct Row2;
12085                   ˇ
12086                   struct Row4;
12087                   ˇstruct Row5;
12088                   struct Row6;
12089                   ˇ
12090                   ˇstruct Row8;
12091                   struct Row9;
12092                   ˇstruct Row10;"#},
12093        base_text,
12094        &mut cx,
12095    );
12096}
12097
12098#[gpui::test]
12099async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12100    init_test(cx, |_| {});
12101    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12102    let base_text = indoc! {r#"
12103        struct Row;
12104        struct Row1;
12105        struct Row2;
12106
12107        struct Row4;
12108        struct Row5;
12109        struct Row6;
12110
12111        struct Row8;
12112        struct Row9;
12113        struct Row10;"#};
12114
12115    // Modification hunks behave the same as the addition ones.
12116    assert_hunk_revert(
12117        indoc! {r#"struct Row;
12118                   struct Row1;
12119                   struct Row33;
12120                   ˇ
12121                   struct Row4;
12122                   struct Row5;
12123                   struct Row6;
12124                   ˇ
12125                   struct Row99;
12126                   struct Row9;
12127                   struct Row10;"#},
12128        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12129        indoc! {r#"struct Row;
12130                   struct Row1;
12131                   struct Row33;
12132                   ˇ
12133                   struct Row4;
12134                   struct Row5;
12135                   struct Row6;
12136                   ˇ
12137                   struct Row99;
12138                   struct Row9;
12139                   struct Row10;"#},
12140        base_text,
12141        &mut cx,
12142    );
12143    assert_hunk_revert(
12144        indoc! {r#"struct Row;
12145                   struct Row1;
12146                   struct Row33;
12147                   «ˇ
12148                   struct Row4;
12149                   struct» Row5;
12150                   «struct Row6;
12151                   ˇ»
12152                   struct Row99;
12153                   struct Row9;
12154                   struct Row10;"#},
12155        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12156        indoc! {r#"struct Row;
12157                   struct Row1;
12158                   struct Row33;
12159                   «ˇ
12160                   struct Row4;
12161                   struct» Row5;
12162                   «struct Row6;
12163                   ˇ»
12164                   struct Row99;
12165                   struct Row9;
12166                   struct Row10;"#},
12167        base_text,
12168        &mut cx,
12169    );
12170
12171    assert_hunk_revert(
12172        indoc! {r#"ˇstruct Row1.1;
12173                   struct Row1;
12174                   «ˇstr»uct Row22;
12175
12176                   struct ˇRow44;
12177                   struct Row5;
12178                   struct «Rˇ»ow66;ˇ
12179
12180                   «struˇ»ct Row88;
12181                   struct Row9;
12182                   struct Row1011;ˇ"#},
12183        vec![
12184            DiffHunkStatus::Modified,
12185            DiffHunkStatus::Modified,
12186            DiffHunkStatus::Modified,
12187            DiffHunkStatus::Modified,
12188            DiffHunkStatus::Modified,
12189            DiffHunkStatus::Modified,
12190        ],
12191        indoc! {r#"struct Row;
12192                   ˇstruct Row1;
12193                   struct Row2;
12194                   ˇ
12195                   struct Row4;
12196                   ˇstruct Row5;
12197                   struct Row6;
12198                   ˇ
12199                   struct Row8;
12200                   ˇstruct Row9;
12201                   struct Row10;ˇ"#},
12202        base_text,
12203        &mut cx,
12204    );
12205}
12206
12207#[gpui::test]
12208async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12209    init_test(cx, |_| {});
12210    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12211    let base_text = indoc! {r#"
12212        one
12213
12214        two
12215        three
12216        "#};
12217
12218    cx.set_diff_base(base_text);
12219    cx.set_state("\nˇ\n");
12220    cx.executor().run_until_parked();
12221    cx.update_editor(|editor, _window, cx| {
12222        editor.expand_selected_diff_hunks(cx);
12223    });
12224    cx.executor().run_until_parked();
12225    cx.update_editor(|editor, window, cx| {
12226        editor.backspace(&Default::default(), window, cx);
12227    });
12228    cx.run_until_parked();
12229    cx.assert_state_with_diff(
12230        indoc! {r#"
12231
12232        - two
12233        - threeˇ
12234        +
12235        "#}
12236        .to_string(),
12237    );
12238}
12239
12240#[gpui::test]
12241async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12242    init_test(cx, |_| {});
12243    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12244    let base_text = indoc! {r#"struct Row;
12245struct Row1;
12246struct Row2;
12247
12248struct Row4;
12249struct Row5;
12250struct Row6;
12251
12252struct Row8;
12253struct Row9;
12254struct Row10;"#};
12255
12256    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12257    assert_hunk_revert(
12258        indoc! {r#"struct Row;
12259                   struct Row2;
12260
12261                   ˇstruct Row4;
12262                   struct Row5;
12263                   struct Row6;
12264                   ˇ
12265                   struct Row8;
12266                   struct Row10;"#},
12267        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12268        indoc! {r#"struct Row;
12269                   struct Row2;
12270
12271                   ˇstruct Row4;
12272                   struct Row5;
12273                   struct Row6;
12274                   ˇ
12275                   struct Row8;
12276                   struct Row10;"#},
12277        base_text,
12278        &mut cx,
12279    );
12280    assert_hunk_revert(
12281        indoc! {r#"struct Row;
12282                   struct Row2;
12283
12284                   «ˇstruct Row4;
12285                   struct» Row5;
12286                   «struct Row6;
12287                   ˇ»
12288                   struct Row8;
12289                   struct Row10;"#},
12290        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12291        indoc! {r#"struct Row;
12292                   struct Row2;
12293
12294                   «ˇstruct Row4;
12295                   struct» Row5;
12296                   «struct Row6;
12297                   ˇ»
12298                   struct Row8;
12299                   struct Row10;"#},
12300        base_text,
12301        &mut cx,
12302    );
12303
12304    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12305    assert_hunk_revert(
12306        indoc! {r#"struct Row;
12307                   ˇstruct Row2;
12308
12309                   struct Row4;
12310                   struct Row5;
12311                   struct Row6;
12312
12313                   struct Row8;ˇ
12314                   struct Row10;"#},
12315        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12316        indoc! {r#"struct Row;
12317                   struct Row1;
12318                   ˇstruct Row2;
12319
12320                   struct Row4;
12321                   struct Row5;
12322                   struct Row6;
12323
12324                   struct Row8;ˇ
12325                   struct Row9;
12326                   struct Row10;"#},
12327        base_text,
12328        &mut cx,
12329    );
12330    assert_hunk_revert(
12331        indoc! {r#"struct Row;
12332                   struct Row2«ˇ;
12333                   struct Row4;
12334                   struct» Row5;
12335                   «struct Row6;
12336
12337                   struct Row8;ˇ»
12338                   struct Row10;"#},
12339        vec![
12340            DiffHunkStatus::Removed,
12341            DiffHunkStatus::Removed,
12342            DiffHunkStatus::Removed,
12343        ],
12344        indoc! {r#"struct Row;
12345                   struct Row1;
12346                   struct Row2«ˇ;
12347
12348                   struct Row4;
12349                   struct» Row5;
12350                   «struct Row6;
12351
12352                   struct Row8;ˇ»
12353                   struct Row9;
12354                   struct Row10;"#},
12355        base_text,
12356        &mut cx,
12357    );
12358}
12359
12360#[gpui::test]
12361async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12362    init_test(cx, |_| {});
12363
12364    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12365    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12366    let base_text_3 =
12367        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12368
12369    let text_1 = edit_first_char_of_every_line(base_text_1);
12370    let text_2 = edit_first_char_of_every_line(base_text_2);
12371    let text_3 = edit_first_char_of_every_line(base_text_3);
12372
12373    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12374    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12375    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12376
12377    let multibuffer = cx.new(|cx| {
12378        let mut multibuffer = MultiBuffer::new(ReadWrite);
12379        multibuffer.push_excerpts(
12380            buffer_1.clone(),
12381            [
12382                ExcerptRange {
12383                    context: Point::new(0, 0)..Point::new(3, 0),
12384                    primary: None,
12385                },
12386                ExcerptRange {
12387                    context: Point::new(5, 0)..Point::new(7, 0),
12388                    primary: None,
12389                },
12390                ExcerptRange {
12391                    context: Point::new(9, 0)..Point::new(10, 4),
12392                    primary: None,
12393                },
12394            ],
12395            cx,
12396        );
12397        multibuffer.push_excerpts(
12398            buffer_2.clone(),
12399            [
12400                ExcerptRange {
12401                    context: Point::new(0, 0)..Point::new(3, 0),
12402                    primary: None,
12403                },
12404                ExcerptRange {
12405                    context: Point::new(5, 0)..Point::new(7, 0),
12406                    primary: None,
12407                },
12408                ExcerptRange {
12409                    context: Point::new(9, 0)..Point::new(10, 4),
12410                    primary: None,
12411                },
12412            ],
12413            cx,
12414        );
12415        multibuffer.push_excerpts(
12416            buffer_3.clone(),
12417            [
12418                ExcerptRange {
12419                    context: Point::new(0, 0)..Point::new(3, 0),
12420                    primary: None,
12421                },
12422                ExcerptRange {
12423                    context: Point::new(5, 0)..Point::new(7, 0),
12424                    primary: None,
12425                },
12426                ExcerptRange {
12427                    context: Point::new(9, 0)..Point::new(10, 4),
12428                    primary: None,
12429                },
12430            ],
12431            cx,
12432        );
12433        multibuffer
12434    });
12435
12436    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12437    editor.update_in(cx, |editor, _window, cx| {
12438        for (buffer, diff_base) in [
12439            (buffer_1.clone(), base_text_1),
12440            (buffer_2.clone(), base_text_2),
12441            (buffer_3.clone(), base_text_3),
12442        ] {
12443            let change_set =
12444                cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
12445            editor
12446                .buffer
12447                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
12448        }
12449    });
12450    cx.executor().run_until_parked();
12451
12452    editor.update_in(cx, |editor, window, cx| {
12453        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}");
12454        editor.select_all(&SelectAll, window, cx);
12455        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12456    });
12457    cx.executor().run_until_parked();
12458
12459    // When all ranges are selected, all buffer hunks are reverted.
12460    editor.update(cx, |editor, cx| {
12461        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");
12462    });
12463    buffer_1.update(cx, |buffer, _| {
12464        assert_eq!(buffer.text(), base_text_1);
12465    });
12466    buffer_2.update(cx, |buffer, _| {
12467        assert_eq!(buffer.text(), base_text_2);
12468    });
12469    buffer_3.update(cx, |buffer, _| {
12470        assert_eq!(buffer.text(), base_text_3);
12471    });
12472
12473    editor.update_in(cx, |editor, window, cx| {
12474        editor.undo(&Default::default(), window, cx);
12475    });
12476
12477    editor.update_in(cx, |editor, window, cx| {
12478        editor.change_selections(None, window, cx, |s| {
12479            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12480        });
12481        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12482    });
12483
12484    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12485    // but not affect buffer_2 and its related excerpts.
12486    editor.update(cx, |editor, cx| {
12487        assert_eq!(
12488            editor.text(cx),
12489            "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}"
12490        );
12491    });
12492    buffer_1.update(cx, |buffer, _| {
12493        assert_eq!(buffer.text(), base_text_1);
12494    });
12495    buffer_2.update(cx, |buffer, _| {
12496        assert_eq!(
12497            buffer.text(),
12498            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12499        );
12500    });
12501    buffer_3.update(cx, |buffer, _| {
12502        assert_eq!(
12503            buffer.text(),
12504            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12505        );
12506    });
12507
12508    fn edit_first_char_of_every_line(text: &str) -> String {
12509        text.split('\n')
12510            .map(|line| format!("X{}", &line[1..]))
12511            .collect::<Vec<_>>()
12512            .join("\n")
12513    }
12514}
12515
12516#[gpui::test]
12517async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12518    init_test(cx, |_| {});
12519
12520    let cols = 4;
12521    let rows = 10;
12522    let sample_text_1 = sample_text(rows, cols, 'a');
12523    assert_eq!(
12524        sample_text_1,
12525        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12526    );
12527    let sample_text_2 = sample_text(rows, cols, 'l');
12528    assert_eq!(
12529        sample_text_2,
12530        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12531    );
12532    let sample_text_3 = sample_text(rows, cols, 'v');
12533    assert_eq!(
12534        sample_text_3,
12535        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12536    );
12537
12538    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12539    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12540    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12541
12542    let multi_buffer = cx.new(|cx| {
12543        let mut multibuffer = MultiBuffer::new(ReadWrite);
12544        multibuffer.push_excerpts(
12545            buffer_1.clone(),
12546            [
12547                ExcerptRange {
12548                    context: Point::new(0, 0)..Point::new(3, 0),
12549                    primary: None,
12550                },
12551                ExcerptRange {
12552                    context: Point::new(5, 0)..Point::new(7, 0),
12553                    primary: None,
12554                },
12555                ExcerptRange {
12556                    context: Point::new(9, 0)..Point::new(10, 4),
12557                    primary: None,
12558                },
12559            ],
12560            cx,
12561        );
12562        multibuffer.push_excerpts(
12563            buffer_2.clone(),
12564            [
12565                ExcerptRange {
12566                    context: Point::new(0, 0)..Point::new(3, 0),
12567                    primary: None,
12568                },
12569                ExcerptRange {
12570                    context: Point::new(5, 0)..Point::new(7, 0),
12571                    primary: None,
12572                },
12573                ExcerptRange {
12574                    context: Point::new(9, 0)..Point::new(10, 4),
12575                    primary: None,
12576                },
12577            ],
12578            cx,
12579        );
12580        multibuffer.push_excerpts(
12581            buffer_3.clone(),
12582            [
12583                ExcerptRange {
12584                    context: Point::new(0, 0)..Point::new(3, 0),
12585                    primary: None,
12586                },
12587                ExcerptRange {
12588                    context: Point::new(5, 0)..Point::new(7, 0),
12589                    primary: None,
12590                },
12591                ExcerptRange {
12592                    context: Point::new(9, 0)..Point::new(10, 4),
12593                    primary: None,
12594                },
12595            ],
12596            cx,
12597        );
12598        multibuffer
12599    });
12600
12601    let fs = FakeFs::new(cx.executor());
12602    fs.insert_tree(
12603        "/a",
12604        json!({
12605            "main.rs": sample_text_1,
12606            "other.rs": sample_text_2,
12607            "lib.rs": sample_text_3,
12608        }),
12609    )
12610    .await;
12611    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12612    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12613    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12614    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12615        Editor::new(
12616            EditorMode::Full,
12617            multi_buffer,
12618            Some(project.clone()),
12619            true,
12620            window,
12621            cx,
12622        )
12623    });
12624    let multibuffer_item_id = workspace
12625        .update(cx, |workspace, window, cx| {
12626            assert!(
12627                workspace.active_item(cx).is_none(),
12628                "active item should be None before the first item is added"
12629            );
12630            workspace.add_item_to_active_pane(
12631                Box::new(multi_buffer_editor.clone()),
12632                None,
12633                true,
12634                window,
12635                cx,
12636            );
12637            let active_item = workspace
12638                .active_item(cx)
12639                .expect("should have an active item after adding the multi buffer");
12640            assert!(
12641                !active_item.is_singleton(cx),
12642                "A multi buffer was expected to active after adding"
12643            );
12644            active_item.item_id()
12645        })
12646        .unwrap();
12647    cx.executor().run_until_parked();
12648
12649    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12650        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12651            s.select_ranges(Some(1..2))
12652        });
12653        editor.open_excerpts(&OpenExcerpts, window, cx);
12654    });
12655    cx.executor().run_until_parked();
12656    let first_item_id = workspace
12657        .update(cx, |workspace, window, cx| {
12658            let active_item = workspace
12659                .active_item(cx)
12660                .expect("should have an active item after navigating into the 1st buffer");
12661            let first_item_id = active_item.item_id();
12662            assert_ne!(
12663                first_item_id, multibuffer_item_id,
12664                "Should navigate into the 1st buffer and activate it"
12665            );
12666            assert!(
12667                active_item.is_singleton(cx),
12668                "New active item should be a singleton buffer"
12669            );
12670            assert_eq!(
12671                active_item
12672                    .act_as::<Editor>(cx)
12673                    .expect("should have navigated into an editor for the 1st buffer")
12674                    .read(cx)
12675                    .text(cx),
12676                sample_text_1
12677            );
12678
12679            workspace
12680                .go_back(workspace.active_pane().downgrade(), window, cx)
12681                .detach_and_log_err(cx);
12682
12683            first_item_id
12684        })
12685        .unwrap();
12686    cx.executor().run_until_parked();
12687    workspace
12688        .update(cx, |workspace, _, cx| {
12689            let active_item = workspace
12690                .active_item(cx)
12691                .expect("should have an active item after navigating back");
12692            assert_eq!(
12693                active_item.item_id(),
12694                multibuffer_item_id,
12695                "Should navigate back to the multi buffer"
12696            );
12697            assert!(!active_item.is_singleton(cx));
12698        })
12699        .unwrap();
12700
12701    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12702        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12703            s.select_ranges(Some(39..40))
12704        });
12705        editor.open_excerpts(&OpenExcerpts, window, cx);
12706    });
12707    cx.executor().run_until_parked();
12708    let second_item_id = workspace
12709        .update(cx, |workspace, window, cx| {
12710            let active_item = workspace
12711                .active_item(cx)
12712                .expect("should have an active item after navigating into the 2nd buffer");
12713            let second_item_id = active_item.item_id();
12714            assert_ne!(
12715                second_item_id, multibuffer_item_id,
12716                "Should navigate away from the multibuffer"
12717            );
12718            assert_ne!(
12719                second_item_id, first_item_id,
12720                "Should navigate into the 2nd buffer and activate it"
12721            );
12722            assert!(
12723                active_item.is_singleton(cx),
12724                "New active item should be a singleton buffer"
12725            );
12726            assert_eq!(
12727                active_item
12728                    .act_as::<Editor>(cx)
12729                    .expect("should have navigated into an editor")
12730                    .read(cx)
12731                    .text(cx),
12732                sample_text_2
12733            );
12734
12735            workspace
12736                .go_back(workspace.active_pane().downgrade(), window, cx)
12737                .detach_and_log_err(cx);
12738
12739            second_item_id
12740        })
12741        .unwrap();
12742    cx.executor().run_until_parked();
12743    workspace
12744        .update(cx, |workspace, _, cx| {
12745            let active_item = workspace
12746                .active_item(cx)
12747                .expect("should have an active item after navigating back from the 2nd buffer");
12748            assert_eq!(
12749                active_item.item_id(),
12750                multibuffer_item_id,
12751                "Should navigate back from the 2nd buffer to the multi buffer"
12752            );
12753            assert!(!active_item.is_singleton(cx));
12754        })
12755        .unwrap();
12756
12757    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12758        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12759            s.select_ranges(Some(70..70))
12760        });
12761        editor.open_excerpts(&OpenExcerpts, window, cx);
12762    });
12763    cx.executor().run_until_parked();
12764    workspace
12765        .update(cx, |workspace, window, cx| {
12766            let active_item = workspace
12767                .active_item(cx)
12768                .expect("should have an active item after navigating into the 3rd buffer");
12769            let third_item_id = active_item.item_id();
12770            assert_ne!(
12771                third_item_id, multibuffer_item_id,
12772                "Should navigate into the 3rd buffer and activate it"
12773            );
12774            assert_ne!(third_item_id, first_item_id);
12775            assert_ne!(third_item_id, second_item_id);
12776            assert!(
12777                active_item.is_singleton(cx),
12778                "New active item should be a singleton buffer"
12779            );
12780            assert_eq!(
12781                active_item
12782                    .act_as::<Editor>(cx)
12783                    .expect("should have navigated into an editor")
12784                    .read(cx)
12785                    .text(cx),
12786                sample_text_3
12787            );
12788
12789            workspace
12790                .go_back(workspace.active_pane().downgrade(), window, cx)
12791                .detach_and_log_err(cx);
12792        })
12793        .unwrap();
12794    cx.executor().run_until_parked();
12795    workspace
12796        .update(cx, |workspace, _, cx| {
12797            let active_item = workspace
12798                .active_item(cx)
12799                .expect("should have an active item after navigating back from the 3rd buffer");
12800            assert_eq!(
12801                active_item.item_id(),
12802                multibuffer_item_id,
12803                "Should navigate back from the 3rd buffer to the multi buffer"
12804            );
12805            assert!(!active_item.is_singleton(cx));
12806        })
12807        .unwrap();
12808}
12809
12810#[gpui::test]
12811async fn test_toggle_selected_diff_hunks(
12812    executor: BackgroundExecutor,
12813    cx: &mut gpui::TestAppContext,
12814) {
12815    init_test(cx, |_| {});
12816
12817    let mut cx = EditorTestContext::new(cx).await;
12818
12819    let diff_base = r#"
12820        use some::mod;
12821
12822        const A: u32 = 42;
12823
12824        fn main() {
12825            println!("hello");
12826
12827            println!("world");
12828        }
12829        "#
12830    .unindent();
12831
12832    cx.set_state(
12833        &r#"
12834        use some::modified;
12835
12836        ˇ
12837        fn main() {
12838            println!("hello there");
12839
12840            println!("around the");
12841            println!("world");
12842        }
12843        "#
12844        .unindent(),
12845    );
12846
12847    cx.set_diff_base(&diff_base);
12848    executor.run_until_parked();
12849
12850    cx.update_editor(|editor, window, cx| {
12851        editor.go_to_next_hunk(&GoToHunk, window, cx);
12852        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12853    });
12854    executor.run_until_parked();
12855    cx.assert_state_with_diff(
12856        r#"
12857          use some::modified;
12858
12859
12860          fn main() {
12861        -     println!("hello");
12862        + ˇ    println!("hello there");
12863
12864              println!("around the");
12865              println!("world");
12866          }
12867        "#
12868        .unindent(),
12869    );
12870
12871    cx.update_editor(|editor, window, cx| {
12872        for _ in 0..2 {
12873            editor.go_to_next_hunk(&GoToHunk, window, cx);
12874            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12875        }
12876    });
12877    executor.run_until_parked();
12878    cx.assert_state_with_diff(
12879        r#"
12880        - use some::mod;
12881        + ˇuse some::modified;
12882
12883
12884          fn main() {
12885        -     println!("hello");
12886        +     println!("hello there");
12887
12888        +     println!("around the");
12889              println!("world");
12890          }
12891        "#
12892        .unindent(),
12893    );
12894
12895    cx.update_editor(|editor, window, cx| {
12896        editor.go_to_next_hunk(&GoToHunk, window, cx);
12897        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
12898    });
12899    executor.run_until_parked();
12900    cx.assert_state_with_diff(
12901        r#"
12902        - use some::mod;
12903        + use some::modified;
12904
12905        - const A: u32 = 42;
12906          ˇ
12907          fn main() {
12908        -     println!("hello");
12909        +     println!("hello there");
12910
12911        +     println!("around the");
12912              println!("world");
12913          }
12914        "#
12915        .unindent(),
12916    );
12917
12918    cx.update_editor(|editor, window, cx| {
12919        editor.cancel(&Cancel, window, cx);
12920    });
12921
12922    cx.assert_state_with_diff(
12923        r#"
12924          use some::modified;
12925
12926          ˇ
12927          fn main() {
12928              println!("hello there");
12929
12930              println!("around the");
12931              println!("world");
12932          }
12933        "#
12934        .unindent(),
12935    );
12936}
12937
12938#[gpui::test]
12939async fn test_diff_base_change_with_expanded_diff_hunks(
12940    executor: BackgroundExecutor,
12941    cx: &mut gpui::TestAppContext,
12942) {
12943    init_test(cx, |_| {});
12944
12945    let mut cx = EditorTestContext::new(cx).await;
12946
12947    let diff_base = r#"
12948        use some::mod1;
12949        use some::mod2;
12950
12951        const A: u32 = 42;
12952        const B: u32 = 42;
12953        const C: u32 = 42;
12954
12955        fn main() {
12956            println!("hello");
12957
12958            println!("world");
12959        }
12960        "#
12961    .unindent();
12962
12963    cx.set_state(
12964        &r#"
12965        use some::mod2;
12966
12967        const A: u32 = 42;
12968        const C: u32 = 42;
12969
12970        fn main(ˇ) {
12971            //println!("hello");
12972
12973            println!("world");
12974            //
12975            //
12976        }
12977        "#
12978        .unindent(),
12979    );
12980
12981    cx.set_diff_base(&diff_base);
12982    executor.run_until_parked();
12983
12984    cx.update_editor(|editor, window, cx| {
12985        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
12986    });
12987    executor.run_until_parked();
12988    cx.assert_state_with_diff(
12989        r#"
12990        - use some::mod1;
12991          use some::mod2;
12992
12993          const A: u32 = 42;
12994        - const B: u32 = 42;
12995          const C: u32 = 42;
12996
12997          fn main(ˇ) {
12998        -     println!("hello");
12999        +     //println!("hello");
13000
13001              println!("world");
13002        +     //
13003        +     //
13004          }
13005        "#
13006        .unindent(),
13007    );
13008
13009    cx.set_diff_base("new diff base!");
13010    executor.run_until_parked();
13011    cx.assert_state_with_diff(
13012        r#"
13013          use some::mod2;
13014
13015          const A: u32 = 42;
13016          const C: u32 = 42;
13017
13018          fn main(ˇ) {
13019              //println!("hello");
13020
13021              println!("world");
13022              //
13023              //
13024          }
13025        "#
13026        .unindent(),
13027    );
13028
13029    cx.update_editor(|editor, window, cx| {
13030        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13031    });
13032    executor.run_until_parked();
13033    cx.assert_state_with_diff(
13034        r#"
13035        - new diff base!
13036        + use some::mod2;
13037        +
13038        + const A: u32 = 42;
13039        + const C: u32 = 42;
13040        +
13041        + fn main(ˇ) {
13042        +     //println!("hello");
13043        +
13044        +     println!("world");
13045        +     //
13046        +     //
13047        + }
13048        "#
13049        .unindent(),
13050    );
13051}
13052
13053#[gpui::test]
13054async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13055    init_test(cx, |_| {});
13056
13057    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13058    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13059    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13060    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13061    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13062    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13063
13064    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13065    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13066    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13067
13068    let multi_buffer = cx.new(|cx| {
13069        let mut multibuffer = MultiBuffer::new(ReadWrite);
13070        multibuffer.push_excerpts(
13071            buffer_1.clone(),
13072            [
13073                ExcerptRange {
13074                    context: Point::new(0, 0)..Point::new(3, 0),
13075                    primary: None,
13076                },
13077                ExcerptRange {
13078                    context: Point::new(5, 0)..Point::new(7, 0),
13079                    primary: None,
13080                },
13081                ExcerptRange {
13082                    context: Point::new(9, 0)..Point::new(10, 3),
13083                    primary: None,
13084                },
13085            ],
13086            cx,
13087        );
13088        multibuffer.push_excerpts(
13089            buffer_2.clone(),
13090            [
13091                ExcerptRange {
13092                    context: Point::new(0, 0)..Point::new(3, 0),
13093                    primary: None,
13094                },
13095                ExcerptRange {
13096                    context: Point::new(5, 0)..Point::new(7, 0),
13097                    primary: None,
13098                },
13099                ExcerptRange {
13100                    context: Point::new(9, 0)..Point::new(10, 3),
13101                    primary: None,
13102                },
13103            ],
13104            cx,
13105        );
13106        multibuffer.push_excerpts(
13107            buffer_3.clone(),
13108            [
13109                ExcerptRange {
13110                    context: Point::new(0, 0)..Point::new(3, 0),
13111                    primary: None,
13112                },
13113                ExcerptRange {
13114                    context: Point::new(5, 0)..Point::new(7, 0),
13115                    primary: None,
13116                },
13117                ExcerptRange {
13118                    context: Point::new(9, 0)..Point::new(10, 3),
13119                    primary: None,
13120                },
13121            ],
13122            cx,
13123        );
13124        multibuffer
13125    });
13126
13127    let editor = cx.add_window(|window, cx| {
13128        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13129    });
13130    editor
13131        .update(cx, |editor, _window, cx| {
13132            for (buffer, diff_base) in [
13133                (buffer_1.clone(), file_1_old),
13134                (buffer_2.clone(), file_2_old),
13135                (buffer_3.clone(), file_3_old),
13136            ] {
13137                let change_set =
13138                    cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
13139                editor
13140                    .buffer
13141                    .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
13142            }
13143        })
13144        .unwrap();
13145
13146    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13147    cx.run_until_parked();
13148
13149    cx.assert_editor_state(
13150        &"
13151            ˇaaa
13152            ccc
13153            ddd
13154
13155            ggg
13156            hhh
13157
13158
13159            lll
13160            mmm
13161            NNN
13162
13163            qqq
13164            rrr
13165
13166            uuu
13167            111
13168            222
13169            333
13170
13171            666
13172            777
13173
13174            000
13175            !!!"
13176        .unindent(),
13177    );
13178
13179    cx.update_editor(|editor, window, cx| {
13180        editor.select_all(&SelectAll, window, cx);
13181        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13182    });
13183    cx.executor().run_until_parked();
13184
13185    cx.assert_state_with_diff(
13186        "
13187            «aaa
13188          - bbb
13189            ccc
13190            ddd
13191
13192            ggg
13193            hhh
13194
13195
13196            lll
13197            mmm
13198          - nnn
13199          + NNN
13200
13201            qqq
13202            rrr
13203
13204            uuu
13205            111
13206            222
13207            333
13208
13209          + 666
13210            777
13211
13212            000
13213            !!!ˇ»"
13214            .unindent(),
13215    );
13216}
13217
13218#[gpui::test]
13219async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13220    init_test(cx, |_| {});
13221
13222    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13223    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13224
13225    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13226    let multi_buffer = cx.new(|cx| {
13227        let mut multibuffer = MultiBuffer::new(ReadWrite);
13228        multibuffer.push_excerpts(
13229            buffer.clone(),
13230            [
13231                ExcerptRange {
13232                    context: Point::new(0, 0)..Point::new(2, 0),
13233                    primary: None,
13234                },
13235                ExcerptRange {
13236                    context: Point::new(4, 0)..Point::new(7, 0),
13237                    primary: None,
13238                },
13239                ExcerptRange {
13240                    context: Point::new(9, 0)..Point::new(10, 0),
13241                    primary: None,
13242                },
13243            ],
13244            cx,
13245        );
13246        multibuffer
13247    });
13248
13249    let editor = cx.add_window(|window, cx| {
13250        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13251    });
13252    editor
13253        .update(cx, |editor, _window, cx| {
13254            let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base, &buffer, cx));
13255            editor
13256                .buffer
13257                .update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
13258        })
13259        .unwrap();
13260
13261    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13262    cx.run_until_parked();
13263
13264    cx.update_editor(|editor, window, cx| {
13265        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13266    });
13267    cx.executor().run_until_parked();
13268
13269    // When the start of a hunk coincides with the start of its excerpt,
13270    // the hunk is expanded. When the start of a a hunk is earlier than
13271    // the start of its excerpt, the hunk is not expanded.
13272    cx.assert_state_with_diff(
13273        "
13274            ˇaaa
13275          - bbb
13276          + BBB
13277
13278          - ddd
13279          - eee
13280          + DDD
13281          + EEE
13282            fff
13283
13284            iii
13285        "
13286        .unindent(),
13287    );
13288}
13289
13290#[gpui::test]
13291async fn test_edits_around_expanded_insertion_hunks(
13292    executor: BackgroundExecutor,
13293    cx: &mut gpui::TestAppContext,
13294) {
13295    init_test(cx, |_| {});
13296
13297    let mut cx = EditorTestContext::new(cx).await;
13298
13299    let diff_base = r#"
13300        use some::mod1;
13301        use some::mod2;
13302
13303        const A: u32 = 42;
13304
13305        fn main() {
13306            println!("hello");
13307
13308            println!("world");
13309        }
13310        "#
13311    .unindent();
13312    executor.run_until_parked();
13313    cx.set_state(
13314        &r#"
13315        use some::mod1;
13316        use some::mod2;
13317
13318        const A: u32 = 42;
13319        const B: u32 = 42;
13320        const C: u32 = 42;
13321        ˇ
13322
13323        fn main() {
13324            println!("hello");
13325
13326            println!("world");
13327        }
13328        "#
13329        .unindent(),
13330    );
13331
13332    cx.set_diff_base(&diff_base);
13333    executor.run_until_parked();
13334
13335    cx.update_editor(|editor, window, cx| {
13336        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13337    });
13338    executor.run_until_parked();
13339
13340    cx.assert_state_with_diff(
13341        r#"
13342        use some::mod1;
13343        use some::mod2;
13344
13345        const A: u32 = 42;
13346      + const B: u32 = 42;
13347      + const C: u32 = 42;
13348      + ˇ
13349
13350        fn main() {
13351            println!("hello");
13352
13353            println!("world");
13354        }
13355      "#
13356        .unindent(),
13357    );
13358
13359    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13360    executor.run_until_parked();
13361
13362    cx.assert_state_with_diff(
13363        r#"
13364        use some::mod1;
13365        use some::mod2;
13366
13367        const A: u32 = 42;
13368      + const B: u32 = 42;
13369      + const C: u32 = 42;
13370      + const D: u32 = 42;
13371      + ˇ
13372
13373        fn main() {
13374            println!("hello");
13375
13376            println!("world");
13377        }
13378      "#
13379        .unindent(),
13380    );
13381
13382    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13383    executor.run_until_parked();
13384
13385    cx.assert_state_with_diff(
13386        r#"
13387        use some::mod1;
13388        use some::mod2;
13389
13390        const A: u32 = 42;
13391      + const B: u32 = 42;
13392      + const C: u32 = 42;
13393      + const D: u32 = 42;
13394      + const E: u32 = 42;
13395      + ˇ
13396
13397        fn main() {
13398            println!("hello");
13399
13400            println!("world");
13401        }
13402      "#
13403        .unindent(),
13404    );
13405
13406    cx.update_editor(|editor, window, cx| {
13407        editor.delete_line(&DeleteLine, window, cx);
13408    });
13409    executor.run_until_parked();
13410
13411    cx.assert_state_with_diff(
13412        r#"
13413        use some::mod1;
13414        use some::mod2;
13415
13416        const A: u32 = 42;
13417      + const B: u32 = 42;
13418      + const C: u32 = 42;
13419      + const D: u32 = 42;
13420      + const E: u32 = 42;
13421        ˇ
13422        fn main() {
13423            println!("hello");
13424
13425            println!("world");
13426        }
13427      "#
13428        .unindent(),
13429    );
13430
13431    cx.update_editor(|editor, window, cx| {
13432        editor.move_up(&MoveUp, window, cx);
13433        editor.delete_line(&DeleteLine, window, cx);
13434        editor.move_up(&MoveUp, window, cx);
13435        editor.delete_line(&DeleteLine, window, cx);
13436        editor.move_up(&MoveUp, window, cx);
13437        editor.delete_line(&DeleteLine, window, cx);
13438    });
13439    executor.run_until_parked();
13440    cx.assert_state_with_diff(
13441        r#"
13442        use some::mod1;
13443        use some::mod2;
13444
13445        const A: u32 = 42;
13446      + const B: u32 = 42;
13447        ˇ
13448        fn main() {
13449            println!("hello");
13450
13451            println!("world");
13452        }
13453      "#
13454        .unindent(),
13455    );
13456
13457    cx.update_editor(|editor, window, cx| {
13458        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13459        editor.delete_line(&DeleteLine, window, cx);
13460    });
13461    executor.run_until_parked();
13462    cx.assert_state_with_diff(
13463        r#"
13464        ˇ
13465        fn main() {
13466            println!("hello");
13467
13468            println!("world");
13469        }
13470      "#
13471        .unindent(),
13472    );
13473}
13474
13475#[gpui::test]
13476async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13477    init_test(cx, |_| {});
13478
13479    let mut cx = EditorTestContext::new(cx).await;
13480    cx.set_diff_base(indoc! { "
13481        one
13482        two
13483        three
13484        four
13485        five
13486        "
13487    });
13488    cx.set_state(indoc! { "
13489        one
13490        ˇthree
13491        five
13492    "});
13493    cx.run_until_parked();
13494    cx.update_editor(|editor, window, cx| {
13495        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13496    });
13497    cx.assert_state_with_diff(
13498        indoc! { "
13499        one
13500      - two
13501        ˇthree
13502      - four
13503        five
13504    "}
13505        .to_string(),
13506    );
13507    cx.update_editor(|editor, window, cx| {
13508        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13509    });
13510
13511    cx.assert_state_with_diff(
13512        indoc! { "
13513        one
13514        ˇthree
13515        five
13516    "}
13517        .to_string(),
13518    );
13519
13520    cx.set_state(indoc! { "
13521        one
13522        ˇTWO
13523        three
13524        four
13525        five
13526    "});
13527    cx.run_until_parked();
13528    cx.update_editor(|editor, window, cx| {
13529        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13530    });
13531
13532    cx.assert_state_with_diff(
13533        indoc! { "
13534            one
13535          - two
13536          + ˇTWO
13537            three
13538            four
13539            five
13540        "}
13541        .to_string(),
13542    );
13543    cx.update_editor(|editor, window, cx| {
13544        editor.move_up(&Default::default(), window, cx);
13545        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13546    });
13547    cx.assert_state_with_diff(
13548        indoc! { "
13549            one
13550            ˇTWO
13551            three
13552            four
13553            five
13554        "}
13555        .to_string(),
13556    );
13557}
13558
13559#[gpui::test]
13560async fn test_edits_around_expanded_deletion_hunks(
13561    executor: BackgroundExecutor,
13562    cx: &mut gpui::TestAppContext,
13563) {
13564    init_test(cx, |_| {});
13565
13566    let mut cx = EditorTestContext::new(cx).await;
13567
13568    let diff_base = r#"
13569        use some::mod1;
13570        use some::mod2;
13571
13572        const A: u32 = 42;
13573        const B: u32 = 42;
13574        const C: u32 = 42;
13575
13576
13577        fn main() {
13578            println!("hello");
13579
13580            println!("world");
13581        }
13582    "#
13583    .unindent();
13584    executor.run_until_parked();
13585    cx.set_state(
13586        &r#"
13587        use some::mod1;
13588        use some::mod2;
13589
13590        ˇconst B: u32 = 42;
13591        const C: u32 = 42;
13592
13593
13594        fn main() {
13595            println!("hello");
13596
13597            println!("world");
13598        }
13599        "#
13600        .unindent(),
13601    );
13602
13603    cx.set_diff_base(&diff_base);
13604    executor.run_until_parked();
13605
13606    cx.update_editor(|editor, window, cx| {
13607        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13608    });
13609    executor.run_until_parked();
13610
13611    cx.assert_state_with_diff(
13612        r#"
13613        use some::mod1;
13614        use some::mod2;
13615
13616      - const A: u32 = 42;
13617        ˇconst B: u32 = 42;
13618        const C: u32 = 42;
13619
13620
13621        fn main() {
13622            println!("hello");
13623
13624            println!("world");
13625        }
13626      "#
13627        .unindent(),
13628    );
13629
13630    cx.update_editor(|editor, window, cx| {
13631        editor.delete_line(&DeleteLine, window, cx);
13632    });
13633    executor.run_until_parked();
13634    cx.assert_state_with_diff(
13635        r#"
13636        use some::mod1;
13637        use some::mod2;
13638
13639      - const A: u32 = 42;
13640      - const B: u32 = 42;
13641        ˇconst C: u32 = 42;
13642
13643
13644        fn main() {
13645            println!("hello");
13646
13647            println!("world");
13648        }
13649      "#
13650        .unindent(),
13651    );
13652
13653    cx.update_editor(|editor, window, cx| {
13654        editor.delete_line(&DeleteLine, window, cx);
13655    });
13656    executor.run_until_parked();
13657    cx.assert_state_with_diff(
13658        r#"
13659        use some::mod1;
13660        use some::mod2;
13661
13662      - const A: u32 = 42;
13663      - const B: u32 = 42;
13664      - const C: u32 = 42;
13665        ˇ
13666
13667        fn main() {
13668            println!("hello");
13669
13670            println!("world");
13671        }
13672      "#
13673        .unindent(),
13674    );
13675
13676    cx.update_editor(|editor, window, cx| {
13677        editor.handle_input("replacement", window, cx);
13678    });
13679    executor.run_until_parked();
13680    cx.assert_state_with_diff(
13681        r#"
13682        use some::mod1;
13683        use some::mod2;
13684
13685      - const A: u32 = 42;
13686      - const B: u32 = 42;
13687      - const C: u32 = 42;
13688      -
13689      + replacementˇ
13690
13691        fn main() {
13692            println!("hello");
13693
13694            println!("world");
13695        }
13696      "#
13697        .unindent(),
13698    );
13699}
13700
13701#[gpui::test]
13702async fn test_backspace_after_deletion_hunk(
13703    executor: BackgroundExecutor,
13704    cx: &mut gpui::TestAppContext,
13705) {
13706    init_test(cx, |_| {});
13707
13708    let mut cx = EditorTestContext::new(cx).await;
13709
13710    let base_text = r#"
13711        one
13712        two
13713        three
13714        four
13715        five
13716    "#
13717    .unindent();
13718    executor.run_until_parked();
13719    cx.set_state(
13720        &r#"
13721        one
13722        two
13723        fˇour
13724        five
13725        "#
13726        .unindent(),
13727    );
13728
13729    cx.set_diff_base(&base_text);
13730    executor.run_until_parked();
13731
13732    cx.update_editor(|editor, window, cx| {
13733        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13734    });
13735    executor.run_until_parked();
13736
13737    cx.assert_state_with_diff(
13738        r#"
13739          one
13740          two
13741        - three
13742          fˇour
13743          five
13744        "#
13745        .unindent(),
13746    );
13747
13748    cx.update_editor(|editor, window, cx| {
13749        editor.backspace(&Backspace, window, cx);
13750        editor.backspace(&Backspace, window, cx);
13751    });
13752    executor.run_until_parked();
13753    cx.assert_state_with_diff(
13754        r#"
13755          one
13756          two
13757        - threeˇ
13758        - four
13759        + our
13760          five
13761        "#
13762        .unindent(),
13763    );
13764}
13765
13766#[gpui::test]
13767async fn test_edit_after_expanded_modification_hunk(
13768    executor: BackgroundExecutor,
13769    cx: &mut gpui::TestAppContext,
13770) {
13771    init_test(cx, |_| {});
13772
13773    let mut cx = EditorTestContext::new(cx).await;
13774
13775    let diff_base = r#"
13776        use some::mod1;
13777        use some::mod2;
13778
13779        const A: u32 = 42;
13780        const B: u32 = 42;
13781        const C: u32 = 42;
13782        const D: u32 = 42;
13783
13784
13785        fn main() {
13786            println!("hello");
13787
13788            println!("world");
13789        }"#
13790    .unindent();
13791
13792    cx.set_state(
13793        &r#"
13794        use some::mod1;
13795        use some::mod2;
13796
13797        const A: u32 = 42;
13798        const B: u32 = 42;
13799        const C: u32 = 43ˇ
13800        const D: u32 = 42;
13801
13802
13803        fn main() {
13804            println!("hello");
13805
13806            println!("world");
13807        }"#
13808        .unindent(),
13809    );
13810
13811    cx.set_diff_base(&diff_base);
13812    executor.run_until_parked();
13813    cx.update_editor(|editor, window, cx| {
13814        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13815    });
13816    executor.run_until_parked();
13817
13818    cx.assert_state_with_diff(
13819        r#"
13820        use some::mod1;
13821        use some::mod2;
13822
13823        const A: u32 = 42;
13824        const B: u32 = 42;
13825      - const C: u32 = 42;
13826      + const C: u32 = 43ˇ
13827        const D: u32 = 42;
13828
13829
13830        fn main() {
13831            println!("hello");
13832
13833            println!("world");
13834        }"#
13835        .unindent(),
13836    );
13837
13838    cx.update_editor(|editor, window, cx| {
13839        editor.handle_input("\nnew_line\n", window, cx);
13840    });
13841    executor.run_until_parked();
13842
13843    cx.assert_state_with_diff(
13844        r#"
13845        use some::mod1;
13846        use some::mod2;
13847
13848        const A: u32 = 42;
13849        const B: u32 = 42;
13850      - const C: u32 = 42;
13851      + const C: u32 = 43
13852      + new_line
13853      + ˇ
13854        const D: u32 = 42;
13855
13856
13857        fn main() {
13858            println!("hello");
13859
13860            println!("world");
13861        }"#
13862        .unindent(),
13863    );
13864}
13865
13866async fn setup_indent_guides_editor(
13867    text: &str,
13868    cx: &mut gpui::TestAppContext,
13869) -> (BufferId, EditorTestContext) {
13870    init_test(cx, |_| {});
13871
13872    let mut cx = EditorTestContext::new(cx).await;
13873
13874    let buffer_id = cx.update_editor(|editor, window, cx| {
13875        editor.set_text(text, window, cx);
13876        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
13877
13878        buffer_ids[0]
13879    });
13880
13881    (buffer_id, cx)
13882}
13883
13884fn assert_indent_guides(
13885    range: Range<u32>,
13886    expected: Vec<IndentGuide>,
13887    active_indices: Option<Vec<usize>>,
13888    cx: &mut EditorTestContext,
13889) {
13890    let indent_guides = cx.update_editor(|editor, window, cx| {
13891        let snapshot = editor.snapshot(window, cx).display_snapshot;
13892        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
13893            editor,
13894            MultiBufferRow(range.start)..MultiBufferRow(range.end),
13895            true,
13896            &snapshot,
13897            cx,
13898        );
13899
13900        indent_guides.sort_by(|a, b| {
13901            a.depth.cmp(&b.depth).then(
13902                a.start_row
13903                    .cmp(&b.start_row)
13904                    .then(a.end_row.cmp(&b.end_row)),
13905            )
13906        });
13907        indent_guides
13908    });
13909
13910    if let Some(expected) = active_indices {
13911        let active_indices = cx.update_editor(|editor, window, cx| {
13912            let snapshot = editor.snapshot(window, cx).display_snapshot;
13913            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
13914        });
13915
13916        assert_eq!(
13917            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
13918            expected,
13919            "Active indent guide indices do not match"
13920        );
13921    }
13922
13923    assert_eq!(indent_guides, expected, "Indent guides do not match");
13924}
13925
13926fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
13927    IndentGuide {
13928        buffer_id,
13929        start_row: MultiBufferRow(start_row),
13930        end_row: MultiBufferRow(end_row),
13931        depth,
13932        tab_size: 4,
13933        settings: IndentGuideSettings {
13934            enabled: true,
13935            line_width: 1,
13936            active_line_width: 1,
13937            ..Default::default()
13938        },
13939    }
13940}
13941
13942#[gpui::test]
13943async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
13944    let (buffer_id, mut cx) = setup_indent_guides_editor(
13945        &"
13946    fn main() {
13947        let a = 1;
13948    }"
13949        .unindent(),
13950        cx,
13951    )
13952    .await;
13953
13954    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
13955}
13956
13957#[gpui::test]
13958async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
13959    let (buffer_id, mut cx) = setup_indent_guides_editor(
13960        &"
13961    fn main() {
13962        let a = 1;
13963        let b = 2;
13964    }"
13965        .unindent(),
13966        cx,
13967    )
13968    .await;
13969
13970    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
13971}
13972
13973#[gpui::test]
13974async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
13975    let (buffer_id, mut cx) = setup_indent_guides_editor(
13976        &"
13977    fn main() {
13978        let a = 1;
13979        if a == 3 {
13980            let b = 2;
13981        } else {
13982            let c = 3;
13983        }
13984    }"
13985        .unindent(),
13986        cx,
13987    )
13988    .await;
13989
13990    assert_indent_guides(
13991        0..8,
13992        vec![
13993            indent_guide(buffer_id, 1, 6, 0),
13994            indent_guide(buffer_id, 3, 3, 1),
13995            indent_guide(buffer_id, 5, 5, 1),
13996        ],
13997        None,
13998        &mut cx,
13999    );
14000}
14001
14002#[gpui::test]
14003async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
14004    let (buffer_id, mut cx) = setup_indent_guides_editor(
14005        &"
14006    fn main() {
14007        let a = 1;
14008            let b = 2;
14009        let c = 3;
14010    }"
14011        .unindent(),
14012        cx,
14013    )
14014    .await;
14015
14016    assert_indent_guides(
14017        0..5,
14018        vec![
14019            indent_guide(buffer_id, 1, 3, 0),
14020            indent_guide(buffer_id, 2, 2, 1),
14021        ],
14022        None,
14023        &mut cx,
14024    );
14025}
14026
14027#[gpui::test]
14028async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14029    let (buffer_id, mut cx) = setup_indent_guides_editor(
14030        &"
14031        fn main() {
14032            let a = 1;
14033
14034            let c = 3;
14035        }"
14036        .unindent(),
14037        cx,
14038    )
14039    .await;
14040
14041    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14042}
14043
14044#[gpui::test]
14045async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14046    let (buffer_id, mut cx) = setup_indent_guides_editor(
14047        &"
14048        fn main() {
14049            let a = 1;
14050
14051            let c = 3;
14052
14053            if a == 3 {
14054                let b = 2;
14055            } else {
14056                let c = 3;
14057            }
14058        }"
14059        .unindent(),
14060        cx,
14061    )
14062    .await;
14063
14064    assert_indent_guides(
14065        0..11,
14066        vec![
14067            indent_guide(buffer_id, 1, 9, 0),
14068            indent_guide(buffer_id, 6, 6, 1),
14069            indent_guide(buffer_id, 8, 8, 1),
14070        ],
14071        None,
14072        &mut cx,
14073    );
14074}
14075
14076#[gpui::test]
14077async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14078    let (buffer_id, mut cx) = setup_indent_guides_editor(
14079        &"
14080        fn main() {
14081            let a = 1;
14082
14083            let c = 3;
14084
14085            if a == 3 {
14086                let b = 2;
14087            } else {
14088                let c = 3;
14089            }
14090        }"
14091        .unindent(),
14092        cx,
14093    )
14094    .await;
14095
14096    assert_indent_guides(
14097        1..11,
14098        vec![
14099            indent_guide(buffer_id, 1, 9, 0),
14100            indent_guide(buffer_id, 6, 6, 1),
14101            indent_guide(buffer_id, 8, 8, 1),
14102        ],
14103        None,
14104        &mut cx,
14105    );
14106}
14107
14108#[gpui::test]
14109async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14110    let (buffer_id, mut cx) = setup_indent_guides_editor(
14111        &"
14112        fn main() {
14113            let a = 1;
14114
14115            let c = 3;
14116
14117            if a == 3 {
14118                let b = 2;
14119            } else {
14120                let c = 3;
14121            }
14122        }"
14123        .unindent(),
14124        cx,
14125    )
14126    .await;
14127
14128    assert_indent_guides(
14129        1..10,
14130        vec![
14131            indent_guide(buffer_id, 1, 9, 0),
14132            indent_guide(buffer_id, 6, 6, 1),
14133            indent_guide(buffer_id, 8, 8, 1),
14134        ],
14135        None,
14136        &mut cx,
14137    );
14138}
14139
14140#[gpui::test]
14141async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14142    let (buffer_id, mut cx) = setup_indent_guides_editor(
14143        &"
14144        block1
14145            block2
14146                block3
14147                    block4
14148            block2
14149        block1
14150        block1"
14151            .unindent(),
14152        cx,
14153    )
14154    .await;
14155
14156    assert_indent_guides(
14157        1..10,
14158        vec![
14159            indent_guide(buffer_id, 1, 4, 0),
14160            indent_guide(buffer_id, 2, 3, 1),
14161            indent_guide(buffer_id, 3, 3, 2),
14162        ],
14163        None,
14164        &mut cx,
14165    );
14166}
14167
14168#[gpui::test]
14169async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14170    let (buffer_id, mut cx) = setup_indent_guides_editor(
14171        &"
14172        block1
14173            block2
14174                block3
14175
14176        block1
14177        block1"
14178            .unindent(),
14179        cx,
14180    )
14181    .await;
14182
14183    assert_indent_guides(
14184        0..6,
14185        vec![
14186            indent_guide(buffer_id, 1, 2, 0),
14187            indent_guide(buffer_id, 2, 2, 1),
14188        ],
14189        None,
14190        &mut cx,
14191    );
14192}
14193
14194#[gpui::test]
14195async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14196    let (buffer_id, mut cx) = setup_indent_guides_editor(
14197        &"
14198        block1
14199
14200
14201
14202            block2
14203        "
14204        .unindent(),
14205        cx,
14206    )
14207    .await;
14208
14209    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14210}
14211
14212#[gpui::test]
14213async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14214    let (buffer_id, mut cx) = setup_indent_guides_editor(
14215        &"
14216        def a:
14217        \tb = 3
14218        \tif True:
14219        \t\tc = 4
14220        \t\td = 5
14221        \tprint(b)
14222        "
14223        .unindent(),
14224        cx,
14225    )
14226    .await;
14227
14228    assert_indent_guides(
14229        0..6,
14230        vec![
14231            indent_guide(buffer_id, 1, 6, 0),
14232            indent_guide(buffer_id, 3, 4, 1),
14233        ],
14234        None,
14235        &mut cx,
14236    );
14237}
14238
14239#[gpui::test]
14240async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14241    let (buffer_id, mut cx) = setup_indent_guides_editor(
14242        &"
14243    fn main() {
14244        let a = 1;
14245    }"
14246        .unindent(),
14247        cx,
14248    )
14249    .await;
14250
14251    cx.update_editor(|editor, window, cx| {
14252        editor.change_selections(None, window, cx, |s| {
14253            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14254        });
14255    });
14256
14257    assert_indent_guides(
14258        0..3,
14259        vec![indent_guide(buffer_id, 1, 1, 0)],
14260        Some(vec![0]),
14261        &mut cx,
14262    );
14263}
14264
14265#[gpui::test]
14266async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14267    let (buffer_id, mut cx) = setup_indent_guides_editor(
14268        &"
14269    fn main() {
14270        if 1 == 2 {
14271            let a = 1;
14272        }
14273    }"
14274        .unindent(),
14275        cx,
14276    )
14277    .await;
14278
14279    cx.update_editor(|editor, window, cx| {
14280        editor.change_selections(None, window, cx, |s| {
14281            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14282        });
14283    });
14284
14285    assert_indent_guides(
14286        0..4,
14287        vec![
14288            indent_guide(buffer_id, 1, 3, 0),
14289            indent_guide(buffer_id, 2, 2, 1),
14290        ],
14291        Some(vec![1]),
14292        &mut cx,
14293    );
14294
14295    cx.update_editor(|editor, window, cx| {
14296        editor.change_selections(None, window, cx, |s| {
14297            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14298        });
14299    });
14300
14301    assert_indent_guides(
14302        0..4,
14303        vec![
14304            indent_guide(buffer_id, 1, 3, 0),
14305            indent_guide(buffer_id, 2, 2, 1),
14306        ],
14307        Some(vec![1]),
14308        &mut cx,
14309    );
14310
14311    cx.update_editor(|editor, window, cx| {
14312        editor.change_selections(None, window, cx, |s| {
14313            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14314        });
14315    });
14316
14317    assert_indent_guides(
14318        0..4,
14319        vec![
14320            indent_guide(buffer_id, 1, 3, 0),
14321            indent_guide(buffer_id, 2, 2, 1),
14322        ],
14323        Some(vec![0]),
14324        &mut cx,
14325    );
14326}
14327
14328#[gpui::test]
14329async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14330    let (buffer_id, mut cx) = setup_indent_guides_editor(
14331        &"
14332    fn main() {
14333        let a = 1;
14334
14335        let b = 2;
14336    }"
14337        .unindent(),
14338        cx,
14339    )
14340    .await;
14341
14342    cx.update_editor(|editor, window, cx| {
14343        editor.change_selections(None, window, cx, |s| {
14344            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14345        });
14346    });
14347
14348    assert_indent_guides(
14349        0..5,
14350        vec![indent_guide(buffer_id, 1, 3, 0)],
14351        Some(vec![0]),
14352        &mut cx,
14353    );
14354}
14355
14356#[gpui::test]
14357async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14358    let (buffer_id, mut cx) = setup_indent_guides_editor(
14359        &"
14360    def m:
14361        a = 1
14362        pass"
14363            .unindent(),
14364        cx,
14365    )
14366    .await;
14367
14368    cx.update_editor(|editor, window, cx| {
14369        editor.change_selections(None, window, cx, |s| {
14370            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14371        });
14372    });
14373
14374    assert_indent_guides(
14375        0..3,
14376        vec![indent_guide(buffer_id, 1, 2, 0)],
14377        Some(vec![0]),
14378        &mut cx,
14379    );
14380}
14381
14382#[gpui::test]
14383async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14384    init_test(cx, |_| {});
14385    let mut cx = EditorTestContext::new(cx).await;
14386    let text = indoc! {
14387        "
14388        impl A {
14389            fn b() {
14390                0;
14391                3;
14392                5;
14393                6;
14394                7;
14395            }
14396        }
14397        "
14398    };
14399    let base_text = indoc! {
14400        "
14401        impl A {
14402            fn b() {
14403                0;
14404                1;
14405                2;
14406                3;
14407                4;
14408            }
14409            fn c() {
14410                5;
14411                6;
14412                7;
14413            }
14414        }
14415        "
14416    };
14417
14418    cx.update_editor(|editor, window, cx| {
14419        editor.set_text(text, window, cx);
14420
14421        editor.buffer().update(cx, |multibuffer, cx| {
14422            let buffer = multibuffer.as_singleton().unwrap();
14423            let change_set =
14424                cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
14425
14426            multibuffer.set_all_diff_hunks_expanded(cx);
14427            multibuffer.add_change_set(change_set, cx);
14428
14429            buffer.read(cx).remote_id()
14430        })
14431    });
14432    cx.run_until_parked();
14433
14434    cx.assert_state_with_diff(
14435        indoc! { "
14436          impl A {
14437              fn b() {
14438                  0;
14439        -         1;
14440        -         2;
14441                  3;
14442        -         4;
14443        -     }
14444        -     fn c() {
14445                  5;
14446                  6;
14447                  7;
14448              }
14449          }
14450          ˇ"
14451        }
14452        .to_string(),
14453    );
14454
14455    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14456        editor
14457            .snapshot(window, cx)
14458            .buffer_snapshot
14459            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14460            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14461            .collect::<Vec<_>>()
14462    });
14463    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14464    assert_eq!(
14465        actual_guides,
14466        vec![
14467            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14468            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14469            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14470        ]
14471    );
14472}
14473
14474#[gpui::test]
14475fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14476    init_test(cx, |_| {});
14477
14478    let editor = cx.add_window(|window, cx| {
14479        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14480        build_editor(buffer, window, cx)
14481    });
14482
14483    let render_args = Arc::new(Mutex::new(None));
14484    let snapshot = editor
14485        .update(cx, |editor, window, cx| {
14486            let snapshot = editor.buffer().read(cx).snapshot(cx);
14487            let range =
14488                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14489
14490            struct RenderArgs {
14491                row: MultiBufferRow,
14492                folded: bool,
14493                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14494            }
14495
14496            let crease = Crease::inline(
14497                range,
14498                FoldPlaceholder::test(),
14499                {
14500                    let toggle_callback = render_args.clone();
14501                    move |row, folded, callback, _window, _cx| {
14502                        *toggle_callback.lock() = Some(RenderArgs {
14503                            row,
14504                            folded,
14505                            callback,
14506                        });
14507                        div()
14508                    }
14509                },
14510                |_row, _folded, _window, _cx| div(),
14511            );
14512
14513            editor.insert_creases(Some(crease), cx);
14514            let snapshot = editor.snapshot(window, cx);
14515            let _div = snapshot.render_crease_toggle(
14516                MultiBufferRow(1),
14517                false,
14518                cx.entity().clone(),
14519                window,
14520                cx,
14521            );
14522            snapshot
14523        })
14524        .unwrap();
14525
14526    let render_args = render_args.lock().take().unwrap();
14527    assert_eq!(render_args.row, MultiBufferRow(1));
14528    assert!(!render_args.folded);
14529    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14530
14531    cx.update_window(*editor, |_, window, cx| {
14532        (render_args.callback)(true, window, cx)
14533    })
14534    .unwrap();
14535    let snapshot = editor
14536        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14537        .unwrap();
14538    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14539
14540    cx.update_window(*editor, |_, window, cx| {
14541        (render_args.callback)(false, window, cx)
14542    })
14543    .unwrap();
14544    let snapshot = editor
14545        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14546        .unwrap();
14547    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14548}
14549
14550#[gpui::test]
14551async fn test_input_text(cx: &mut gpui::TestAppContext) {
14552    init_test(cx, |_| {});
14553    let mut cx = EditorTestContext::new(cx).await;
14554
14555    cx.set_state(
14556        &r#"ˇone
14557        two
14558
14559        three
14560        fourˇ
14561        five
14562
14563        siˇx"#
14564            .unindent(),
14565    );
14566
14567    cx.dispatch_action(HandleInput(String::new()));
14568    cx.assert_editor_state(
14569        &r#"ˇone
14570        two
14571
14572        three
14573        fourˇ
14574        five
14575
14576        siˇx"#
14577            .unindent(),
14578    );
14579
14580    cx.dispatch_action(HandleInput("AAAA".to_string()));
14581    cx.assert_editor_state(
14582        &r#"AAAAˇone
14583        two
14584
14585        three
14586        fourAAAAˇ
14587        five
14588
14589        siAAAAˇx"#
14590            .unindent(),
14591    );
14592}
14593
14594#[gpui::test]
14595async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14596    init_test(cx, |_| {});
14597
14598    let mut cx = EditorTestContext::new(cx).await;
14599    cx.set_state(
14600        r#"let foo = 1;
14601let foo = 2;
14602let foo = 3;
14603let fooˇ = 4;
14604let foo = 5;
14605let foo = 6;
14606let foo = 7;
14607let foo = 8;
14608let foo = 9;
14609let foo = 10;
14610let foo = 11;
14611let foo = 12;
14612let foo = 13;
14613let foo = 14;
14614let foo = 15;"#,
14615    );
14616
14617    cx.update_editor(|e, window, cx| {
14618        assert_eq!(
14619            e.next_scroll_position,
14620            NextScrollCursorCenterTopBottom::Center,
14621            "Default next scroll direction is center",
14622        );
14623
14624        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14625        assert_eq!(
14626            e.next_scroll_position,
14627            NextScrollCursorCenterTopBottom::Top,
14628            "After center, next scroll direction should be top",
14629        );
14630
14631        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14632        assert_eq!(
14633            e.next_scroll_position,
14634            NextScrollCursorCenterTopBottom::Bottom,
14635            "After top, next scroll direction should be bottom",
14636        );
14637
14638        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14639        assert_eq!(
14640            e.next_scroll_position,
14641            NextScrollCursorCenterTopBottom::Center,
14642            "After bottom, scrolling should start over",
14643        );
14644
14645        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14646        assert_eq!(
14647            e.next_scroll_position,
14648            NextScrollCursorCenterTopBottom::Top,
14649            "Scrolling continues if retriggered fast enough"
14650        );
14651    });
14652
14653    cx.executor()
14654        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14655    cx.executor().run_until_parked();
14656    cx.update_editor(|e, _, _| {
14657        assert_eq!(
14658            e.next_scroll_position,
14659            NextScrollCursorCenterTopBottom::Center,
14660            "If scrolling is not triggered fast enough, it should reset"
14661        );
14662    });
14663}
14664
14665#[gpui::test]
14666async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14667    init_test(cx, |_| {});
14668    let mut cx = EditorLspTestContext::new_rust(
14669        lsp::ServerCapabilities {
14670            definition_provider: Some(lsp::OneOf::Left(true)),
14671            references_provider: Some(lsp::OneOf::Left(true)),
14672            ..lsp::ServerCapabilities::default()
14673        },
14674        cx,
14675    )
14676    .await;
14677
14678    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14679        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14680            move |params, _| async move {
14681                if empty_go_to_definition {
14682                    Ok(None)
14683                } else {
14684                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14685                        uri: params.text_document_position_params.text_document.uri,
14686                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14687                    })))
14688                }
14689            },
14690        );
14691        let references =
14692            cx.lsp
14693                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14694                    Ok(Some(vec![lsp::Location {
14695                        uri: params.text_document_position.text_document.uri,
14696                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14697                    }]))
14698                });
14699        (go_to_definition, references)
14700    };
14701
14702    cx.set_state(
14703        &r#"fn one() {
14704            let mut a = ˇtwo();
14705        }
14706
14707        fn two() {}"#
14708            .unindent(),
14709    );
14710    set_up_lsp_handlers(false, &mut cx);
14711    let navigated = cx
14712        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14713        .await
14714        .expect("Failed to navigate to definition");
14715    assert_eq!(
14716        navigated,
14717        Navigated::Yes,
14718        "Should have navigated to definition from the GetDefinition response"
14719    );
14720    cx.assert_editor_state(
14721        &r#"fn one() {
14722            let mut a = two();
14723        }
14724
14725        fn «twoˇ»() {}"#
14726            .unindent(),
14727    );
14728
14729    let editors = cx.update_workspace(|workspace, _, cx| {
14730        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14731    });
14732    cx.update_editor(|_, _, test_editor_cx| {
14733        assert_eq!(
14734            editors.len(),
14735            1,
14736            "Initially, only one, test, editor should be open in the workspace"
14737        );
14738        assert_eq!(
14739            test_editor_cx.entity(),
14740            editors.last().expect("Asserted len is 1").clone()
14741        );
14742    });
14743
14744    set_up_lsp_handlers(true, &mut cx);
14745    let navigated = cx
14746        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14747        .await
14748        .expect("Failed to navigate to lookup references");
14749    assert_eq!(
14750        navigated,
14751        Navigated::Yes,
14752        "Should have navigated to references as a fallback after empty GoToDefinition response"
14753    );
14754    // We should not change the selections in the existing file,
14755    // if opening another milti buffer with the references
14756    cx.assert_editor_state(
14757        &r#"fn one() {
14758            let mut a = two();
14759        }
14760
14761        fn «twoˇ»() {}"#
14762            .unindent(),
14763    );
14764    let editors = cx.update_workspace(|workspace, _, cx| {
14765        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14766    });
14767    cx.update_editor(|_, _, test_editor_cx| {
14768        assert_eq!(
14769            editors.len(),
14770            2,
14771            "After falling back to references search, we open a new editor with the results"
14772        );
14773        let references_fallback_text = editors
14774            .into_iter()
14775            .find(|new_editor| *new_editor != test_editor_cx.entity())
14776            .expect("Should have one non-test editor now")
14777            .read(test_editor_cx)
14778            .text(test_editor_cx);
14779        assert_eq!(
14780            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14781            "Should use the range from the references response and not the GoToDefinition one"
14782        );
14783    });
14784}
14785
14786#[gpui::test]
14787async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14788    init_test(cx, |_| {});
14789
14790    let language = Arc::new(Language::new(
14791        LanguageConfig::default(),
14792        Some(tree_sitter_rust::LANGUAGE.into()),
14793    ));
14794
14795    let text = r#"
14796        #[cfg(test)]
14797        mod tests() {
14798            #[test]
14799            fn runnable_1() {
14800                let a = 1;
14801            }
14802
14803            #[test]
14804            fn runnable_2() {
14805                let a = 1;
14806                let b = 2;
14807            }
14808        }
14809    "#
14810    .unindent();
14811
14812    let fs = FakeFs::new(cx.executor());
14813    fs.insert_file("/file.rs", Default::default()).await;
14814
14815    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14816    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14817    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14818    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14819    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14820
14821    let editor = cx.new_window_entity(|window, cx| {
14822        Editor::new(
14823            EditorMode::Full,
14824            multi_buffer,
14825            Some(project.clone()),
14826            true,
14827            window,
14828            cx,
14829        )
14830    });
14831
14832    editor.update_in(cx, |editor, window, cx| {
14833        editor.tasks.insert(
14834            (buffer.read(cx).remote_id(), 3),
14835            RunnableTasks {
14836                templates: vec![],
14837                offset: MultiBufferOffset(43),
14838                column: 0,
14839                extra_variables: HashMap::default(),
14840                context_range: BufferOffset(43)..BufferOffset(85),
14841            },
14842        );
14843        editor.tasks.insert(
14844            (buffer.read(cx).remote_id(), 8),
14845            RunnableTasks {
14846                templates: vec![],
14847                offset: MultiBufferOffset(86),
14848                column: 0,
14849                extra_variables: HashMap::default(),
14850                context_range: BufferOffset(86)..BufferOffset(191),
14851            },
14852        );
14853
14854        // Test finding task when cursor is inside function body
14855        editor.change_selections(None, window, cx, |s| {
14856            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
14857        });
14858        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14859        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
14860
14861        // Test finding task when cursor is on function name
14862        editor.change_selections(None, window, cx, |s| {
14863            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
14864        });
14865        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
14866        assert_eq!(row, 8, "Should find task when cursor is on function name");
14867    });
14868}
14869
14870#[gpui::test]
14871async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
14872    init_test(cx, |_| {});
14873
14874    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
14875    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
14876    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
14877
14878    let fs = FakeFs::new(cx.executor());
14879    fs.insert_tree(
14880        "/a",
14881        json!({
14882            "first.rs": sample_text_1,
14883            "second.rs": sample_text_2,
14884            "third.rs": sample_text_3,
14885        }),
14886    )
14887    .await;
14888    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14889    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14890    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14891    let worktree = project.update(cx, |project, cx| {
14892        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
14893        assert_eq!(worktrees.len(), 1);
14894        worktrees.pop().unwrap()
14895    });
14896    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
14897
14898    let buffer_1 = project
14899        .update(cx, |project, cx| {
14900            project.open_buffer((worktree_id, "first.rs"), cx)
14901        })
14902        .await
14903        .unwrap();
14904    let buffer_2 = project
14905        .update(cx, |project, cx| {
14906            project.open_buffer((worktree_id, "second.rs"), cx)
14907        })
14908        .await
14909        .unwrap();
14910    let buffer_3 = project
14911        .update(cx, |project, cx| {
14912            project.open_buffer((worktree_id, "third.rs"), cx)
14913        })
14914        .await
14915        .unwrap();
14916
14917    let multi_buffer = cx.new(|cx| {
14918        let mut multi_buffer = MultiBuffer::new(ReadWrite);
14919        multi_buffer.push_excerpts(
14920            buffer_1.clone(),
14921            [
14922                ExcerptRange {
14923                    context: Point::new(0, 0)..Point::new(3, 0),
14924                    primary: None,
14925                },
14926                ExcerptRange {
14927                    context: Point::new(5, 0)..Point::new(7, 0),
14928                    primary: None,
14929                },
14930                ExcerptRange {
14931                    context: Point::new(9, 0)..Point::new(10, 4),
14932                    primary: None,
14933                },
14934            ],
14935            cx,
14936        );
14937        multi_buffer.push_excerpts(
14938            buffer_2.clone(),
14939            [
14940                ExcerptRange {
14941                    context: Point::new(0, 0)..Point::new(3, 0),
14942                    primary: None,
14943                },
14944                ExcerptRange {
14945                    context: Point::new(5, 0)..Point::new(7, 0),
14946                    primary: None,
14947                },
14948                ExcerptRange {
14949                    context: Point::new(9, 0)..Point::new(10, 4),
14950                    primary: None,
14951                },
14952            ],
14953            cx,
14954        );
14955        multi_buffer.push_excerpts(
14956            buffer_3.clone(),
14957            [
14958                ExcerptRange {
14959                    context: Point::new(0, 0)..Point::new(3, 0),
14960                    primary: None,
14961                },
14962                ExcerptRange {
14963                    context: Point::new(5, 0)..Point::new(7, 0),
14964                    primary: None,
14965                },
14966                ExcerptRange {
14967                    context: Point::new(9, 0)..Point::new(10, 4),
14968                    primary: None,
14969                },
14970            ],
14971            cx,
14972        );
14973        multi_buffer
14974    });
14975    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14976        Editor::new(
14977            EditorMode::Full,
14978            multi_buffer,
14979            Some(project.clone()),
14980            true,
14981            window,
14982            cx,
14983        )
14984    });
14985
14986    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";
14987    assert_eq!(
14988        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14989        full_text,
14990    );
14991
14992    multi_buffer_editor.update(cx, |editor, cx| {
14993        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
14994    });
14995    assert_eq!(
14996        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
14997        "\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",
14998        "After folding the first buffer, its text should not be displayed"
14999    );
15000
15001    multi_buffer_editor.update(cx, |editor, cx| {
15002        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15003    });
15004    assert_eq!(
15005        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15006        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15007        "After folding the second buffer, its text should not be displayed"
15008    );
15009
15010    multi_buffer_editor.update(cx, |editor, cx| {
15011        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15012    });
15013    assert_eq!(
15014        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15015        "\n\n\n\n\n",
15016        "After folding the third buffer, its text should not be displayed"
15017    );
15018
15019    // Emulate selection inside the fold logic, that should work
15020    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15021        editor
15022            .snapshot(window, cx)
15023            .next_line_boundary(Point::new(0, 4));
15024    });
15025
15026    multi_buffer_editor.update(cx, |editor, cx| {
15027        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15028    });
15029    assert_eq!(
15030        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15031        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15032        "After unfolding the second buffer, its text should be displayed"
15033    );
15034
15035    multi_buffer_editor.update(cx, |editor, cx| {
15036        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15037    });
15038    assert_eq!(
15039        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15040        "\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",
15041        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15042    );
15043
15044    multi_buffer_editor.update(cx, |editor, cx| {
15045        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15046    });
15047    assert_eq!(
15048        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15049        full_text,
15050        "After unfolding the all buffers, all original text should be displayed"
15051    );
15052}
15053
15054#[gpui::test]
15055async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15056    init_test(cx, |_| {});
15057
15058    let sample_text_1 = "1111\n2222\n3333".to_string();
15059    let sample_text_2 = "4444\n5555\n6666".to_string();
15060    let sample_text_3 = "7777\n8888\n9999".to_string();
15061
15062    let fs = FakeFs::new(cx.executor());
15063    fs.insert_tree(
15064        "/a",
15065        json!({
15066            "first.rs": sample_text_1,
15067            "second.rs": sample_text_2,
15068            "third.rs": sample_text_3,
15069        }),
15070    )
15071    .await;
15072    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15073    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15074    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15075    let worktree = project.update(cx, |project, cx| {
15076        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15077        assert_eq!(worktrees.len(), 1);
15078        worktrees.pop().unwrap()
15079    });
15080    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15081
15082    let buffer_1 = project
15083        .update(cx, |project, cx| {
15084            project.open_buffer((worktree_id, "first.rs"), cx)
15085        })
15086        .await
15087        .unwrap();
15088    let buffer_2 = project
15089        .update(cx, |project, cx| {
15090            project.open_buffer((worktree_id, "second.rs"), cx)
15091        })
15092        .await
15093        .unwrap();
15094    let buffer_3 = project
15095        .update(cx, |project, cx| {
15096            project.open_buffer((worktree_id, "third.rs"), cx)
15097        })
15098        .await
15099        .unwrap();
15100
15101    let multi_buffer = cx.new(|cx| {
15102        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15103        multi_buffer.push_excerpts(
15104            buffer_1.clone(),
15105            [ExcerptRange {
15106                context: Point::new(0, 0)..Point::new(3, 0),
15107                primary: None,
15108            }],
15109            cx,
15110        );
15111        multi_buffer.push_excerpts(
15112            buffer_2.clone(),
15113            [ExcerptRange {
15114                context: Point::new(0, 0)..Point::new(3, 0),
15115                primary: None,
15116            }],
15117            cx,
15118        );
15119        multi_buffer.push_excerpts(
15120            buffer_3.clone(),
15121            [ExcerptRange {
15122                context: Point::new(0, 0)..Point::new(3, 0),
15123                primary: None,
15124            }],
15125            cx,
15126        );
15127        multi_buffer
15128    });
15129
15130    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15131        Editor::new(
15132            EditorMode::Full,
15133            multi_buffer,
15134            Some(project.clone()),
15135            true,
15136            window,
15137            cx,
15138        )
15139    });
15140
15141    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15142    assert_eq!(
15143        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15144        full_text,
15145    );
15146
15147    multi_buffer_editor.update(cx, |editor, cx| {
15148        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15149    });
15150    assert_eq!(
15151        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15152        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15153        "After folding the first buffer, its text should not be displayed"
15154    );
15155
15156    multi_buffer_editor.update(cx, |editor, cx| {
15157        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15158    });
15159
15160    assert_eq!(
15161        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15162        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15163        "After folding the second buffer, its text should not be displayed"
15164    );
15165
15166    multi_buffer_editor.update(cx, |editor, cx| {
15167        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15168    });
15169    assert_eq!(
15170        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15171        "\n\n\n\n\n",
15172        "After folding the third buffer, its text should not be displayed"
15173    );
15174
15175    multi_buffer_editor.update(cx, |editor, cx| {
15176        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15177    });
15178    assert_eq!(
15179        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15180        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15181        "After unfolding the second buffer, its text should be displayed"
15182    );
15183
15184    multi_buffer_editor.update(cx, |editor, cx| {
15185        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15186    });
15187    assert_eq!(
15188        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15189        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15190        "After unfolding the first buffer, its text should be displayed"
15191    );
15192
15193    multi_buffer_editor.update(cx, |editor, cx| {
15194        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15195    });
15196    assert_eq!(
15197        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15198        full_text,
15199        "After unfolding all buffers, all original text should be displayed"
15200    );
15201}
15202
15203#[gpui::test]
15204async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15205    init_test(cx, |_| {});
15206
15207    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15208
15209    let fs = FakeFs::new(cx.executor());
15210    fs.insert_tree(
15211        "/a",
15212        json!({
15213            "main.rs": sample_text,
15214        }),
15215    )
15216    .await;
15217    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15218    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15219    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15220    let worktree = project.update(cx, |project, cx| {
15221        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15222        assert_eq!(worktrees.len(), 1);
15223        worktrees.pop().unwrap()
15224    });
15225    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15226
15227    let buffer_1 = project
15228        .update(cx, |project, cx| {
15229            project.open_buffer((worktree_id, "main.rs"), cx)
15230        })
15231        .await
15232        .unwrap();
15233
15234    let multi_buffer = cx.new(|cx| {
15235        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15236        multi_buffer.push_excerpts(
15237            buffer_1.clone(),
15238            [ExcerptRange {
15239                context: Point::new(0, 0)
15240                    ..Point::new(
15241                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15242                        0,
15243                    ),
15244                primary: None,
15245            }],
15246            cx,
15247        );
15248        multi_buffer
15249    });
15250    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15251        Editor::new(
15252            EditorMode::Full,
15253            multi_buffer,
15254            Some(project.clone()),
15255            true,
15256            window,
15257            cx,
15258        )
15259    });
15260
15261    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15262    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15263        enum TestHighlight {}
15264        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15265        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15266        editor.highlight_text::<TestHighlight>(
15267            vec![highlight_range.clone()],
15268            HighlightStyle::color(Hsla::green()),
15269            cx,
15270        );
15271        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15272    });
15273
15274    let full_text = format!("\n\n\n{sample_text}\n");
15275    assert_eq!(
15276        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15277        full_text,
15278    );
15279}
15280
15281#[gpui::test]
15282async fn test_inline_completion_text(cx: &mut TestAppContext) {
15283    init_test(cx, |_| {});
15284
15285    // Simple insertion
15286    assert_highlighted_edits(
15287        "Hello, world!",
15288        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15289        true,
15290        cx,
15291        |highlighted_edits, cx| {
15292            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15293            assert_eq!(highlighted_edits.highlights.len(), 1);
15294            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15295            assert_eq!(
15296                highlighted_edits.highlights[0].1.background_color,
15297                Some(cx.theme().status().created_background)
15298            );
15299        },
15300    )
15301    .await;
15302
15303    // Replacement
15304    assert_highlighted_edits(
15305        "This is a test.",
15306        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15307        false,
15308        cx,
15309        |highlighted_edits, cx| {
15310            assert_eq!(highlighted_edits.text, "That is a test.");
15311            assert_eq!(highlighted_edits.highlights.len(), 1);
15312            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15313            assert_eq!(
15314                highlighted_edits.highlights[0].1.background_color,
15315                Some(cx.theme().status().created_background)
15316            );
15317        },
15318    )
15319    .await;
15320
15321    // Multiple edits
15322    assert_highlighted_edits(
15323        "Hello, world!",
15324        vec![
15325            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15326            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15327        ],
15328        false,
15329        cx,
15330        |highlighted_edits, cx| {
15331            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15332            assert_eq!(highlighted_edits.highlights.len(), 2);
15333            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15334            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15335            assert_eq!(
15336                highlighted_edits.highlights[0].1.background_color,
15337                Some(cx.theme().status().created_background)
15338            );
15339            assert_eq!(
15340                highlighted_edits.highlights[1].1.background_color,
15341                Some(cx.theme().status().created_background)
15342            );
15343        },
15344    )
15345    .await;
15346
15347    // Multiple lines with edits
15348    assert_highlighted_edits(
15349        "First line\nSecond line\nThird line\nFourth line",
15350        vec![
15351            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15352            (
15353                Point::new(2, 0)..Point::new(2, 10),
15354                "New third line".to_string(),
15355            ),
15356            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15357        ],
15358        false,
15359        cx,
15360        |highlighted_edits, cx| {
15361            assert_eq!(
15362                highlighted_edits.text,
15363                "Second modified\nNew third line\nFourth updated line"
15364            );
15365            assert_eq!(highlighted_edits.highlights.len(), 3);
15366            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15367            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15368            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15369            for highlight in &highlighted_edits.highlights {
15370                assert_eq!(
15371                    highlight.1.background_color,
15372                    Some(cx.theme().status().created_background)
15373                );
15374            }
15375        },
15376    )
15377    .await;
15378}
15379
15380#[gpui::test]
15381async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15382    init_test(cx, |_| {});
15383
15384    // Deletion
15385    assert_highlighted_edits(
15386        "Hello, world!",
15387        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15388        true,
15389        cx,
15390        |highlighted_edits, cx| {
15391            assert_eq!(highlighted_edits.text, "Hello, world!");
15392            assert_eq!(highlighted_edits.highlights.len(), 1);
15393            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15394            assert_eq!(
15395                highlighted_edits.highlights[0].1.background_color,
15396                Some(cx.theme().status().deleted_background)
15397            );
15398        },
15399    )
15400    .await;
15401
15402    // Insertion
15403    assert_highlighted_edits(
15404        "Hello, world!",
15405        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15406        true,
15407        cx,
15408        |highlighted_edits, cx| {
15409            assert_eq!(highlighted_edits.highlights.len(), 1);
15410            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15411            assert_eq!(
15412                highlighted_edits.highlights[0].1.background_color,
15413                Some(cx.theme().status().created_background)
15414            );
15415        },
15416    )
15417    .await;
15418}
15419
15420async fn assert_highlighted_edits(
15421    text: &str,
15422    edits: Vec<(Range<Point>, String)>,
15423    include_deletions: bool,
15424    cx: &mut TestAppContext,
15425    assertion_fn: impl Fn(HighlightedText, &App),
15426) {
15427    let window = cx.add_window(|window, cx| {
15428        let buffer = MultiBuffer::build_simple(text, cx);
15429        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15430    });
15431    let cx = &mut VisualTestContext::from_window(*window, cx);
15432
15433    let (buffer, snapshot) = window
15434        .update(cx, |editor, _window, cx| {
15435            (
15436                editor.buffer().clone(),
15437                editor.buffer().read(cx).snapshot(cx),
15438            )
15439        })
15440        .unwrap();
15441
15442    let edits = edits
15443        .into_iter()
15444        .map(|(range, edit)| {
15445            (
15446                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15447                edit,
15448            )
15449        })
15450        .collect::<Vec<_>>();
15451
15452    let text_anchor_edits = edits
15453        .clone()
15454        .into_iter()
15455        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15456        .collect::<Vec<_>>();
15457
15458    let edit_preview = window
15459        .update(cx, |_, _window, cx| {
15460            buffer
15461                .read(cx)
15462                .as_singleton()
15463                .unwrap()
15464                .read(cx)
15465                .preview_edits(text_anchor_edits.into(), cx)
15466        })
15467        .unwrap()
15468        .await;
15469
15470    cx.update(|_window, cx| {
15471        let highlighted_edits = inline_completion_edit_text(
15472            &snapshot.as_singleton().unwrap().2,
15473            &edits,
15474            &edit_preview,
15475            include_deletions,
15476            cx,
15477        );
15478        assertion_fn(highlighted_edits, cx)
15479    });
15480}
15481
15482#[gpui::test]
15483async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15484    init_test(cx, |_| {});
15485    let capabilities = lsp::ServerCapabilities {
15486        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15487            prepare_provider: Some(true),
15488            work_done_progress_options: Default::default(),
15489        })),
15490        ..Default::default()
15491    };
15492    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15493
15494    cx.set_state(indoc! {"
15495        struct Fˇoo {}
15496    "});
15497
15498    cx.update_editor(|editor, _, cx| {
15499        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15500        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15501        editor.highlight_background::<DocumentHighlightRead>(
15502            &[highlight_range],
15503            |c| c.editor_document_highlight_read_background,
15504            cx,
15505        );
15506    });
15507
15508    let mut prepare_rename_handler =
15509        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15510            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15511                start: lsp::Position {
15512                    line: 0,
15513                    character: 7,
15514                },
15515                end: lsp::Position {
15516                    line: 0,
15517                    character: 10,
15518                },
15519            })))
15520        });
15521    let prepare_rename_task = cx
15522        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15523        .expect("Prepare rename was not started");
15524    prepare_rename_handler.next().await.unwrap();
15525    prepare_rename_task.await.expect("Prepare rename failed");
15526
15527    let mut rename_handler =
15528        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15529            let edit = lsp::TextEdit {
15530                range: lsp::Range {
15531                    start: lsp::Position {
15532                        line: 0,
15533                        character: 7,
15534                    },
15535                    end: lsp::Position {
15536                        line: 0,
15537                        character: 10,
15538                    },
15539                },
15540                new_text: "FooRenamed".to_string(),
15541            };
15542            Ok(Some(lsp::WorkspaceEdit::new(
15543                // Specify the same edit twice
15544                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15545            )))
15546        });
15547    let rename_task = cx
15548        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15549        .expect("Confirm rename was not started");
15550    rename_handler.next().await.unwrap();
15551    rename_task.await.expect("Confirm rename failed");
15552    cx.run_until_parked();
15553
15554    // Despite two edits, only one is actually applied as those are identical
15555    cx.assert_editor_state(indoc! {"
15556        struct FooRenamedˇ {}
15557    "});
15558}
15559
15560#[gpui::test]
15561async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15562    init_test(cx, |_| {});
15563    // These capabilities indicate that the server does not support prepare rename.
15564    let capabilities = lsp::ServerCapabilities {
15565        rename_provider: Some(lsp::OneOf::Left(true)),
15566        ..Default::default()
15567    };
15568    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15569
15570    cx.set_state(indoc! {"
15571        struct Fˇoo {}
15572    "});
15573
15574    cx.update_editor(|editor, _window, cx| {
15575        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15576        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15577        editor.highlight_background::<DocumentHighlightRead>(
15578            &[highlight_range],
15579            |c| c.editor_document_highlight_read_background,
15580            cx,
15581        );
15582    });
15583
15584    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15585        .expect("Prepare rename was not started")
15586        .await
15587        .expect("Prepare rename failed");
15588
15589    let mut rename_handler =
15590        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15591            let edit = lsp::TextEdit {
15592                range: lsp::Range {
15593                    start: lsp::Position {
15594                        line: 0,
15595                        character: 7,
15596                    },
15597                    end: lsp::Position {
15598                        line: 0,
15599                        character: 10,
15600                    },
15601                },
15602                new_text: "FooRenamed".to_string(),
15603            };
15604            Ok(Some(lsp::WorkspaceEdit::new(
15605                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15606            )))
15607        });
15608    let rename_task = cx
15609        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15610        .expect("Confirm rename was not started");
15611    rename_handler.next().await.unwrap();
15612    rename_task.await.expect("Confirm rename failed");
15613    cx.run_until_parked();
15614
15615    // Correct range is renamed, as `surrounding_word` is used to find it.
15616    cx.assert_editor_state(indoc! {"
15617        struct FooRenamedˇ {}
15618    "});
15619}
15620
15621fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15622    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15623    point..point
15624}
15625
15626fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15627    let (text, ranges) = marked_text_ranges(marked_text, true);
15628    assert_eq!(editor.text(cx), text);
15629    assert_eq!(
15630        editor.selections.ranges(cx),
15631        ranges,
15632        "Assert selections are {}",
15633        marked_text
15634    );
15635}
15636
15637pub fn handle_signature_help_request(
15638    cx: &mut EditorLspTestContext,
15639    mocked_response: lsp::SignatureHelp,
15640) -> impl Future<Output = ()> {
15641    let mut request =
15642        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15643            let mocked_response = mocked_response.clone();
15644            async move { Ok(Some(mocked_response)) }
15645        });
15646
15647    async move {
15648        request.next().await;
15649    }
15650}
15651
15652/// Handle completion request passing a marked string specifying where the completion
15653/// should be triggered from using '|' character, what range should be replaced, and what completions
15654/// should be returned using '<' and '>' to delimit the range
15655pub fn handle_completion_request(
15656    cx: &mut EditorLspTestContext,
15657    marked_string: &str,
15658    completions: Vec<&'static str>,
15659    counter: Arc<AtomicUsize>,
15660) -> impl Future<Output = ()> {
15661    let complete_from_marker: TextRangeMarker = '|'.into();
15662    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15663    let (_, mut marked_ranges) = marked_text_ranges_by(
15664        marked_string,
15665        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15666    );
15667
15668    let complete_from_position =
15669        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15670    let replace_range =
15671        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15672
15673    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15674        let completions = completions.clone();
15675        counter.fetch_add(1, atomic::Ordering::Release);
15676        async move {
15677            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15678            assert_eq!(
15679                params.text_document_position.position,
15680                complete_from_position
15681            );
15682            Ok(Some(lsp::CompletionResponse::Array(
15683                completions
15684                    .iter()
15685                    .map(|completion_text| lsp::CompletionItem {
15686                        label: completion_text.to_string(),
15687                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15688                            range: replace_range,
15689                            new_text: completion_text.to_string(),
15690                        })),
15691                        ..Default::default()
15692                    })
15693                    .collect(),
15694            )))
15695        }
15696    });
15697
15698    async move {
15699        request.next().await;
15700    }
15701}
15702
15703fn handle_resolve_completion_request(
15704    cx: &mut EditorLspTestContext,
15705    edits: Option<Vec<(&'static str, &'static str)>>,
15706) -> impl Future<Output = ()> {
15707    let edits = edits.map(|edits| {
15708        edits
15709            .iter()
15710            .map(|(marked_string, new_text)| {
15711                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15712                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15713                lsp::TextEdit::new(replace_range, new_text.to_string())
15714            })
15715            .collect::<Vec<_>>()
15716    });
15717
15718    let mut request =
15719        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15720            let edits = edits.clone();
15721            async move {
15722                Ok(lsp::CompletionItem {
15723                    additional_text_edits: edits,
15724                    ..Default::default()
15725                })
15726            }
15727        });
15728
15729    async move {
15730        request.next().await;
15731    }
15732}
15733
15734pub(crate) fn update_test_language_settings(
15735    cx: &mut TestAppContext,
15736    f: impl Fn(&mut AllLanguageSettingsContent),
15737) {
15738    cx.update(|cx| {
15739        SettingsStore::update_global(cx, |store, cx| {
15740            store.update_user_settings::<AllLanguageSettings>(cx, f);
15741        });
15742    });
15743}
15744
15745pub(crate) fn update_test_project_settings(
15746    cx: &mut TestAppContext,
15747    f: impl Fn(&mut ProjectSettings),
15748) {
15749    cx.update(|cx| {
15750        SettingsStore::update_global(cx, |store, cx| {
15751            store.update_user_settings::<ProjectSettings>(cx, f);
15752        });
15753    });
15754}
15755
15756pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15757    cx.update(|cx| {
15758        assets::Assets.load_test_fonts(cx);
15759        let store = SettingsStore::test(cx);
15760        cx.set_global(store);
15761        theme::init(theme::LoadThemes::JustBase, cx);
15762        release_channel::init(SemanticVersion::default(), cx);
15763        client::init_settings(cx);
15764        language::init(cx);
15765        Project::init_settings(cx);
15766        workspace::init_settings(cx);
15767        crate::init(cx);
15768    });
15769
15770    update_test_language_settings(cx, f);
15771}
15772
15773#[track_caller]
15774fn assert_hunk_revert(
15775    not_reverted_text_with_selections: &str,
15776    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15777    expected_reverted_text_with_selections: &str,
15778    base_text: &str,
15779    cx: &mut EditorLspTestContext,
15780) {
15781    cx.set_state(not_reverted_text_with_selections);
15782    cx.set_diff_base(base_text);
15783    cx.executor().run_until_parked();
15784
15785    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15786        let snapshot = editor.snapshot(window, cx);
15787        let reverted_hunk_statuses = snapshot
15788            .buffer_snapshot
15789            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15790            .map(|hunk| hunk.status())
15791            .collect::<Vec<_>>();
15792
15793        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15794        reverted_hunk_statuses
15795    });
15796    cx.executor().run_until_parked();
15797    cx.assert_editor_state(expected_reverted_text_with_selections);
15798    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15799}