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 diff::{BufferDiff, DiffHunkStatus};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, ParsedMarkdown, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::IndentGuide;
   28use parking_lot::Mutex;
   29use pretty_assertions::{assert_eq, assert_ne};
   30use project::FakeFs;
   31use project::{
   32    lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
   33    project_settings::{LspSettings, ProjectSettings},
   34};
   35use serde_json::{self, json};
   36use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   37use std::{
   38    iter,
   39    sync::atomic::{self, AtomicUsize},
   40};
   41use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   42use unindent::Unindent;
   43use util::{
   44    assert_set_eq, path,
   45    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   46    uri,
   47};
   48use workspace::{
   49    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   50    NavigationEntry, ViewId,
   51};
   52
   53#[gpui::test]
   54fn test_edit_events(cx: &mut TestAppContext) {
   55    init_test(cx, |_| {});
   56
   57    let buffer = cx.new(|cx| {
   58        let mut buffer = language::Buffer::local("123456", cx);
   59        buffer.set_group_interval(Duration::from_secs(1));
   60        buffer
   61    });
   62
   63    let events = Rc::new(RefCell::new(Vec::new()));
   64    let editor1 = cx.add_window({
   65        let events = events.clone();
   66        |window, cx| {
   67            let entity = cx.entity().clone();
   68            cx.subscribe_in(
   69                &entity,
   70                window,
   71                move |_, _, event: &EditorEvent, _, _| match event {
   72                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   73                    EditorEvent::BufferEdited => {
   74                        events.borrow_mut().push(("editor1", "buffer edited"))
   75                    }
   76                    _ => {}
   77                },
   78            )
   79            .detach();
   80            Editor::for_buffer(buffer.clone(), None, window, cx)
   81        }
   82    });
   83
   84    let editor2 = cx.add_window({
   85        let events = events.clone();
   86        |window, cx| {
   87            cx.subscribe_in(
   88                &cx.entity().clone(),
   89                window,
   90                move |_, _, event: &EditorEvent, _, _| match event {
   91                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   92                    EditorEvent::BufferEdited => {
   93                        events.borrow_mut().push(("editor2", "buffer edited"))
   94                    }
   95                    _ => {}
   96                },
   97            )
   98            .detach();
   99            Editor::for_buffer(buffer.clone(), None, window, cx)
  100        }
  101    });
  102
  103    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  104
  105    // Mutating editor 1 will emit an `Edited` event only for that editor.
  106    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  107    assert_eq!(
  108        mem::take(&mut *events.borrow_mut()),
  109        [
  110            ("editor1", "edited"),
  111            ("editor1", "buffer edited"),
  112            ("editor2", "buffer edited"),
  113        ]
  114    );
  115
  116    // Mutating editor 2 will emit an `Edited` event only for that editor.
  117    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  118    assert_eq!(
  119        mem::take(&mut *events.borrow_mut()),
  120        [
  121            ("editor2", "edited"),
  122            ("editor1", "buffer edited"),
  123            ("editor2", "buffer edited"),
  124        ]
  125    );
  126
  127    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  128    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  129    assert_eq!(
  130        mem::take(&mut *events.borrow_mut()),
  131        [
  132            ("editor1", "edited"),
  133            ("editor1", "buffer edited"),
  134            ("editor2", "buffer edited"),
  135        ]
  136    );
  137
  138    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  139    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  140    assert_eq!(
  141        mem::take(&mut *events.borrow_mut()),
  142        [
  143            ("editor1", "edited"),
  144            ("editor1", "buffer edited"),
  145            ("editor2", "buffer edited"),
  146        ]
  147    );
  148
  149    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  150    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  151    assert_eq!(
  152        mem::take(&mut *events.borrow_mut()),
  153        [
  154            ("editor2", "edited"),
  155            ("editor1", "buffer edited"),
  156            ("editor2", "buffer edited"),
  157        ]
  158    );
  159
  160    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  161    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  162    assert_eq!(
  163        mem::take(&mut *events.borrow_mut()),
  164        [
  165            ("editor2", "edited"),
  166            ("editor1", "buffer edited"),
  167            ("editor2", "buffer edited"),
  168        ]
  169    );
  170
  171    // No event is emitted when the mutation is a no-op.
  172    _ = editor2.update(cx, |editor, window, cx| {
  173        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  174
  175        editor.backspace(&Backspace, window, cx);
  176    });
  177    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  178}
  179
  180#[gpui::test]
  181fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  182    init_test(cx, |_| {});
  183
  184    let mut now = Instant::now();
  185    let group_interval = Duration::from_millis(1);
  186    let buffer = cx.new(|cx| {
  187        let mut buf = language::Buffer::local("123456", cx);
  188        buf.set_group_interval(group_interval);
  189        buf
  190    });
  191    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  192    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  193
  194    _ = editor.update(cx, |editor, window, cx| {
  195        editor.start_transaction_at(now, window, cx);
  196        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  197
  198        editor.insert("cd", window, cx);
  199        editor.end_transaction_at(now, cx);
  200        assert_eq!(editor.text(cx), "12cd56");
  201        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  202
  203        editor.start_transaction_at(now, window, cx);
  204        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  205        editor.insert("e", window, cx);
  206        editor.end_transaction_at(now, cx);
  207        assert_eq!(editor.text(cx), "12cde6");
  208        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  209
  210        now += group_interval + Duration::from_millis(1);
  211        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  212
  213        // Simulate an edit in another editor
  214        buffer.update(cx, |buffer, cx| {
  215            buffer.start_transaction_at(now, cx);
  216            buffer.edit([(0..1, "a")], None, cx);
  217            buffer.edit([(1..1, "b")], None, cx);
  218            buffer.end_transaction_at(now, cx);
  219        });
  220
  221        assert_eq!(editor.text(cx), "ab2cde6");
  222        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  223
  224        // Last transaction happened past the group interval in a different editor.
  225        // Undo it individually and don't restore selections.
  226        editor.undo(&Undo, window, cx);
  227        assert_eq!(editor.text(cx), "12cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  229
  230        // First two transactions happened within the group interval in this editor.
  231        // Undo them together and restore selections.
  232        editor.undo(&Undo, window, cx);
  233        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  234        assert_eq!(editor.text(cx), "123456");
  235        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  236
  237        // Redo the first two transactions together.
  238        editor.redo(&Redo, window, cx);
  239        assert_eq!(editor.text(cx), "12cde6");
  240        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  241
  242        // Redo the last transaction on its own.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "ab2cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  246
  247        // Test empty transactions.
  248        editor.start_transaction_at(now, window, cx);
  249        editor.end_transaction_at(now, cx);
  250        editor.undo(&Undo, window, cx);
  251        assert_eq!(editor.text(cx), "12cde6");
  252    });
  253}
  254
  255#[gpui::test]
  256fn test_ime_composition(cx: &mut TestAppContext) {
  257    init_test(cx, |_| {});
  258
  259    let buffer = cx.new(|cx| {
  260        let mut buffer = language::Buffer::local("abcde", cx);
  261        // Ensure automatic grouping doesn't occur.
  262        buffer.set_group_interval(Duration::ZERO);
  263        buffer
  264    });
  265
  266    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  267    cx.add_window(|window, cx| {
  268        let mut editor = build_editor(buffer.clone(), window, cx);
  269
  270        // Start a new IME composition.
  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        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  274        assert_eq!(editor.text(cx), "äbcde");
  275        assert_eq!(
  276            editor.marked_text_ranges(cx),
  277            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  278        );
  279
  280        // Finalize IME composition.
  281        editor.replace_text_in_range(None, "ā", window, cx);
  282        assert_eq!(editor.text(cx), "ābcde");
  283        assert_eq!(editor.marked_text_ranges(cx), None);
  284
  285        // IME composition edits are grouped and are undone/redone at once.
  286        editor.undo(&Default::default(), window, cx);
  287        assert_eq!(editor.text(cx), "abcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289        editor.redo(&Default::default(), window, cx);
  290        assert_eq!(editor.text(cx), "ābcde");
  291        assert_eq!(editor.marked_text_ranges(cx), None);
  292
  293        // Start a new IME composition.
  294        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  295        assert_eq!(
  296            editor.marked_text_ranges(cx),
  297            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  298        );
  299
  300        // Undoing during an IME composition cancels it.
  301        editor.undo(&Default::default(), window, cx);
  302        assert_eq!(editor.text(cx), "ābcde");
  303        assert_eq!(editor.marked_text_ranges(cx), None);
  304
  305        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  306        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  307        assert_eq!(editor.text(cx), "ābcdè");
  308        assert_eq!(
  309            editor.marked_text_ranges(cx),
  310            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  311        );
  312
  313        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  314        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  315        assert_eq!(editor.text(cx), "ābcdę");
  316        assert_eq!(editor.marked_text_ranges(cx), None);
  317
  318        // Start a new IME composition with multiple cursors.
  319        editor.change_selections(None, window, cx, |s| {
  320            s.select_ranges([
  321                OffsetUtf16(1)..OffsetUtf16(1),
  322                OffsetUtf16(3)..OffsetUtf16(3),
  323                OffsetUtf16(5)..OffsetUtf16(5),
  324            ])
  325        });
  326        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  327        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  328        assert_eq!(
  329            editor.marked_text_ranges(cx),
  330            Some(vec![
  331                OffsetUtf16(0)..OffsetUtf16(3),
  332                OffsetUtf16(4)..OffsetUtf16(7),
  333                OffsetUtf16(8)..OffsetUtf16(11)
  334            ])
  335        );
  336
  337        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  338        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  339        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  340        assert_eq!(
  341            editor.marked_text_ranges(cx),
  342            Some(vec![
  343                OffsetUtf16(1)..OffsetUtf16(2),
  344                OffsetUtf16(5)..OffsetUtf16(6),
  345                OffsetUtf16(9)..OffsetUtf16(10)
  346            ])
  347        );
  348
  349        // Finalize IME composition with multiple cursors.
  350        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  351        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  352        assert_eq!(editor.marked_text_ranges(cx), None);
  353
  354        editor
  355    });
  356}
  357
  358#[gpui::test]
  359fn test_selection_with_mouse(cx: &mut TestAppContext) {
  360    init_test(cx, |_| {});
  361
  362    let editor = cx.add_window(|window, cx| {
  363        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  364        build_editor(buffer, window, cx)
  365    });
  366
  367    _ = editor.update(cx, |editor, window, cx| {
  368        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  369    });
  370    assert_eq!(
  371        editor
  372            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  373            .unwrap(),
  374        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  375    );
  376
  377    _ = editor.update(cx, |editor, window, cx| {
  378        editor.update_selection(
  379            DisplayPoint::new(DisplayRow(3), 3),
  380            0,
  381            gpui::Point::<f32>::default(),
  382            window,
  383            cx,
  384        );
  385    });
  386
  387    assert_eq!(
  388        editor
  389            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  390            .unwrap(),
  391        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  392    );
  393
  394    _ = editor.update(cx, |editor, window, cx| {
  395        editor.update_selection(
  396            DisplayPoint::new(DisplayRow(1), 1),
  397            0,
  398            gpui::Point::<f32>::default(),
  399            window,
  400            cx,
  401        );
  402    });
  403
  404    assert_eq!(
  405        editor
  406            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  407            .unwrap(),
  408        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  409    );
  410
  411    _ = editor.update(cx, |editor, window, cx| {
  412        editor.end_selection(window, cx);
  413        editor.update_selection(
  414            DisplayPoint::new(DisplayRow(3), 3),
  415            0,
  416            gpui::Point::<f32>::default(),
  417            window,
  418            cx,
  419        );
  420    });
  421
  422    assert_eq!(
  423        editor
  424            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  425            .unwrap(),
  426        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  427    );
  428
  429    _ = editor.update(cx, |editor, window, cx| {
  430        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  431        editor.update_selection(
  432            DisplayPoint::new(DisplayRow(0), 0),
  433            0,
  434            gpui::Point::<f32>::default(),
  435            window,
  436            cx,
  437        );
  438    });
  439
  440    assert_eq!(
  441        editor
  442            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  443            .unwrap(),
  444        [
  445            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  446            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  447        ]
  448    );
  449
  450    _ = editor.update(cx, |editor, window, cx| {
  451        editor.end_selection(window, cx);
  452    });
  453
  454    assert_eq!(
  455        editor
  456            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  457            .unwrap(),
  458        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  459    );
  460}
  461
  462#[gpui::test]
  463fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  464    init_test(cx, |_| {});
  465
  466    let editor = cx.add_window(|window, cx| {
  467        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  468        build_editor(buffer, window, cx)
  469    });
  470
  471    _ = editor.update(cx, |editor, window, cx| {
  472        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  473    });
  474
  475    _ = editor.update(cx, |editor, window, cx| {
  476        editor.end_selection(window, cx);
  477    });
  478
  479    _ = editor.update(cx, |editor, window, cx| {
  480        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  481    });
  482
  483    _ = editor.update(cx, |editor, window, cx| {
  484        editor.end_selection(window, cx);
  485    });
  486
  487    assert_eq!(
  488        editor
  489            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  490            .unwrap(),
  491        [
  492            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  493            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  494        ]
  495    );
  496
  497    _ = editor.update(cx, |editor, window, cx| {
  498        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  499    });
  500
  501    _ = editor.update(cx, |editor, window, cx| {
  502        editor.end_selection(window, cx);
  503    });
  504
  505    assert_eq!(
  506        editor
  507            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  508            .unwrap(),
  509        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  510    );
  511}
  512
  513#[gpui::test]
  514fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  515    init_test(cx, |_| {});
  516
  517    let editor = cx.add_window(|window, cx| {
  518        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  519        build_editor(buffer, window, cx)
  520    });
  521
  522    _ = editor.update(cx, |editor, window, cx| {
  523        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  524        assert_eq!(
  525            editor.selections.display_ranges(cx),
  526            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  527        );
  528    });
  529
  530    _ = editor.update(cx, |editor, window, cx| {
  531        editor.update_selection(
  532            DisplayPoint::new(DisplayRow(3), 3),
  533            0,
  534            gpui::Point::<f32>::default(),
  535            window,
  536            cx,
  537        );
  538        assert_eq!(
  539            editor.selections.display_ranges(cx),
  540            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  541        );
  542    });
  543
  544    _ = editor.update(cx, |editor, window, cx| {
  545        editor.cancel(&Cancel, window, cx);
  546        editor.update_selection(
  547            DisplayPoint::new(DisplayRow(1), 1),
  548            0,
  549            gpui::Point::<f32>::default(),
  550            window,
  551            cx,
  552        );
  553        assert_eq!(
  554            editor.selections.display_ranges(cx),
  555            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  556        );
  557    });
  558}
  559
  560#[gpui::test]
  561fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  562    init_test(cx, |_| {});
  563
  564    let editor = cx.add_window(|window, cx| {
  565        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  566        build_editor(buffer, window, cx)
  567    });
  568
  569    _ = editor.update(cx, |editor, window, cx| {
  570        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  571        assert_eq!(
  572            editor.selections.display_ranges(cx),
  573            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  574        );
  575
  576        editor.move_down(&Default::default(), window, cx);
  577        assert_eq!(
  578            editor.selections.display_ranges(cx),
  579            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  580        );
  581
  582        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  583        assert_eq!(
  584            editor.selections.display_ranges(cx),
  585            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  586        );
  587
  588        editor.move_up(&Default::default(), window, cx);
  589        assert_eq!(
  590            editor.selections.display_ranges(cx),
  591            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  592        );
  593    });
  594}
  595
  596#[gpui::test]
  597fn test_clone(cx: &mut TestAppContext) {
  598    init_test(cx, |_| {});
  599
  600    let (text, selection_ranges) = marked_text_ranges(
  601        indoc! {"
  602            one
  603            two
  604            threeˇ
  605            four
  606            fiveˇ
  607        "},
  608        true,
  609    );
  610
  611    let editor = cx.add_window(|window, cx| {
  612        let buffer = MultiBuffer::build_simple(&text, cx);
  613        build_editor(buffer, window, cx)
  614    });
  615
  616    _ = editor.update(cx, |editor, window, cx| {
  617        editor.change_selections(None, window, cx, |s| {
  618            s.select_ranges(selection_ranges.clone())
  619        });
  620        editor.fold_creases(
  621            vec![
  622                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  623                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  624            ],
  625            true,
  626            window,
  627            cx,
  628        );
  629    });
  630
  631    let cloned_editor = editor
  632        .update(cx, |editor, _, cx| {
  633            cx.open_window(Default::default(), |window, cx| {
  634                cx.new(|cx| editor.clone(window, cx))
  635            })
  636        })
  637        .unwrap()
  638        .unwrap();
  639
  640    let snapshot = editor
  641        .update(cx, |e, window, cx| e.snapshot(window, cx))
  642        .unwrap();
  643    let cloned_snapshot = cloned_editor
  644        .update(cx, |e, window, cx| e.snapshot(window, cx))
  645        .unwrap();
  646
  647    assert_eq!(
  648        cloned_editor
  649            .update(cx, |e, _, cx| e.display_text(cx))
  650            .unwrap(),
  651        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  652    );
  653    assert_eq!(
  654        cloned_snapshot
  655            .folds_in_range(0..text.len())
  656            .collect::<Vec<_>>(),
  657        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  658    );
  659    assert_set_eq!(
  660        cloned_editor
  661            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  662            .unwrap(),
  663        editor
  664            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  665            .unwrap()
  666    );
  667    assert_set_eq!(
  668        cloned_editor
  669            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  670            .unwrap(),
  671        editor
  672            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  673            .unwrap()
  674    );
  675}
  676
  677#[gpui::test]
  678async fn test_navigation_history(cx: &mut TestAppContext) {
  679    init_test(cx, |_| {});
  680
  681    use workspace::item::Item;
  682
  683    let fs = FakeFs::new(cx.executor());
  684    let project = Project::test(fs, [], cx).await;
  685    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  686    let pane = workspace
  687        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  688        .unwrap();
  689
  690    _ = workspace.update(cx, |_v, window, cx| {
  691        cx.new(|cx| {
  692            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  693            let mut editor = build_editor(buffer.clone(), window, cx);
  694            let handle = cx.entity();
  695            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  696
  697            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  698                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  699            }
  700
  701            // Move the cursor a small distance.
  702            // Nothing is added to the navigation history.
  703            editor.change_selections(None, window, cx, |s| {
  704                s.select_display_ranges([
  705                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  706                ])
  707            });
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  711                ])
  712            });
  713            assert!(pop_history(&mut editor, cx).is_none());
  714
  715            // Move the cursor a large distance.
  716            // The history can jump back to the previous position.
  717            editor.change_selections(None, window, cx, |s| {
  718                s.select_display_ranges([
  719                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  720                ])
  721            });
  722            let nav_entry = pop_history(&mut editor, cx).unwrap();
  723            editor.navigate(nav_entry.data.unwrap(), window, cx);
  724            assert_eq!(nav_entry.item.id(), cx.entity_id());
  725            assert_eq!(
  726                editor.selections.display_ranges(cx),
  727                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  728            );
  729            assert!(pop_history(&mut editor, cx).is_none());
  730
  731            // Move the cursor a small distance via the mouse.
  732            // Nothing is added to the navigation history.
  733            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  734            editor.end_selection(window, cx);
  735            assert_eq!(
  736                editor.selections.display_ranges(cx),
  737                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  738            );
  739            assert!(pop_history(&mut editor, cx).is_none());
  740
  741            // Move the cursor a large distance via the mouse.
  742            // The history can jump back to the previous position.
  743            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  744            editor.end_selection(window, cx);
  745            assert_eq!(
  746                editor.selections.display_ranges(cx),
  747                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  748            );
  749            let nav_entry = pop_history(&mut editor, cx).unwrap();
  750            editor.navigate(nav_entry.data.unwrap(), window, cx);
  751            assert_eq!(nav_entry.item.id(), cx.entity_id());
  752            assert_eq!(
  753                editor.selections.display_ranges(cx),
  754                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  755            );
  756            assert!(pop_history(&mut editor, cx).is_none());
  757
  758            // Set scroll position to check later
  759            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  760            let original_scroll_position = editor.scroll_manager.anchor();
  761
  762            // Jump to the end of the document and adjust scroll
  763            editor.move_to_end(&MoveToEnd, window, cx);
  764            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  765            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  766
  767            let nav_entry = pop_history(&mut editor, cx).unwrap();
  768            editor.navigate(nav_entry.data.unwrap(), window, cx);
  769            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  770
  771            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  772            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  773            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  774            let invalid_point = Point::new(9999, 0);
  775            editor.navigate(
  776                Box::new(NavigationData {
  777                    cursor_anchor: invalid_anchor,
  778                    cursor_position: invalid_point,
  779                    scroll_anchor: ScrollAnchor {
  780                        anchor: invalid_anchor,
  781                        offset: Default::default(),
  782                    },
  783                    scroll_top_row: invalid_point.row,
  784                }),
  785                window,
  786                cx,
  787            );
  788            assert_eq!(
  789                editor.selections.display_ranges(cx),
  790                &[editor.max_point(cx)..editor.max_point(cx)]
  791            );
  792            assert_eq!(
  793                editor.scroll_position(cx),
  794                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  795            );
  796
  797            editor
  798        })
  799    });
  800}
  801
  802#[gpui::test]
  803fn test_cancel(cx: &mut TestAppContext) {
  804    init_test(cx, |_| {});
  805
  806    let editor = cx.add_window(|window, cx| {
  807        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  808        build_editor(buffer, window, cx)
  809    });
  810
  811    _ = editor.update(cx, |editor, window, cx| {
  812        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  813        editor.update_selection(
  814            DisplayPoint::new(DisplayRow(1), 1),
  815            0,
  816            gpui::Point::<f32>::default(),
  817            window,
  818            cx,
  819        );
  820        editor.end_selection(window, cx);
  821
  822        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  823        editor.update_selection(
  824            DisplayPoint::new(DisplayRow(0), 3),
  825            0,
  826            gpui::Point::<f32>::default(),
  827            window,
  828            cx,
  829        );
  830        editor.end_selection(window, cx);
  831        assert_eq!(
  832            editor.selections.display_ranges(cx),
  833            [
  834                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  835                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  836            ]
  837        );
  838    });
  839
  840    _ = editor.update(cx, |editor, window, cx| {
  841        editor.cancel(&Cancel, window, cx);
  842        assert_eq!(
  843            editor.selections.display_ranges(cx),
  844            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  845        );
  846    });
  847
  848    _ = editor.update(cx, |editor, window, cx| {
  849        editor.cancel(&Cancel, window, cx);
  850        assert_eq!(
  851            editor.selections.display_ranges(cx),
  852            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  853        );
  854    });
  855}
  856
  857#[gpui::test]
  858fn test_fold_action(cx: &mut TestAppContext) {
  859    init_test(cx, |_| {});
  860
  861    let editor = cx.add_window(|window, cx| {
  862        let buffer = MultiBuffer::build_simple(
  863            &"
  864                impl Foo {
  865                    // Hello!
  866
  867                    fn a() {
  868                        1
  869                    }
  870
  871                    fn b() {
  872                        2
  873                    }
  874
  875                    fn c() {
  876                        3
  877                    }
  878                }
  879            "
  880            .unindent(),
  881            cx,
  882        );
  883        build_editor(buffer.clone(), window, cx)
  884    });
  885
  886    _ = editor.update(cx, |editor, window, cx| {
  887        editor.change_selections(None, window, cx, |s| {
  888            s.select_display_ranges([
  889                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  890            ]);
  891        });
  892        editor.fold(&Fold, window, cx);
  893        assert_eq!(
  894            editor.display_text(cx),
  895            "
  896                impl Foo {
  897                    // Hello!
  898
  899                    fn a() {
  900                        1
  901                    }
  902
  903                    fn b() {⋯
  904                    }
  905
  906                    fn c() {⋯
  907                    }
  908                }
  909            "
  910            .unindent(),
  911        );
  912
  913        editor.fold(&Fold, window, cx);
  914        assert_eq!(
  915            editor.display_text(cx),
  916            "
  917                impl Foo {⋯
  918                }
  919            "
  920            .unindent(),
  921        );
  922
  923        editor.unfold_lines(&UnfoldLines, window, cx);
  924        assert_eq!(
  925            editor.display_text(cx),
  926            "
  927                impl Foo {
  928                    // Hello!
  929
  930                    fn a() {
  931                        1
  932                    }
  933
  934                    fn b() {⋯
  935                    }
  936
  937                    fn c() {⋯
  938                    }
  939                }
  940            "
  941            .unindent(),
  942        );
  943
  944        editor.unfold_lines(&UnfoldLines, window, cx);
  945        assert_eq!(
  946            editor.display_text(cx),
  947            editor.buffer.read(cx).read(cx).text()
  948        );
  949    });
  950}
  951
  952#[gpui::test]
  953fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  954    init_test(cx, |_| {});
  955
  956    let editor = cx.add_window(|window, cx| {
  957        let buffer = MultiBuffer::build_simple(
  958            &"
  959                class Foo:
  960                    # Hello!
  961
  962                    def a():
  963                        print(1)
  964
  965                    def b():
  966                        print(2)
  967
  968                    def c():
  969                        print(3)
  970            "
  971            .unindent(),
  972            cx,
  973        );
  974        build_editor(buffer.clone(), window, cx)
  975    });
  976
  977    _ = editor.update(cx, |editor, window, cx| {
  978        editor.change_selections(None, window, cx, |s| {
  979            s.select_display_ranges([
  980                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  981            ]);
  982        });
  983        editor.fold(&Fold, window, cx);
  984        assert_eq!(
  985            editor.display_text(cx),
  986            "
  987                class Foo:
  988                    # Hello!
  989
  990                    def a():
  991                        print(1)
  992
  993                    def b():⋯
  994
  995                    def c():⋯
  996            "
  997            .unindent(),
  998        );
  999
 1000        editor.fold(&Fold, window, cx);
 1001        assert_eq!(
 1002            editor.display_text(cx),
 1003            "
 1004                class Foo:⋯
 1005            "
 1006            .unindent(),
 1007        );
 1008
 1009        editor.unfold_lines(&UnfoldLines, window, cx);
 1010        assert_eq!(
 1011            editor.display_text(cx),
 1012            "
 1013                class Foo:
 1014                    # Hello!
 1015
 1016                    def a():
 1017                        print(1)
 1018
 1019                    def b():⋯
 1020
 1021                    def c():⋯
 1022            "
 1023            .unindent(),
 1024        );
 1025
 1026        editor.unfold_lines(&UnfoldLines, window, cx);
 1027        assert_eq!(
 1028            editor.display_text(cx),
 1029            editor.buffer.read(cx).read(cx).text()
 1030        );
 1031    });
 1032}
 1033
 1034#[gpui::test]
 1035fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1036    init_test(cx, |_| {});
 1037
 1038    let editor = cx.add_window(|window, cx| {
 1039        let buffer = MultiBuffer::build_simple(
 1040            &"
 1041                class Foo:
 1042                    # Hello!
 1043
 1044                    def a():
 1045                        print(1)
 1046
 1047                    def b():
 1048                        print(2)
 1049
 1050
 1051                    def c():
 1052                        print(3)
 1053
 1054
 1055            "
 1056            .unindent(),
 1057            cx,
 1058        );
 1059        build_editor(buffer.clone(), window, cx)
 1060    });
 1061
 1062    _ = editor.update(cx, |editor, window, cx| {
 1063        editor.change_selections(None, window, cx, |s| {
 1064            s.select_display_ranges([
 1065                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1066            ]);
 1067        });
 1068        editor.fold(&Fold, window, cx);
 1069        assert_eq!(
 1070            editor.display_text(cx),
 1071            "
 1072                class Foo:
 1073                    # Hello!
 1074
 1075                    def a():
 1076                        print(1)
 1077
 1078                    def b():⋯
 1079
 1080
 1081                    def c():⋯
 1082
 1083
 1084            "
 1085            .unindent(),
 1086        );
 1087
 1088        editor.fold(&Fold, window, cx);
 1089        assert_eq!(
 1090            editor.display_text(cx),
 1091            "
 1092                class Foo:⋯
 1093
 1094
 1095            "
 1096            .unindent(),
 1097        );
 1098
 1099        editor.unfold_lines(&UnfoldLines, window, cx);
 1100        assert_eq!(
 1101            editor.display_text(cx),
 1102            "
 1103                class Foo:
 1104                    # Hello!
 1105
 1106                    def a():
 1107                        print(1)
 1108
 1109                    def b():⋯
 1110
 1111
 1112                    def c():⋯
 1113
 1114
 1115            "
 1116            .unindent(),
 1117        );
 1118
 1119        editor.unfold_lines(&UnfoldLines, window, cx);
 1120        assert_eq!(
 1121            editor.display_text(cx),
 1122            editor.buffer.read(cx).read(cx).text()
 1123        );
 1124    });
 1125}
 1126
 1127#[gpui::test]
 1128fn test_fold_at_level(cx: &mut TestAppContext) {
 1129    init_test(cx, |_| {});
 1130
 1131    let editor = cx.add_window(|window, cx| {
 1132        let buffer = MultiBuffer::build_simple(
 1133            &"
 1134                class Foo:
 1135                    # Hello!
 1136
 1137                    def a():
 1138                        print(1)
 1139
 1140                    def b():
 1141                        print(2)
 1142
 1143
 1144                class Bar:
 1145                    # World!
 1146
 1147                    def a():
 1148                        print(1)
 1149
 1150                    def b():
 1151                        print(2)
 1152
 1153
 1154            "
 1155            .unindent(),
 1156            cx,
 1157        );
 1158        build_editor(buffer.clone(), window, cx)
 1159    });
 1160
 1161    _ = editor.update(cx, |editor, window, cx| {
 1162        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1163        assert_eq!(
 1164            editor.display_text(cx),
 1165            "
 1166                class Foo:
 1167                    # Hello!
 1168
 1169                    def a():⋯
 1170
 1171                    def b():⋯
 1172
 1173
 1174                class Bar:
 1175                    # World!
 1176
 1177                    def a():⋯
 1178
 1179                    def b():⋯
 1180
 1181
 1182            "
 1183            .unindent(),
 1184        );
 1185
 1186        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1187        assert_eq!(
 1188            editor.display_text(cx),
 1189            "
 1190                class Foo:⋯
 1191
 1192
 1193                class Bar:⋯
 1194
 1195
 1196            "
 1197            .unindent(),
 1198        );
 1199
 1200        editor.unfold_all(&UnfoldAll, window, cx);
 1201        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1202        assert_eq!(
 1203            editor.display_text(cx),
 1204            "
 1205                class Foo:
 1206                    # Hello!
 1207
 1208                    def a():
 1209                        print(1)
 1210
 1211                    def b():
 1212                        print(2)
 1213
 1214
 1215                class Bar:
 1216                    # World!
 1217
 1218                    def a():
 1219                        print(1)
 1220
 1221                    def b():
 1222                        print(2)
 1223
 1224
 1225            "
 1226            .unindent(),
 1227        );
 1228
 1229        assert_eq!(
 1230            editor.display_text(cx),
 1231            editor.buffer.read(cx).read(cx).text()
 1232        );
 1233    });
 1234}
 1235
 1236#[gpui::test]
 1237fn test_move_cursor(cx: &mut TestAppContext) {
 1238    init_test(cx, |_| {});
 1239
 1240    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1241    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1242
 1243    buffer.update(cx, |buffer, cx| {
 1244        buffer.edit(
 1245            vec![
 1246                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1247                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1248            ],
 1249            None,
 1250            cx,
 1251        );
 1252    });
 1253    _ = editor.update(cx, |editor, window, cx| {
 1254        assert_eq!(
 1255            editor.selections.display_ranges(cx),
 1256            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1257        );
 1258
 1259        editor.move_down(&MoveDown, window, cx);
 1260        assert_eq!(
 1261            editor.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1263        );
 1264
 1265        editor.move_right(&MoveRight, window, cx);
 1266        assert_eq!(
 1267            editor.selections.display_ranges(cx),
 1268            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1269        );
 1270
 1271        editor.move_left(&MoveLeft, window, cx);
 1272        assert_eq!(
 1273            editor.selections.display_ranges(cx),
 1274            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1275        );
 1276
 1277        editor.move_up(&MoveUp, window, cx);
 1278        assert_eq!(
 1279            editor.selections.display_ranges(cx),
 1280            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1281        );
 1282
 1283        editor.move_to_end(&MoveToEnd, window, cx);
 1284        assert_eq!(
 1285            editor.selections.display_ranges(cx),
 1286            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1287        );
 1288
 1289        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1290        assert_eq!(
 1291            editor.selections.display_ranges(cx),
 1292            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1293        );
 1294
 1295        editor.change_selections(None, window, cx, |s| {
 1296            s.select_display_ranges([
 1297                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1298            ]);
 1299        });
 1300        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1301        assert_eq!(
 1302            editor.selections.display_ranges(cx),
 1303            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1304        );
 1305
 1306        editor.select_to_end(&SelectToEnd, window, cx);
 1307        assert_eq!(
 1308            editor.selections.display_ranges(cx),
 1309            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1310        );
 1311    });
 1312}
 1313
 1314// TODO: Re-enable this test
 1315#[cfg(target_os = "macos")]
 1316#[gpui::test]
 1317fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1318    init_test(cx, |_| {});
 1319
 1320    let editor = cx.add_window(|window, cx| {
 1321        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1322        build_editor(buffer.clone(), window, cx)
 1323    });
 1324
 1325    assert_eq!('🟥'.len_utf8(), 4);
 1326    assert_eq!('α'.len_utf8(), 2);
 1327
 1328    _ = editor.update(cx, |editor, window, cx| {
 1329        editor.fold_creases(
 1330            vec![
 1331                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1332                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1333                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1334            ],
 1335            true,
 1336            window,
 1337            cx,
 1338        );
 1339        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1340
 1341        editor.move_right(&MoveRight, window, cx);
 1342        assert_eq!(
 1343            editor.selections.display_ranges(cx),
 1344            &[empty_range(0, "🟥".len())]
 1345        );
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥🟧".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧⋯".len())]
 1355        );
 1356
 1357        editor.move_down(&MoveDown, window, cx);
 1358        assert_eq!(
 1359            editor.selections.display_ranges(cx),
 1360            &[empty_range(1, "ab⋯e".len())]
 1361        );
 1362        editor.move_left(&MoveLeft, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "a".len())]
 1376        );
 1377
 1378        editor.move_down(&MoveDown, window, cx);
 1379        assert_eq!(
 1380            editor.selections.display_ranges(cx),
 1381            &[empty_range(2, "α".len())]
 1382        );
 1383        editor.move_right(&MoveRight, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "αβ".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ⋯".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯ε".len())]
 1397        );
 1398
 1399        editor.move_up(&MoveUp, window, cx);
 1400        assert_eq!(
 1401            editor.selections.display_ranges(cx),
 1402            &[empty_range(1, "ab⋯e".len())]
 1403        );
 1404        editor.move_down(&MoveDown, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(2, "αβ⋯ε".len())]
 1408        );
 1409        editor.move_up(&MoveUp, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(1, "ab⋯e".len())]
 1413        );
 1414
 1415        editor.move_up(&MoveUp, window, cx);
 1416        assert_eq!(
 1417            editor.selections.display_ranges(cx),
 1418            &[empty_range(0, "🟥🟧".len())]
 1419        );
 1420        editor.move_left(&MoveLeft, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "".len())]
 1429        );
 1430    });
 1431}
 1432
 1433#[gpui::test]
 1434fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1435    init_test(cx, |_| {});
 1436
 1437    let editor = cx.add_window(|window, cx| {
 1438        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1439        build_editor(buffer.clone(), window, cx)
 1440    });
 1441    _ = editor.update(cx, |editor, window, cx| {
 1442        editor.change_selections(None, window, cx, |s| {
 1443            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1444        });
 1445
 1446        // moving above start of document should move selection to start of document,
 1447        // but the next move down should still be at the original goal_x
 1448        editor.move_up(&MoveUp, window, cx);
 1449        assert_eq!(
 1450            editor.selections.display_ranges(cx),
 1451            &[empty_range(0, "".len())]
 1452        );
 1453
 1454        editor.move_down(&MoveDown, window, cx);
 1455        assert_eq!(
 1456            editor.selections.display_ranges(cx),
 1457            &[empty_range(1, "abcd".len())]
 1458        );
 1459
 1460        editor.move_down(&MoveDown, window, cx);
 1461        assert_eq!(
 1462            editor.selections.display_ranges(cx),
 1463            &[empty_range(2, "αβγ".len())]
 1464        );
 1465
 1466        editor.move_down(&MoveDown, window, cx);
 1467        assert_eq!(
 1468            editor.selections.display_ranges(cx),
 1469            &[empty_range(3, "abcd".len())]
 1470        );
 1471
 1472        editor.move_down(&MoveDown, window, cx);
 1473        assert_eq!(
 1474            editor.selections.display_ranges(cx),
 1475            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1476        );
 1477
 1478        // moving past end of document should not change goal_x
 1479        editor.move_down(&MoveDown, window, cx);
 1480        assert_eq!(
 1481            editor.selections.display_ranges(cx),
 1482            &[empty_range(5, "".len())]
 1483        );
 1484
 1485        editor.move_down(&MoveDown, window, cx);
 1486        assert_eq!(
 1487            editor.selections.display_ranges(cx),
 1488            &[empty_range(5, "".len())]
 1489        );
 1490
 1491        editor.move_up(&MoveUp, window, cx);
 1492        assert_eq!(
 1493            editor.selections.display_ranges(cx),
 1494            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1495        );
 1496
 1497        editor.move_up(&MoveUp, window, cx);
 1498        assert_eq!(
 1499            editor.selections.display_ranges(cx),
 1500            &[empty_range(3, "abcd".len())]
 1501        );
 1502
 1503        editor.move_up(&MoveUp, window, cx);
 1504        assert_eq!(
 1505            editor.selections.display_ranges(cx),
 1506            &[empty_range(2, "αβγ".len())]
 1507        );
 1508    });
 1509}
 1510
 1511#[gpui::test]
 1512fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1513    init_test(cx, |_| {});
 1514    let move_to_beg = MoveToBeginningOfLine {
 1515        stop_at_soft_wraps: true,
 1516    };
 1517
 1518    let move_to_end = MoveToEndOfLine {
 1519        stop_at_soft_wraps: true,
 1520    };
 1521
 1522    let editor = cx.add_window(|window, cx| {
 1523        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1524        build_editor(buffer, window, cx)
 1525    });
 1526    _ = editor.update(cx, |editor, window, cx| {
 1527        editor.change_selections(None, window, cx, |s| {
 1528            s.select_display_ranges([
 1529                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1530                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1531            ]);
 1532        });
 1533    });
 1534
 1535    _ = editor.update(cx, |editor, window, cx| {
 1536        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1537        assert_eq!(
 1538            editor.selections.display_ranges(cx),
 1539            &[
 1540                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1541                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1542            ]
 1543        );
 1544    });
 1545
 1546    _ = editor.update(cx, |editor, window, cx| {
 1547        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1548        assert_eq!(
 1549            editor.selections.display_ranges(cx),
 1550            &[
 1551                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1552                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1553            ]
 1554        );
 1555    });
 1556
 1557    _ = editor.update(cx, |editor, window, cx| {
 1558        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1559        assert_eq!(
 1560            editor.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = editor.update(cx, |editor, window, cx| {
 1569        editor.move_to_end_of_line(&move_to_end, window, cx);
 1570        assert_eq!(
 1571            editor.selections.display_ranges(cx),
 1572            &[
 1573                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1574                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1575            ]
 1576        );
 1577    });
 1578
 1579    // Moving to the end of line again is a no-op.
 1580    _ = editor.update(cx, |editor, window, cx| {
 1581        editor.move_to_end_of_line(&move_to_end, window, cx);
 1582        assert_eq!(
 1583            editor.selections.display_ranges(cx),
 1584            &[
 1585                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1586                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1587            ]
 1588        );
 1589    });
 1590
 1591    _ = editor.update(cx, |editor, window, cx| {
 1592        editor.move_left(&MoveLeft, window, cx);
 1593        editor.select_to_beginning_of_line(
 1594            &SelectToBeginningOfLine {
 1595                stop_at_soft_wraps: true,
 1596            },
 1597            window,
 1598            cx,
 1599        );
 1600        assert_eq!(
 1601            editor.selections.display_ranges(cx),
 1602            &[
 1603                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1604                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1605            ]
 1606        );
 1607    });
 1608
 1609    _ = editor.update(cx, |editor, window, cx| {
 1610        editor.select_to_beginning_of_line(
 1611            &SelectToBeginningOfLine {
 1612                stop_at_soft_wraps: true,
 1613            },
 1614            window,
 1615            cx,
 1616        );
 1617        assert_eq!(
 1618            editor.selections.display_ranges(cx),
 1619            &[
 1620                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1621                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1622            ]
 1623        );
 1624    });
 1625
 1626    _ = editor.update(cx, |editor, window, cx| {
 1627        editor.select_to_beginning_of_line(
 1628            &SelectToBeginningOfLine {
 1629                stop_at_soft_wraps: true,
 1630            },
 1631            window,
 1632            cx,
 1633        );
 1634        assert_eq!(
 1635            editor.selections.display_ranges(cx),
 1636            &[
 1637                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1638                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1639            ]
 1640        );
 1641    });
 1642
 1643    _ = editor.update(cx, |editor, window, cx| {
 1644        editor.select_to_end_of_line(
 1645            &SelectToEndOfLine {
 1646                stop_at_soft_wraps: true,
 1647            },
 1648            window,
 1649            cx,
 1650        );
 1651        assert_eq!(
 1652            editor.selections.display_ranges(cx),
 1653            &[
 1654                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1655                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1656            ]
 1657        );
 1658    });
 1659
 1660    _ = editor.update(cx, |editor, window, cx| {
 1661        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1662        assert_eq!(editor.display_text(cx), "ab\n  de");
 1663        assert_eq!(
 1664            editor.selections.display_ranges(cx),
 1665            &[
 1666                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1667                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1668            ]
 1669        );
 1670    });
 1671
 1672    _ = editor.update(cx, |editor, window, cx| {
 1673        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1674        assert_eq!(editor.display_text(cx), "\n");
 1675        assert_eq!(
 1676            editor.selections.display_ranges(cx),
 1677            &[
 1678                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1679                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1680            ]
 1681        );
 1682    });
 1683}
 1684
 1685#[gpui::test]
 1686fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1687    init_test(cx, |_| {});
 1688    let move_to_beg = MoveToBeginningOfLine {
 1689        stop_at_soft_wraps: false,
 1690    };
 1691
 1692    let move_to_end = MoveToEndOfLine {
 1693        stop_at_soft_wraps: false,
 1694    };
 1695
 1696    let editor = cx.add_window(|window, cx| {
 1697        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1698        build_editor(buffer, window, cx)
 1699    });
 1700
 1701    _ = editor.update(cx, |editor, window, cx| {
 1702        editor.set_wrap_width(Some(140.0.into()), cx);
 1703
 1704        // We expect the following lines after wrapping
 1705        // ```
 1706        // thequickbrownfox
 1707        // jumpedoverthelazydo
 1708        // gs
 1709        // ```
 1710        // The final `gs` was soft-wrapped onto a new line.
 1711        assert_eq!(
 1712            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1713            editor.display_text(cx),
 1714        );
 1715
 1716        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1717        // Start the cursor at the `k` on the first line
 1718        editor.change_selections(None, window, cx, |s| {
 1719            s.select_display_ranges([
 1720                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1721            ]);
 1722        });
 1723
 1724        // Moving to the beginning of the line should put us at the beginning of the line.
 1725        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1726        assert_eq!(
 1727            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1728            editor.selections.display_ranges(cx)
 1729        );
 1730
 1731        // Moving to the end of the line should put us at the end of the line.
 1732        editor.move_to_end_of_line(&move_to_end, window, cx);
 1733        assert_eq!(
 1734            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1735            editor.selections.display_ranges(cx)
 1736        );
 1737
 1738        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1739        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1740        editor.change_selections(None, window, cx, |s| {
 1741            s.select_display_ranges([
 1742                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1743            ]);
 1744        });
 1745
 1746        // Moving to the beginning of the line should put us at the start of the second line of
 1747        // display text, i.e., the `j`.
 1748        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1749        assert_eq!(
 1750            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1751            editor.selections.display_ranges(cx)
 1752        );
 1753
 1754        // Moving to the beginning of the line again should be a no-op.
 1755        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1756        assert_eq!(
 1757            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1758            editor.selections.display_ranges(cx)
 1759        );
 1760
 1761        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1762        // next display line.
 1763        editor.move_to_end_of_line(&move_to_end, window, cx);
 1764        assert_eq!(
 1765            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1766            editor.selections.display_ranges(cx)
 1767        );
 1768
 1769        // Moving to the end of the line again should be a no-op.
 1770        editor.move_to_end_of_line(&move_to_end, window, cx);
 1771        assert_eq!(
 1772            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1773            editor.selections.display_ranges(cx)
 1774        );
 1775    });
 1776}
 1777
 1778#[gpui::test]
 1779fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1780    init_test(cx, |_| {});
 1781
 1782    let editor = cx.add_window(|window, cx| {
 1783        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1784        build_editor(buffer, window, cx)
 1785    });
 1786    _ = editor.update(cx, |editor, window, cx| {
 1787        editor.change_selections(None, window, cx, |s| {
 1788            s.select_display_ranges([
 1789                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1790                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1791            ])
 1792        });
 1793
 1794        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1795        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1796
 1797        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1798        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1799
 1800        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1801        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1802
 1803        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1804        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1805
 1806        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1807        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1808
 1809        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1810        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1811
 1812        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1813        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1814
 1815        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1816        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1817
 1818        editor.move_right(&MoveRight, window, cx);
 1819        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1820        assert_selection_ranges(
 1821            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1822            editor,
 1823            cx,
 1824        );
 1825
 1826        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1827        assert_selection_ranges(
 1828            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1829            editor,
 1830            cx,
 1831        );
 1832
 1833        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1834        assert_selection_ranges(
 1835            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1836            editor,
 1837            cx,
 1838        );
 1839    });
 1840}
 1841
 1842#[gpui::test]
 1843fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1844    init_test(cx, |_| {});
 1845
 1846    let editor = cx.add_window(|window, cx| {
 1847        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1848        build_editor(buffer, window, cx)
 1849    });
 1850
 1851    _ = editor.update(cx, |editor, window, cx| {
 1852        editor.set_wrap_width(Some(140.0.into()), cx);
 1853        assert_eq!(
 1854            editor.display_text(cx),
 1855            "use one::{\n    two::three::\n    four::five\n};"
 1856        );
 1857
 1858        editor.change_selections(None, window, cx, |s| {
 1859            s.select_display_ranges([
 1860                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1861            ]);
 1862        });
 1863
 1864        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1868        );
 1869
 1870        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1871        assert_eq!(
 1872            editor.selections.display_ranges(cx),
 1873            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1874        );
 1875
 1876        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1877        assert_eq!(
 1878            editor.selections.display_ranges(cx),
 1879            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1880        );
 1881
 1882        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1883        assert_eq!(
 1884            editor.selections.display_ranges(cx),
 1885            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1886        );
 1887
 1888        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1889        assert_eq!(
 1890            editor.selections.display_ranges(cx),
 1891            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1892        );
 1893
 1894        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1895        assert_eq!(
 1896            editor.selections.display_ranges(cx),
 1897            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1898        );
 1899    });
 1900}
 1901
 1902#[gpui::test]
 1903async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1904    init_test(cx, |_| {});
 1905    let mut cx = EditorTestContext::new(cx).await;
 1906
 1907    let line_height = cx.editor(|editor, window, _| {
 1908        editor
 1909            .style()
 1910            .unwrap()
 1911            .text
 1912            .line_height_in_pixels(window.rem_size())
 1913    });
 1914    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1915
 1916    cx.set_state(
 1917        &r#"ˇone
 1918        two
 1919
 1920        three
 1921        fourˇ
 1922        five
 1923
 1924        six"#
 1925            .unindent(),
 1926    );
 1927
 1928    cx.update_editor(|editor, window, cx| {
 1929        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1930    });
 1931    cx.assert_editor_state(
 1932        &r#"one
 1933        two
 1934        ˇ
 1935        three
 1936        four
 1937        five
 1938        ˇ
 1939        six"#
 1940            .unindent(),
 1941    );
 1942
 1943    cx.update_editor(|editor, window, cx| {
 1944        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1945    });
 1946    cx.assert_editor_state(
 1947        &r#"one
 1948        two
 1949
 1950        three
 1951        four
 1952        five
 1953        ˇ
 1954        sixˇ"#
 1955            .unindent(),
 1956    );
 1957
 1958    cx.update_editor(|editor, window, cx| {
 1959        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1960    });
 1961    cx.assert_editor_state(
 1962        &r#"one
 1963        two
 1964
 1965        three
 1966        four
 1967        five
 1968
 1969        sixˇ"#
 1970            .unindent(),
 1971    );
 1972
 1973    cx.update_editor(|editor, window, cx| {
 1974        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1975    });
 1976    cx.assert_editor_state(
 1977        &r#"one
 1978        two
 1979
 1980        three
 1981        four
 1982        five
 1983        ˇ
 1984        six"#
 1985            .unindent(),
 1986    );
 1987
 1988    cx.update_editor(|editor, window, cx| {
 1989        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1990    });
 1991    cx.assert_editor_state(
 1992        &r#"one
 1993        two
 1994        ˇ
 1995        three
 1996        four
 1997        five
 1998
 1999        six"#
 2000            .unindent(),
 2001    );
 2002
 2003    cx.update_editor(|editor, window, cx| {
 2004        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2005    });
 2006    cx.assert_editor_state(
 2007        &r#"ˇone
 2008        two
 2009
 2010        three
 2011        four
 2012        five
 2013
 2014        six"#
 2015            .unindent(),
 2016    );
 2017}
 2018
 2019#[gpui::test]
 2020async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2021    init_test(cx, |_| {});
 2022    let mut cx = EditorTestContext::new(cx).await;
 2023    let line_height = cx.editor(|editor, window, _| {
 2024        editor
 2025            .style()
 2026            .unwrap()
 2027            .text
 2028            .line_height_in_pixels(window.rem_size())
 2029    });
 2030    let window = cx.window;
 2031    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2032
 2033    cx.set_state(
 2034        r#"ˇone
 2035        two
 2036        three
 2037        four
 2038        five
 2039        six
 2040        seven
 2041        eight
 2042        nine
 2043        ten
 2044        "#,
 2045    );
 2046
 2047    cx.update_editor(|editor, window, cx| {
 2048        assert_eq!(
 2049            editor.snapshot(window, cx).scroll_position(),
 2050            gpui::Point::new(0., 0.)
 2051        );
 2052        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2053        assert_eq!(
 2054            editor.snapshot(window, cx).scroll_position(),
 2055            gpui::Point::new(0., 3.)
 2056        );
 2057        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2058        assert_eq!(
 2059            editor.snapshot(window, cx).scroll_position(),
 2060            gpui::Point::new(0., 6.)
 2061        );
 2062        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2063        assert_eq!(
 2064            editor.snapshot(window, cx).scroll_position(),
 2065            gpui::Point::new(0., 3.)
 2066        );
 2067
 2068        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2069        assert_eq!(
 2070            editor.snapshot(window, cx).scroll_position(),
 2071            gpui::Point::new(0., 1.)
 2072        );
 2073        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2074        assert_eq!(
 2075            editor.snapshot(window, cx).scroll_position(),
 2076            gpui::Point::new(0., 3.)
 2077        );
 2078    });
 2079}
 2080
 2081#[gpui::test]
 2082async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2083    init_test(cx, |_| {});
 2084    let mut cx = EditorTestContext::new(cx).await;
 2085
 2086    let line_height = cx.update_editor(|editor, window, cx| {
 2087        editor.set_vertical_scroll_margin(2, cx);
 2088        editor
 2089            .style()
 2090            .unwrap()
 2091            .text
 2092            .line_height_in_pixels(window.rem_size())
 2093    });
 2094    let window = cx.window;
 2095    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2096
 2097    cx.set_state(
 2098        r#"ˇone
 2099            two
 2100            three
 2101            four
 2102            five
 2103            six
 2104            seven
 2105            eight
 2106            nine
 2107            ten
 2108        "#,
 2109    );
 2110    cx.update_editor(|editor, window, cx| {
 2111        assert_eq!(
 2112            editor.snapshot(window, cx).scroll_position(),
 2113            gpui::Point::new(0., 0.0)
 2114        );
 2115    });
 2116
 2117    // Add a cursor below the visible area. Since both cursors cannot fit
 2118    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2119    // allows the vertical scroll margin below that cursor.
 2120    cx.update_editor(|editor, window, cx| {
 2121        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2122            selections.select_ranges([
 2123                Point::new(0, 0)..Point::new(0, 0),
 2124                Point::new(6, 0)..Point::new(6, 0),
 2125            ]);
 2126        })
 2127    });
 2128    cx.update_editor(|editor, window, cx| {
 2129        assert_eq!(
 2130            editor.snapshot(window, cx).scroll_position(),
 2131            gpui::Point::new(0., 3.0)
 2132        );
 2133    });
 2134
 2135    // Move down. The editor cursor scrolls down to track the newest cursor.
 2136    cx.update_editor(|editor, window, cx| {
 2137        editor.move_down(&Default::default(), window, cx);
 2138    });
 2139    cx.update_editor(|editor, window, cx| {
 2140        assert_eq!(
 2141            editor.snapshot(window, cx).scroll_position(),
 2142            gpui::Point::new(0., 4.0)
 2143        );
 2144    });
 2145
 2146    // Add a cursor above the visible area. Since both cursors fit on screen,
 2147    // the editor scrolls to show both.
 2148    cx.update_editor(|editor, window, cx| {
 2149        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2150            selections.select_ranges([
 2151                Point::new(1, 0)..Point::new(1, 0),
 2152                Point::new(6, 0)..Point::new(6, 0),
 2153            ]);
 2154        })
 2155    });
 2156    cx.update_editor(|editor, window, cx| {
 2157        assert_eq!(
 2158            editor.snapshot(window, cx).scroll_position(),
 2159            gpui::Point::new(0., 1.0)
 2160        );
 2161    });
 2162}
 2163
 2164#[gpui::test]
 2165async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2166    init_test(cx, |_| {});
 2167    let mut cx = EditorTestContext::new(cx).await;
 2168
 2169    let line_height = cx.editor(|editor, window, _cx| {
 2170        editor
 2171            .style()
 2172            .unwrap()
 2173            .text
 2174            .line_height_in_pixels(window.rem_size())
 2175    });
 2176    let window = cx.window;
 2177    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2178    cx.set_state(
 2179        &r#"
 2180        ˇone
 2181        two
 2182        threeˇ
 2183        four
 2184        five
 2185        six
 2186        seven
 2187        eight
 2188        nine
 2189        ten
 2190        "#
 2191        .unindent(),
 2192    );
 2193
 2194    cx.update_editor(|editor, window, cx| {
 2195        editor.move_page_down(&MovePageDown::default(), window, cx)
 2196    });
 2197    cx.assert_editor_state(
 2198        &r#"
 2199        one
 2200        two
 2201        three
 2202        ˇfour
 2203        five
 2204        sixˇ
 2205        seven
 2206        eight
 2207        nine
 2208        ten
 2209        "#
 2210        .unindent(),
 2211    );
 2212
 2213    cx.update_editor(|editor, window, cx| {
 2214        editor.move_page_down(&MovePageDown::default(), window, cx)
 2215    });
 2216    cx.assert_editor_state(
 2217        &r#"
 2218        one
 2219        two
 2220        three
 2221        four
 2222        five
 2223        six
 2224        ˇseven
 2225        eight
 2226        nineˇ
 2227        ten
 2228        "#
 2229        .unindent(),
 2230    );
 2231
 2232    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2233    cx.assert_editor_state(
 2234        &r#"
 2235        one
 2236        two
 2237        three
 2238        ˇfour
 2239        five
 2240        sixˇ
 2241        seven
 2242        eight
 2243        nine
 2244        ten
 2245        "#
 2246        .unindent(),
 2247    );
 2248
 2249    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2250    cx.assert_editor_state(
 2251        &r#"
 2252        ˇone
 2253        two
 2254        threeˇ
 2255        four
 2256        five
 2257        six
 2258        seven
 2259        eight
 2260        nine
 2261        ten
 2262        "#
 2263        .unindent(),
 2264    );
 2265
 2266    // Test select collapsing
 2267    cx.update_editor(|editor, window, cx| {
 2268        editor.move_page_down(&MovePageDown::default(), window, cx);
 2269        editor.move_page_down(&MovePageDown::default(), window, cx);
 2270        editor.move_page_down(&MovePageDown::default(), window, cx);
 2271    });
 2272    cx.assert_editor_state(
 2273        &r#"
 2274        one
 2275        two
 2276        three
 2277        four
 2278        five
 2279        six
 2280        seven
 2281        eight
 2282        nine
 2283        ˇten
 2284        ˇ"#
 2285        .unindent(),
 2286    );
 2287}
 2288
 2289#[gpui::test]
 2290async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2291    init_test(cx, |_| {});
 2292    let mut cx = EditorTestContext::new(cx).await;
 2293    cx.set_state("one «two threeˇ» four");
 2294    cx.update_editor(|editor, window, cx| {
 2295        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2296        assert_eq!(editor.text(cx), " four");
 2297    });
 2298}
 2299
 2300#[gpui::test]
 2301fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2302    init_test(cx, |_| {});
 2303
 2304    let editor = cx.add_window(|window, cx| {
 2305        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2306        build_editor(buffer.clone(), window, cx)
 2307    });
 2308
 2309    _ = editor.update(cx, |editor, window, cx| {
 2310        editor.change_selections(None, window, cx, |s| {
 2311            s.select_display_ranges([
 2312                // an empty selection - the preceding word fragment is deleted
 2313                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2314                // characters selected - they are deleted
 2315                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2316            ])
 2317        });
 2318        editor.delete_to_previous_word_start(
 2319            &DeleteToPreviousWordStart {
 2320                ignore_newlines: false,
 2321            },
 2322            window,
 2323            cx,
 2324        );
 2325        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2326    });
 2327
 2328    _ = editor.update(cx, |editor, window, cx| {
 2329        editor.change_selections(None, window, cx, |s| {
 2330            s.select_display_ranges([
 2331                // an empty selection - the following word fragment is deleted
 2332                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2333                // characters selected - they are deleted
 2334                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2335            ])
 2336        });
 2337        editor.delete_to_next_word_end(
 2338            &DeleteToNextWordEnd {
 2339                ignore_newlines: false,
 2340            },
 2341            window,
 2342            cx,
 2343        );
 2344        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2345    });
 2346}
 2347
 2348#[gpui::test]
 2349fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2350    init_test(cx, |_| {});
 2351
 2352    let editor = cx.add_window(|window, cx| {
 2353        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2354        build_editor(buffer.clone(), window, cx)
 2355    });
 2356    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2357        ignore_newlines: false,
 2358    };
 2359    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2360        ignore_newlines: true,
 2361    };
 2362
 2363    _ = editor.update(cx, |editor, window, cx| {
 2364        editor.change_selections(None, window, cx, |s| {
 2365            s.select_display_ranges([
 2366                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2367            ])
 2368        });
 2369        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2370        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2371        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2372        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2373        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2374        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2375        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2376        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2377        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2378        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2379        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2380        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2381    });
 2382}
 2383
 2384#[gpui::test]
 2385fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2386    init_test(cx, |_| {});
 2387
 2388    let editor = cx.add_window(|window, cx| {
 2389        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2390        build_editor(buffer.clone(), window, cx)
 2391    });
 2392    let del_to_next_word_end = DeleteToNextWordEnd {
 2393        ignore_newlines: false,
 2394    };
 2395    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2396        ignore_newlines: true,
 2397    };
 2398
 2399    _ = editor.update(cx, |editor, window, cx| {
 2400        editor.change_selections(None, window, cx, |s| {
 2401            s.select_display_ranges([
 2402                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2403            ])
 2404        });
 2405        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2406        assert_eq!(
 2407            editor.buffer.read(cx).read(cx).text(),
 2408            "one\n   two\nthree\n   four"
 2409        );
 2410        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2411        assert_eq!(
 2412            editor.buffer.read(cx).read(cx).text(),
 2413            "\n   two\nthree\n   four"
 2414        );
 2415        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2416        assert_eq!(
 2417            editor.buffer.read(cx).read(cx).text(),
 2418            "two\nthree\n   four"
 2419        );
 2420        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2421        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2422        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2423        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2424        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2425        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2426    });
 2427}
 2428
 2429#[gpui::test]
 2430fn test_newline(cx: &mut TestAppContext) {
 2431    init_test(cx, |_| {});
 2432
 2433    let editor = cx.add_window(|window, cx| {
 2434        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2435        build_editor(buffer.clone(), window, cx)
 2436    });
 2437
 2438    _ = editor.update(cx, |editor, window, cx| {
 2439        editor.change_selections(None, window, cx, |s| {
 2440            s.select_display_ranges([
 2441                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2442                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2443                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2444            ])
 2445        });
 2446
 2447        editor.newline(&Newline, window, cx);
 2448        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2449    });
 2450}
 2451
 2452#[gpui::test]
 2453fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2454    init_test(cx, |_| {});
 2455
 2456    let editor = cx.add_window(|window, cx| {
 2457        let buffer = MultiBuffer::build_simple(
 2458            "
 2459                a
 2460                b(
 2461                    X
 2462                )
 2463                c(
 2464                    X
 2465                )
 2466            "
 2467            .unindent()
 2468            .as_str(),
 2469            cx,
 2470        );
 2471        let mut editor = build_editor(buffer.clone(), window, cx);
 2472        editor.change_selections(None, window, cx, |s| {
 2473            s.select_ranges([
 2474                Point::new(2, 4)..Point::new(2, 5),
 2475                Point::new(5, 4)..Point::new(5, 5),
 2476            ])
 2477        });
 2478        editor
 2479    });
 2480
 2481    _ = editor.update(cx, |editor, window, cx| {
 2482        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2483        editor.buffer.update(cx, |buffer, cx| {
 2484            buffer.edit(
 2485                [
 2486                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2487                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2488                ],
 2489                None,
 2490                cx,
 2491            );
 2492            assert_eq!(
 2493                buffer.read(cx).text(),
 2494                "
 2495                    a
 2496                    b()
 2497                    c()
 2498                "
 2499                .unindent()
 2500            );
 2501        });
 2502        assert_eq!(
 2503            editor.selections.ranges(cx),
 2504            &[
 2505                Point::new(1, 2)..Point::new(1, 2),
 2506                Point::new(2, 2)..Point::new(2, 2),
 2507            ],
 2508        );
 2509
 2510        editor.newline(&Newline, window, cx);
 2511        assert_eq!(
 2512            editor.text(cx),
 2513            "
 2514                a
 2515                b(
 2516                )
 2517                c(
 2518                )
 2519            "
 2520            .unindent()
 2521        );
 2522
 2523        // The selections are moved after the inserted newlines
 2524        assert_eq!(
 2525            editor.selections.ranges(cx),
 2526            &[
 2527                Point::new(2, 0)..Point::new(2, 0),
 2528                Point::new(4, 0)..Point::new(4, 0),
 2529            ],
 2530        );
 2531    });
 2532}
 2533
 2534#[gpui::test]
 2535async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2536    init_test(cx, |settings| {
 2537        settings.defaults.tab_size = NonZeroU32::new(4)
 2538    });
 2539
 2540    let language = Arc::new(
 2541        Language::new(
 2542            LanguageConfig::default(),
 2543            Some(tree_sitter_rust::LANGUAGE.into()),
 2544        )
 2545        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2546        .unwrap(),
 2547    );
 2548
 2549    let mut cx = EditorTestContext::new(cx).await;
 2550    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2551    cx.set_state(indoc! {"
 2552        const a: ˇA = (
 2553 2554                «const_functionˇ»(ˇ),
 2555                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2556 2557        ˇ);ˇ
 2558    "});
 2559
 2560    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2561    cx.assert_editor_state(indoc! {"
 2562        ˇ
 2563        const a: A = (
 2564            ˇ
 2565            (
 2566                ˇ
 2567                ˇ
 2568                const_function(),
 2569                ˇ
 2570                ˇ
 2571                ˇ
 2572                ˇ
 2573                something_else,
 2574                ˇ
 2575            )
 2576            ˇ
 2577            ˇ
 2578        );
 2579    "});
 2580}
 2581
 2582#[gpui::test]
 2583async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2584    init_test(cx, |settings| {
 2585        settings.defaults.tab_size = NonZeroU32::new(4)
 2586    });
 2587
 2588    let language = Arc::new(
 2589        Language::new(
 2590            LanguageConfig::default(),
 2591            Some(tree_sitter_rust::LANGUAGE.into()),
 2592        )
 2593        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2594        .unwrap(),
 2595    );
 2596
 2597    let mut cx = EditorTestContext::new(cx).await;
 2598    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2599    cx.set_state(indoc! {"
 2600        const a: ˇA = (
 2601 2602                «const_functionˇ»(ˇ),
 2603                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2604 2605        ˇ);ˇ
 2606    "});
 2607
 2608    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2609    cx.assert_editor_state(indoc! {"
 2610        const a: A = (
 2611            ˇ
 2612            (
 2613                ˇ
 2614                const_function(),
 2615                ˇ
 2616                ˇ
 2617                something_else,
 2618                ˇ
 2619                ˇ
 2620                ˇ
 2621                ˇ
 2622            )
 2623            ˇ
 2624        );
 2625        ˇ
 2626        ˇ
 2627    "});
 2628}
 2629
 2630#[gpui::test]
 2631async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2632    init_test(cx, |settings| {
 2633        settings.defaults.tab_size = NonZeroU32::new(4)
 2634    });
 2635
 2636    let language = Arc::new(Language::new(
 2637        LanguageConfig {
 2638            line_comments: vec!["//".into()],
 2639            ..LanguageConfig::default()
 2640        },
 2641        None,
 2642    ));
 2643    {
 2644        let mut cx = EditorTestContext::new(cx).await;
 2645        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2646        cx.set_state(indoc! {"
 2647        // Fooˇ
 2648    "});
 2649
 2650        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2651        cx.assert_editor_state(indoc! {"
 2652        // Foo
 2653        //ˇ
 2654    "});
 2655        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2656        cx.set_state(indoc! {"
 2657        ˇ// Foo
 2658    "});
 2659        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2660        cx.assert_editor_state(indoc! {"
 2661
 2662        ˇ// Foo
 2663    "});
 2664    }
 2665    // Ensure that comment continuations can be disabled.
 2666    update_test_language_settings(cx, |settings| {
 2667        settings.defaults.extend_comment_on_newline = Some(false);
 2668    });
 2669    let mut cx = EditorTestContext::new(cx).await;
 2670    cx.set_state(indoc! {"
 2671        // Fooˇ
 2672    "});
 2673    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2674    cx.assert_editor_state(indoc! {"
 2675        // Foo
 2676        ˇ
 2677    "});
 2678}
 2679
 2680#[gpui::test]
 2681fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2682    init_test(cx, |_| {});
 2683
 2684    let editor = cx.add_window(|window, cx| {
 2685        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2686        let mut editor = build_editor(buffer.clone(), window, cx);
 2687        editor.change_selections(None, window, cx, |s| {
 2688            s.select_ranges([3..4, 11..12, 19..20])
 2689        });
 2690        editor
 2691    });
 2692
 2693    _ = editor.update(cx, |editor, window, cx| {
 2694        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2695        editor.buffer.update(cx, |buffer, cx| {
 2696            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2697            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2698        });
 2699        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2700
 2701        editor.insert("Z", window, cx);
 2702        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2703
 2704        // The selections are moved after the inserted characters
 2705        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2706    });
 2707}
 2708
 2709#[gpui::test]
 2710async fn test_tab(cx: &mut gpui::TestAppContext) {
 2711    init_test(cx, |settings| {
 2712        settings.defaults.tab_size = NonZeroU32::new(3)
 2713    });
 2714
 2715    let mut cx = EditorTestContext::new(cx).await;
 2716    cx.set_state(indoc! {"
 2717        ˇabˇc
 2718        ˇ🏀ˇ🏀ˇefg
 2719 2720    "});
 2721    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2722    cx.assert_editor_state(indoc! {"
 2723           ˇab ˇc
 2724           ˇ🏀  ˇ🏀  ˇefg
 2725        d  ˇ
 2726    "});
 2727
 2728    cx.set_state(indoc! {"
 2729        a
 2730        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2731    "});
 2732    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2733    cx.assert_editor_state(indoc! {"
 2734        a
 2735           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2736    "});
 2737}
 2738
 2739#[gpui::test]
 2740async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2741    init_test(cx, |_| {});
 2742
 2743    let mut cx = EditorTestContext::new(cx).await;
 2744    let language = Arc::new(
 2745        Language::new(
 2746            LanguageConfig::default(),
 2747            Some(tree_sitter_rust::LANGUAGE.into()),
 2748        )
 2749        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2750        .unwrap(),
 2751    );
 2752    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2753
 2754    // cursors that are already at the suggested indent level insert
 2755    // a soft tab. cursors that are to the left of the suggested indent
 2756    // auto-indent their line.
 2757    cx.set_state(indoc! {"
 2758        ˇ
 2759        const a: B = (
 2760            c(
 2761                d(
 2762        ˇ
 2763                )
 2764        ˇ
 2765        ˇ    )
 2766        );
 2767    "});
 2768    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2769    cx.assert_editor_state(indoc! {"
 2770            ˇ
 2771        const a: B = (
 2772            c(
 2773                d(
 2774                    ˇ
 2775                )
 2776                ˇ
 2777            ˇ)
 2778        );
 2779    "});
 2780
 2781    // handle auto-indent when there are multiple cursors on the same line
 2782    cx.set_state(indoc! {"
 2783        const a: B = (
 2784            c(
 2785        ˇ    ˇ
 2786        ˇ    )
 2787        );
 2788    "});
 2789    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2790    cx.assert_editor_state(indoc! {"
 2791        const a: B = (
 2792            c(
 2793                ˇ
 2794            ˇ)
 2795        );
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2801    init_test(cx, |settings| {
 2802        settings.defaults.tab_size = NonZeroU32::new(4)
 2803    });
 2804
 2805    let language = Arc::new(
 2806        Language::new(
 2807            LanguageConfig::default(),
 2808            Some(tree_sitter_rust::LANGUAGE.into()),
 2809        )
 2810        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2811        .unwrap(),
 2812    );
 2813
 2814    let mut cx = EditorTestContext::new(cx).await;
 2815    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2816    cx.set_state(indoc! {"
 2817        fn a() {
 2818            if b {
 2819        \t ˇc
 2820            }
 2821        }
 2822    "});
 2823
 2824    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2825    cx.assert_editor_state(indoc! {"
 2826        fn a() {
 2827            if b {
 2828                ˇc
 2829            }
 2830        }
 2831    "});
 2832}
 2833
 2834#[gpui::test]
 2835async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2836    init_test(cx, |settings| {
 2837        settings.defaults.tab_size = NonZeroU32::new(4);
 2838    });
 2839
 2840    let mut cx = EditorTestContext::new(cx).await;
 2841
 2842    cx.set_state(indoc! {"
 2843          «oneˇ» «twoˇ»
 2844        three
 2845         four
 2846    "});
 2847    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2848    cx.assert_editor_state(indoc! {"
 2849            «oneˇ» «twoˇ»
 2850        three
 2851         four
 2852    "});
 2853
 2854    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2855    cx.assert_editor_state(indoc! {"
 2856        «oneˇ» «twoˇ»
 2857        three
 2858         four
 2859    "});
 2860
 2861    // select across line ending
 2862    cx.set_state(indoc! {"
 2863        one two
 2864        t«hree
 2865        ˇ» four
 2866    "});
 2867    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2868    cx.assert_editor_state(indoc! {"
 2869        one two
 2870            t«hree
 2871        ˇ» four
 2872    "});
 2873
 2874    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2875    cx.assert_editor_state(indoc! {"
 2876        one two
 2877        t«hree
 2878        ˇ» four
 2879    "});
 2880
 2881    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2882    cx.set_state(indoc! {"
 2883        one two
 2884        ˇthree
 2885            four
 2886    "});
 2887    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889        one two
 2890            ˇthree
 2891            four
 2892    "});
 2893
 2894    cx.set_state(indoc! {"
 2895        one two
 2896        ˇ    three
 2897            four
 2898    "});
 2899    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2900    cx.assert_editor_state(indoc! {"
 2901        one two
 2902        ˇthree
 2903            four
 2904    "});
 2905}
 2906
 2907#[gpui::test]
 2908async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2909    init_test(cx, |settings| {
 2910        settings.defaults.hard_tabs = Some(true);
 2911    });
 2912
 2913    let mut cx = EditorTestContext::new(cx).await;
 2914
 2915    // select two ranges on one line
 2916    cx.set_state(indoc! {"
 2917        «oneˇ» «twoˇ»
 2918        three
 2919        four
 2920    "});
 2921    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2922    cx.assert_editor_state(indoc! {"
 2923        \t«oneˇ» «twoˇ»
 2924        three
 2925        four
 2926    "});
 2927    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2928    cx.assert_editor_state(indoc! {"
 2929        \t\t«oneˇ» «twoˇ»
 2930        three
 2931        four
 2932    "});
 2933    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2934    cx.assert_editor_state(indoc! {"
 2935        \t«oneˇ» «twoˇ»
 2936        three
 2937        four
 2938    "});
 2939    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        «oneˇ» «twoˇ»
 2942        three
 2943        four
 2944    "});
 2945
 2946    // select across a line ending
 2947    cx.set_state(indoc! {"
 2948        one two
 2949        t«hree
 2950        ˇ»four
 2951    "});
 2952    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2953    cx.assert_editor_state(indoc! {"
 2954        one two
 2955        \tt«hree
 2956        ˇ»four
 2957    "});
 2958    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2959    cx.assert_editor_state(indoc! {"
 2960        one two
 2961        \t\tt«hree
 2962        ˇ»four
 2963    "});
 2964    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2965    cx.assert_editor_state(indoc! {"
 2966        one two
 2967        \tt«hree
 2968        ˇ»four
 2969    "});
 2970    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2971    cx.assert_editor_state(indoc! {"
 2972        one two
 2973        t«hree
 2974        ˇ»four
 2975    "});
 2976
 2977    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2978    cx.set_state(indoc! {"
 2979        one two
 2980        ˇthree
 2981        four
 2982    "});
 2983    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2984    cx.assert_editor_state(indoc! {"
 2985        one two
 2986        ˇthree
 2987        four
 2988    "});
 2989    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2990    cx.assert_editor_state(indoc! {"
 2991        one two
 2992        \tˇthree
 2993        four
 2994    "});
 2995    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2996    cx.assert_editor_state(indoc! {"
 2997        one two
 2998        ˇthree
 2999        four
 3000    "});
 3001}
 3002
 3003#[gpui::test]
 3004fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3005    init_test(cx, |settings| {
 3006        settings.languages.extend([
 3007            (
 3008                "TOML".into(),
 3009                LanguageSettingsContent {
 3010                    tab_size: NonZeroU32::new(2),
 3011                    ..Default::default()
 3012                },
 3013            ),
 3014            (
 3015                "Rust".into(),
 3016                LanguageSettingsContent {
 3017                    tab_size: NonZeroU32::new(4),
 3018                    ..Default::default()
 3019                },
 3020            ),
 3021        ]);
 3022    });
 3023
 3024    let toml_language = Arc::new(Language::new(
 3025        LanguageConfig {
 3026            name: "TOML".into(),
 3027            ..Default::default()
 3028        },
 3029        None,
 3030    ));
 3031    let rust_language = Arc::new(Language::new(
 3032        LanguageConfig {
 3033            name: "Rust".into(),
 3034            ..Default::default()
 3035        },
 3036        None,
 3037    ));
 3038
 3039    let toml_buffer =
 3040        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3041    let rust_buffer =
 3042        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3043    let multibuffer = cx.new(|cx| {
 3044        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3045        multibuffer.push_excerpts(
 3046            toml_buffer.clone(),
 3047            [ExcerptRange {
 3048                context: Point::new(0, 0)..Point::new(2, 0),
 3049                primary: None,
 3050            }],
 3051            cx,
 3052        );
 3053        multibuffer.push_excerpts(
 3054            rust_buffer.clone(),
 3055            [ExcerptRange {
 3056                context: Point::new(0, 0)..Point::new(1, 0),
 3057                primary: None,
 3058            }],
 3059            cx,
 3060        );
 3061        multibuffer
 3062    });
 3063
 3064    cx.add_window(|window, cx| {
 3065        let mut editor = build_editor(multibuffer, window, cx);
 3066
 3067        assert_eq!(
 3068            editor.text(cx),
 3069            indoc! {"
 3070                a = 1
 3071                b = 2
 3072
 3073                const c: usize = 3;
 3074            "}
 3075        );
 3076
 3077        select_ranges(
 3078            &mut editor,
 3079            indoc! {"
 3080                «aˇ» = 1
 3081                b = 2
 3082
 3083                «const c:ˇ» usize = 3;
 3084            "},
 3085            window,
 3086            cx,
 3087        );
 3088
 3089        editor.tab(&Tab, window, cx);
 3090        assert_text_with_selections(
 3091            &mut editor,
 3092            indoc! {"
 3093                  «aˇ» = 1
 3094                b = 2
 3095
 3096                    «const c:ˇ» usize = 3;
 3097            "},
 3098            cx,
 3099        );
 3100        editor.tab_prev(&TabPrev, window, cx);
 3101        assert_text_with_selections(
 3102            &mut editor,
 3103            indoc! {"
 3104                «aˇ» = 1
 3105                b = 2
 3106
 3107                «const c:ˇ» usize = 3;
 3108            "},
 3109            cx,
 3110        );
 3111
 3112        editor
 3113    });
 3114}
 3115
 3116#[gpui::test]
 3117async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3118    init_test(cx, |_| {});
 3119
 3120    let mut cx = EditorTestContext::new(cx).await;
 3121
 3122    // Basic backspace
 3123    cx.set_state(indoc! {"
 3124        onˇe two three
 3125        fou«rˇ» five six
 3126        seven «ˇeight nine
 3127        »ten
 3128    "});
 3129    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3130    cx.assert_editor_state(indoc! {"
 3131        oˇe two three
 3132        fouˇ five six
 3133        seven ˇten
 3134    "});
 3135
 3136    // Test backspace inside and around indents
 3137    cx.set_state(indoc! {"
 3138        zero
 3139            ˇone
 3140                ˇtwo
 3141            ˇ ˇ ˇ  three
 3142        ˇ  ˇ  four
 3143    "});
 3144    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3145    cx.assert_editor_state(indoc! {"
 3146        zero
 3147        ˇone
 3148            ˇtwo
 3149        ˇ  threeˇ  four
 3150    "});
 3151
 3152    // Test backspace with line_mode set to true
 3153    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3154    cx.set_state(indoc! {"
 3155        The ˇquick ˇbrown
 3156        fox jumps over
 3157        the lazy dog
 3158        ˇThe qu«ick bˇ»rown"});
 3159    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3160    cx.assert_editor_state(indoc! {"
 3161        ˇfox jumps over
 3162        the lazy dogˇ"});
 3163}
 3164
 3165#[gpui::test]
 3166async fn test_delete(cx: &mut gpui::TestAppContext) {
 3167    init_test(cx, |_| {});
 3168
 3169    let mut cx = EditorTestContext::new(cx).await;
 3170    cx.set_state(indoc! {"
 3171        onˇe two three
 3172        fou«rˇ» five six
 3173        seven «ˇeight nine
 3174        »ten
 3175    "});
 3176    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3177    cx.assert_editor_state(indoc! {"
 3178        onˇ two three
 3179        fouˇ five six
 3180        seven ˇten
 3181    "});
 3182
 3183    // Test backspace with line_mode set to true
 3184    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3185    cx.set_state(indoc! {"
 3186        The ˇquick ˇbrown
 3187        fox «ˇjum»ps over
 3188        the lazy dog
 3189        ˇThe qu«ick bˇ»rown"});
 3190    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3191    cx.assert_editor_state("ˇthe lazy dogˇ");
 3192}
 3193
 3194#[gpui::test]
 3195fn test_delete_line(cx: &mut TestAppContext) {
 3196    init_test(cx, |_| {});
 3197
 3198    let editor = cx.add_window(|window, cx| {
 3199        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3200        build_editor(buffer, window, cx)
 3201    });
 3202    _ = editor.update(cx, |editor, window, cx| {
 3203        editor.change_selections(None, window, cx, |s| {
 3204            s.select_display_ranges([
 3205                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3206                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3207                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3208            ])
 3209        });
 3210        editor.delete_line(&DeleteLine, window, cx);
 3211        assert_eq!(editor.display_text(cx), "ghi");
 3212        assert_eq!(
 3213            editor.selections.display_ranges(cx),
 3214            vec![
 3215                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3216                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3217            ]
 3218        );
 3219    });
 3220
 3221    let editor = cx.add_window(|window, cx| {
 3222        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3223        build_editor(buffer, window, cx)
 3224    });
 3225    _ = editor.update(cx, |editor, window, cx| {
 3226        editor.change_selections(None, window, cx, |s| {
 3227            s.select_display_ranges([
 3228                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3229            ])
 3230        });
 3231        editor.delete_line(&DeleteLine, window, cx);
 3232        assert_eq!(editor.display_text(cx), "ghi\n");
 3233        assert_eq!(
 3234            editor.selections.display_ranges(cx),
 3235            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3236        );
 3237    });
 3238}
 3239
 3240#[gpui::test]
 3241fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3242    init_test(cx, |_| {});
 3243
 3244    cx.add_window(|window, cx| {
 3245        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3246        let mut editor = build_editor(buffer.clone(), window, cx);
 3247        let buffer = buffer.read(cx).as_singleton().unwrap();
 3248
 3249        assert_eq!(
 3250            editor.selections.ranges::<Point>(cx),
 3251            &[Point::new(0, 0)..Point::new(0, 0)]
 3252        );
 3253
 3254        // When on single line, replace newline at end by space
 3255        editor.join_lines(&JoinLines, window, cx);
 3256        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3257        assert_eq!(
 3258            editor.selections.ranges::<Point>(cx),
 3259            &[Point::new(0, 3)..Point::new(0, 3)]
 3260        );
 3261
 3262        // When multiple lines are selected, remove newlines that are spanned by the selection
 3263        editor.change_selections(None, window, cx, |s| {
 3264            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3265        });
 3266        editor.join_lines(&JoinLines, window, cx);
 3267        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3268        assert_eq!(
 3269            editor.selections.ranges::<Point>(cx),
 3270            &[Point::new(0, 11)..Point::new(0, 11)]
 3271        );
 3272
 3273        // Undo should be transactional
 3274        editor.undo(&Undo, window, cx);
 3275        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3276        assert_eq!(
 3277            editor.selections.ranges::<Point>(cx),
 3278            &[Point::new(0, 5)..Point::new(2, 2)]
 3279        );
 3280
 3281        // When joining an empty line don't insert a space
 3282        editor.change_selections(None, window, cx, |s| {
 3283            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3284        });
 3285        editor.join_lines(&JoinLines, window, cx);
 3286        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3287        assert_eq!(
 3288            editor.selections.ranges::<Point>(cx),
 3289            [Point::new(2, 3)..Point::new(2, 3)]
 3290        );
 3291
 3292        // We can remove trailing newlines
 3293        editor.join_lines(&JoinLines, window, cx);
 3294        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3295        assert_eq!(
 3296            editor.selections.ranges::<Point>(cx),
 3297            [Point::new(2, 3)..Point::new(2, 3)]
 3298        );
 3299
 3300        // We don't blow up on the last line
 3301        editor.join_lines(&JoinLines, window, cx);
 3302        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3303        assert_eq!(
 3304            editor.selections.ranges::<Point>(cx),
 3305            [Point::new(2, 3)..Point::new(2, 3)]
 3306        );
 3307
 3308        // reset to test indentation
 3309        editor.buffer.update(cx, |buffer, cx| {
 3310            buffer.edit(
 3311                [
 3312                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3313                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3314                ],
 3315                None,
 3316                cx,
 3317            )
 3318        });
 3319
 3320        // We remove any leading spaces
 3321        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3322        editor.change_selections(None, window, cx, |s| {
 3323            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3324        });
 3325        editor.join_lines(&JoinLines, window, cx);
 3326        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3327
 3328        // We don't insert a space for a line containing only spaces
 3329        editor.join_lines(&JoinLines, window, cx);
 3330        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3331
 3332        // We ignore any leading tabs
 3333        editor.join_lines(&JoinLines, window, cx);
 3334        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3335
 3336        editor
 3337    });
 3338}
 3339
 3340#[gpui::test]
 3341fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3342    init_test(cx, |_| {});
 3343
 3344    cx.add_window(|window, cx| {
 3345        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3346        let mut editor = build_editor(buffer.clone(), window, cx);
 3347        let buffer = buffer.read(cx).as_singleton().unwrap();
 3348
 3349        editor.change_selections(None, window, cx, |s| {
 3350            s.select_ranges([
 3351                Point::new(0, 2)..Point::new(1, 1),
 3352                Point::new(1, 2)..Point::new(1, 2),
 3353                Point::new(3, 1)..Point::new(3, 2),
 3354            ])
 3355        });
 3356
 3357        editor.join_lines(&JoinLines, window, cx);
 3358        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3359
 3360        assert_eq!(
 3361            editor.selections.ranges::<Point>(cx),
 3362            [
 3363                Point::new(0, 7)..Point::new(0, 7),
 3364                Point::new(1, 3)..Point::new(1, 3)
 3365            ]
 3366        );
 3367        editor
 3368    });
 3369}
 3370
 3371#[gpui::test]
 3372async fn test_join_lines_with_git_diff_base(
 3373    executor: BackgroundExecutor,
 3374    cx: &mut gpui::TestAppContext,
 3375) {
 3376    init_test(cx, |_| {});
 3377
 3378    let mut cx = EditorTestContext::new(cx).await;
 3379
 3380    let diff_base = r#"
 3381        Line 0
 3382        Line 1
 3383        Line 2
 3384        Line 3
 3385        "#
 3386    .unindent();
 3387
 3388    cx.set_state(
 3389        &r#"
 3390        ˇLine 0
 3391        Line 1
 3392        Line 2
 3393        Line 3
 3394        "#
 3395        .unindent(),
 3396    );
 3397
 3398    cx.set_diff_base(&diff_base);
 3399    executor.run_until_parked();
 3400
 3401    // Join lines
 3402    cx.update_editor(|editor, window, cx| {
 3403        editor.join_lines(&JoinLines, window, cx);
 3404    });
 3405    executor.run_until_parked();
 3406
 3407    cx.assert_editor_state(
 3408        &r#"
 3409        Line 0ˇ Line 1
 3410        Line 2
 3411        Line 3
 3412        "#
 3413        .unindent(),
 3414    );
 3415    // Join again
 3416    cx.update_editor(|editor, window, cx| {
 3417        editor.join_lines(&JoinLines, window, cx);
 3418    });
 3419    executor.run_until_parked();
 3420
 3421    cx.assert_editor_state(
 3422        &r#"
 3423        Line 0 Line 1ˇ Line 2
 3424        Line 3
 3425        "#
 3426        .unindent(),
 3427    );
 3428}
 3429
 3430#[gpui::test]
 3431async fn test_custom_newlines_cause_no_false_positive_diffs(
 3432    executor: BackgroundExecutor,
 3433    cx: &mut gpui::TestAppContext,
 3434) {
 3435    init_test(cx, |_| {});
 3436    let mut cx = EditorTestContext::new(cx).await;
 3437    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3438    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3439    executor.run_until_parked();
 3440
 3441    cx.update_editor(|editor, window, cx| {
 3442        let snapshot = editor.snapshot(window, cx);
 3443        assert_eq!(
 3444            snapshot
 3445                .buffer_snapshot
 3446                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3447                .collect::<Vec<_>>(),
 3448            Vec::new(),
 3449            "Should not have any diffs for files with custom newlines"
 3450        );
 3451    });
 3452}
 3453
 3454#[gpui::test]
 3455async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3456    init_test(cx, |_| {});
 3457
 3458    let mut cx = EditorTestContext::new(cx).await;
 3459
 3460    // Test sort_lines_case_insensitive()
 3461    cx.set_state(indoc! {"
 3462        «z
 3463        y
 3464        x
 3465        Z
 3466        Y
 3467        Xˇ»
 3468    "});
 3469    cx.update_editor(|e, window, cx| {
 3470        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3471    });
 3472    cx.assert_editor_state(indoc! {"
 3473        «x
 3474        X
 3475        y
 3476        Y
 3477        z
 3478        Zˇ»
 3479    "});
 3480
 3481    // Test reverse_lines()
 3482    cx.set_state(indoc! {"
 3483        «5
 3484        4
 3485        3
 3486        2
 3487        1ˇ»
 3488    "});
 3489    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3490    cx.assert_editor_state(indoc! {"
 3491        «1
 3492        2
 3493        3
 3494        4
 3495        5ˇ»
 3496    "});
 3497
 3498    // Skip testing shuffle_line()
 3499
 3500    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3501    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3502
 3503    // Don't manipulate when cursor is on single line, but expand the selection
 3504    cx.set_state(indoc! {"
 3505        ddˇdd
 3506        ccc
 3507        bb
 3508        a
 3509    "});
 3510    cx.update_editor(|e, window, cx| {
 3511        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3512    });
 3513    cx.assert_editor_state(indoc! {"
 3514        «ddddˇ»
 3515        ccc
 3516        bb
 3517        a
 3518    "});
 3519
 3520    // Basic manipulate case
 3521    // Start selection moves to column 0
 3522    // End of selection shrinks to fit shorter line
 3523    cx.set_state(indoc! {"
 3524        dd«d
 3525        ccc
 3526        bb
 3527        aaaaaˇ»
 3528    "});
 3529    cx.update_editor(|e, window, cx| {
 3530        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3531    });
 3532    cx.assert_editor_state(indoc! {"
 3533        «aaaaa
 3534        bb
 3535        ccc
 3536        dddˇ»
 3537    "});
 3538
 3539    // Manipulate case with newlines
 3540    cx.set_state(indoc! {"
 3541        dd«d
 3542        ccc
 3543
 3544        bb
 3545        aaaaa
 3546
 3547        ˇ»
 3548    "});
 3549    cx.update_editor(|e, window, cx| {
 3550        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3551    });
 3552    cx.assert_editor_state(indoc! {"
 3553        «
 3554
 3555        aaaaa
 3556        bb
 3557        ccc
 3558        dddˇ»
 3559
 3560    "});
 3561
 3562    // Adding new line
 3563    cx.set_state(indoc! {"
 3564        aa«a
 3565        bbˇ»b
 3566    "});
 3567    cx.update_editor(|e, window, cx| {
 3568        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3569    });
 3570    cx.assert_editor_state(indoc! {"
 3571        «aaa
 3572        bbb
 3573        added_lineˇ»
 3574    "});
 3575
 3576    // Removing line
 3577    cx.set_state(indoc! {"
 3578        aa«a
 3579        bbbˇ»
 3580    "});
 3581    cx.update_editor(|e, window, cx| {
 3582        e.manipulate_lines(window, cx, |lines| {
 3583            lines.pop();
 3584        })
 3585    });
 3586    cx.assert_editor_state(indoc! {"
 3587        «aaaˇ»
 3588    "});
 3589
 3590    // Removing all lines
 3591    cx.set_state(indoc! {"
 3592        aa«a
 3593        bbbˇ»
 3594    "});
 3595    cx.update_editor(|e, window, cx| {
 3596        e.manipulate_lines(window, cx, |lines| {
 3597            lines.drain(..);
 3598        })
 3599    });
 3600    cx.assert_editor_state(indoc! {"
 3601        ˇ
 3602    "});
 3603}
 3604
 3605#[gpui::test]
 3606async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3607    init_test(cx, |_| {});
 3608
 3609    let mut cx = EditorTestContext::new(cx).await;
 3610
 3611    // Consider continuous selection as single selection
 3612    cx.set_state(indoc! {"
 3613        Aaa«aa
 3614        cˇ»c«c
 3615        bb
 3616        aaaˇ»aa
 3617    "});
 3618    cx.update_editor(|e, window, cx| {
 3619        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3620    });
 3621    cx.assert_editor_state(indoc! {"
 3622        «Aaaaa
 3623        ccc
 3624        bb
 3625        aaaaaˇ»
 3626    "});
 3627
 3628    cx.set_state(indoc! {"
 3629        Aaa«aa
 3630        cˇ»c«c
 3631        bb
 3632        aaaˇ»aa
 3633    "});
 3634    cx.update_editor(|e, window, cx| {
 3635        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3636    });
 3637    cx.assert_editor_state(indoc! {"
 3638        «Aaaaa
 3639        ccc
 3640        bbˇ»
 3641    "});
 3642
 3643    // Consider non continuous selection as distinct dedup operations
 3644    cx.set_state(indoc! {"
 3645        «aaaaa
 3646        bb
 3647        aaaaa
 3648        aaaaaˇ»
 3649
 3650        aaa«aaˇ»
 3651    "});
 3652    cx.update_editor(|e, window, cx| {
 3653        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3654    });
 3655    cx.assert_editor_state(indoc! {"
 3656        «aaaaa
 3657        bbˇ»
 3658
 3659        «aaaaaˇ»
 3660    "});
 3661}
 3662
 3663#[gpui::test]
 3664async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3665    init_test(cx, |_| {});
 3666
 3667    let mut cx = EditorTestContext::new(cx).await;
 3668
 3669    cx.set_state(indoc! {"
 3670        «Aaa
 3671        aAa
 3672        Aaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «Aaa
 3679        aAaˇ»
 3680    "});
 3681
 3682    cx.set_state(indoc! {"
 3683        «Aaa
 3684        aAa
 3685        aaAˇ»
 3686    "});
 3687    cx.update_editor(|e, window, cx| {
 3688        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        «Aaaˇ»
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Manipulate with multiple selections on a single line
 3702    cx.set_state(indoc! {"
 3703        dd«dd
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «aaaaa
 3713        bb
 3714        ccc
 3715        ddddˇ»
 3716    "});
 3717
 3718    // Manipulate with multiple disjoin selections
 3719    cx.set_state(indoc! {"
 3720 3721        4
 3722        3
 3723        2
 3724        1ˇ»
 3725
 3726        dd«dd
 3727        ccc
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «1
 3736        2
 3737        3
 3738        4
 3739        5ˇ»
 3740
 3741        «aaaaa
 3742        bb
 3743        ccc
 3744        ddddˇ»
 3745    "});
 3746
 3747    // Adding lines on each selection
 3748    cx.set_state(indoc! {"
 3749 3750        1ˇ»
 3751
 3752        bb«bb
 3753        aaaˇ»aa
 3754    "});
 3755    cx.update_editor(|e, window, cx| {
 3756        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3757    });
 3758    cx.assert_editor_state(indoc! {"
 3759        «2
 3760        1
 3761        added lineˇ»
 3762
 3763        «bbbb
 3764        aaaaa
 3765        added lineˇ»
 3766    "});
 3767
 3768    // Removing lines on each selection
 3769    cx.set_state(indoc! {"
 3770 3771        1ˇ»
 3772
 3773        bb«bb
 3774        aaaˇ»aa
 3775    "});
 3776    cx.update_editor(|e, window, cx| {
 3777        e.manipulate_lines(window, cx, |lines| {
 3778            lines.pop();
 3779        })
 3780    });
 3781    cx.assert_editor_state(indoc! {"
 3782        «2ˇ»
 3783
 3784        «bbbbˇ»
 3785    "});
 3786}
 3787
 3788#[gpui::test]
 3789async fn test_manipulate_text(cx: &mut TestAppContext) {
 3790    init_test(cx, |_| {});
 3791
 3792    let mut cx = EditorTestContext::new(cx).await;
 3793
 3794    // Test convert_to_upper_case()
 3795    cx.set_state(indoc! {"
 3796        «hello worldˇ»
 3797    "});
 3798    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3799    cx.assert_editor_state(indoc! {"
 3800        «HELLO WORLDˇ»
 3801    "});
 3802
 3803    // Test convert_to_lower_case()
 3804    cx.set_state(indoc! {"
 3805        «HELLO WORLDˇ»
 3806    "});
 3807    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3808    cx.assert_editor_state(indoc! {"
 3809        «hello worldˇ»
 3810    "});
 3811
 3812    // Test multiple line, single selection case
 3813    cx.set_state(indoc! {"
 3814        «The quick brown
 3815        fox jumps over
 3816        the lazy dogˇ»
 3817    "});
 3818    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3819    cx.assert_editor_state(indoc! {"
 3820        «The Quick Brown
 3821        Fox Jumps Over
 3822        The Lazy Dogˇ»
 3823    "});
 3824
 3825    // Test multiple line, single selection case
 3826    cx.set_state(indoc! {"
 3827        «The quick brown
 3828        fox jumps over
 3829        the lazy dogˇ»
 3830    "});
 3831    cx.update_editor(|e, window, cx| {
 3832        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3833    });
 3834    cx.assert_editor_state(indoc! {"
 3835        «TheQuickBrown
 3836        FoxJumpsOver
 3837        TheLazyDogˇ»
 3838    "});
 3839
 3840    // From here on out, test more complex cases of manipulate_text()
 3841
 3842    // Test no selection case - should affect words cursors are in
 3843    // Cursor at beginning, middle, and end of word
 3844    cx.set_state(indoc! {"
 3845        ˇhello big beauˇtiful worldˇ
 3846    "});
 3847    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3848    cx.assert_editor_state(indoc! {"
 3849        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3850    "});
 3851
 3852    // Test multiple selections on a single line and across multiple lines
 3853    cx.set_state(indoc! {"
 3854        «Theˇ» quick «brown
 3855        foxˇ» jumps «overˇ»
 3856        the «lazyˇ» dog
 3857    "});
 3858    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3859    cx.assert_editor_state(indoc! {"
 3860        «THEˇ» quick «BROWN
 3861        FOXˇ» jumps «OVERˇ»
 3862        the «LAZYˇ» dog
 3863    "});
 3864
 3865    // Test case where text length grows
 3866    cx.set_state(indoc! {"
 3867        «tschüߡ»
 3868    "});
 3869    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3870    cx.assert_editor_state(indoc! {"
 3871        «TSCHÜSSˇ»
 3872    "});
 3873
 3874    // Test to make sure we don't crash when text shrinks
 3875    cx.set_state(indoc! {"
 3876        aaa_bbbˇ
 3877    "});
 3878    cx.update_editor(|e, window, cx| {
 3879        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3880    });
 3881    cx.assert_editor_state(indoc! {"
 3882        «aaaBbbˇ»
 3883    "});
 3884
 3885    // Test to make sure we all aware of the fact that each word can grow and shrink
 3886    // Final selections should be aware of this fact
 3887    cx.set_state(indoc! {"
 3888        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3889    "});
 3890    cx.update_editor(|e, window, cx| {
 3891        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3892    });
 3893    cx.assert_editor_state(indoc! {"
 3894        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3895    "});
 3896
 3897    cx.set_state(indoc! {"
 3898        «hElLo, WoRld!ˇ»
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «HeLlO, wOrLD!ˇ»
 3905    "});
 3906}
 3907
 3908#[gpui::test]
 3909fn test_duplicate_line(cx: &mut TestAppContext) {
 3910    init_test(cx, |_| {});
 3911
 3912    let editor = cx.add_window(|window, cx| {
 3913        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3914        build_editor(buffer, window, cx)
 3915    });
 3916    _ = editor.update(cx, |editor, window, cx| {
 3917        editor.change_selections(None, window, cx, |s| {
 3918            s.select_display_ranges([
 3919                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3920                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3921                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3922                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3923            ])
 3924        });
 3925        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3926        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3927        assert_eq!(
 3928            editor.selections.display_ranges(cx),
 3929            vec![
 3930                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3931                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3932                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3933                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3934            ]
 3935        );
 3936    });
 3937
 3938    let editor = cx.add_window(|window, cx| {
 3939        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3940        build_editor(buffer, window, cx)
 3941    });
 3942    _ = editor.update(cx, |editor, window, cx| {
 3943        editor.change_selections(None, window, cx, |s| {
 3944            s.select_display_ranges([
 3945                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3946                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3947            ])
 3948        });
 3949        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3950        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3951        assert_eq!(
 3952            editor.selections.display_ranges(cx),
 3953            vec![
 3954                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3955                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3956            ]
 3957        );
 3958    });
 3959
 3960    // With `move_upwards` the selections stay in place, except for
 3961    // the lines inserted above them
 3962    let editor = cx.add_window(|window, cx| {
 3963        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3964        build_editor(buffer, window, cx)
 3965    });
 3966    _ = editor.update(cx, |editor, window, cx| {
 3967        editor.change_selections(None, window, cx, |s| {
 3968            s.select_display_ranges([
 3969                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3970                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3971                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3972                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3973            ])
 3974        });
 3975        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3976        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3977        assert_eq!(
 3978            editor.selections.display_ranges(cx),
 3979            vec![
 3980                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3981                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3982                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3983                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3984            ]
 3985        );
 3986    });
 3987
 3988    let editor = cx.add_window(|window, cx| {
 3989        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3990        build_editor(buffer, window, cx)
 3991    });
 3992    _ = editor.update(cx, |editor, window, cx| {
 3993        editor.change_selections(None, window, cx, |s| {
 3994            s.select_display_ranges([
 3995                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3996                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3997            ])
 3998        });
 3999        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4000        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4001        assert_eq!(
 4002            editor.selections.display_ranges(cx),
 4003            vec![
 4004                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4005                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4006            ]
 4007        );
 4008    });
 4009
 4010    let editor = cx.add_window(|window, cx| {
 4011        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4012        build_editor(buffer, window, cx)
 4013    });
 4014    _ = editor.update(cx, |editor, window, cx| {
 4015        editor.change_selections(None, window, cx, |s| {
 4016            s.select_display_ranges([
 4017                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4018                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4019            ])
 4020        });
 4021        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4022        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4023        assert_eq!(
 4024            editor.selections.display_ranges(cx),
 4025            vec![
 4026                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4027                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4028            ]
 4029        );
 4030    });
 4031}
 4032
 4033#[gpui::test]
 4034fn test_move_line_up_down(cx: &mut TestAppContext) {
 4035    init_test(cx, |_| {});
 4036
 4037    let editor = cx.add_window(|window, cx| {
 4038        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4039        build_editor(buffer, window, cx)
 4040    });
 4041    _ = editor.update(cx, |editor, window, cx| {
 4042        editor.fold_creases(
 4043            vec![
 4044                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4045                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4046                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4047            ],
 4048            true,
 4049            window,
 4050            cx,
 4051        );
 4052        editor.change_selections(None, window, cx, |s| {
 4053            s.select_display_ranges([
 4054                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4055                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4056                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4057                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4058            ])
 4059        });
 4060        assert_eq!(
 4061            editor.display_text(cx),
 4062            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4063        );
 4064
 4065        editor.move_line_up(&MoveLineUp, window, cx);
 4066        assert_eq!(
 4067            editor.display_text(cx),
 4068            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4069        );
 4070        assert_eq!(
 4071            editor.selections.display_ranges(cx),
 4072            vec![
 4073                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4074                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4075                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4076                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4077            ]
 4078        );
 4079    });
 4080
 4081    _ = editor.update(cx, |editor, window, cx| {
 4082        editor.move_line_down(&MoveLineDown, window, cx);
 4083        assert_eq!(
 4084            editor.display_text(cx),
 4085            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4086        );
 4087        assert_eq!(
 4088            editor.selections.display_ranges(cx),
 4089            vec![
 4090                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4091                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4092                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4093                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4094            ]
 4095        );
 4096    });
 4097
 4098    _ = editor.update(cx, |editor, window, cx| {
 4099        editor.move_line_down(&MoveLineDown, window, cx);
 4100        assert_eq!(
 4101            editor.display_text(cx),
 4102            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4103        );
 4104        assert_eq!(
 4105            editor.selections.display_ranges(cx),
 4106            vec![
 4107                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4108                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4109                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4110                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4111            ]
 4112        );
 4113    });
 4114
 4115    _ = editor.update(cx, |editor, window, cx| {
 4116        editor.move_line_up(&MoveLineUp, window, cx);
 4117        assert_eq!(
 4118            editor.display_text(cx),
 4119            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4120        );
 4121        assert_eq!(
 4122            editor.selections.display_ranges(cx),
 4123            vec![
 4124                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4125                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4126                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4127                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4128            ]
 4129        );
 4130    });
 4131}
 4132
 4133#[gpui::test]
 4134fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4135    init_test(cx, |_| {});
 4136
 4137    let editor = cx.add_window(|window, cx| {
 4138        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4139        build_editor(buffer, window, cx)
 4140    });
 4141    _ = editor.update(cx, |editor, window, cx| {
 4142        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4143        editor.insert_blocks(
 4144            [BlockProperties {
 4145                style: BlockStyle::Fixed,
 4146                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4147                height: 1,
 4148                render: Arc::new(|_| div().into_any()),
 4149                priority: 0,
 4150            }],
 4151            Some(Autoscroll::fit()),
 4152            cx,
 4153        );
 4154        editor.change_selections(None, window, cx, |s| {
 4155            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4156        });
 4157        editor.move_line_down(&MoveLineDown, window, cx);
 4158    });
 4159}
 4160
 4161#[gpui::test]
 4162async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4163    init_test(cx, |_| {});
 4164
 4165    let mut cx = EditorTestContext::new(cx).await;
 4166    cx.set_state(
 4167        &"
 4168            ˇzero
 4169            one
 4170            two
 4171            three
 4172            four
 4173            five
 4174        "
 4175        .unindent(),
 4176    );
 4177
 4178    // Create a four-line block that replaces three lines of text.
 4179    cx.update_editor(|editor, window, cx| {
 4180        let snapshot = editor.snapshot(window, cx);
 4181        let snapshot = &snapshot.buffer_snapshot;
 4182        let placement = BlockPlacement::Replace(
 4183            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4184        );
 4185        editor.insert_blocks(
 4186            [BlockProperties {
 4187                placement,
 4188                height: 4,
 4189                style: BlockStyle::Sticky,
 4190                render: Arc::new(|_| gpui::div().into_any_element()),
 4191                priority: 0,
 4192            }],
 4193            None,
 4194            cx,
 4195        );
 4196    });
 4197
 4198    // Move down so that the cursor touches the block.
 4199    cx.update_editor(|editor, window, cx| {
 4200        editor.move_down(&Default::default(), window, cx);
 4201    });
 4202    cx.assert_editor_state(
 4203        &"
 4204            zero
 4205            «one
 4206            two
 4207            threeˇ»
 4208            four
 4209            five
 4210        "
 4211        .unindent(),
 4212    );
 4213
 4214    // Move down past the block.
 4215    cx.update_editor(|editor, window, cx| {
 4216        editor.move_down(&Default::default(), window, cx);
 4217    });
 4218    cx.assert_editor_state(
 4219        &"
 4220            zero
 4221            one
 4222            two
 4223            three
 4224            ˇfour
 4225            five
 4226        "
 4227        .unindent(),
 4228    );
 4229}
 4230
 4231#[gpui::test]
 4232fn test_transpose(cx: &mut TestAppContext) {
 4233    init_test(cx, |_| {});
 4234
 4235    _ = cx.add_window(|window, cx| {
 4236        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4237        editor.set_style(EditorStyle::default(), window, cx);
 4238        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4239        editor.transpose(&Default::default(), window, cx);
 4240        assert_eq!(editor.text(cx), "bac");
 4241        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4242
 4243        editor.transpose(&Default::default(), window, cx);
 4244        assert_eq!(editor.text(cx), "bca");
 4245        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4246
 4247        editor.transpose(&Default::default(), window, cx);
 4248        assert_eq!(editor.text(cx), "bac");
 4249        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4250
 4251        editor
 4252    });
 4253
 4254    _ = cx.add_window(|window, cx| {
 4255        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4256        editor.set_style(EditorStyle::default(), window, cx);
 4257        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4258        editor.transpose(&Default::default(), window, cx);
 4259        assert_eq!(editor.text(cx), "acb\nde");
 4260        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4261
 4262        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4263        editor.transpose(&Default::default(), window, cx);
 4264        assert_eq!(editor.text(cx), "acbd\ne");
 4265        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4266
 4267        editor.transpose(&Default::default(), window, cx);
 4268        assert_eq!(editor.text(cx), "acbde\n");
 4269        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4270
 4271        editor.transpose(&Default::default(), window, cx);
 4272        assert_eq!(editor.text(cx), "acbd\ne");
 4273        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4274
 4275        editor
 4276    });
 4277
 4278    _ = cx.add_window(|window, cx| {
 4279        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4280        editor.set_style(EditorStyle::default(), window, cx);
 4281        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4282        editor.transpose(&Default::default(), window, cx);
 4283        assert_eq!(editor.text(cx), "bacd\ne");
 4284        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4285
 4286        editor.transpose(&Default::default(), window, cx);
 4287        assert_eq!(editor.text(cx), "bcade\n");
 4288        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4289
 4290        editor.transpose(&Default::default(), window, cx);
 4291        assert_eq!(editor.text(cx), "bcda\ne");
 4292        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4293
 4294        editor.transpose(&Default::default(), window, cx);
 4295        assert_eq!(editor.text(cx), "bcade\n");
 4296        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4297
 4298        editor.transpose(&Default::default(), window, cx);
 4299        assert_eq!(editor.text(cx), "bcaed\n");
 4300        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4301
 4302        editor
 4303    });
 4304
 4305    _ = cx.add_window(|window, cx| {
 4306        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4307        editor.set_style(EditorStyle::default(), window, cx);
 4308        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4309        editor.transpose(&Default::default(), window, cx);
 4310        assert_eq!(editor.text(cx), "🏀🍐✋");
 4311        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4312
 4313        editor.transpose(&Default::default(), window, cx);
 4314        assert_eq!(editor.text(cx), "🏀✋🍐");
 4315        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4316
 4317        editor.transpose(&Default::default(), window, cx);
 4318        assert_eq!(editor.text(cx), "🏀🍐✋");
 4319        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4320
 4321        editor
 4322    });
 4323}
 4324
 4325#[gpui::test]
 4326async fn test_rewrap(cx: &mut TestAppContext) {
 4327    init_test(cx, |_| {});
 4328
 4329    let mut cx = EditorTestContext::new(cx).await;
 4330
 4331    let language_with_c_comments = Arc::new(Language::new(
 4332        LanguageConfig {
 4333            line_comments: vec!["// ".into()],
 4334            ..LanguageConfig::default()
 4335        },
 4336        None,
 4337    ));
 4338    let language_with_pound_comments = Arc::new(Language::new(
 4339        LanguageConfig {
 4340            line_comments: vec!["# ".into()],
 4341            ..LanguageConfig::default()
 4342        },
 4343        None,
 4344    ));
 4345    let markdown_language = Arc::new(Language::new(
 4346        LanguageConfig {
 4347            name: "Markdown".into(),
 4348            ..LanguageConfig::default()
 4349        },
 4350        None,
 4351    ));
 4352    let language_with_doc_comments = Arc::new(Language::new(
 4353        LanguageConfig {
 4354            line_comments: vec!["// ".into(), "/// ".into()],
 4355            ..LanguageConfig::default()
 4356        },
 4357        Some(tree_sitter_rust::LANGUAGE.into()),
 4358    ));
 4359
 4360    let plaintext_language = Arc::new(Language::new(
 4361        LanguageConfig {
 4362            name: "Plain Text".into(),
 4363            ..LanguageConfig::default()
 4364        },
 4365        None,
 4366    ));
 4367
 4368    assert_rewrap(
 4369        indoc! {"
 4370            // ˇ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.
 4371        "},
 4372        indoc! {"
 4373            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4374            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4375            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4376            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4377            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4378            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4379            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4380            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4381            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4382            // porttitor id. Aliquam id accumsan eros.
 4383        "},
 4384        language_with_c_comments.clone(),
 4385        &mut cx,
 4386    );
 4387
 4388    // Test that rewrapping works inside of a selection
 4389    assert_rewrap(
 4390        indoc! {"
 4391            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. 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.ˇ»
 4392        "},
 4393        indoc! {"
 4394            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4395            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4396            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4397            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4398            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4399            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4400            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4401            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4402            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4403            // porttitor id. Aliquam id accumsan eros.ˇ»
 4404        "},
 4405        language_with_c_comments.clone(),
 4406        &mut cx,
 4407    );
 4408
 4409    // Test that cursors that expand to the same region are collapsed.
 4410    assert_rewrap(
 4411        indoc! {"
 4412            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4413            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4414            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4415            // ˇ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.
 4416        "},
 4417        indoc! {"
 4418            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4419            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4420            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4421            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4422            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4423            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4424            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4425            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4426            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4427            // porttitor id. Aliquam id accumsan eros.
 4428        "},
 4429        language_with_c_comments.clone(),
 4430        &mut cx,
 4431    );
 4432
 4433    // Test that non-contiguous selections are treated separately.
 4434    assert_rewrap(
 4435        indoc! {"
 4436            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4437            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4438            //
 4439            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4440            // ˇ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.
 4441        "},
 4442        indoc! {"
 4443            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4444            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4445            // auctor, eu lacinia sapien scelerisque.
 4446            //
 4447            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4448            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4449            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4450            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4451            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4452            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4453            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4454        "},
 4455        language_with_c_comments.clone(),
 4456        &mut cx,
 4457    );
 4458
 4459    // Test that different comment prefixes are supported.
 4460    assert_rewrap(
 4461        indoc! {"
 4462            # ˇ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.
 4463        "},
 4464        indoc! {"
 4465            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4466            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4467            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4468            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4469            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4470            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4471            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4472            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4473            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4474            # accumsan eros.
 4475        "},
 4476        language_with_pound_comments.clone(),
 4477        &mut cx,
 4478    );
 4479
 4480    // Test that rewrapping is ignored outside of comments in most languages.
 4481    assert_rewrap(
 4482        indoc! {"
 4483            /// Adds two numbers.
 4484            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4485            fn add(a: u32, b: u32) -> u32 {
 4486                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ˇ
 4487            }
 4488        "},
 4489        indoc! {"
 4490            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4491            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4492            fn add(a: u32, b: u32) -> u32 {
 4493                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ˇ
 4494            }
 4495        "},
 4496        language_with_doc_comments.clone(),
 4497        &mut cx,
 4498    );
 4499
 4500    // Test that rewrapping works in Markdown and Plain Text languages.
 4501    assert_rewrap(
 4502        indoc! {"
 4503            # Hello
 4504
 4505            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.
 4506        "},
 4507        indoc! {"
 4508            # Hello
 4509
 4510            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4511            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4512            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4513            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4514            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4515            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4516            Integer sit amet scelerisque nisi.
 4517        "},
 4518        markdown_language,
 4519        &mut cx,
 4520    );
 4521
 4522    assert_rewrap(
 4523        indoc! {"
 4524            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.
 4525        "},
 4526        indoc! {"
 4527            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4528            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4529            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4530            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4531            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4532            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4533            Integer sit amet scelerisque nisi.
 4534        "},
 4535        plaintext_language,
 4536        &mut cx,
 4537    );
 4538
 4539    // Test rewrapping unaligned comments in a selection.
 4540    assert_rewrap(
 4541        indoc! {"
 4542            fn foo() {
 4543                if true {
 4544            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4545            // Praesent semper egestas tellus id dignissim.ˇ»
 4546                    do_something();
 4547                } else {
 4548                    //
 4549                }
 4550            }
 4551        "},
 4552        indoc! {"
 4553            fn foo() {
 4554                if true {
 4555            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4556                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4557                    // egestas tellus id dignissim.ˇ»
 4558                    do_something();
 4559                } else {
 4560                    //
 4561                }
 4562            }
 4563        "},
 4564        language_with_doc_comments.clone(),
 4565        &mut cx,
 4566    );
 4567
 4568    assert_rewrap(
 4569        indoc! {"
 4570            fn foo() {
 4571                if true {
 4572            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4573            // Praesent semper egestas tellus id dignissim.»
 4574                    do_something();
 4575                } else {
 4576                    //
 4577                }
 4578
 4579            }
 4580        "},
 4581        indoc! {"
 4582            fn foo() {
 4583                if true {
 4584            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4585                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4586                    // egestas tellus id dignissim.»
 4587                    do_something();
 4588                } else {
 4589                    //
 4590                }
 4591
 4592            }
 4593        "},
 4594        language_with_doc_comments.clone(),
 4595        &mut cx,
 4596    );
 4597
 4598    #[track_caller]
 4599    fn assert_rewrap(
 4600        unwrapped_text: &str,
 4601        wrapped_text: &str,
 4602        language: Arc<Language>,
 4603        cx: &mut EditorTestContext,
 4604    ) {
 4605        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4606        cx.set_state(unwrapped_text);
 4607        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4608        cx.assert_editor_state(wrapped_text);
 4609    }
 4610}
 4611
 4612#[gpui::test]
 4613async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4614    init_test(cx, |_| {});
 4615
 4616    let mut cx = EditorTestContext::new(cx).await;
 4617
 4618    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4619    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4620    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4621
 4622    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4623    cx.set_state("two ˇfour ˇsix ˇ");
 4624    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4625    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4626
 4627    // Paste again but with only two cursors. Since the number of cursors doesn't
 4628    // match the number of slices in the clipboard, the entire clipboard text
 4629    // is pasted at each cursor.
 4630    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4631    cx.update_editor(|e, window, cx| {
 4632        e.handle_input("( ", window, cx);
 4633        e.paste(&Paste, window, cx);
 4634        e.handle_input(") ", window, cx);
 4635    });
 4636    cx.assert_editor_state(
 4637        &([
 4638            "( one✅ ",
 4639            "three ",
 4640            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4641            "three ",
 4642            "five ) ˇ",
 4643        ]
 4644        .join("\n")),
 4645    );
 4646
 4647    // Cut with three selections, one of which is full-line.
 4648    cx.set_state(indoc! {"
 4649        1«2ˇ»3
 4650        4ˇ567
 4651        «8ˇ»9"});
 4652    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4653    cx.assert_editor_state(indoc! {"
 4654        1ˇ3
 4655        ˇ9"});
 4656
 4657    // Paste with three selections, noticing how the copied selection that was full-line
 4658    // gets inserted before the second cursor.
 4659    cx.set_state(indoc! {"
 4660        1ˇ3
 4661 4662        «oˇ»ne"});
 4663    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4664    cx.assert_editor_state(indoc! {"
 4665        12ˇ3
 4666        4567
 4667 4668        8ˇne"});
 4669
 4670    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4671    cx.set_state(indoc! {"
 4672        The quick brown
 4673        fox juˇmps over
 4674        the lazy dog"});
 4675    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4676    assert_eq!(
 4677        cx.read_from_clipboard()
 4678            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4679        Some("fox jumps over\n".to_string())
 4680    );
 4681
 4682    // Paste with three selections, noticing how the copied full-line selection is inserted
 4683    // before the empty selections but replaces the selection that is non-empty.
 4684    cx.set_state(indoc! {"
 4685        Tˇhe quick brown
 4686        «foˇ»x jumps over
 4687        tˇhe lazy dog"});
 4688    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4689    cx.assert_editor_state(indoc! {"
 4690        fox jumps over
 4691        Tˇhe quick brown
 4692        fox jumps over
 4693        ˇx jumps over
 4694        fox jumps over
 4695        tˇhe lazy dog"});
 4696}
 4697
 4698#[gpui::test]
 4699async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4700    init_test(cx, |_| {});
 4701
 4702    let mut cx = EditorTestContext::new(cx).await;
 4703    let language = Arc::new(Language::new(
 4704        LanguageConfig::default(),
 4705        Some(tree_sitter_rust::LANGUAGE.into()),
 4706    ));
 4707    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4708
 4709    // Cut an indented block, without the leading whitespace.
 4710    cx.set_state(indoc! {"
 4711        const a: B = (
 4712            c(),
 4713            «d(
 4714                e,
 4715                f
 4716            )ˇ»
 4717        );
 4718    "});
 4719    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4720    cx.assert_editor_state(indoc! {"
 4721        const a: B = (
 4722            c(),
 4723            ˇ
 4724        );
 4725    "});
 4726
 4727    // Paste it at the same position.
 4728    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4729    cx.assert_editor_state(indoc! {"
 4730        const a: B = (
 4731            c(),
 4732            d(
 4733                e,
 4734                f
 4735 4736        );
 4737    "});
 4738
 4739    // Paste it at a line with a lower indent level.
 4740    cx.set_state(indoc! {"
 4741        ˇ
 4742        const a: B = (
 4743            c(),
 4744        );
 4745    "});
 4746    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4747    cx.assert_editor_state(indoc! {"
 4748        d(
 4749            e,
 4750            f
 4751 4752        const a: B = (
 4753            c(),
 4754        );
 4755    "});
 4756
 4757    // Cut an indented block, with the leading whitespace.
 4758    cx.set_state(indoc! {"
 4759        const a: B = (
 4760            c(),
 4761        «    d(
 4762                e,
 4763                f
 4764            )
 4765        ˇ»);
 4766    "});
 4767    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4768    cx.assert_editor_state(indoc! {"
 4769        const a: B = (
 4770            c(),
 4771        ˇ);
 4772    "});
 4773
 4774    // Paste it at the same position.
 4775    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4776    cx.assert_editor_state(indoc! {"
 4777        const a: B = (
 4778            c(),
 4779            d(
 4780                e,
 4781                f
 4782            )
 4783        ˇ);
 4784    "});
 4785
 4786    // Paste it at a line with a higher indent level.
 4787    cx.set_state(indoc! {"
 4788        const a: B = (
 4789            c(),
 4790            d(
 4791                e,
 4792 4793            )
 4794        );
 4795    "});
 4796    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4797    cx.assert_editor_state(indoc! {"
 4798        const a: B = (
 4799            c(),
 4800            d(
 4801                e,
 4802                f    d(
 4803                    e,
 4804                    f
 4805                )
 4806        ˇ
 4807            )
 4808        );
 4809    "});
 4810}
 4811
 4812#[gpui::test]
 4813fn test_select_all(cx: &mut TestAppContext) {
 4814    init_test(cx, |_| {});
 4815
 4816    let editor = cx.add_window(|window, cx| {
 4817        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4818        build_editor(buffer, window, cx)
 4819    });
 4820    _ = editor.update(cx, |editor, window, cx| {
 4821        editor.select_all(&SelectAll, window, cx);
 4822        assert_eq!(
 4823            editor.selections.display_ranges(cx),
 4824            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4825        );
 4826    });
 4827}
 4828
 4829#[gpui::test]
 4830fn test_select_line(cx: &mut TestAppContext) {
 4831    init_test(cx, |_| {});
 4832
 4833    let editor = cx.add_window(|window, cx| {
 4834        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4835        build_editor(buffer, window, cx)
 4836    });
 4837    _ = editor.update(cx, |editor, window, cx| {
 4838        editor.change_selections(None, window, cx, |s| {
 4839            s.select_display_ranges([
 4840                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4841                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4842                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4843                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4844            ])
 4845        });
 4846        editor.select_line(&SelectLine, window, cx);
 4847        assert_eq!(
 4848            editor.selections.display_ranges(cx),
 4849            vec![
 4850                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4851                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4852            ]
 4853        );
 4854    });
 4855
 4856    _ = editor.update(cx, |editor, window, cx| {
 4857        editor.select_line(&SelectLine, window, cx);
 4858        assert_eq!(
 4859            editor.selections.display_ranges(cx),
 4860            vec![
 4861                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4862                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4863            ]
 4864        );
 4865    });
 4866
 4867    _ = editor.update(cx, |editor, window, cx| {
 4868        editor.select_line(&SelectLine, window, cx);
 4869        assert_eq!(
 4870            editor.selections.display_ranges(cx),
 4871            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4872        );
 4873    });
 4874}
 4875
 4876#[gpui::test]
 4877fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4878    init_test(cx, |_| {});
 4879
 4880    let editor = cx.add_window(|window, cx| {
 4881        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4882        build_editor(buffer, window, cx)
 4883    });
 4884    _ = editor.update(cx, |editor, window, cx| {
 4885        editor.fold_creases(
 4886            vec![
 4887                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4888                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4889                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4890            ],
 4891            true,
 4892            window,
 4893            cx,
 4894        );
 4895        editor.change_selections(None, window, cx, |s| {
 4896            s.select_display_ranges([
 4897                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4898                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4899                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4900                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4901            ])
 4902        });
 4903        assert_eq!(
 4904            editor.display_text(cx),
 4905            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4906        );
 4907    });
 4908
 4909    _ = editor.update(cx, |editor, window, cx| {
 4910        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4911        assert_eq!(
 4912            editor.display_text(cx),
 4913            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4914        );
 4915        assert_eq!(
 4916            editor.selections.display_ranges(cx),
 4917            [
 4918                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4919                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4920                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4921                DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(5), 4)
 4922            ]
 4923        );
 4924    });
 4925
 4926    _ = editor.update(cx, |editor, window, cx| {
 4927        editor.change_selections(None, window, cx, |s| {
 4928            s.select_display_ranges([
 4929                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 4930            ])
 4931        });
 4932        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4933        assert_eq!(
 4934            editor.display_text(cx),
 4935            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 4936        );
 4937        assert_eq!(
 4938            editor.selections.display_ranges(cx),
 4939            [
 4940                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 4941                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 4942                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 4943                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 4944                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 4945                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 4946                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5),
 4947                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(7), 0)
 4948            ]
 4949        );
 4950    });
 4951}
 4952
 4953#[gpui::test]
 4954async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 4955    init_test(cx, |_| {});
 4956
 4957    let mut cx = EditorTestContext::new(cx).await;
 4958
 4959    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
 4960    cx.set_state(indoc!(
 4961        r#"abc
 4962           defˇghi
 4963
 4964           jk
 4965           nlmo
 4966           "#
 4967    ));
 4968
 4969    cx.update_editor(|editor, window, cx| {
 4970        editor.add_selection_above(&Default::default(), window, cx);
 4971    });
 4972
 4973    cx.assert_editor_state(indoc!(
 4974        r#"abcˇ
 4975           defˇghi
 4976
 4977           jk
 4978           nlmo
 4979           "#
 4980    ));
 4981
 4982    cx.update_editor(|editor, window, cx| {
 4983        editor.add_selection_above(&Default::default(), window, cx);
 4984    });
 4985
 4986    cx.assert_editor_state(indoc!(
 4987        r#"abcˇ
 4988            defˇghi
 4989
 4990            jk
 4991            nlmo
 4992            "#
 4993    ));
 4994
 4995    cx.update_editor(|editor, window, cx| {
 4996        editor.add_selection_below(&Default::default(), window, cx);
 4997    });
 4998
 4999    cx.assert_editor_state(indoc!(
 5000        r#"abc
 5001           defˇghi
 5002
 5003           jk
 5004           nlmo
 5005           "#
 5006    ));
 5007
 5008    cx.update_editor(|editor, window, cx| {
 5009        editor.undo_selection(&Default::default(), window, cx);
 5010    });
 5011
 5012    cx.assert_editor_state(indoc!(
 5013        r#"abcˇ
 5014           defˇghi
 5015
 5016           jk
 5017           nlmo
 5018           "#
 5019    ));
 5020
 5021    cx.update_editor(|editor, window, cx| {
 5022        editor.redo_selection(&Default::default(), window, cx);
 5023    });
 5024
 5025    cx.assert_editor_state(indoc!(
 5026        r#"abc
 5027           defˇghi
 5028
 5029           jk
 5030           nlmo
 5031           "#
 5032    ));
 5033
 5034    cx.update_editor(|editor, window, cx| {
 5035        editor.add_selection_below(&Default::default(), window, cx);
 5036    });
 5037
 5038    cx.assert_editor_state(indoc!(
 5039        r#"abc
 5040           defˇghi
 5041
 5042           jk
 5043           nlmˇo
 5044           "#
 5045    ));
 5046
 5047    cx.update_editor(|editor, window, cx| {
 5048        editor.add_selection_below(&Default::default(), window, cx);
 5049    });
 5050
 5051    cx.assert_editor_state(indoc!(
 5052        r#"abc
 5053           defˇghi
 5054
 5055           jk
 5056           nlmˇo
 5057           "#
 5058    ));
 5059
 5060    // change selections
 5061    cx.set_state(indoc!(
 5062        r#"abc
 5063           def«ˇg»hi
 5064
 5065           jk
 5066           nlmo
 5067           "#
 5068    ));
 5069
 5070    cx.update_editor(|editor, window, cx| {
 5071        editor.add_selection_below(&Default::default(), window, cx);
 5072    });
 5073
 5074    cx.assert_editor_state(indoc!(
 5075        r#"abc
 5076           def«ˇg»hi
 5077
 5078           jk
 5079           nlm«ˇo»
 5080           "#
 5081    ));
 5082
 5083    cx.update_editor(|editor, window, cx| {
 5084        editor.add_selection_below(&Default::default(), window, cx);
 5085    });
 5086
 5087    cx.assert_editor_state(indoc!(
 5088        r#"abc
 5089           def«ˇg»hi
 5090
 5091           jk
 5092           nlm«ˇo»
 5093           "#
 5094    ));
 5095
 5096    cx.update_editor(|editor, window, cx| {
 5097        editor.add_selection_above(&Default::default(), window, cx);
 5098    });
 5099
 5100    cx.assert_editor_state(indoc!(
 5101        r#"abc
 5102           def«ˇg»hi
 5103
 5104           jk
 5105           nlmo
 5106           "#
 5107    ));
 5108
 5109    cx.update_editor(|editor, window, cx| {
 5110        editor.add_selection_above(&Default::default(), window, cx);
 5111    });
 5112
 5113    cx.assert_editor_state(indoc!(
 5114        r#"abc
 5115           def«ˇg»hi
 5116
 5117           jk
 5118           nlmo
 5119           "#
 5120    ));
 5121
 5122    // Change selections again
 5123    cx.set_state(indoc!(
 5124        r#"a«bc
 5125           defgˇ»hi
 5126
 5127           jk
 5128           nlmo
 5129           "#
 5130    ));
 5131
 5132    cx.update_editor(|editor, window, cx| {
 5133        editor.add_selection_below(&Default::default(), window, cx);
 5134    });
 5135
 5136    cx.assert_editor_state(indoc!(
 5137        r#"a«bcˇ»
 5138           d«efgˇ»hi
 5139
 5140           j«kˇ»
 5141           nlmo
 5142           "#
 5143    ));
 5144
 5145    cx.update_editor(|editor, window, cx| {
 5146        editor.add_selection_below(&Default::default(), window, cx);
 5147    });
 5148    cx.assert_editor_state(indoc!(
 5149        r#"a«bcˇ»
 5150           d«efgˇ»hi
 5151
 5152           j«kˇ»
 5153           n«lmoˇ»
 5154           "#
 5155    ));
 5156    cx.update_editor(|editor, window, cx| {
 5157        editor.add_selection_above(&Default::default(), window, cx);
 5158    });
 5159
 5160    cx.assert_editor_state(indoc!(
 5161        r#"a«bcˇ»
 5162           d«efgˇ»hi
 5163
 5164           j«kˇ»
 5165           nlmo
 5166           "#
 5167    ));
 5168
 5169    // Change selections again
 5170    cx.set_state(indoc!(
 5171        r#"abc
 5172           d«ˇefghi
 5173
 5174           jk
 5175           nlm»o
 5176           "#
 5177    ));
 5178
 5179    cx.update_editor(|editor, window, cx| {
 5180        editor.add_selection_above(&Default::default(), window, cx);
 5181    });
 5182
 5183    cx.assert_editor_state(indoc!(
 5184        r#"a«ˇbc»
 5185           d«ˇef»ghi
 5186
 5187           j«ˇk»
 5188           n«ˇlm»o
 5189           "#
 5190    ));
 5191
 5192    cx.update_editor(|editor, window, cx| {
 5193        editor.add_selection_below(&Default::default(), window, cx);
 5194    });
 5195
 5196    cx.assert_editor_state(indoc!(
 5197        r#"abc
 5198           d«ˇef»ghi
 5199
 5200           j«ˇk»
 5201           n«ˇlm»o
 5202           "#
 5203    ));
 5204}
 5205
 5206#[gpui::test]
 5207async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5208    init_test(cx, |_| {});
 5209
 5210    let mut cx = EditorTestContext::new(cx).await;
 5211    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5212
 5213    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5214        .unwrap();
 5215    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5216
 5217    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5218        .unwrap();
 5219    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5220
 5221    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5222    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5223
 5224    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5225    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5226
 5227    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5228        .unwrap();
 5229    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5230
 5231    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5232        .unwrap();
 5233    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5234}
 5235
 5236#[gpui::test]
 5237async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5238    init_test(cx, |_| {});
 5239
 5240    let mut cx = EditorTestContext::new(cx).await;
 5241
 5242    // Test caret-only selections
 5243    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5244
 5245    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5246        .unwrap();
 5247    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5248
 5249    // Test left-to-right selections
 5250    cx.set_state("abc\n«abcˇ»\nabc");
 5251
 5252    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5253        .unwrap();
 5254    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5255
 5256    // Test right-to-left selections
 5257    cx.set_state("abc\n«ˇabc»\nabc");
 5258
 5259    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5260        .unwrap();
 5261    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5262}
 5263
 5264#[gpui::test]
 5265async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5266    init_test(cx, |_| {});
 5267
 5268    let mut cx = EditorTestContext::new(cx).await;
 5269    cx.set_state(
 5270        r#"let foo = 2;
 5271lˇet foo = 2;
 5272let fooˇ = 2;
 5273let foo = 2;
 5274let foo = ˇ2;"#,
 5275    );
 5276
 5277    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5278        .unwrap();
 5279    cx.assert_editor_state(
 5280        r#"let foo = 2;
 5281«letˇ» foo = 2;
 5282let «fooˇ» = 2;
 5283let foo = 2;
 5284let foo = «2ˇ»;"#,
 5285    );
 5286
 5287    // noop for multiple selections with different contents
 5288    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5289        .unwrap();
 5290    cx.assert_editor_state(
 5291        r#"let foo = 2;
 5292«letˇ» foo = 2;
 5293let «fooˇ» = 2;
 5294let foo = 2;
 5295let foo = «2ˇ»;"#,
 5296    );
 5297}
 5298
 5299#[gpui::test]
 5300async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5301    init_test(cx, |_| {});
 5302
 5303    let mut cx =
 5304        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5305
 5306    cx.assert_editor_state(indoc! {"
 5307        ˇbbb
 5308        ccc
 5309
 5310        bbb
 5311        ccc
 5312        "});
 5313    cx.dispatch_action(SelectPrevious::default());
 5314    cx.assert_editor_state(indoc! {"
 5315                «bbbˇ»
 5316                ccc
 5317
 5318                bbb
 5319                ccc
 5320                "});
 5321    cx.dispatch_action(SelectPrevious::default());
 5322    cx.assert_editor_state(indoc! {"
 5323                «bbbˇ»
 5324                ccc
 5325
 5326                «bbbˇ»
 5327                ccc
 5328                "});
 5329}
 5330
 5331#[gpui::test]
 5332async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5333    init_test(cx, |_| {});
 5334
 5335    let mut cx = EditorTestContext::new(cx).await;
 5336    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5337
 5338    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5339        .unwrap();
 5340    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5341
 5342    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5343        .unwrap();
 5344    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5345
 5346    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5347    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5348
 5349    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5350    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5351
 5352    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5353        .unwrap();
 5354    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5355
 5356    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5357        .unwrap();
 5358    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5359
 5360    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5361        .unwrap();
 5362    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5363}
 5364
 5365#[gpui::test]
 5366async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5367    init_test(cx, |_| {});
 5368
 5369    let mut cx = EditorTestContext::new(cx).await;
 5370    cx.set_state(
 5371        r#"let foo = 2;
 5372lˇet foo = 2;
 5373let fooˇ = 2;
 5374let foo = 2;
 5375let foo = ˇ2;"#,
 5376    );
 5377
 5378    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5379        .unwrap();
 5380    cx.assert_editor_state(
 5381        r#"let foo = 2;
 5382«letˇ» foo = 2;
 5383let «fooˇ» = 2;
 5384let foo = 2;
 5385let foo = «2ˇ»;"#,
 5386    );
 5387
 5388    // noop for multiple selections with different contents
 5389    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5390        .unwrap();
 5391    cx.assert_editor_state(
 5392        r#"let foo = 2;
 5393«letˇ» foo = 2;
 5394let «fooˇ» = 2;
 5395let foo = 2;
 5396let foo = «2ˇ»;"#,
 5397    );
 5398}
 5399
 5400#[gpui::test]
 5401async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5402    init_test(cx, |_| {});
 5403
 5404    let mut cx = EditorTestContext::new(cx).await;
 5405    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5406
 5407    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5408        .unwrap();
 5409    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5410
 5411    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5412        .unwrap();
 5413    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5414
 5415    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5416    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5417
 5418    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5419    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5420
 5421    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5422        .unwrap();
 5423    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5424
 5425    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5426        .unwrap();
 5427    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5428}
 5429
 5430#[gpui::test]
 5431async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5432    init_test(cx, |_| {});
 5433
 5434    let language = Arc::new(Language::new(
 5435        LanguageConfig::default(),
 5436        Some(tree_sitter_rust::LANGUAGE.into()),
 5437    ));
 5438
 5439    let text = r#"
 5440        use mod1::mod2::{mod3, mod4};
 5441
 5442        fn fn_1(param1: bool, param2: &str) {
 5443            let var1 = "text";
 5444        }
 5445    "#
 5446    .unindent();
 5447
 5448    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5449    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5450    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5451
 5452    editor
 5453        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5454        .await;
 5455
 5456    editor.update_in(cx, |editor, window, cx| {
 5457        editor.change_selections(None, window, cx, |s| {
 5458            s.select_display_ranges([
 5459                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5460                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5461                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5462            ]);
 5463        });
 5464        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5465    });
 5466    editor.update(cx, |editor, cx| {
 5467        assert_text_with_selections(
 5468            editor,
 5469            indoc! {r#"
 5470                use mod1::mod2::{mod3, «mod4ˇ»};
 5471
 5472                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5473                    let var1 = "«textˇ»";
 5474                }
 5475            "#},
 5476            cx,
 5477        );
 5478    });
 5479
 5480    editor.update_in(cx, |editor, window, cx| {
 5481        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5482    });
 5483    editor.update(cx, |editor, cx| {
 5484        assert_text_with_selections(
 5485            editor,
 5486            indoc! {r#"
 5487                use mod1::mod2::«{mod3, mod4}ˇ»;
 5488
 5489                «ˇfn fn_1(param1: bool, param2: &str) {
 5490                    let var1 = "text";
 5491 5492            "#},
 5493            cx,
 5494        );
 5495    });
 5496
 5497    editor.update_in(cx, |editor, window, cx| {
 5498        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5499    });
 5500    assert_eq!(
 5501        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5502        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5503    );
 5504
 5505    // Trying to expand the selected syntax node one more time has no effect.
 5506    editor.update_in(cx, |editor, window, cx| {
 5507        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5508    });
 5509    assert_eq!(
 5510        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5511        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5512    );
 5513
 5514    editor.update_in(cx, |editor, window, cx| {
 5515        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5516    });
 5517    editor.update(cx, |editor, cx| {
 5518        assert_text_with_selections(
 5519            editor,
 5520            indoc! {r#"
 5521                use mod1::mod2::«{mod3, mod4}ˇ»;
 5522
 5523                «ˇfn fn_1(param1: bool, param2: &str) {
 5524                    let var1 = "text";
 5525 5526            "#},
 5527            cx,
 5528        );
 5529    });
 5530
 5531    editor.update_in(cx, |editor, window, cx| {
 5532        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5533    });
 5534    editor.update(cx, |editor, cx| {
 5535        assert_text_with_selections(
 5536            editor,
 5537            indoc! {r#"
 5538                use mod1::mod2::{mod3, «mod4ˇ»};
 5539
 5540                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5541                    let var1 = "«textˇ»";
 5542                }
 5543            "#},
 5544            cx,
 5545        );
 5546    });
 5547
 5548    editor.update_in(cx, |editor, window, cx| {
 5549        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5550    });
 5551    editor.update(cx, |editor, cx| {
 5552        assert_text_with_selections(
 5553            editor,
 5554            indoc! {r#"
 5555                use mod1::mod2::{mod3, mo«ˇ»d4};
 5556
 5557                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5558                    let var1 = "te«ˇ»xt";
 5559                }
 5560            "#},
 5561            cx,
 5562        );
 5563    });
 5564
 5565    // Trying to shrink the selected syntax node one more time has no effect.
 5566    editor.update_in(cx, |editor, window, cx| {
 5567        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5568    });
 5569    editor.update_in(cx, |editor, _, cx| {
 5570        assert_text_with_selections(
 5571            editor,
 5572            indoc! {r#"
 5573                use mod1::mod2::{mod3, mo«ˇ»d4};
 5574
 5575                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5576                    let var1 = "te«ˇ»xt";
 5577                }
 5578            "#},
 5579            cx,
 5580        );
 5581    });
 5582
 5583    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5584    // a fold.
 5585    editor.update_in(cx, |editor, window, cx| {
 5586        editor.fold_creases(
 5587            vec![
 5588                Crease::simple(
 5589                    Point::new(0, 21)..Point::new(0, 24),
 5590                    FoldPlaceholder::test(),
 5591                ),
 5592                Crease::simple(
 5593                    Point::new(3, 20)..Point::new(3, 22),
 5594                    FoldPlaceholder::test(),
 5595                ),
 5596            ],
 5597            true,
 5598            window,
 5599            cx,
 5600        );
 5601        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5602    });
 5603    editor.update(cx, |editor, cx| {
 5604        assert_text_with_selections(
 5605            editor,
 5606            indoc! {r#"
 5607                use mod1::mod2::«{mod3, mod4}ˇ»;
 5608
 5609                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5610                    «let var1 = "text";ˇ»
 5611                }
 5612            "#},
 5613            cx,
 5614        );
 5615    });
 5616}
 5617
 5618#[gpui::test]
 5619async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5620    init_test(cx, |_| {});
 5621
 5622    let base_text = r#"
 5623        impl A {
 5624            // this is an uncommitted comment
 5625
 5626            fn b() {
 5627                c();
 5628            }
 5629
 5630            // this is another uncommitted comment
 5631
 5632            fn d() {
 5633                // e
 5634                // f
 5635            }
 5636        }
 5637
 5638        fn g() {
 5639            // h
 5640        }
 5641    "#
 5642    .unindent();
 5643
 5644    let text = r#"
 5645        ˇimpl A {
 5646
 5647            fn b() {
 5648                c();
 5649            }
 5650
 5651            fn d() {
 5652                // e
 5653                // f
 5654            }
 5655        }
 5656
 5657        fn g() {
 5658            // h
 5659        }
 5660    "#
 5661    .unindent();
 5662
 5663    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5664    cx.set_state(&text);
 5665    cx.set_diff_base(&base_text);
 5666    cx.update_editor(|editor, window, cx| {
 5667        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5668    });
 5669
 5670    cx.assert_state_with_diff(
 5671        "
 5672        ˇimpl A {
 5673      -     // this is an uncommitted comment
 5674
 5675            fn b() {
 5676                c();
 5677            }
 5678
 5679      -     // this is another uncommitted comment
 5680      -
 5681            fn d() {
 5682                // e
 5683                // f
 5684            }
 5685        }
 5686
 5687        fn g() {
 5688            // h
 5689        }
 5690    "
 5691        .unindent(),
 5692    );
 5693
 5694    let expected_display_text = "
 5695        impl A {
 5696            // this is an uncommitted comment
 5697
 5698            fn b() {
 5699 5700            }
 5701
 5702            // this is another uncommitted comment
 5703
 5704            fn d() {
 5705 5706            }
 5707        }
 5708
 5709        fn g() {
 5710 5711        }
 5712        "
 5713    .unindent();
 5714
 5715    cx.update_editor(|editor, window, cx| {
 5716        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5717        assert_eq!(editor.display_text(cx), expected_display_text);
 5718    });
 5719}
 5720
 5721#[gpui::test]
 5722async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5723    init_test(cx, |_| {});
 5724
 5725    let language = Arc::new(
 5726        Language::new(
 5727            LanguageConfig {
 5728                brackets: BracketPairConfig {
 5729                    pairs: vec![
 5730                        BracketPair {
 5731                            start: "{".to_string(),
 5732                            end: "}".to_string(),
 5733                            close: false,
 5734                            surround: false,
 5735                            newline: true,
 5736                        },
 5737                        BracketPair {
 5738                            start: "(".to_string(),
 5739                            end: ")".to_string(),
 5740                            close: false,
 5741                            surround: false,
 5742                            newline: true,
 5743                        },
 5744                    ],
 5745                    ..Default::default()
 5746                },
 5747                ..Default::default()
 5748            },
 5749            Some(tree_sitter_rust::LANGUAGE.into()),
 5750        )
 5751        .with_indents_query(
 5752            r#"
 5753                (_ "(" ")" @end) @indent
 5754                (_ "{" "}" @end) @indent
 5755            "#,
 5756        )
 5757        .unwrap(),
 5758    );
 5759
 5760    let text = "fn a() {}";
 5761
 5762    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5763    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5764    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5765    editor
 5766        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5767        .await;
 5768
 5769    editor.update_in(cx, |editor, window, cx| {
 5770        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5771        editor.newline(&Newline, window, cx);
 5772        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5773        assert_eq!(
 5774            editor.selections.ranges(cx),
 5775            &[
 5776                Point::new(1, 4)..Point::new(1, 4),
 5777                Point::new(3, 4)..Point::new(3, 4),
 5778                Point::new(5, 0)..Point::new(5, 0)
 5779            ]
 5780        );
 5781    });
 5782}
 5783
 5784#[gpui::test]
 5785async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5786    init_test(cx, |_| {});
 5787
 5788    {
 5789        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5790        cx.set_state(indoc! {"
 5791            impl A {
 5792
 5793                fn b() {}
 5794
 5795            «fn c() {
 5796
 5797            }ˇ»
 5798            }
 5799        "});
 5800
 5801        cx.update_editor(|editor, window, cx| {
 5802            editor.autoindent(&Default::default(), window, cx);
 5803        });
 5804
 5805        cx.assert_editor_state(indoc! {"
 5806            impl A {
 5807
 5808                fn b() {}
 5809
 5810                «fn c() {
 5811
 5812                }ˇ»
 5813            }
 5814        "});
 5815    }
 5816
 5817    {
 5818        let mut cx = EditorTestContext::new_multibuffer(
 5819            cx,
 5820            [indoc! { "
 5821                impl A {
 5822                «
 5823                // a
 5824                fn b(){}
 5825                »
 5826                «
 5827                    }
 5828                    fn c(){}
 5829                »
 5830            "}],
 5831        );
 5832
 5833        let buffer = cx.update_editor(|editor, _, cx| {
 5834            let buffer = editor.buffer().update(cx, |buffer, _| {
 5835                buffer.all_buffers().iter().next().unwrap().clone()
 5836            });
 5837            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5838            buffer
 5839        });
 5840
 5841        cx.run_until_parked();
 5842        cx.update_editor(|editor, window, cx| {
 5843            editor.select_all(&Default::default(), window, cx);
 5844            editor.autoindent(&Default::default(), window, cx)
 5845        });
 5846        cx.run_until_parked();
 5847
 5848        cx.update(|_, cx| {
 5849            pretty_assertions::assert_eq!(
 5850                buffer.read(cx).text(),
 5851                indoc! { "
 5852                    impl A {
 5853
 5854                        // a
 5855                        fn b(){}
 5856
 5857
 5858                    }
 5859                    fn c(){}
 5860
 5861                " }
 5862            )
 5863        });
 5864    }
 5865}
 5866
 5867#[gpui::test]
 5868async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5869    init_test(cx, |_| {});
 5870
 5871    let mut cx = EditorTestContext::new(cx).await;
 5872
 5873    let language = Arc::new(Language::new(
 5874        LanguageConfig {
 5875            brackets: BracketPairConfig {
 5876                pairs: vec![
 5877                    BracketPair {
 5878                        start: "{".to_string(),
 5879                        end: "}".to_string(),
 5880                        close: true,
 5881                        surround: true,
 5882                        newline: true,
 5883                    },
 5884                    BracketPair {
 5885                        start: "(".to_string(),
 5886                        end: ")".to_string(),
 5887                        close: true,
 5888                        surround: true,
 5889                        newline: true,
 5890                    },
 5891                    BracketPair {
 5892                        start: "/*".to_string(),
 5893                        end: " */".to_string(),
 5894                        close: true,
 5895                        surround: true,
 5896                        newline: true,
 5897                    },
 5898                    BracketPair {
 5899                        start: "[".to_string(),
 5900                        end: "]".to_string(),
 5901                        close: false,
 5902                        surround: false,
 5903                        newline: true,
 5904                    },
 5905                    BracketPair {
 5906                        start: "\"".to_string(),
 5907                        end: "\"".to_string(),
 5908                        close: true,
 5909                        surround: true,
 5910                        newline: false,
 5911                    },
 5912                    BracketPair {
 5913                        start: "<".to_string(),
 5914                        end: ">".to_string(),
 5915                        close: false,
 5916                        surround: true,
 5917                        newline: true,
 5918                    },
 5919                ],
 5920                ..Default::default()
 5921            },
 5922            autoclose_before: "})]".to_string(),
 5923            ..Default::default()
 5924        },
 5925        Some(tree_sitter_rust::LANGUAGE.into()),
 5926    ));
 5927
 5928    cx.language_registry().add(language.clone());
 5929    cx.update_buffer(|buffer, cx| {
 5930        buffer.set_language(Some(language), cx);
 5931    });
 5932
 5933    cx.set_state(
 5934        &r#"
 5935            🏀ˇ
 5936            εˇ
 5937            ❤️ˇ
 5938        "#
 5939        .unindent(),
 5940    );
 5941
 5942    // autoclose multiple nested brackets at multiple cursors
 5943    cx.update_editor(|editor, window, cx| {
 5944        editor.handle_input("{", window, cx);
 5945        editor.handle_input("{", window, cx);
 5946        editor.handle_input("{", window, cx);
 5947    });
 5948    cx.assert_editor_state(
 5949        &"
 5950            🏀{{{ˇ}}}
 5951            ε{{{ˇ}}}
 5952            ❤️{{{ˇ}}}
 5953        "
 5954        .unindent(),
 5955    );
 5956
 5957    // insert a different closing bracket
 5958    cx.update_editor(|editor, window, cx| {
 5959        editor.handle_input(")", window, cx);
 5960    });
 5961    cx.assert_editor_state(
 5962        &"
 5963            🏀{{{)ˇ}}}
 5964            ε{{{)ˇ}}}
 5965            ❤️{{{)ˇ}}}
 5966        "
 5967        .unindent(),
 5968    );
 5969
 5970    // skip over the auto-closed brackets when typing a closing bracket
 5971    cx.update_editor(|editor, window, cx| {
 5972        editor.move_right(&MoveRight, window, cx);
 5973        editor.handle_input("}", window, cx);
 5974        editor.handle_input("}", window, cx);
 5975        editor.handle_input("}", window, cx);
 5976    });
 5977    cx.assert_editor_state(
 5978        &"
 5979            🏀{{{)}}}}ˇ
 5980            ε{{{)}}}}ˇ
 5981            ❤️{{{)}}}}ˇ
 5982        "
 5983        .unindent(),
 5984    );
 5985
 5986    // autoclose multi-character pairs
 5987    cx.set_state(
 5988        &"
 5989            ˇ
 5990            ˇ
 5991        "
 5992        .unindent(),
 5993    );
 5994    cx.update_editor(|editor, window, cx| {
 5995        editor.handle_input("/", window, cx);
 5996        editor.handle_input("*", window, cx);
 5997    });
 5998    cx.assert_editor_state(
 5999        &"
 6000            /*ˇ */
 6001            /*ˇ */
 6002        "
 6003        .unindent(),
 6004    );
 6005
 6006    // one cursor autocloses a multi-character pair, one cursor
 6007    // does not autoclose.
 6008    cx.set_state(
 6009        &"
 6010 6011            ˇ
 6012        "
 6013        .unindent(),
 6014    );
 6015    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6016    cx.assert_editor_state(
 6017        &"
 6018            /*ˇ */
 6019 6020        "
 6021        .unindent(),
 6022    );
 6023
 6024    // Don't autoclose if the next character isn't whitespace and isn't
 6025    // listed in the language's "autoclose_before" section.
 6026    cx.set_state("ˇa b");
 6027    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6028    cx.assert_editor_state("{ˇa b");
 6029
 6030    // Don't autoclose if `close` is false for the bracket pair
 6031    cx.set_state("ˇ");
 6032    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6033    cx.assert_editor_state("");
 6034
 6035    // Surround with brackets if text is selected
 6036    cx.set_state("«aˇ» b");
 6037    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6038    cx.assert_editor_state("{«aˇ»} b");
 6039
 6040    // Autclose pair where the start and end characters are the same
 6041    cx.set_state("");
 6042    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6043    cx.assert_editor_state("a\"ˇ\"");
 6044    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6045    cx.assert_editor_state("a\"\"ˇ");
 6046
 6047    // Don't autoclose pair if autoclose is disabled
 6048    cx.set_state("ˇ");
 6049    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6050    cx.assert_editor_state("");
 6051
 6052    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6053    cx.set_state("«aˇ» b");
 6054    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6055    cx.assert_editor_state("<«aˇ»> b");
 6056}
 6057
 6058#[gpui::test]
 6059async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6060    init_test(cx, |settings| {
 6061        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6062    });
 6063
 6064    let mut cx = EditorTestContext::new(cx).await;
 6065
 6066    let language = Arc::new(Language::new(
 6067        LanguageConfig {
 6068            brackets: BracketPairConfig {
 6069                pairs: vec![
 6070                    BracketPair {
 6071                        start: "{".to_string(),
 6072                        end: "}".to_string(),
 6073                        close: true,
 6074                        surround: true,
 6075                        newline: true,
 6076                    },
 6077                    BracketPair {
 6078                        start: "(".to_string(),
 6079                        end: ")".to_string(),
 6080                        close: true,
 6081                        surround: true,
 6082                        newline: true,
 6083                    },
 6084                    BracketPair {
 6085                        start: "[".to_string(),
 6086                        end: "]".to_string(),
 6087                        close: false,
 6088                        surround: false,
 6089                        newline: true,
 6090                    },
 6091                ],
 6092                ..Default::default()
 6093            },
 6094            autoclose_before: "})]".to_string(),
 6095            ..Default::default()
 6096        },
 6097        Some(tree_sitter_rust::LANGUAGE.into()),
 6098    ));
 6099
 6100    cx.language_registry().add(language.clone());
 6101    cx.update_buffer(|buffer, cx| {
 6102        buffer.set_language(Some(language), cx);
 6103    });
 6104
 6105    cx.set_state(
 6106        &"
 6107            ˇ
 6108            ˇ
 6109            ˇ
 6110        "
 6111        .unindent(),
 6112    );
 6113
 6114    // ensure only matching closing brackets are skipped over
 6115    cx.update_editor(|editor, window, cx| {
 6116        editor.handle_input("}", window, cx);
 6117        editor.move_left(&MoveLeft, window, cx);
 6118        editor.handle_input(")", window, cx);
 6119        editor.move_left(&MoveLeft, window, cx);
 6120    });
 6121    cx.assert_editor_state(
 6122        &"
 6123            ˇ)}
 6124            ˇ)}
 6125            ˇ)}
 6126        "
 6127        .unindent(),
 6128    );
 6129
 6130    // skip-over closing brackets at multiple cursors
 6131    cx.update_editor(|editor, window, cx| {
 6132        editor.handle_input(")", window, cx);
 6133        editor.handle_input("}", window, cx);
 6134    });
 6135    cx.assert_editor_state(
 6136        &"
 6137            )}ˇ
 6138            )}ˇ
 6139            )}ˇ
 6140        "
 6141        .unindent(),
 6142    );
 6143
 6144    // ignore non-close brackets
 6145    cx.update_editor(|editor, window, cx| {
 6146        editor.handle_input("]", window, cx);
 6147        editor.move_left(&MoveLeft, window, cx);
 6148        editor.handle_input("]", window, cx);
 6149    });
 6150    cx.assert_editor_state(
 6151        &"
 6152            )}]ˇ]
 6153            )}]ˇ]
 6154            )}]ˇ]
 6155        "
 6156        .unindent(),
 6157    );
 6158}
 6159
 6160#[gpui::test]
 6161async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6162    init_test(cx, |_| {});
 6163
 6164    let mut cx = EditorTestContext::new(cx).await;
 6165
 6166    let html_language = Arc::new(
 6167        Language::new(
 6168            LanguageConfig {
 6169                name: "HTML".into(),
 6170                brackets: BracketPairConfig {
 6171                    pairs: vec![
 6172                        BracketPair {
 6173                            start: "<".into(),
 6174                            end: ">".into(),
 6175                            close: true,
 6176                            ..Default::default()
 6177                        },
 6178                        BracketPair {
 6179                            start: "{".into(),
 6180                            end: "}".into(),
 6181                            close: true,
 6182                            ..Default::default()
 6183                        },
 6184                        BracketPair {
 6185                            start: "(".into(),
 6186                            end: ")".into(),
 6187                            close: true,
 6188                            ..Default::default()
 6189                        },
 6190                    ],
 6191                    ..Default::default()
 6192                },
 6193                autoclose_before: "})]>".into(),
 6194                ..Default::default()
 6195            },
 6196            Some(tree_sitter_html::language()),
 6197        )
 6198        .with_injection_query(
 6199            r#"
 6200            (script_element
 6201                (raw_text) @injection.content
 6202                (#set! injection.language "javascript"))
 6203            "#,
 6204        )
 6205        .unwrap(),
 6206    );
 6207
 6208    let javascript_language = Arc::new(Language::new(
 6209        LanguageConfig {
 6210            name: "JavaScript".into(),
 6211            brackets: BracketPairConfig {
 6212                pairs: vec![
 6213                    BracketPair {
 6214                        start: "/*".into(),
 6215                        end: " */".into(),
 6216                        close: true,
 6217                        ..Default::default()
 6218                    },
 6219                    BracketPair {
 6220                        start: "{".into(),
 6221                        end: "}".into(),
 6222                        close: true,
 6223                        ..Default::default()
 6224                    },
 6225                    BracketPair {
 6226                        start: "(".into(),
 6227                        end: ")".into(),
 6228                        close: true,
 6229                        ..Default::default()
 6230                    },
 6231                ],
 6232                ..Default::default()
 6233            },
 6234            autoclose_before: "})]>".into(),
 6235            ..Default::default()
 6236        },
 6237        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6238    ));
 6239
 6240    cx.language_registry().add(html_language.clone());
 6241    cx.language_registry().add(javascript_language.clone());
 6242
 6243    cx.update_buffer(|buffer, cx| {
 6244        buffer.set_language(Some(html_language), cx);
 6245    });
 6246
 6247    cx.set_state(
 6248        &r#"
 6249            <body>ˇ
 6250                <script>
 6251                    var x = 1;ˇ
 6252                </script>
 6253            </body>ˇ
 6254        "#
 6255        .unindent(),
 6256    );
 6257
 6258    // Precondition: different languages are active at different locations.
 6259    cx.update_editor(|editor, window, cx| {
 6260        let snapshot = editor.snapshot(window, cx);
 6261        let cursors = editor.selections.ranges::<usize>(cx);
 6262        let languages = cursors
 6263            .iter()
 6264            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6265            .collect::<Vec<_>>();
 6266        assert_eq!(
 6267            languages,
 6268            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6269        );
 6270    });
 6271
 6272    // Angle brackets autoclose in HTML, but not JavaScript.
 6273    cx.update_editor(|editor, window, cx| {
 6274        editor.handle_input("<", window, cx);
 6275        editor.handle_input("a", window, cx);
 6276    });
 6277    cx.assert_editor_state(
 6278        &r#"
 6279            <body><aˇ>
 6280                <script>
 6281                    var x = 1;<aˇ
 6282                </script>
 6283            </body><aˇ>
 6284        "#
 6285        .unindent(),
 6286    );
 6287
 6288    // Curly braces and parens autoclose in both HTML and JavaScript.
 6289    cx.update_editor(|editor, window, cx| {
 6290        editor.handle_input(" b=", window, cx);
 6291        editor.handle_input("{", window, cx);
 6292        editor.handle_input("c", window, cx);
 6293        editor.handle_input("(", window, cx);
 6294    });
 6295    cx.assert_editor_state(
 6296        &r#"
 6297            <body><a b={c(ˇ)}>
 6298                <script>
 6299                    var x = 1;<a b={c(ˇ)}
 6300                </script>
 6301            </body><a b={c(ˇ)}>
 6302        "#
 6303        .unindent(),
 6304    );
 6305
 6306    // Brackets that were already autoclosed are skipped.
 6307    cx.update_editor(|editor, window, cx| {
 6308        editor.handle_input(")", window, cx);
 6309        editor.handle_input("d", window, cx);
 6310        editor.handle_input("}", window, cx);
 6311    });
 6312    cx.assert_editor_state(
 6313        &r#"
 6314            <body><a b={c()d}ˇ>
 6315                <script>
 6316                    var x = 1;<a b={c()d}ˇ
 6317                </script>
 6318            </body><a b={c()d}ˇ>
 6319        "#
 6320        .unindent(),
 6321    );
 6322    cx.update_editor(|editor, window, cx| {
 6323        editor.handle_input(">", window, cx);
 6324    });
 6325    cx.assert_editor_state(
 6326        &r#"
 6327            <body><a b={c()d}>ˇ
 6328                <script>
 6329                    var x = 1;<a b={c()d}>ˇ
 6330                </script>
 6331            </body><a b={c()d}>ˇ
 6332        "#
 6333        .unindent(),
 6334    );
 6335
 6336    // Reset
 6337    cx.set_state(
 6338        &r#"
 6339            <body>ˇ
 6340                <script>
 6341                    var x = 1;ˇ
 6342                </script>
 6343            </body>ˇ
 6344        "#
 6345        .unindent(),
 6346    );
 6347
 6348    cx.update_editor(|editor, window, cx| {
 6349        editor.handle_input("<", window, cx);
 6350    });
 6351    cx.assert_editor_state(
 6352        &r#"
 6353            <body><ˇ>
 6354                <script>
 6355                    var x = 1;<ˇ
 6356                </script>
 6357            </body><ˇ>
 6358        "#
 6359        .unindent(),
 6360    );
 6361
 6362    // When backspacing, the closing angle brackets are removed.
 6363    cx.update_editor(|editor, window, cx| {
 6364        editor.backspace(&Backspace, window, cx);
 6365    });
 6366    cx.assert_editor_state(
 6367        &r#"
 6368            <body>ˇ
 6369                <script>
 6370                    var x = 1;ˇ
 6371                </script>
 6372            </body>ˇ
 6373        "#
 6374        .unindent(),
 6375    );
 6376
 6377    // Block comments autoclose in JavaScript, but not HTML.
 6378    cx.update_editor(|editor, window, cx| {
 6379        editor.handle_input("/", window, cx);
 6380        editor.handle_input("*", window, cx);
 6381    });
 6382    cx.assert_editor_state(
 6383        &r#"
 6384            <body>/*ˇ
 6385                <script>
 6386                    var x = 1;/*ˇ */
 6387                </script>
 6388            </body>/*ˇ
 6389        "#
 6390        .unindent(),
 6391    );
 6392}
 6393
 6394#[gpui::test]
 6395async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6396    init_test(cx, |_| {});
 6397
 6398    let mut cx = EditorTestContext::new(cx).await;
 6399
 6400    let rust_language = Arc::new(
 6401        Language::new(
 6402            LanguageConfig {
 6403                name: "Rust".into(),
 6404                brackets: serde_json::from_value(json!([
 6405                    { "start": "{", "end": "}", "close": true, "newline": true },
 6406                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6407                ]))
 6408                .unwrap(),
 6409                autoclose_before: "})]>".into(),
 6410                ..Default::default()
 6411            },
 6412            Some(tree_sitter_rust::LANGUAGE.into()),
 6413        )
 6414        .with_override_query("(string_literal) @string")
 6415        .unwrap(),
 6416    );
 6417
 6418    cx.language_registry().add(rust_language.clone());
 6419    cx.update_buffer(|buffer, cx| {
 6420        buffer.set_language(Some(rust_language), cx);
 6421    });
 6422
 6423    cx.set_state(
 6424        &r#"
 6425            let x = ˇ
 6426        "#
 6427        .unindent(),
 6428    );
 6429
 6430    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6431    cx.update_editor(|editor, window, cx| {
 6432        editor.handle_input("\"", window, cx);
 6433    });
 6434    cx.assert_editor_state(
 6435        &r#"
 6436            let x = "ˇ"
 6437        "#
 6438        .unindent(),
 6439    );
 6440
 6441    // Inserting another quotation mark. The cursor moves across the existing
 6442    // automatically-inserted quotation mark.
 6443    cx.update_editor(|editor, window, cx| {
 6444        editor.handle_input("\"", window, cx);
 6445    });
 6446    cx.assert_editor_state(
 6447        &r#"
 6448            let x = ""ˇ
 6449        "#
 6450        .unindent(),
 6451    );
 6452
 6453    // Reset
 6454    cx.set_state(
 6455        &r#"
 6456            let x = ˇ
 6457        "#
 6458        .unindent(),
 6459    );
 6460
 6461    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6462    cx.update_editor(|editor, window, cx| {
 6463        editor.handle_input("\"", window, cx);
 6464        editor.handle_input(" ", window, cx);
 6465        editor.move_left(&Default::default(), window, cx);
 6466        editor.handle_input("\\", window, cx);
 6467        editor.handle_input("\"", window, cx);
 6468    });
 6469    cx.assert_editor_state(
 6470        &r#"
 6471            let x = "\"ˇ "
 6472        "#
 6473        .unindent(),
 6474    );
 6475
 6476    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6477    // mark. Nothing is inserted.
 6478    cx.update_editor(|editor, window, cx| {
 6479        editor.move_right(&Default::default(), window, cx);
 6480        editor.handle_input("\"", window, cx);
 6481    });
 6482    cx.assert_editor_state(
 6483        &r#"
 6484            let x = "\" "ˇ
 6485        "#
 6486        .unindent(),
 6487    );
 6488}
 6489
 6490#[gpui::test]
 6491async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6492    init_test(cx, |_| {});
 6493
 6494    let language = Arc::new(Language::new(
 6495        LanguageConfig {
 6496            brackets: BracketPairConfig {
 6497                pairs: vec![
 6498                    BracketPair {
 6499                        start: "{".to_string(),
 6500                        end: "}".to_string(),
 6501                        close: true,
 6502                        surround: true,
 6503                        newline: true,
 6504                    },
 6505                    BracketPair {
 6506                        start: "/* ".to_string(),
 6507                        end: "*/".to_string(),
 6508                        close: true,
 6509                        surround: true,
 6510                        ..Default::default()
 6511                    },
 6512                ],
 6513                ..Default::default()
 6514            },
 6515            ..Default::default()
 6516        },
 6517        Some(tree_sitter_rust::LANGUAGE.into()),
 6518    ));
 6519
 6520    let text = r#"
 6521        a
 6522        b
 6523        c
 6524    "#
 6525    .unindent();
 6526
 6527    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6528    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6529    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6530    editor
 6531        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6532        .await;
 6533
 6534    editor.update_in(cx, |editor, window, cx| {
 6535        editor.change_selections(None, window, cx, |s| {
 6536            s.select_display_ranges([
 6537                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6538                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6539                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6540            ])
 6541        });
 6542
 6543        editor.handle_input("{", window, cx);
 6544        editor.handle_input("{", window, cx);
 6545        editor.handle_input("{", window, cx);
 6546        assert_eq!(
 6547            editor.text(cx),
 6548            "
 6549                {{{a}}}
 6550                {{{b}}}
 6551                {{{c}}}
 6552            "
 6553            .unindent()
 6554        );
 6555        assert_eq!(
 6556            editor.selections.display_ranges(cx),
 6557            [
 6558                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6559                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6560                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6561            ]
 6562        );
 6563
 6564        editor.undo(&Undo, window, cx);
 6565        editor.undo(&Undo, window, cx);
 6566        editor.undo(&Undo, window, cx);
 6567        assert_eq!(
 6568            editor.text(cx),
 6569            "
 6570                a
 6571                b
 6572                c
 6573            "
 6574            .unindent()
 6575        );
 6576        assert_eq!(
 6577            editor.selections.display_ranges(cx),
 6578            [
 6579                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6580                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6581                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6582            ]
 6583        );
 6584
 6585        // Ensure inserting the first character of a multi-byte bracket pair
 6586        // doesn't surround the selections with the bracket.
 6587        editor.handle_input("/", window, cx);
 6588        assert_eq!(
 6589            editor.text(cx),
 6590            "
 6591                /
 6592                /
 6593                /
 6594            "
 6595            .unindent()
 6596        );
 6597        assert_eq!(
 6598            editor.selections.display_ranges(cx),
 6599            [
 6600                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6601                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6602                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6603            ]
 6604        );
 6605
 6606        editor.undo(&Undo, window, cx);
 6607        assert_eq!(
 6608            editor.text(cx),
 6609            "
 6610                a
 6611                b
 6612                c
 6613            "
 6614            .unindent()
 6615        );
 6616        assert_eq!(
 6617            editor.selections.display_ranges(cx),
 6618            [
 6619                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6620                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6621                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6622            ]
 6623        );
 6624
 6625        // Ensure inserting the last character of a multi-byte bracket pair
 6626        // doesn't surround the selections with the bracket.
 6627        editor.handle_input("*", window, cx);
 6628        assert_eq!(
 6629            editor.text(cx),
 6630            "
 6631                *
 6632                *
 6633                *
 6634            "
 6635            .unindent()
 6636        );
 6637        assert_eq!(
 6638            editor.selections.display_ranges(cx),
 6639            [
 6640                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6641                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6642                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6643            ]
 6644        );
 6645    });
 6646}
 6647
 6648#[gpui::test]
 6649async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6650    init_test(cx, |_| {});
 6651
 6652    let language = Arc::new(Language::new(
 6653        LanguageConfig {
 6654            brackets: BracketPairConfig {
 6655                pairs: vec![BracketPair {
 6656                    start: "{".to_string(),
 6657                    end: "}".to_string(),
 6658                    close: true,
 6659                    surround: true,
 6660                    newline: true,
 6661                }],
 6662                ..Default::default()
 6663            },
 6664            autoclose_before: "}".to_string(),
 6665            ..Default::default()
 6666        },
 6667        Some(tree_sitter_rust::LANGUAGE.into()),
 6668    ));
 6669
 6670    let text = r#"
 6671        a
 6672        b
 6673        c
 6674    "#
 6675    .unindent();
 6676
 6677    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6678    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6679    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6680    editor
 6681        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6682        .await;
 6683
 6684    editor.update_in(cx, |editor, window, cx| {
 6685        editor.change_selections(None, window, cx, |s| {
 6686            s.select_ranges([
 6687                Point::new(0, 1)..Point::new(0, 1),
 6688                Point::new(1, 1)..Point::new(1, 1),
 6689                Point::new(2, 1)..Point::new(2, 1),
 6690            ])
 6691        });
 6692
 6693        editor.handle_input("{", window, cx);
 6694        editor.handle_input("{", window, cx);
 6695        editor.handle_input("_", window, cx);
 6696        assert_eq!(
 6697            editor.text(cx),
 6698            "
 6699                a{{_}}
 6700                b{{_}}
 6701                c{{_}}
 6702            "
 6703            .unindent()
 6704        );
 6705        assert_eq!(
 6706            editor.selections.ranges::<Point>(cx),
 6707            [
 6708                Point::new(0, 4)..Point::new(0, 4),
 6709                Point::new(1, 4)..Point::new(1, 4),
 6710                Point::new(2, 4)..Point::new(2, 4)
 6711            ]
 6712        );
 6713
 6714        editor.backspace(&Default::default(), window, cx);
 6715        editor.backspace(&Default::default(), window, cx);
 6716        assert_eq!(
 6717            editor.text(cx),
 6718            "
 6719                a{}
 6720                b{}
 6721                c{}
 6722            "
 6723            .unindent()
 6724        );
 6725        assert_eq!(
 6726            editor.selections.ranges::<Point>(cx),
 6727            [
 6728                Point::new(0, 2)..Point::new(0, 2),
 6729                Point::new(1, 2)..Point::new(1, 2),
 6730                Point::new(2, 2)..Point::new(2, 2)
 6731            ]
 6732        );
 6733
 6734        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6735        assert_eq!(
 6736            editor.text(cx),
 6737            "
 6738                a
 6739                b
 6740                c
 6741            "
 6742            .unindent()
 6743        );
 6744        assert_eq!(
 6745            editor.selections.ranges::<Point>(cx),
 6746            [
 6747                Point::new(0, 1)..Point::new(0, 1),
 6748                Point::new(1, 1)..Point::new(1, 1),
 6749                Point::new(2, 1)..Point::new(2, 1)
 6750            ]
 6751        );
 6752    });
 6753}
 6754
 6755#[gpui::test]
 6756async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6757    init_test(cx, |settings| {
 6758        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6759    });
 6760
 6761    let mut cx = EditorTestContext::new(cx).await;
 6762
 6763    let language = Arc::new(Language::new(
 6764        LanguageConfig {
 6765            brackets: BracketPairConfig {
 6766                pairs: vec![
 6767                    BracketPair {
 6768                        start: "{".to_string(),
 6769                        end: "}".to_string(),
 6770                        close: true,
 6771                        surround: true,
 6772                        newline: true,
 6773                    },
 6774                    BracketPair {
 6775                        start: "(".to_string(),
 6776                        end: ")".to_string(),
 6777                        close: true,
 6778                        surround: true,
 6779                        newline: true,
 6780                    },
 6781                    BracketPair {
 6782                        start: "[".to_string(),
 6783                        end: "]".to_string(),
 6784                        close: false,
 6785                        surround: true,
 6786                        newline: true,
 6787                    },
 6788                ],
 6789                ..Default::default()
 6790            },
 6791            autoclose_before: "})]".to_string(),
 6792            ..Default::default()
 6793        },
 6794        Some(tree_sitter_rust::LANGUAGE.into()),
 6795    ));
 6796
 6797    cx.language_registry().add(language.clone());
 6798    cx.update_buffer(|buffer, cx| {
 6799        buffer.set_language(Some(language), cx);
 6800    });
 6801
 6802    cx.set_state(
 6803        &"
 6804            {(ˇ)}
 6805            [[ˇ]]
 6806            {(ˇ)}
 6807        "
 6808        .unindent(),
 6809    );
 6810
 6811    cx.update_editor(|editor, window, cx| {
 6812        editor.backspace(&Default::default(), window, cx);
 6813        editor.backspace(&Default::default(), window, cx);
 6814    });
 6815
 6816    cx.assert_editor_state(
 6817        &"
 6818            ˇ
 6819            ˇ]]
 6820            ˇ
 6821        "
 6822        .unindent(),
 6823    );
 6824
 6825    cx.update_editor(|editor, window, cx| {
 6826        editor.handle_input("{", window, cx);
 6827        editor.handle_input("{", window, cx);
 6828        editor.move_right(&MoveRight, window, cx);
 6829        editor.move_right(&MoveRight, window, cx);
 6830        editor.move_left(&MoveLeft, window, cx);
 6831        editor.move_left(&MoveLeft, window, cx);
 6832        editor.backspace(&Default::default(), window, cx);
 6833    });
 6834
 6835    cx.assert_editor_state(
 6836        &"
 6837            {ˇ}
 6838            {ˇ}]]
 6839            {ˇ}
 6840        "
 6841        .unindent(),
 6842    );
 6843
 6844    cx.update_editor(|editor, window, cx| {
 6845        editor.backspace(&Default::default(), window, cx);
 6846    });
 6847
 6848    cx.assert_editor_state(
 6849        &"
 6850            ˇ
 6851            ˇ]]
 6852            ˇ
 6853        "
 6854        .unindent(),
 6855    );
 6856}
 6857
 6858#[gpui::test]
 6859async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6860    init_test(cx, |_| {});
 6861
 6862    let language = Arc::new(Language::new(
 6863        LanguageConfig::default(),
 6864        Some(tree_sitter_rust::LANGUAGE.into()),
 6865    ));
 6866
 6867    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6868    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6869    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6870    editor
 6871        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6872        .await;
 6873
 6874    editor.update_in(cx, |editor, window, cx| {
 6875        editor.set_auto_replace_emoji_shortcode(true);
 6876
 6877        editor.handle_input("Hello ", window, cx);
 6878        editor.handle_input(":wave", window, cx);
 6879        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6880
 6881        editor.handle_input(":", window, cx);
 6882        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6883
 6884        editor.handle_input(" :smile", window, cx);
 6885        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6886
 6887        editor.handle_input(":", window, cx);
 6888        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6889
 6890        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6891        editor.handle_input(":wave", window, cx);
 6892        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6893
 6894        editor.handle_input(":", window, cx);
 6895        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 6896
 6897        editor.handle_input(":1", window, cx);
 6898        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 6899
 6900        editor.handle_input(":", window, cx);
 6901        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 6902
 6903        // Ensure shortcode does not get replaced when it is part of a word
 6904        editor.handle_input(" Test:wave", window, cx);
 6905        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 6906
 6907        editor.handle_input(":", window, cx);
 6908        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 6909
 6910        editor.set_auto_replace_emoji_shortcode(false);
 6911
 6912        // Ensure shortcode does not get replaced when auto replace is off
 6913        editor.handle_input(" :wave", window, cx);
 6914        assert_eq!(
 6915            editor.text(cx),
 6916            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 6917        );
 6918
 6919        editor.handle_input(":", window, cx);
 6920        assert_eq!(
 6921            editor.text(cx),
 6922            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 6923        );
 6924    });
 6925}
 6926
 6927#[gpui::test]
 6928async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 6929    init_test(cx, |_| {});
 6930
 6931    let (text, insertion_ranges) = marked_text_ranges(
 6932        indoc! {"
 6933            ˇ
 6934        "},
 6935        false,
 6936    );
 6937
 6938    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6939    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6940
 6941    _ = editor.update_in(cx, |editor, window, cx| {
 6942        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 6943
 6944        editor
 6945            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6946            .unwrap();
 6947
 6948        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6949            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6950            assert_eq!(editor.text(cx), expected_text);
 6951            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6952        }
 6953
 6954        assert(
 6955            editor,
 6956            cx,
 6957            indoc! {"
 6958            type «» =•
 6959            "},
 6960        );
 6961
 6962        assert!(editor.context_menu_visible(), "There should be a matches");
 6963    });
 6964}
 6965
 6966#[gpui::test]
 6967async fn test_snippets(cx: &mut gpui::TestAppContext) {
 6968    init_test(cx, |_| {});
 6969
 6970    let (text, insertion_ranges) = marked_text_ranges(
 6971        indoc! {"
 6972            a.ˇ b
 6973            a.ˇ b
 6974            a.ˇ b
 6975        "},
 6976        false,
 6977    );
 6978
 6979    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 6980    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6981
 6982    editor.update_in(cx, |editor, window, cx| {
 6983        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 6984
 6985        editor
 6986            .insert_snippet(&insertion_ranges, snippet, window, cx)
 6987            .unwrap();
 6988
 6989        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 6990            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 6991            assert_eq!(editor.text(cx), expected_text);
 6992            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 6993        }
 6994
 6995        assert(
 6996            editor,
 6997            cx,
 6998            indoc! {"
 6999                a.f(«one», two, «three») b
 7000                a.f(«one», two, «three») b
 7001                a.f(«one», two, «three») b
 7002            "},
 7003        );
 7004
 7005        // Can't move earlier than the first tab stop
 7006        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7007        assert(
 7008            editor,
 7009            cx,
 7010            indoc! {"
 7011                a.f(«one», two, «three») b
 7012                a.f(«one», two, «three») b
 7013                a.f(«one», two, «three») b
 7014            "},
 7015        );
 7016
 7017        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7018        assert(
 7019            editor,
 7020            cx,
 7021            indoc! {"
 7022                a.f(one, «two», three) b
 7023                a.f(one, «two», three) b
 7024                a.f(one, «two», three) b
 7025            "},
 7026        );
 7027
 7028        editor.move_to_prev_snippet_tabstop(window, cx);
 7029        assert(
 7030            editor,
 7031            cx,
 7032            indoc! {"
 7033                a.f(«one», two, «three») b
 7034                a.f(«one», two, «three») b
 7035                a.f(«one», two, «three») b
 7036            "},
 7037        );
 7038
 7039        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7040        assert(
 7041            editor,
 7042            cx,
 7043            indoc! {"
 7044                a.f(one, «two», three) b
 7045                a.f(one, «two», three) b
 7046                a.f(one, «two», three) b
 7047            "},
 7048        );
 7049        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7050        assert(
 7051            editor,
 7052            cx,
 7053            indoc! {"
 7054                a.f(one, two, three)ˇ b
 7055                a.f(one, two, three)ˇ b
 7056                a.f(one, two, three)ˇ b
 7057            "},
 7058        );
 7059
 7060        // As soon as the last tab stop is reached, snippet state is gone
 7061        editor.move_to_prev_snippet_tabstop(window, cx);
 7062        assert(
 7063            editor,
 7064            cx,
 7065            indoc! {"
 7066                a.f(one, two, three)ˇ b
 7067                a.f(one, two, three)ˇ b
 7068                a.f(one, two, three)ˇ b
 7069            "},
 7070        );
 7071    });
 7072}
 7073
 7074#[gpui::test]
 7075async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7076    init_test(cx, |_| {});
 7077
 7078    let fs = FakeFs::new(cx.executor());
 7079    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7080
 7081    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7082
 7083    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7084    language_registry.add(rust_lang());
 7085    let mut fake_servers = language_registry.register_fake_lsp(
 7086        "Rust",
 7087        FakeLspAdapter {
 7088            capabilities: lsp::ServerCapabilities {
 7089                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7090                ..Default::default()
 7091            },
 7092            ..Default::default()
 7093        },
 7094    );
 7095
 7096    let buffer = project
 7097        .update(cx, |project, cx| {
 7098            project.open_local_buffer(path!("/file.rs"), cx)
 7099        })
 7100        .await
 7101        .unwrap();
 7102
 7103    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7104    let (editor, cx) = cx.add_window_view(|window, cx| {
 7105        build_editor_with_project(project.clone(), buffer, window, cx)
 7106    });
 7107    editor.update_in(cx, |editor, window, cx| {
 7108        editor.set_text("one\ntwo\nthree\n", window, cx)
 7109    });
 7110    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7111
 7112    cx.executor().start_waiting();
 7113    let fake_server = fake_servers.next().await.unwrap();
 7114
 7115    let save = editor
 7116        .update_in(cx, |editor, window, cx| {
 7117            editor.save(true, project.clone(), window, cx)
 7118        })
 7119        .unwrap();
 7120    fake_server
 7121        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7122            assert_eq!(
 7123                params.text_document.uri,
 7124                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7125            );
 7126            assert_eq!(params.options.tab_size, 4);
 7127            Ok(Some(vec![lsp::TextEdit::new(
 7128                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7129                ", ".to_string(),
 7130            )]))
 7131        })
 7132        .next()
 7133        .await;
 7134    cx.executor().start_waiting();
 7135    save.await;
 7136
 7137    assert_eq!(
 7138        editor.update(cx, |editor, cx| editor.text(cx)),
 7139        "one, two\nthree\n"
 7140    );
 7141    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7142
 7143    editor.update_in(cx, |editor, window, cx| {
 7144        editor.set_text("one\ntwo\nthree\n", window, cx)
 7145    });
 7146    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7147
 7148    // Ensure we can still save even if formatting hangs.
 7149    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7150        assert_eq!(
 7151            params.text_document.uri,
 7152            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7153        );
 7154        futures::future::pending::<()>().await;
 7155        unreachable!()
 7156    });
 7157    let save = editor
 7158        .update_in(cx, |editor, window, cx| {
 7159            editor.save(true, project.clone(), window, cx)
 7160        })
 7161        .unwrap();
 7162    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7163    cx.executor().start_waiting();
 7164    save.await;
 7165    assert_eq!(
 7166        editor.update(cx, |editor, cx| editor.text(cx)),
 7167        "one\ntwo\nthree\n"
 7168    );
 7169    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7170
 7171    // For non-dirty buffer, no formatting request should be sent
 7172    let save = editor
 7173        .update_in(cx, |editor, window, cx| {
 7174            editor.save(true, project.clone(), window, cx)
 7175        })
 7176        .unwrap();
 7177    let _pending_format_request = fake_server
 7178        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7179            panic!("Should not be invoked on non-dirty buffer");
 7180        })
 7181        .next();
 7182    cx.executor().start_waiting();
 7183    save.await;
 7184
 7185    // Set rust language override and assert overridden tabsize is sent to language server
 7186    update_test_language_settings(cx, |settings| {
 7187        settings.languages.insert(
 7188            "Rust".into(),
 7189            LanguageSettingsContent {
 7190                tab_size: NonZeroU32::new(8),
 7191                ..Default::default()
 7192            },
 7193        );
 7194    });
 7195
 7196    editor.update_in(cx, |editor, window, cx| {
 7197        editor.set_text("somehting_new\n", window, cx)
 7198    });
 7199    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7200    let save = editor
 7201        .update_in(cx, |editor, window, cx| {
 7202            editor.save(true, project.clone(), window, cx)
 7203        })
 7204        .unwrap();
 7205    fake_server
 7206        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7207            assert_eq!(
 7208                params.text_document.uri,
 7209                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7210            );
 7211            assert_eq!(params.options.tab_size, 8);
 7212            Ok(Some(vec![]))
 7213        })
 7214        .next()
 7215        .await;
 7216    cx.executor().start_waiting();
 7217    save.await;
 7218}
 7219
 7220#[gpui::test]
 7221async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7222    init_test(cx, |_| {});
 7223
 7224    let cols = 4;
 7225    let rows = 10;
 7226    let sample_text_1 = sample_text(rows, cols, 'a');
 7227    assert_eq!(
 7228        sample_text_1,
 7229        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7230    );
 7231    let sample_text_2 = sample_text(rows, cols, 'l');
 7232    assert_eq!(
 7233        sample_text_2,
 7234        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7235    );
 7236    let sample_text_3 = sample_text(rows, cols, 'v');
 7237    assert_eq!(
 7238        sample_text_3,
 7239        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7240    );
 7241
 7242    let fs = FakeFs::new(cx.executor());
 7243    fs.insert_tree(
 7244        path!("/a"),
 7245        json!({
 7246            "main.rs": sample_text_1,
 7247            "other.rs": sample_text_2,
 7248            "lib.rs": sample_text_3,
 7249        }),
 7250    )
 7251    .await;
 7252
 7253    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7254    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7255    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7256
 7257    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7258    language_registry.add(rust_lang());
 7259    let mut fake_servers = language_registry.register_fake_lsp(
 7260        "Rust",
 7261        FakeLspAdapter {
 7262            capabilities: lsp::ServerCapabilities {
 7263                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7264                ..Default::default()
 7265            },
 7266            ..Default::default()
 7267        },
 7268    );
 7269
 7270    let worktree = project.update(cx, |project, cx| {
 7271        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7272        assert_eq!(worktrees.len(), 1);
 7273        worktrees.pop().unwrap()
 7274    });
 7275    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7276
 7277    let buffer_1 = project
 7278        .update(cx, |project, cx| {
 7279            project.open_buffer((worktree_id, "main.rs"), cx)
 7280        })
 7281        .await
 7282        .unwrap();
 7283    let buffer_2 = project
 7284        .update(cx, |project, cx| {
 7285            project.open_buffer((worktree_id, "other.rs"), cx)
 7286        })
 7287        .await
 7288        .unwrap();
 7289    let buffer_3 = project
 7290        .update(cx, |project, cx| {
 7291            project.open_buffer((worktree_id, "lib.rs"), cx)
 7292        })
 7293        .await
 7294        .unwrap();
 7295
 7296    let multi_buffer = cx.new(|cx| {
 7297        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7298        multi_buffer.push_excerpts(
 7299            buffer_1.clone(),
 7300            [
 7301                ExcerptRange {
 7302                    context: Point::new(0, 0)..Point::new(3, 0),
 7303                    primary: None,
 7304                },
 7305                ExcerptRange {
 7306                    context: Point::new(5, 0)..Point::new(7, 0),
 7307                    primary: None,
 7308                },
 7309                ExcerptRange {
 7310                    context: Point::new(9, 0)..Point::new(10, 4),
 7311                    primary: None,
 7312                },
 7313            ],
 7314            cx,
 7315        );
 7316        multi_buffer.push_excerpts(
 7317            buffer_2.clone(),
 7318            [
 7319                ExcerptRange {
 7320                    context: Point::new(0, 0)..Point::new(3, 0),
 7321                    primary: None,
 7322                },
 7323                ExcerptRange {
 7324                    context: Point::new(5, 0)..Point::new(7, 0),
 7325                    primary: None,
 7326                },
 7327                ExcerptRange {
 7328                    context: Point::new(9, 0)..Point::new(10, 4),
 7329                    primary: None,
 7330                },
 7331            ],
 7332            cx,
 7333        );
 7334        multi_buffer.push_excerpts(
 7335            buffer_3.clone(),
 7336            [
 7337                ExcerptRange {
 7338                    context: Point::new(0, 0)..Point::new(3, 0),
 7339                    primary: None,
 7340                },
 7341                ExcerptRange {
 7342                    context: Point::new(5, 0)..Point::new(7, 0),
 7343                    primary: None,
 7344                },
 7345                ExcerptRange {
 7346                    context: Point::new(9, 0)..Point::new(10, 4),
 7347                    primary: None,
 7348                },
 7349            ],
 7350            cx,
 7351        );
 7352        multi_buffer
 7353    });
 7354    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7355        Editor::new(
 7356            EditorMode::Full,
 7357            multi_buffer,
 7358            Some(project.clone()),
 7359            true,
 7360            window,
 7361            cx,
 7362        )
 7363    });
 7364
 7365    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7366        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7367            s.select_ranges(Some(1..2))
 7368        });
 7369        editor.insert("|one|two|three|", window, cx);
 7370    });
 7371    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7372    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7373        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7374            s.select_ranges(Some(60..70))
 7375        });
 7376        editor.insert("|four|five|six|", window, cx);
 7377    });
 7378    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7379
 7380    // First two buffers should be edited, but not the third one.
 7381    assert_eq!(
 7382        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7383        "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}",
 7384    );
 7385    buffer_1.update(cx, |buffer, _| {
 7386        assert!(buffer.is_dirty());
 7387        assert_eq!(
 7388            buffer.text(),
 7389            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7390        )
 7391    });
 7392    buffer_2.update(cx, |buffer, _| {
 7393        assert!(buffer.is_dirty());
 7394        assert_eq!(
 7395            buffer.text(),
 7396            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7397        )
 7398    });
 7399    buffer_3.update(cx, |buffer, _| {
 7400        assert!(!buffer.is_dirty());
 7401        assert_eq!(buffer.text(), sample_text_3,)
 7402    });
 7403    cx.executor().run_until_parked();
 7404
 7405    cx.executor().start_waiting();
 7406    let save = multi_buffer_editor
 7407        .update_in(cx, |editor, window, cx| {
 7408            editor.save(true, project.clone(), window, cx)
 7409        })
 7410        .unwrap();
 7411
 7412    let fake_server = fake_servers.next().await.unwrap();
 7413    fake_server
 7414        .server
 7415        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7416            Ok(Some(vec![lsp::TextEdit::new(
 7417                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7418                format!("[{} formatted]", params.text_document.uri),
 7419            )]))
 7420        })
 7421        .detach();
 7422    save.await;
 7423
 7424    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7425    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7426    assert_eq!(
 7427        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7428        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}"),
 7429    );
 7430    buffer_1.update(cx, |buffer, _| {
 7431        assert!(!buffer.is_dirty());
 7432        assert_eq!(
 7433            buffer.text(),
 7434            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7435        )
 7436    });
 7437    buffer_2.update(cx, |buffer, _| {
 7438        assert!(!buffer.is_dirty());
 7439        assert_eq!(
 7440            buffer.text(),
 7441            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7442        )
 7443    });
 7444    buffer_3.update(cx, |buffer, _| {
 7445        assert!(!buffer.is_dirty());
 7446        assert_eq!(buffer.text(), sample_text_3,)
 7447    });
 7448}
 7449
 7450#[gpui::test]
 7451async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7452    init_test(cx, |_| {});
 7453
 7454    let fs = FakeFs::new(cx.executor());
 7455    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7456
 7457    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7458
 7459    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7460    language_registry.add(rust_lang());
 7461    let mut fake_servers = language_registry.register_fake_lsp(
 7462        "Rust",
 7463        FakeLspAdapter {
 7464            capabilities: lsp::ServerCapabilities {
 7465                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7466                ..Default::default()
 7467            },
 7468            ..Default::default()
 7469        },
 7470    );
 7471
 7472    let buffer = project
 7473        .update(cx, |project, cx| {
 7474            project.open_local_buffer(path!("/file.rs"), cx)
 7475        })
 7476        .await
 7477        .unwrap();
 7478
 7479    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7480    let (editor, cx) = cx.add_window_view(|window, cx| {
 7481        build_editor_with_project(project.clone(), buffer, window, cx)
 7482    });
 7483    editor.update_in(cx, |editor, window, cx| {
 7484        editor.set_text("one\ntwo\nthree\n", window, cx)
 7485    });
 7486    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7487
 7488    cx.executor().start_waiting();
 7489    let fake_server = fake_servers.next().await.unwrap();
 7490
 7491    let save = editor
 7492        .update_in(cx, |editor, window, cx| {
 7493            editor.save(true, project.clone(), window, cx)
 7494        })
 7495        .unwrap();
 7496    fake_server
 7497        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7498            assert_eq!(
 7499                params.text_document.uri,
 7500                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7501            );
 7502            assert_eq!(params.options.tab_size, 4);
 7503            Ok(Some(vec![lsp::TextEdit::new(
 7504                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7505                ", ".to_string(),
 7506            )]))
 7507        })
 7508        .next()
 7509        .await;
 7510    cx.executor().start_waiting();
 7511    save.await;
 7512    assert_eq!(
 7513        editor.update(cx, |editor, cx| editor.text(cx)),
 7514        "one, two\nthree\n"
 7515    );
 7516    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7517
 7518    editor.update_in(cx, |editor, window, cx| {
 7519        editor.set_text("one\ntwo\nthree\n", window, cx)
 7520    });
 7521    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7522
 7523    // Ensure we can still save even if formatting hangs.
 7524    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7525        move |params, _| async move {
 7526            assert_eq!(
 7527                params.text_document.uri,
 7528                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7529            );
 7530            futures::future::pending::<()>().await;
 7531            unreachable!()
 7532        },
 7533    );
 7534    let save = editor
 7535        .update_in(cx, |editor, window, cx| {
 7536            editor.save(true, project.clone(), window, cx)
 7537        })
 7538        .unwrap();
 7539    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7540    cx.executor().start_waiting();
 7541    save.await;
 7542    assert_eq!(
 7543        editor.update(cx, |editor, cx| editor.text(cx)),
 7544        "one\ntwo\nthree\n"
 7545    );
 7546    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7547
 7548    // For non-dirty buffer, no formatting request should be sent
 7549    let save = editor
 7550        .update_in(cx, |editor, window, cx| {
 7551            editor.save(true, project.clone(), window, cx)
 7552        })
 7553        .unwrap();
 7554    let _pending_format_request = fake_server
 7555        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7556            panic!("Should not be invoked on non-dirty buffer");
 7557        })
 7558        .next();
 7559    cx.executor().start_waiting();
 7560    save.await;
 7561
 7562    // Set Rust language override and assert overridden tabsize is sent to language server
 7563    update_test_language_settings(cx, |settings| {
 7564        settings.languages.insert(
 7565            "Rust".into(),
 7566            LanguageSettingsContent {
 7567                tab_size: NonZeroU32::new(8),
 7568                ..Default::default()
 7569            },
 7570        );
 7571    });
 7572
 7573    editor.update_in(cx, |editor, window, cx| {
 7574        editor.set_text("somehting_new\n", window, cx)
 7575    });
 7576    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7577    let save = editor
 7578        .update_in(cx, |editor, window, cx| {
 7579            editor.save(true, project.clone(), window, cx)
 7580        })
 7581        .unwrap();
 7582    fake_server
 7583        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7584            assert_eq!(
 7585                params.text_document.uri,
 7586                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7587            );
 7588            assert_eq!(params.options.tab_size, 8);
 7589            Ok(Some(vec![]))
 7590        })
 7591        .next()
 7592        .await;
 7593    cx.executor().start_waiting();
 7594    save.await;
 7595}
 7596
 7597#[gpui::test]
 7598async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7599    init_test(cx, |settings| {
 7600        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7601            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7602        ))
 7603    });
 7604
 7605    let fs = FakeFs::new(cx.executor());
 7606    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7607
 7608    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7609
 7610    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7611    language_registry.add(Arc::new(Language::new(
 7612        LanguageConfig {
 7613            name: "Rust".into(),
 7614            matcher: LanguageMatcher {
 7615                path_suffixes: vec!["rs".to_string()],
 7616                ..Default::default()
 7617            },
 7618            ..LanguageConfig::default()
 7619        },
 7620        Some(tree_sitter_rust::LANGUAGE.into()),
 7621    )));
 7622    update_test_language_settings(cx, |settings| {
 7623        // Enable Prettier formatting for the same buffer, and ensure
 7624        // LSP is called instead of Prettier.
 7625        settings.defaults.prettier = Some(PrettierSettings {
 7626            allowed: true,
 7627            ..PrettierSettings::default()
 7628        });
 7629    });
 7630    let mut fake_servers = language_registry.register_fake_lsp(
 7631        "Rust",
 7632        FakeLspAdapter {
 7633            capabilities: lsp::ServerCapabilities {
 7634                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7635                ..Default::default()
 7636            },
 7637            ..Default::default()
 7638        },
 7639    );
 7640
 7641    let buffer = project
 7642        .update(cx, |project, cx| {
 7643            project.open_local_buffer(path!("/file.rs"), cx)
 7644        })
 7645        .await
 7646        .unwrap();
 7647
 7648    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7649    let (editor, cx) = cx.add_window_view(|window, cx| {
 7650        build_editor_with_project(project.clone(), buffer, window, cx)
 7651    });
 7652    editor.update_in(cx, |editor, window, cx| {
 7653        editor.set_text("one\ntwo\nthree\n", window, cx)
 7654    });
 7655
 7656    cx.executor().start_waiting();
 7657    let fake_server = fake_servers.next().await.unwrap();
 7658
 7659    let format = editor
 7660        .update_in(cx, |editor, window, cx| {
 7661            editor.perform_format(
 7662                project.clone(),
 7663                FormatTrigger::Manual,
 7664                FormatTarget::Buffers,
 7665                window,
 7666                cx,
 7667            )
 7668        })
 7669        .unwrap();
 7670    fake_server
 7671        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7672            assert_eq!(
 7673                params.text_document.uri,
 7674                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7675            );
 7676            assert_eq!(params.options.tab_size, 4);
 7677            Ok(Some(vec![lsp::TextEdit::new(
 7678                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7679                ", ".to_string(),
 7680            )]))
 7681        })
 7682        .next()
 7683        .await;
 7684    cx.executor().start_waiting();
 7685    format.await;
 7686    assert_eq!(
 7687        editor.update(cx, |editor, cx| editor.text(cx)),
 7688        "one, two\nthree\n"
 7689    );
 7690
 7691    editor.update_in(cx, |editor, window, cx| {
 7692        editor.set_text("one\ntwo\nthree\n", window, cx)
 7693    });
 7694    // Ensure we don't lock if formatting hangs.
 7695    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7696        assert_eq!(
 7697            params.text_document.uri,
 7698            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7699        );
 7700        futures::future::pending::<()>().await;
 7701        unreachable!()
 7702    });
 7703    let format = editor
 7704        .update_in(cx, |editor, window, cx| {
 7705            editor.perform_format(
 7706                project,
 7707                FormatTrigger::Manual,
 7708                FormatTarget::Buffers,
 7709                window,
 7710                cx,
 7711            )
 7712        })
 7713        .unwrap();
 7714    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7715    cx.executor().start_waiting();
 7716    format.await;
 7717    assert_eq!(
 7718        editor.update(cx, |editor, cx| editor.text(cx)),
 7719        "one\ntwo\nthree\n"
 7720    );
 7721}
 7722
 7723#[gpui::test]
 7724async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7725    init_test(cx, |_| {});
 7726
 7727    let mut cx = EditorLspTestContext::new_rust(
 7728        lsp::ServerCapabilities {
 7729            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7730            ..Default::default()
 7731        },
 7732        cx,
 7733    )
 7734    .await;
 7735
 7736    cx.set_state(indoc! {"
 7737        one.twoˇ
 7738    "});
 7739
 7740    // The format request takes a long time. When it completes, it inserts
 7741    // a newline and an indent before the `.`
 7742    cx.lsp
 7743        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7744            let executor = cx.background_executor().clone();
 7745            async move {
 7746                executor.timer(Duration::from_millis(100)).await;
 7747                Ok(Some(vec![lsp::TextEdit {
 7748                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7749                    new_text: "\n    ".into(),
 7750                }]))
 7751            }
 7752        });
 7753
 7754    // Submit a format request.
 7755    let format_1 = cx
 7756        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7757        .unwrap();
 7758    cx.executor().run_until_parked();
 7759
 7760    // Submit a second format request.
 7761    let format_2 = cx
 7762        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7763        .unwrap();
 7764    cx.executor().run_until_parked();
 7765
 7766    // Wait for both format requests to complete
 7767    cx.executor().advance_clock(Duration::from_millis(200));
 7768    cx.executor().start_waiting();
 7769    format_1.await.unwrap();
 7770    cx.executor().start_waiting();
 7771    format_2.await.unwrap();
 7772
 7773    // The formatting edits only happens once.
 7774    cx.assert_editor_state(indoc! {"
 7775        one
 7776            .twoˇ
 7777    "});
 7778}
 7779
 7780#[gpui::test]
 7781async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7782    init_test(cx, |settings| {
 7783        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7784    });
 7785
 7786    let mut cx = EditorLspTestContext::new_rust(
 7787        lsp::ServerCapabilities {
 7788            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7789            ..Default::default()
 7790        },
 7791        cx,
 7792    )
 7793    .await;
 7794
 7795    // Set up a buffer white some trailing whitespace and no trailing newline.
 7796    cx.set_state(
 7797        &[
 7798            "one ",   //
 7799            "twoˇ",   //
 7800            "three ", //
 7801            "four",   //
 7802        ]
 7803        .join("\n"),
 7804    );
 7805
 7806    // Submit a format request.
 7807    let format = cx
 7808        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7809        .unwrap();
 7810
 7811    // Record which buffer changes have been sent to the language server
 7812    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7813    cx.lsp
 7814        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7815            let buffer_changes = buffer_changes.clone();
 7816            move |params, _| {
 7817                buffer_changes.lock().extend(
 7818                    params
 7819                        .content_changes
 7820                        .into_iter()
 7821                        .map(|e| (e.range.unwrap(), e.text)),
 7822                );
 7823            }
 7824        });
 7825
 7826    // Handle formatting requests to the language server.
 7827    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7828        let buffer_changes = buffer_changes.clone();
 7829        move |_, _| {
 7830            // When formatting is requested, trailing whitespace has already been stripped,
 7831            // and the trailing newline has already been added.
 7832            assert_eq!(
 7833                &buffer_changes.lock()[1..],
 7834                &[
 7835                    (
 7836                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7837                        "".into()
 7838                    ),
 7839                    (
 7840                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7841                        "".into()
 7842                    ),
 7843                    (
 7844                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7845                        "\n".into()
 7846                    ),
 7847                ]
 7848            );
 7849
 7850            // Insert blank lines between each line of the buffer.
 7851            async move {
 7852                Ok(Some(vec![
 7853                    lsp::TextEdit {
 7854                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7855                        new_text: "\n".into(),
 7856                    },
 7857                    lsp::TextEdit {
 7858                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7859                        new_text: "\n".into(),
 7860                    },
 7861                ]))
 7862            }
 7863        }
 7864    });
 7865
 7866    // After formatting the buffer, the trailing whitespace is stripped,
 7867    // a newline is appended, and the edits provided by the language server
 7868    // have been applied.
 7869    format.await.unwrap();
 7870    cx.assert_editor_state(
 7871        &[
 7872            "one",   //
 7873            "",      //
 7874            "twoˇ",  //
 7875            "",      //
 7876            "three", //
 7877            "four",  //
 7878            "",      //
 7879        ]
 7880        .join("\n"),
 7881    );
 7882
 7883    // Undoing the formatting undoes the trailing whitespace removal, the
 7884    // trailing newline, and the LSP edits.
 7885    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7886    cx.assert_editor_state(
 7887        &[
 7888            "one ",   //
 7889            "twoˇ",   //
 7890            "three ", //
 7891            "four",   //
 7892        ]
 7893        .join("\n"),
 7894    );
 7895}
 7896
 7897#[gpui::test]
 7898async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 7899    cx: &mut gpui::TestAppContext,
 7900) {
 7901    init_test(cx, |_| {});
 7902
 7903    cx.update(|cx| {
 7904        cx.update_global::<SettingsStore, _>(|settings, cx| {
 7905            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 7906                settings.auto_signature_help = Some(true);
 7907            });
 7908        });
 7909    });
 7910
 7911    let mut cx = EditorLspTestContext::new_rust(
 7912        lsp::ServerCapabilities {
 7913            signature_help_provider: Some(lsp::SignatureHelpOptions {
 7914                ..Default::default()
 7915            }),
 7916            ..Default::default()
 7917        },
 7918        cx,
 7919    )
 7920    .await;
 7921
 7922    let language = Language::new(
 7923        LanguageConfig {
 7924            name: "Rust".into(),
 7925            brackets: BracketPairConfig {
 7926                pairs: vec![
 7927                    BracketPair {
 7928                        start: "{".to_string(),
 7929                        end: "}".to_string(),
 7930                        close: true,
 7931                        surround: true,
 7932                        newline: true,
 7933                    },
 7934                    BracketPair {
 7935                        start: "(".to_string(),
 7936                        end: ")".to_string(),
 7937                        close: true,
 7938                        surround: true,
 7939                        newline: true,
 7940                    },
 7941                    BracketPair {
 7942                        start: "/*".to_string(),
 7943                        end: " */".to_string(),
 7944                        close: true,
 7945                        surround: true,
 7946                        newline: true,
 7947                    },
 7948                    BracketPair {
 7949                        start: "[".to_string(),
 7950                        end: "]".to_string(),
 7951                        close: false,
 7952                        surround: false,
 7953                        newline: true,
 7954                    },
 7955                    BracketPair {
 7956                        start: "\"".to_string(),
 7957                        end: "\"".to_string(),
 7958                        close: true,
 7959                        surround: true,
 7960                        newline: false,
 7961                    },
 7962                    BracketPair {
 7963                        start: "<".to_string(),
 7964                        end: ">".to_string(),
 7965                        close: false,
 7966                        surround: true,
 7967                        newline: true,
 7968                    },
 7969                ],
 7970                ..Default::default()
 7971            },
 7972            autoclose_before: "})]".to_string(),
 7973            ..Default::default()
 7974        },
 7975        Some(tree_sitter_rust::LANGUAGE.into()),
 7976    );
 7977    let language = Arc::new(language);
 7978
 7979    cx.language_registry().add(language.clone());
 7980    cx.update_buffer(|buffer, cx| {
 7981        buffer.set_language(Some(language), cx);
 7982    });
 7983
 7984    cx.set_state(
 7985        &r#"
 7986            fn main() {
 7987                sampleˇ
 7988            }
 7989        "#
 7990        .unindent(),
 7991    );
 7992
 7993    cx.update_editor(|editor, window, cx| {
 7994        editor.handle_input("(", window, cx);
 7995    });
 7996    cx.assert_editor_state(
 7997        &"
 7998            fn main() {
 7999                sample(ˇ)
 8000            }
 8001        "
 8002        .unindent(),
 8003    );
 8004
 8005    let mocked_response = lsp::SignatureHelp {
 8006        signatures: vec![lsp::SignatureInformation {
 8007            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8008            documentation: None,
 8009            parameters: Some(vec![
 8010                lsp::ParameterInformation {
 8011                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8012                    documentation: None,
 8013                },
 8014                lsp::ParameterInformation {
 8015                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8016                    documentation: None,
 8017                },
 8018            ]),
 8019            active_parameter: None,
 8020        }],
 8021        active_signature: Some(0),
 8022        active_parameter: Some(0),
 8023    };
 8024    handle_signature_help_request(&mut cx, mocked_response).await;
 8025
 8026    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8027        .await;
 8028
 8029    cx.editor(|editor, _, _| {
 8030        let signature_help_state = editor.signature_help_state.popover().cloned();
 8031        assert!(signature_help_state.is_some());
 8032        let ParsedMarkdown {
 8033            text, highlights, ..
 8034        } = signature_help_state.unwrap().parsed_content;
 8035        assert_eq!(text, "param1: u8, param2: u8");
 8036        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8037    });
 8038}
 8039
 8040#[gpui::test]
 8041async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8042    init_test(cx, |_| {});
 8043
 8044    cx.update(|cx| {
 8045        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8046            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8047                settings.auto_signature_help = Some(false);
 8048                settings.show_signature_help_after_edits = Some(false);
 8049            });
 8050        });
 8051    });
 8052
 8053    let mut cx = EditorLspTestContext::new_rust(
 8054        lsp::ServerCapabilities {
 8055            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8056                ..Default::default()
 8057            }),
 8058            ..Default::default()
 8059        },
 8060        cx,
 8061    )
 8062    .await;
 8063
 8064    let language = Language::new(
 8065        LanguageConfig {
 8066            name: "Rust".into(),
 8067            brackets: BracketPairConfig {
 8068                pairs: vec![
 8069                    BracketPair {
 8070                        start: "{".to_string(),
 8071                        end: "}".to_string(),
 8072                        close: true,
 8073                        surround: true,
 8074                        newline: true,
 8075                    },
 8076                    BracketPair {
 8077                        start: "(".to_string(),
 8078                        end: ")".to_string(),
 8079                        close: true,
 8080                        surround: true,
 8081                        newline: true,
 8082                    },
 8083                    BracketPair {
 8084                        start: "/*".to_string(),
 8085                        end: " */".to_string(),
 8086                        close: true,
 8087                        surround: true,
 8088                        newline: true,
 8089                    },
 8090                    BracketPair {
 8091                        start: "[".to_string(),
 8092                        end: "]".to_string(),
 8093                        close: false,
 8094                        surround: false,
 8095                        newline: true,
 8096                    },
 8097                    BracketPair {
 8098                        start: "\"".to_string(),
 8099                        end: "\"".to_string(),
 8100                        close: true,
 8101                        surround: true,
 8102                        newline: false,
 8103                    },
 8104                    BracketPair {
 8105                        start: "<".to_string(),
 8106                        end: ">".to_string(),
 8107                        close: false,
 8108                        surround: true,
 8109                        newline: true,
 8110                    },
 8111                ],
 8112                ..Default::default()
 8113            },
 8114            autoclose_before: "})]".to_string(),
 8115            ..Default::default()
 8116        },
 8117        Some(tree_sitter_rust::LANGUAGE.into()),
 8118    );
 8119    let language = Arc::new(language);
 8120
 8121    cx.language_registry().add(language.clone());
 8122    cx.update_buffer(|buffer, cx| {
 8123        buffer.set_language(Some(language), cx);
 8124    });
 8125
 8126    // Ensure that signature_help is not called when no signature help is enabled.
 8127    cx.set_state(
 8128        &r#"
 8129            fn main() {
 8130                sampleˇ
 8131            }
 8132        "#
 8133        .unindent(),
 8134    );
 8135    cx.update_editor(|editor, window, cx| {
 8136        editor.handle_input("(", window, cx);
 8137    });
 8138    cx.assert_editor_state(
 8139        &"
 8140            fn main() {
 8141                sample(ˇ)
 8142            }
 8143        "
 8144        .unindent(),
 8145    );
 8146    cx.editor(|editor, _, _| {
 8147        assert!(editor.signature_help_state.task().is_none());
 8148    });
 8149
 8150    let mocked_response = lsp::SignatureHelp {
 8151        signatures: vec![lsp::SignatureInformation {
 8152            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8153            documentation: None,
 8154            parameters: Some(vec![
 8155                lsp::ParameterInformation {
 8156                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8157                    documentation: None,
 8158                },
 8159                lsp::ParameterInformation {
 8160                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8161                    documentation: None,
 8162                },
 8163            ]),
 8164            active_parameter: None,
 8165        }],
 8166        active_signature: Some(0),
 8167        active_parameter: Some(0),
 8168    };
 8169
 8170    // Ensure that signature_help is called when enabled afte edits
 8171    cx.update(|_, cx| {
 8172        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8173            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8174                settings.auto_signature_help = Some(false);
 8175                settings.show_signature_help_after_edits = Some(true);
 8176            });
 8177        });
 8178    });
 8179    cx.set_state(
 8180        &r#"
 8181            fn main() {
 8182                sampleˇ
 8183            }
 8184        "#
 8185        .unindent(),
 8186    );
 8187    cx.update_editor(|editor, window, cx| {
 8188        editor.handle_input("(", window, cx);
 8189    });
 8190    cx.assert_editor_state(
 8191        &"
 8192            fn main() {
 8193                sample(ˇ)
 8194            }
 8195        "
 8196        .unindent(),
 8197    );
 8198    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8199    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8200        .await;
 8201    cx.update_editor(|editor, _, _| {
 8202        let signature_help_state = editor.signature_help_state.popover().cloned();
 8203        assert!(signature_help_state.is_some());
 8204        let ParsedMarkdown {
 8205            text, highlights, ..
 8206        } = signature_help_state.unwrap().parsed_content;
 8207        assert_eq!(text, "param1: u8, param2: u8");
 8208        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8209        editor.signature_help_state = SignatureHelpState::default();
 8210    });
 8211
 8212    // Ensure that signature_help is called when auto signature help override is enabled
 8213    cx.update(|_, cx| {
 8214        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8215            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8216                settings.auto_signature_help = Some(true);
 8217                settings.show_signature_help_after_edits = Some(false);
 8218            });
 8219        });
 8220    });
 8221    cx.set_state(
 8222        &r#"
 8223            fn main() {
 8224                sampleˇ
 8225            }
 8226        "#
 8227        .unindent(),
 8228    );
 8229    cx.update_editor(|editor, window, cx| {
 8230        editor.handle_input("(", window, cx);
 8231    });
 8232    cx.assert_editor_state(
 8233        &"
 8234            fn main() {
 8235                sample(ˇ)
 8236            }
 8237        "
 8238        .unindent(),
 8239    );
 8240    handle_signature_help_request(&mut cx, mocked_response).await;
 8241    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8242        .await;
 8243    cx.editor(|editor, _, _| {
 8244        let signature_help_state = editor.signature_help_state.popover().cloned();
 8245        assert!(signature_help_state.is_some());
 8246        let ParsedMarkdown {
 8247            text, highlights, ..
 8248        } = signature_help_state.unwrap().parsed_content;
 8249        assert_eq!(text, "param1: u8, param2: u8");
 8250        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8251    });
 8252}
 8253
 8254#[gpui::test]
 8255async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8256    init_test(cx, |_| {});
 8257    cx.update(|cx| {
 8258        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8259            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8260                settings.auto_signature_help = Some(true);
 8261            });
 8262        });
 8263    });
 8264
 8265    let mut cx = EditorLspTestContext::new_rust(
 8266        lsp::ServerCapabilities {
 8267            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8268                ..Default::default()
 8269            }),
 8270            ..Default::default()
 8271        },
 8272        cx,
 8273    )
 8274    .await;
 8275
 8276    // A test that directly calls `show_signature_help`
 8277    cx.update_editor(|editor, window, cx| {
 8278        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8279    });
 8280
 8281    let mocked_response = lsp::SignatureHelp {
 8282        signatures: vec![lsp::SignatureInformation {
 8283            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8284            documentation: None,
 8285            parameters: Some(vec![
 8286                lsp::ParameterInformation {
 8287                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8288                    documentation: None,
 8289                },
 8290                lsp::ParameterInformation {
 8291                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8292                    documentation: None,
 8293                },
 8294            ]),
 8295            active_parameter: None,
 8296        }],
 8297        active_signature: Some(0),
 8298        active_parameter: Some(0),
 8299    };
 8300    handle_signature_help_request(&mut cx, mocked_response).await;
 8301
 8302    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8303        .await;
 8304
 8305    cx.editor(|editor, _, _| {
 8306        let signature_help_state = editor.signature_help_state.popover().cloned();
 8307        assert!(signature_help_state.is_some());
 8308        let ParsedMarkdown {
 8309            text, highlights, ..
 8310        } = signature_help_state.unwrap().parsed_content;
 8311        assert_eq!(text, "param1: u8, param2: u8");
 8312        assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
 8313    });
 8314
 8315    // When exiting outside from inside the brackets, `signature_help` is closed.
 8316    cx.set_state(indoc! {"
 8317        fn main() {
 8318            sample(ˇ);
 8319        }
 8320
 8321        fn sample(param1: u8, param2: u8) {}
 8322    "});
 8323
 8324    cx.update_editor(|editor, window, cx| {
 8325        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8326    });
 8327
 8328    let mocked_response = lsp::SignatureHelp {
 8329        signatures: Vec::new(),
 8330        active_signature: None,
 8331        active_parameter: None,
 8332    };
 8333    handle_signature_help_request(&mut cx, mocked_response).await;
 8334
 8335    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8336        .await;
 8337
 8338    cx.editor(|editor, _, _| {
 8339        assert!(!editor.signature_help_state.is_shown());
 8340    });
 8341
 8342    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8343    cx.set_state(indoc! {"
 8344        fn main() {
 8345            sample(ˇ);
 8346        }
 8347
 8348        fn sample(param1: u8, param2: u8) {}
 8349    "});
 8350
 8351    let mocked_response = lsp::SignatureHelp {
 8352        signatures: vec![lsp::SignatureInformation {
 8353            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8354            documentation: None,
 8355            parameters: Some(vec![
 8356                lsp::ParameterInformation {
 8357                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8358                    documentation: None,
 8359                },
 8360                lsp::ParameterInformation {
 8361                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8362                    documentation: None,
 8363                },
 8364            ]),
 8365            active_parameter: None,
 8366        }],
 8367        active_signature: Some(0),
 8368        active_parameter: Some(0),
 8369    };
 8370    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8371    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8372        .await;
 8373    cx.editor(|editor, _, _| {
 8374        assert!(editor.signature_help_state.is_shown());
 8375    });
 8376
 8377    // Restore the popover with more parameter input
 8378    cx.set_state(indoc! {"
 8379        fn main() {
 8380            sample(param1, param2ˇ);
 8381        }
 8382
 8383        fn sample(param1: u8, param2: u8) {}
 8384    "});
 8385
 8386    let mocked_response = lsp::SignatureHelp {
 8387        signatures: vec![lsp::SignatureInformation {
 8388            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8389            documentation: None,
 8390            parameters: Some(vec![
 8391                lsp::ParameterInformation {
 8392                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8393                    documentation: None,
 8394                },
 8395                lsp::ParameterInformation {
 8396                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8397                    documentation: None,
 8398                },
 8399            ]),
 8400            active_parameter: None,
 8401        }],
 8402        active_signature: Some(0),
 8403        active_parameter: Some(1),
 8404    };
 8405    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8406    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8407        .await;
 8408
 8409    // When selecting a range, the popover is gone.
 8410    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8411    cx.update_editor(|editor, window, cx| {
 8412        editor.change_selections(None, window, cx, |s| {
 8413            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8414        })
 8415    });
 8416    cx.assert_editor_state(indoc! {"
 8417        fn main() {
 8418            sample(param1, «ˇparam2»);
 8419        }
 8420
 8421        fn sample(param1: u8, param2: u8) {}
 8422    "});
 8423    cx.editor(|editor, _, _| {
 8424        assert!(!editor.signature_help_state.is_shown());
 8425    });
 8426
 8427    // When unselecting again, the popover is back if within the brackets.
 8428    cx.update_editor(|editor, window, cx| {
 8429        editor.change_selections(None, window, cx, |s| {
 8430            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8431        })
 8432    });
 8433    cx.assert_editor_state(indoc! {"
 8434        fn main() {
 8435            sample(param1, ˇparam2);
 8436        }
 8437
 8438        fn sample(param1: u8, param2: u8) {}
 8439    "});
 8440    handle_signature_help_request(&mut cx, mocked_response).await;
 8441    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8442        .await;
 8443    cx.editor(|editor, _, _| {
 8444        assert!(editor.signature_help_state.is_shown());
 8445    });
 8446
 8447    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8448    cx.update_editor(|editor, window, cx| {
 8449        editor.change_selections(None, window, cx, |s| {
 8450            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8451            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8452        })
 8453    });
 8454    cx.assert_editor_state(indoc! {"
 8455        fn main() {
 8456            sample(param1, ˇparam2);
 8457        }
 8458
 8459        fn sample(param1: u8, param2: u8) {}
 8460    "});
 8461
 8462    let mocked_response = lsp::SignatureHelp {
 8463        signatures: vec![lsp::SignatureInformation {
 8464            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8465            documentation: None,
 8466            parameters: Some(vec![
 8467                lsp::ParameterInformation {
 8468                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8469                    documentation: None,
 8470                },
 8471                lsp::ParameterInformation {
 8472                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8473                    documentation: None,
 8474                },
 8475            ]),
 8476            active_parameter: None,
 8477        }],
 8478        active_signature: Some(0),
 8479        active_parameter: Some(1),
 8480    };
 8481    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8482    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8483        .await;
 8484    cx.update_editor(|editor, _, cx| {
 8485        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8486    });
 8487    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8488        .await;
 8489    cx.update_editor(|editor, window, cx| {
 8490        editor.change_selections(None, window, cx, |s| {
 8491            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8492        })
 8493    });
 8494    cx.assert_editor_state(indoc! {"
 8495        fn main() {
 8496            sample(param1, «ˇparam2»);
 8497        }
 8498
 8499        fn sample(param1: u8, param2: u8) {}
 8500    "});
 8501    cx.update_editor(|editor, window, cx| {
 8502        editor.change_selections(None, window, cx, |s| {
 8503            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8504        })
 8505    });
 8506    cx.assert_editor_state(indoc! {"
 8507        fn main() {
 8508            sample(param1, ˇparam2);
 8509        }
 8510
 8511        fn sample(param1: u8, param2: u8) {}
 8512    "});
 8513    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8514        .await;
 8515}
 8516
 8517#[gpui::test]
 8518async fn test_completion(cx: &mut gpui::TestAppContext) {
 8519    init_test(cx, |_| {});
 8520
 8521    let mut cx = EditorLspTestContext::new_rust(
 8522        lsp::ServerCapabilities {
 8523            completion_provider: Some(lsp::CompletionOptions {
 8524                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8525                resolve_provider: Some(true),
 8526                ..Default::default()
 8527            }),
 8528            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8529            ..Default::default()
 8530        },
 8531        cx,
 8532    )
 8533    .await;
 8534    let counter = Arc::new(AtomicUsize::new(0));
 8535
 8536    cx.set_state(indoc! {"
 8537        oneˇ
 8538        two
 8539        three
 8540    "});
 8541    cx.simulate_keystroke(".");
 8542    handle_completion_request(
 8543        &mut cx,
 8544        indoc! {"
 8545            one.|<>
 8546            two
 8547            three
 8548        "},
 8549        vec!["first_completion", "second_completion"],
 8550        counter.clone(),
 8551    )
 8552    .await;
 8553    cx.condition(|editor, _| editor.context_menu_visible())
 8554        .await;
 8555    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8556
 8557    let _handler = handle_signature_help_request(
 8558        &mut cx,
 8559        lsp::SignatureHelp {
 8560            signatures: vec![lsp::SignatureInformation {
 8561                label: "test signature".to_string(),
 8562                documentation: None,
 8563                parameters: Some(vec![lsp::ParameterInformation {
 8564                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8565                    documentation: None,
 8566                }]),
 8567                active_parameter: None,
 8568            }],
 8569            active_signature: None,
 8570            active_parameter: None,
 8571        },
 8572    );
 8573    cx.update_editor(|editor, window, cx| {
 8574        assert!(
 8575            !editor.signature_help_state.is_shown(),
 8576            "No signature help was called for"
 8577        );
 8578        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8579    });
 8580    cx.run_until_parked();
 8581    cx.update_editor(|editor, _, _| {
 8582        assert!(
 8583            !editor.signature_help_state.is_shown(),
 8584            "No signature help should be shown when completions menu is open"
 8585        );
 8586    });
 8587
 8588    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8589        editor.context_menu_next(&Default::default(), window, cx);
 8590        editor
 8591            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8592            .unwrap()
 8593    });
 8594    cx.assert_editor_state(indoc! {"
 8595        one.second_completionˇ
 8596        two
 8597        three
 8598    "});
 8599
 8600    handle_resolve_completion_request(
 8601        &mut cx,
 8602        Some(vec![
 8603            (
 8604                //This overlaps with the primary completion edit which is
 8605                //misbehavior from the LSP spec, test that we filter it out
 8606                indoc! {"
 8607                    one.second_ˇcompletion
 8608                    two
 8609                    threeˇ
 8610                "},
 8611                "overlapping additional edit",
 8612            ),
 8613            (
 8614                indoc! {"
 8615                    one.second_completion
 8616                    two
 8617                    threeˇ
 8618                "},
 8619                "\nadditional edit",
 8620            ),
 8621        ]),
 8622    )
 8623    .await;
 8624    apply_additional_edits.await.unwrap();
 8625    cx.assert_editor_state(indoc! {"
 8626        one.second_completionˇ
 8627        two
 8628        three
 8629        additional edit
 8630    "});
 8631
 8632    cx.set_state(indoc! {"
 8633        one.second_completion
 8634        twoˇ
 8635        threeˇ
 8636        additional edit
 8637    "});
 8638    cx.simulate_keystroke(" ");
 8639    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8640    cx.simulate_keystroke("s");
 8641    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8642
 8643    cx.assert_editor_state(indoc! {"
 8644        one.second_completion
 8645        two sˇ
 8646        three sˇ
 8647        additional edit
 8648    "});
 8649    handle_completion_request(
 8650        &mut cx,
 8651        indoc! {"
 8652            one.second_completion
 8653            two s
 8654            three <s|>
 8655            additional edit
 8656        "},
 8657        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8658        counter.clone(),
 8659    )
 8660    .await;
 8661    cx.condition(|editor, _| editor.context_menu_visible())
 8662        .await;
 8663    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8664
 8665    cx.simulate_keystroke("i");
 8666
 8667    handle_completion_request(
 8668        &mut cx,
 8669        indoc! {"
 8670            one.second_completion
 8671            two si
 8672            three <si|>
 8673            additional edit
 8674        "},
 8675        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8676        counter.clone(),
 8677    )
 8678    .await;
 8679    cx.condition(|editor, _| editor.context_menu_visible())
 8680        .await;
 8681    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8682
 8683    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8684        editor
 8685            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8686            .unwrap()
 8687    });
 8688    cx.assert_editor_state(indoc! {"
 8689        one.second_completion
 8690        two sixth_completionˇ
 8691        three sixth_completionˇ
 8692        additional edit
 8693    "});
 8694
 8695    apply_additional_edits.await.unwrap();
 8696
 8697    update_test_language_settings(&mut cx, |settings| {
 8698        settings.defaults.show_completions_on_input = Some(false);
 8699    });
 8700    cx.set_state("editorˇ");
 8701    cx.simulate_keystroke(".");
 8702    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8703    cx.simulate_keystroke("c");
 8704    cx.simulate_keystroke("l");
 8705    cx.simulate_keystroke("o");
 8706    cx.assert_editor_state("editor.cloˇ");
 8707    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8708    cx.update_editor(|editor, window, cx| {
 8709        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8710    });
 8711    handle_completion_request(
 8712        &mut cx,
 8713        "editor.<clo|>",
 8714        vec!["close", "clobber"],
 8715        counter.clone(),
 8716    )
 8717    .await;
 8718    cx.condition(|editor, _| editor.context_menu_visible())
 8719        .await;
 8720    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8721
 8722    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8723        editor
 8724            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8725            .unwrap()
 8726    });
 8727    cx.assert_editor_state("editor.closeˇ");
 8728    handle_resolve_completion_request(&mut cx, None).await;
 8729    apply_additional_edits.await.unwrap();
 8730}
 8731
 8732#[gpui::test]
 8733async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8734    init_test(cx, |_| {});
 8735
 8736    let fs = FakeFs::new(cx.executor());
 8737    fs.insert_tree(
 8738        path!("/a"),
 8739        json!({
 8740            "main.ts": "a",
 8741        }),
 8742    )
 8743    .await;
 8744
 8745    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8746    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8747    let typescript_language = Arc::new(Language::new(
 8748        LanguageConfig {
 8749            name: "TypeScript".into(),
 8750            matcher: LanguageMatcher {
 8751                path_suffixes: vec!["ts".to_string()],
 8752                ..LanguageMatcher::default()
 8753            },
 8754            line_comments: vec!["// ".into()],
 8755            ..LanguageConfig::default()
 8756        },
 8757        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8758    ));
 8759    language_registry.add(typescript_language.clone());
 8760    let mut fake_servers = language_registry.register_fake_lsp(
 8761        "TypeScript",
 8762        FakeLspAdapter {
 8763            capabilities: lsp::ServerCapabilities {
 8764                completion_provider: Some(lsp::CompletionOptions {
 8765                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8766                    ..lsp::CompletionOptions::default()
 8767                }),
 8768                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8769                ..lsp::ServerCapabilities::default()
 8770            },
 8771            // Emulate vtsls label generation
 8772            label_for_completion: Some(Box::new(|item, _| {
 8773                let text = if let Some(description) = item
 8774                    .label_details
 8775                    .as_ref()
 8776                    .and_then(|label_details| label_details.description.as_ref())
 8777                {
 8778                    format!("{} {}", item.label, description)
 8779                } else if let Some(detail) = &item.detail {
 8780                    format!("{} {}", item.label, detail)
 8781                } else {
 8782                    item.label.clone()
 8783                };
 8784                let len = text.len();
 8785                Some(language::CodeLabel {
 8786                    text,
 8787                    runs: Vec::new(),
 8788                    filter_range: 0..len,
 8789                })
 8790            })),
 8791            ..FakeLspAdapter::default()
 8792        },
 8793    );
 8794    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8795    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8796    let worktree_id = workspace
 8797        .update(cx, |workspace, _window, cx| {
 8798            workspace.project().update(cx, |project, cx| {
 8799                project.worktrees(cx).next().unwrap().read(cx).id()
 8800            })
 8801        })
 8802        .unwrap();
 8803    let _buffer = project
 8804        .update(cx, |project, cx| {
 8805            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8806        })
 8807        .await
 8808        .unwrap();
 8809    let editor = workspace
 8810        .update(cx, |workspace, window, cx| {
 8811            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8812        })
 8813        .unwrap()
 8814        .await
 8815        .unwrap()
 8816        .downcast::<Editor>()
 8817        .unwrap();
 8818    let fake_server = fake_servers.next().await.unwrap();
 8819
 8820    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8821    let multiline_label_2 = "a\nb\nc\n";
 8822    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8823    let multiline_description = "d\ne\nf\n";
 8824    let multiline_detail_2 = "g\nh\ni\n";
 8825
 8826    let mut completion_handle =
 8827        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8828            Ok(Some(lsp::CompletionResponse::Array(vec![
 8829                lsp::CompletionItem {
 8830                    label: multiline_label.to_string(),
 8831                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8832                        range: lsp::Range {
 8833                            start: lsp::Position {
 8834                                line: params.text_document_position.position.line,
 8835                                character: params.text_document_position.position.character,
 8836                            },
 8837                            end: lsp::Position {
 8838                                line: params.text_document_position.position.line,
 8839                                character: params.text_document_position.position.character,
 8840                            },
 8841                        },
 8842                        new_text: "new_text_1".to_string(),
 8843                    })),
 8844                    ..lsp::CompletionItem::default()
 8845                },
 8846                lsp::CompletionItem {
 8847                    label: "single line label 1".to_string(),
 8848                    detail: Some(multiline_detail.to_string()),
 8849                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8850                        range: lsp::Range {
 8851                            start: lsp::Position {
 8852                                line: params.text_document_position.position.line,
 8853                                character: params.text_document_position.position.character,
 8854                            },
 8855                            end: lsp::Position {
 8856                                line: params.text_document_position.position.line,
 8857                                character: params.text_document_position.position.character,
 8858                            },
 8859                        },
 8860                        new_text: "new_text_2".to_string(),
 8861                    })),
 8862                    ..lsp::CompletionItem::default()
 8863                },
 8864                lsp::CompletionItem {
 8865                    label: "single line label 2".to_string(),
 8866                    label_details: Some(lsp::CompletionItemLabelDetails {
 8867                        description: Some(multiline_description.to_string()),
 8868                        detail: None,
 8869                    }),
 8870                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8871                        range: lsp::Range {
 8872                            start: lsp::Position {
 8873                                line: params.text_document_position.position.line,
 8874                                character: params.text_document_position.position.character,
 8875                            },
 8876                            end: lsp::Position {
 8877                                line: params.text_document_position.position.line,
 8878                                character: params.text_document_position.position.character,
 8879                            },
 8880                        },
 8881                        new_text: "new_text_2".to_string(),
 8882                    })),
 8883                    ..lsp::CompletionItem::default()
 8884                },
 8885                lsp::CompletionItem {
 8886                    label: multiline_label_2.to_string(),
 8887                    detail: Some(multiline_detail_2.to_string()),
 8888                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8889                        range: lsp::Range {
 8890                            start: lsp::Position {
 8891                                line: params.text_document_position.position.line,
 8892                                character: params.text_document_position.position.character,
 8893                            },
 8894                            end: lsp::Position {
 8895                                line: params.text_document_position.position.line,
 8896                                character: params.text_document_position.position.character,
 8897                            },
 8898                        },
 8899                        new_text: "new_text_3".to_string(),
 8900                    })),
 8901                    ..lsp::CompletionItem::default()
 8902                },
 8903                lsp::CompletionItem {
 8904                    label: "Label with many     spaces and \t but without newlines".to_string(),
 8905                    detail: Some(
 8906                        "Details with many     spaces and \t but without newlines".to_string(),
 8907                    ),
 8908                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8909                        range: lsp::Range {
 8910                            start: lsp::Position {
 8911                                line: params.text_document_position.position.line,
 8912                                character: params.text_document_position.position.character,
 8913                            },
 8914                            end: lsp::Position {
 8915                                line: params.text_document_position.position.line,
 8916                                character: params.text_document_position.position.character,
 8917                            },
 8918                        },
 8919                        new_text: "new_text_4".to_string(),
 8920                    })),
 8921                    ..lsp::CompletionItem::default()
 8922                },
 8923            ])))
 8924        });
 8925
 8926    editor.update_in(cx, |editor, window, cx| {
 8927        cx.focus_self(window);
 8928        editor.move_to_end(&MoveToEnd, window, cx);
 8929        editor.handle_input(".", window, cx);
 8930    });
 8931    cx.run_until_parked();
 8932    completion_handle.next().await.unwrap();
 8933
 8934    editor.update(cx, |editor, _| {
 8935        assert!(editor.context_menu_visible());
 8936        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 8937        {
 8938            let completion_labels = menu
 8939                .completions
 8940                .borrow()
 8941                .iter()
 8942                .map(|c| c.label.text.clone())
 8943                .collect::<Vec<_>>();
 8944            assert_eq!(
 8945                completion_labels,
 8946                &[
 8947                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 8948                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 8949                    "single line label 2 d e f ",
 8950                    "a b c g h i ",
 8951                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 8952                ],
 8953                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 8954            );
 8955
 8956            for completion in menu
 8957                .completions
 8958                .borrow()
 8959                .iter() {
 8960                    assert_eq!(
 8961                        completion.label.filter_range,
 8962                        0..completion.label.text.len(),
 8963                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 8964                    );
 8965                }
 8966
 8967        } else {
 8968            panic!("expected completion menu to be open");
 8969        }
 8970    });
 8971}
 8972
 8973#[gpui::test]
 8974async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 8975    init_test(cx, |_| {});
 8976    let mut cx = EditorLspTestContext::new_rust(
 8977        lsp::ServerCapabilities {
 8978            completion_provider: Some(lsp::CompletionOptions {
 8979                trigger_characters: Some(vec![".".to_string()]),
 8980                ..Default::default()
 8981            }),
 8982            ..Default::default()
 8983        },
 8984        cx,
 8985    )
 8986    .await;
 8987    cx.lsp
 8988        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 8989            Ok(Some(lsp::CompletionResponse::Array(vec![
 8990                lsp::CompletionItem {
 8991                    label: "first".into(),
 8992                    ..Default::default()
 8993                },
 8994                lsp::CompletionItem {
 8995                    label: "last".into(),
 8996                    ..Default::default()
 8997                },
 8998            ])))
 8999        });
 9000    cx.set_state("variableˇ");
 9001    cx.simulate_keystroke(".");
 9002    cx.executor().run_until_parked();
 9003
 9004    cx.update_editor(|editor, _, _| {
 9005        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9006        {
 9007            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9008        } else {
 9009            panic!("expected completion menu to be open");
 9010        }
 9011    });
 9012
 9013    cx.update_editor(|editor, window, cx| {
 9014        editor.move_page_down(&MovePageDown::default(), window, cx);
 9015        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9016        {
 9017            assert!(
 9018                menu.selected_item == 1,
 9019                "expected PageDown to select the last item from the context menu"
 9020            );
 9021        } else {
 9022            panic!("expected completion menu to stay open after PageDown");
 9023        }
 9024    });
 9025
 9026    cx.update_editor(|editor, window, cx| {
 9027        editor.move_page_up(&MovePageUp::default(), window, cx);
 9028        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9029        {
 9030            assert!(
 9031                menu.selected_item == 0,
 9032                "expected PageUp to select the first item from the context menu"
 9033            );
 9034        } else {
 9035            panic!("expected completion menu to stay open after PageUp");
 9036        }
 9037    });
 9038}
 9039
 9040#[gpui::test]
 9041async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9042    init_test(cx, |_| {});
 9043    let mut cx = EditorLspTestContext::new_rust(
 9044        lsp::ServerCapabilities {
 9045            completion_provider: Some(lsp::CompletionOptions {
 9046                trigger_characters: Some(vec![".".to_string()]),
 9047                ..Default::default()
 9048            }),
 9049            ..Default::default()
 9050        },
 9051        cx,
 9052    )
 9053    .await;
 9054    cx.lsp
 9055        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9056            Ok(Some(lsp::CompletionResponse::Array(vec![
 9057                lsp::CompletionItem {
 9058                    label: "Range".into(),
 9059                    sort_text: Some("a".into()),
 9060                    ..Default::default()
 9061                },
 9062                lsp::CompletionItem {
 9063                    label: "r".into(),
 9064                    sort_text: Some("b".into()),
 9065                    ..Default::default()
 9066                },
 9067                lsp::CompletionItem {
 9068                    label: "ret".into(),
 9069                    sort_text: Some("c".into()),
 9070                    ..Default::default()
 9071                },
 9072                lsp::CompletionItem {
 9073                    label: "return".into(),
 9074                    sort_text: Some("d".into()),
 9075                    ..Default::default()
 9076                },
 9077                lsp::CompletionItem {
 9078                    label: "slice".into(),
 9079                    sort_text: Some("d".into()),
 9080                    ..Default::default()
 9081                },
 9082            ])))
 9083        });
 9084    cx.set_state("");
 9085    cx.executor().run_until_parked();
 9086    cx.update_editor(|editor, window, cx| {
 9087        editor.show_completions(
 9088            &ShowCompletions {
 9089                trigger: Some("r".into()),
 9090            },
 9091            window,
 9092            cx,
 9093        );
 9094    });
 9095    cx.executor().run_until_parked();
 9096
 9097    cx.update_editor(|editor, _, _| {
 9098        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9099        {
 9100            assert_eq!(
 9101                completion_menu_entries(&menu),
 9102                &["r", "ret", "Range", "return"]
 9103            );
 9104        } else {
 9105            panic!("expected completion menu to be open");
 9106        }
 9107    });
 9108}
 9109
 9110#[gpui::test]
 9111async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9112    init_test(cx, |_| {});
 9113
 9114    let mut cx = EditorLspTestContext::new_rust(
 9115        lsp::ServerCapabilities {
 9116            completion_provider: Some(lsp::CompletionOptions {
 9117                trigger_characters: Some(vec![".".to_string()]),
 9118                resolve_provider: Some(true),
 9119                ..Default::default()
 9120            }),
 9121            ..Default::default()
 9122        },
 9123        cx,
 9124    )
 9125    .await;
 9126
 9127    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9128    cx.simulate_keystroke(".");
 9129    let completion_item = lsp::CompletionItem {
 9130        label: "Some".into(),
 9131        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9132        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9133        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9134            kind: lsp::MarkupKind::Markdown,
 9135            value: "```rust\nSome(2)\n```".to_string(),
 9136        })),
 9137        deprecated: Some(false),
 9138        sort_text: Some("Some".to_string()),
 9139        filter_text: Some("Some".to_string()),
 9140        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9141        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9142            range: lsp::Range {
 9143                start: lsp::Position {
 9144                    line: 0,
 9145                    character: 22,
 9146                },
 9147                end: lsp::Position {
 9148                    line: 0,
 9149                    character: 22,
 9150                },
 9151            },
 9152            new_text: "Some(2)".to_string(),
 9153        })),
 9154        additional_text_edits: Some(vec![lsp::TextEdit {
 9155            range: lsp::Range {
 9156                start: lsp::Position {
 9157                    line: 0,
 9158                    character: 20,
 9159                },
 9160                end: lsp::Position {
 9161                    line: 0,
 9162                    character: 22,
 9163                },
 9164            },
 9165            new_text: "".to_string(),
 9166        }]),
 9167        ..Default::default()
 9168    };
 9169
 9170    let closure_completion_item = completion_item.clone();
 9171    let counter = Arc::new(AtomicUsize::new(0));
 9172    let counter_clone = counter.clone();
 9173    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9174        let task_completion_item = closure_completion_item.clone();
 9175        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9176        async move {
 9177            Ok(Some(lsp::CompletionResponse::Array(vec![
 9178                task_completion_item,
 9179            ])))
 9180        }
 9181    });
 9182
 9183    cx.condition(|editor, _| editor.context_menu_visible())
 9184        .await;
 9185    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9186    assert!(request.next().await.is_some());
 9187    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9188
 9189    cx.simulate_keystroke("S");
 9190    cx.simulate_keystroke("o");
 9191    cx.simulate_keystroke("m");
 9192    cx.condition(|editor, _| editor.context_menu_visible())
 9193        .await;
 9194    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9195    assert!(request.next().await.is_some());
 9196    assert!(request.next().await.is_some());
 9197    assert!(request.next().await.is_some());
 9198    request.close();
 9199    assert!(request.next().await.is_none());
 9200    assert_eq!(
 9201        counter.load(atomic::Ordering::Acquire),
 9202        4,
 9203        "With the completions menu open, only one LSP request should happen per input"
 9204    );
 9205}
 9206
 9207#[gpui::test]
 9208async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9209    init_test(cx, |_| {});
 9210    let mut cx = EditorTestContext::new(cx).await;
 9211    let language = Arc::new(Language::new(
 9212        LanguageConfig {
 9213            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9214            ..Default::default()
 9215        },
 9216        Some(tree_sitter_rust::LANGUAGE.into()),
 9217    ));
 9218    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9219
 9220    // If multiple selections intersect a line, the line is only toggled once.
 9221    cx.set_state(indoc! {"
 9222        fn a() {
 9223            «//b();
 9224            ˇ»// «c();
 9225            //ˇ»  d();
 9226        }
 9227    "});
 9228
 9229    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9230
 9231    cx.assert_editor_state(indoc! {"
 9232        fn a() {
 9233            «b();
 9234            c();
 9235            ˇ» d();
 9236        }
 9237    "});
 9238
 9239    // The comment prefix is inserted at the same column for every line in a
 9240    // selection.
 9241    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9242
 9243    cx.assert_editor_state(indoc! {"
 9244        fn a() {
 9245            // «b();
 9246            // c();
 9247            ˇ»//  d();
 9248        }
 9249    "});
 9250
 9251    // If a selection ends at the beginning of a line, that line is not toggled.
 9252    cx.set_selections_state(indoc! {"
 9253        fn a() {
 9254            // b();
 9255            «// c();
 9256        ˇ»    //  d();
 9257        }
 9258    "});
 9259
 9260    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9261
 9262    cx.assert_editor_state(indoc! {"
 9263        fn a() {
 9264            // b();
 9265            «c();
 9266        ˇ»    //  d();
 9267        }
 9268    "});
 9269
 9270    // If a selection span a single line and is empty, the line is toggled.
 9271    cx.set_state(indoc! {"
 9272        fn a() {
 9273            a();
 9274            b();
 9275        ˇ
 9276        }
 9277    "});
 9278
 9279    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9280
 9281    cx.assert_editor_state(indoc! {"
 9282        fn a() {
 9283            a();
 9284            b();
 9285        //•ˇ
 9286        }
 9287    "});
 9288
 9289    // If a selection span multiple lines, empty lines are not toggled.
 9290    cx.set_state(indoc! {"
 9291        fn a() {
 9292            «a();
 9293
 9294            c();ˇ»
 9295        }
 9296    "});
 9297
 9298    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9299
 9300    cx.assert_editor_state(indoc! {"
 9301        fn a() {
 9302            // «a();
 9303
 9304            // c();ˇ»
 9305        }
 9306    "});
 9307
 9308    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9309    cx.set_state(indoc! {"
 9310        fn a() {
 9311            «// a();
 9312            /// b();
 9313            //! c();ˇ»
 9314        }
 9315    "});
 9316
 9317    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9318
 9319    cx.assert_editor_state(indoc! {"
 9320        fn a() {
 9321            «a();
 9322            b();
 9323            c();ˇ»
 9324        }
 9325    "});
 9326}
 9327
 9328#[gpui::test]
 9329async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9330    init_test(cx, |_| {});
 9331    let mut cx = EditorTestContext::new(cx).await;
 9332    let language = Arc::new(Language::new(
 9333        LanguageConfig {
 9334            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9335            ..Default::default()
 9336        },
 9337        Some(tree_sitter_rust::LANGUAGE.into()),
 9338    ));
 9339    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9340
 9341    let toggle_comments = &ToggleComments {
 9342        advance_downwards: false,
 9343        ignore_indent: true,
 9344    };
 9345
 9346    // If multiple selections intersect a line, the line is only toggled once.
 9347    cx.set_state(indoc! {"
 9348        fn a() {
 9349        //    «b();
 9350        //    c();
 9351        //    ˇ» d();
 9352        }
 9353    "});
 9354
 9355    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9356
 9357    cx.assert_editor_state(indoc! {"
 9358        fn a() {
 9359            «b();
 9360            c();
 9361            ˇ» d();
 9362        }
 9363    "});
 9364
 9365    // The comment prefix is inserted at the beginning of each line
 9366    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9367
 9368    cx.assert_editor_state(indoc! {"
 9369        fn a() {
 9370        //    «b();
 9371        //    c();
 9372        //    ˇ» d();
 9373        }
 9374    "});
 9375
 9376    // If a selection ends at the beginning of a line, that line is not toggled.
 9377    cx.set_selections_state(indoc! {"
 9378        fn a() {
 9379        //    b();
 9380        //    «c();
 9381        ˇ»//     d();
 9382        }
 9383    "});
 9384
 9385    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9386
 9387    cx.assert_editor_state(indoc! {"
 9388        fn a() {
 9389        //    b();
 9390            «c();
 9391        ˇ»//     d();
 9392        }
 9393    "});
 9394
 9395    // If a selection span a single line and is empty, the line is toggled.
 9396    cx.set_state(indoc! {"
 9397        fn a() {
 9398            a();
 9399            b();
 9400        ˇ
 9401        }
 9402    "});
 9403
 9404    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9405
 9406    cx.assert_editor_state(indoc! {"
 9407        fn a() {
 9408            a();
 9409            b();
 9410        //ˇ
 9411        }
 9412    "});
 9413
 9414    // If a selection span multiple lines, empty lines are not toggled.
 9415    cx.set_state(indoc! {"
 9416        fn a() {
 9417            «a();
 9418
 9419            c();ˇ»
 9420        }
 9421    "});
 9422
 9423    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9424
 9425    cx.assert_editor_state(indoc! {"
 9426        fn a() {
 9427        //    «a();
 9428
 9429        //    c();ˇ»
 9430        }
 9431    "});
 9432
 9433    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9434    cx.set_state(indoc! {"
 9435        fn a() {
 9436        //    «a();
 9437        ///    b();
 9438        //!    c();ˇ»
 9439        }
 9440    "});
 9441
 9442    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9443
 9444    cx.assert_editor_state(indoc! {"
 9445        fn a() {
 9446            «a();
 9447            b();
 9448            c();ˇ»
 9449        }
 9450    "});
 9451}
 9452
 9453#[gpui::test]
 9454async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9455    init_test(cx, |_| {});
 9456
 9457    let language = Arc::new(Language::new(
 9458        LanguageConfig {
 9459            line_comments: vec!["// ".into()],
 9460            ..Default::default()
 9461        },
 9462        Some(tree_sitter_rust::LANGUAGE.into()),
 9463    ));
 9464
 9465    let mut cx = EditorTestContext::new(cx).await;
 9466
 9467    cx.language_registry().add(language.clone());
 9468    cx.update_buffer(|buffer, cx| {
 9469        buffer.set_language(Some(language), cx);
 9470    });
 9471
 9472    let toggle_comments = &ToggleComments {
 9473        advance_downwards: true,
 9474        ignore_indent: false,
 9475    };
 9476
 9477    // Single cursor on one line -> advance
 9478    // Cursor moves horizontally 3 characters as well on non-blank line
 9479    cx.set_state(indoc!(
 9480        "fn a() {
 9481             ˇdog();
 9482             cat();
 9483        }"
 9484    ));
 9485    cx.update_editor(|editor, window, cx| {
 9486        editor.toggle_comments(toggle_comments, window, cx);
 9487    });
 9488    cx.assert_editor_state(indoc!(
 9489        "fn a() {
 9490             // dog();
 9491             catˇ();
 9492        }"
 9493    ));
 9494
 9495    // Single selection on one line -> don't advance
 9496    cx.set_state(indoc!(
 9497        "fn a() {
 9498             «dog()ˇ»;
 9499             cat();
 9500        }"
 9501    ));
 9502    cx.update_editor(|editor, window, cx| {
 9503        editor.toggle_comments(toggle_comments, window, cx);
 9504    });
 9505    cx.assert_editor_state(indoc!(
 9506        "fn a() {
 9507             // «dog()ˇ»;
 9508             cat();
 9509        }"
 9510    ));
 9511
 9512    // Multiple cursors on one line -> advance
 9513    cx.set_state(indoc!(
 9514        "fn a() {
 9515             ˇdˇog();
 9516             cat();
 9517        }"
 9518    ));
 9519    cx.update_editor(|editor, window, cx| {
 9520        editor.toggle_comments(toggle_comments, window, cx);
 9521    });
 9522    cx.assert_editor_state(indoc!(
 9523        "fn a() {
 9524             // dog();
 9525             catˇ(ˇ);
 9526        }"
 9527    ));
 9528
 9529    // Multiple cursors on one line, with selection -> don't advance
 9530    cx.set_state(indoc!(
 9531        "fn a() {
 9532             ˇdˇog«()ˇ»;
 9533             cat();
 9534        }"
 9535    ));
 9536    cx.update_editor(|editor, window, cx| {
 9537        editor.toggle_comments(toggle_comments, window, cx);
 9538    });
 9539    cx.assert_editor_state(indoc!(
 9540        "fn a() {
 9541             // ˇdˇog«()ˇ»;
 9542             cat();
 9543        }"
 9544    ));
 9545
 9546    // Single cursor on one line -> advance
 9547    // Cursor moves to column 0 on blank line
 9548    cx.set_state(indoc!(
 9549        "fn a() {
 9550             ˇdog();
 9551
 9552             cat();
 9553        }"
 9554    ));
 9555    cx.update_editor(|editor, window, cx| {
 9556        editor.toggle_comments(toggle_comments, window, cx);
 9557    });
 9558    cx.assert_editor_state(indoc!(
 9559        "fn a() {
 9560             // dog();
 9561        ˇ
 9562             cat();
 9563        }"
 9564    ));
 9565
 9566    // Single cursor on one line -> advance
 9567    // Cursor starts and ends at column 0
 9568    cx.set_state(indoc!(
 9569        "fn a() {
 9570         ˇ    dog();
 9571             cat();
 9572        }"
 9573    ));
 9574    cx.update_editor(|editor, window, cx| {
 9575        editor.toggle_comments(toggle_comments, window, cx);
 9576    });
 9577    cx.assert_editor_state(indoc!(
 9578        "fn a() {
 9579             // dog();
 9580         ˇ    cat();
 9581        }"
 9582    ));
 9583}
 9584
 9585#[gpui::test]
 9586async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9587    init_test(cx, |_| {});
 9588
 9589    let mut cx = EditorTestContext::new(cx).await;
 9590
 9591    let html_language = Arc::new(
 9592        Language::new(
 9593            LanguageConfig {
 9594                name: "HTML".into(),
 9595                block_comment: Some(("<!-- ".into(), " -->".into())),
 9596                ..Default::default()
 9597            },
 9598            Some(tree_sitter_html::language()),
 9599        )
 9600        .with_injection_query(
 9601            r#"
 9602            (script_element
 9603                (raw_text) @injection.content
 9604                (#set! injection.language "javascript"))
 9605            "#,
 9606        )
 9607        .unwrap(),
 9608    );
 9609
 9610    let javascript_language = Arc::new(Language::new(
 9611        LanguageConfig {
 9612            name: "JavaScript".into(),
 9613            line_comments: vec!["// ".into()],
 9614            ..Default::default()
 9615        },
 9616        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9617    ));
 9618
 9619    cx.language_registry().add(html_language.clone());
 9620    cx.language_registry().add(javascript_language.clone());
 9621    cx.update_buffer(|buffer, cx| {
 9622        buffer.set_language(Some(html_language), cx);
 9623    });
 9624
 9625    // Toggle comments for empty selections
 9626    cx.set_state(
 9627        &r#"
 9628            <p>A</p>ˇ
 9629            <p>B</p>ˇ
 9630            <p>C</p>ˇ
 9631        "#
 9632        .unindent(),
 9633    );
 9634    cx.update_editor(|editor, window, cx| {
 9635        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9636    });
 9637    cx.assert_editor_state(
 9638        &r#"
 9639            <!-- <p>A</p>ˇ -->
 9640            <!-- <p>B</p>ˇ -->
 9641            <!-- <p>C</p>ˇ -->
 9642        "#
 9643        .unindent(),
 9644    );
 9645    cx.update_editor(|editor, window, cx| {
 9646        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9647    });
 9648    cx.assert_editor_state(
 9649        &r#"
 9650            <p>A</p>ˇ
 9651            <p>B</p>ˇ
 9652            <p>C</p>ˇ
 9653        "#
 9654        .unindent(),
 9655    );
 9656
 9657    // Toggle comments for mixture of empty and non-empty selections, where
 9658    // multiple selections occupy a given line.
 9659    cx.set_state(
 9660        &r#"
 9661            <p>A«</p>
 9662            <p>ˇ»B</p>ˇ
 9663            <p>C«</p>
 9664            <p>ˇ»D</p>ˇ
 9665        "#
 9666        .unindent(),
 9667    );
 9668
 9669    cx.update_editor(|editor, window, cx| {
 9670        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9671    });
 9672    cx.assert_editor_state(
 9673        &r#"
 9674            <!-- <p>A«</p>
 9675            <p>ˇ»B</p>ˇ -->
 9676            <!-- <p>C«</p>
 9677            <p>ˇ»D</p>ˇ -->
 9678        "#
 9679        .unindent(),
 9680    );
 9681    cx.update_editor(|editor, window, cx| {
 9682        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9683    });
 9684    cx.assert_editor_state(
 9685        &r#"
 9686            <p>A«</p>
 9687            <p>ˇ»B</p>ˇ
 9688            <p>C«</p>
 9689            <p>ˇ»D</p>ˇ
 9690        "#
 9691        .unindent(),
 9692    );
 9693
 9694    // Toggle comments when different languages are active for different
 9695    // selections.
 9696    cx.set_state(
 9697        &r#"
 9698            ˇ<script>
 9699                ˇvar x = new Y();
 9700            ˇ</script>
 9701        "#
 9702        .unindent(),
 9703    );
 9704    cx.executor().run_until_parked();
 9705    cx.update_editor(|editor, window, cx| {
 9706        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9707    });
 9708    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9709    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9710    cx.assert_editor_state(
 9711        &r#"
 9712            <!-- ˇ<script> -->
 9713                // ˇvar x = new Y();
 9714            // ˇ</script>
 9715        "#
 9716        .unindent(),
 9717    );
 9718}
 9719
 9720#[gpui::test]
 9721fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9722    init_test(cx, |_| {});
 9723
 9724    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9725    let multibuffer = cx.new(|cx| {
 9726        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9727        multibuffer.push_excerpts(
 9728            buffer.clone(),
 9729            [
 9730                ExcerptRange {
 9731                    context: Point::new(0, 0)..Point::new(0, 4),
 9732                    primary: None,
 9733                },
 9734                ExcerptRange {
 9735                    context: Point::new(1, 0)..Point::new(1, 4),
 9736                    primary: None,
 9737                },
 9738            ],
 9739            cx,
 9740        );
 9741        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9742        multibuffer
 9743    });
 9744
 9745    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9746    editor.update_in(cx, |editor, window, cx| {
 9747        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9748        editor.change_selections(None, window, cx, |s| {
 9749            s.select_ranges([
 9750                Point::new(0, 0)..Point::new(0, 0),
 9751                Point::new(1, 0)..Point::new(1, 0),
 9752            ])
 9753        });
 9754
 9755        editor.handle_input("X", window, cx);
 9756        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9757        assert_eq!(
 9758            editor.selections.ranges(cx),
 9759            [
 9760                Point::new(0, 1)..Point::new(0, 1),
 9761                Point::new(1, 1)..Point::new(1, 1),
 9762            ]
 9763        );
 9764
 9765        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9766        editor.change_selections(None, window, cx, |s| {
 9767            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9768        });
 9769        editor.backspace(&Default::default(), window, cx);
 9770        assert_eq!(editor.text(cx), "Xa\nbbb");
 9771        assert_eq!(
 9772            editor.selections.ranges(cx),
 9773            [Point::new(1, 0)..Point::new(1, 0)]
 9774        );
 9775
 9776        editor.change_selections(None, window, cx, |s| {
 9777            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9778        });
 9779        editor.backspace(&Default::default(), window, cx);
 9780        assert_eq!(editor.text(cx), "X\nbb");
 9781        assert_eq!(
 9782            editor.selections.ranges(cx),
 9783            [Point::new(0, 1)..Point::new(0, 1)]
 9784        );
 9785    });
 9786}
 9787
 9788#[gpui::test]
 9789fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9790    init_test(cx, |_| {});
 9791
 9792    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9793    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9794        indoc! {"
 9795            [aaaa
 9796            (bbbb]
 9797            cccc)",
 9798        },
 9799        markers.clone(),
 9800    );
 9801    let excerpt_ranges = markers.into_iter().map(|marker| {
 9802        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9803        ExcerptRange {
 9804            context,
 9805            primary: None,
 9806        }
 9807    });
 9808    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9809    let multibuffer = cx.new(|cx| {
 9810        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9811        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9812        multibuffer
 9813    });
 9814
 9815    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9816    editor.update_in(cx, |editor, window, cx| {
 9817        let (expected_text, selection_ranges) = marked_text_ranges(
 9818            indoc! {"
 9819                aaaa
 9820                bˇbbb
 9821                bˇbbˇb
 9822                cccc"
 9823            },
 9824            true,
 9825        );
 9826        assert_eq!(editor.text(cx), expected_text);
 9827        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9828
 9829        editor.handle_input("X", window, cx);
 9830
 9831        let (expected_text, expected_selections) = marked_text_ranges(
 9832            indoc! {"
 9833                aaaa
 9834                bXˇbbXb
 9835                bXˇbbXˇb
 9836                cccc"
 9837            },
 9838            false,
 9839        );
 9840        assert_eq!(editor.text(cx), expected_text);
 9841        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9842
 9843        editor.newline(&Newline, window, cx);
 9844        let (expected_text, expected_selections) = marked_text_ranges(
 9845            indoc! {"
 9846                aaaa
 9847                bX
 9848                ˇbbX
 9849                b
 9850                bX
 9851                ˇbbX
 9852                ˇb
 9853                cccc"
 9854            },
 9855            false,
 9856        );
 9857        assert_eq!(editor.text(cx), expected_text);
 9858        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9859    });
 9860}
 9861
 9862#[gpui::test]
 9863fn test_refresh_selections(cx: &mut TestAppContext) {
 9864    init_test(cx, |_| {});
 9865
 9866    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9867    let mut excerpt1_id = None;
 9868    let multibuffer = cx.new(|cx| {
 9869        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9870        excerpt1_id = multibuffer
 9871            .push_excerpts(
 9872                buffer.clone(),
 9873                [
 9874                    ExcerptRange {
 9875                        context: Point::new(0, 0)..Point::new(1, 4),
 9876                        primary: None,
 9877                    },
 9878                    ExcerptRange {
 9879                        context: Point::new(1, 0)..Point::new(2, 4),
 9880                        primary: None,
 9881                    },
 9882                ],
 9883                cx,
 9884            )
 9885            .into_iter()
 9886            .next();
 9887        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9888        multibuffer
 9889    });
 9890
 9891    let editor = cx.add_window(|window, cx| {
 9892        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9893        let snapshot = editor.snapshot(window, cx);
 9894        editor.change_selections(None, window, cx, |s| {
 9895            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9896        });
 9897        editor.begin_selection(
 9898            Point::new(2, 1).to_display_point(&snapshot),
 9899            true,
 9900            1,
 9901            window,
 9902            cx,
 9903        );
 9904        assert_eq!(
 9905            editor.selections.ranges(cx),
 9906            [
 9907                Point::new(1, 3)..Point::new(1, 3),
 9908                Point::new(2, 1)..Point::new(2, 1),
 9909            ]
 9910        );
 9911        editor
 9912    });
 9913
 9914    // Refreshing selections is a no-op when excerpts haven't changed.
 9915    _ = editor.update(cx, |editor, window, cx| {
 9916        editor.change_selections(None, window, cx, |s| s.refresh());
 9917        assert_eq!(
 9918            editor.selections.ranges(cx),
 9919            [
 9920                Point::new(1, 3)..Point::new(1, 3),
 9921                Point::new(2, 1)..Point::new(2, 1),
 9922            ]
 9923        );
 9924    });
 9925
 9926    multibuffer.update(cx, |multibuffer, cx| {
 9927        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
 9928    });
 9929    _ = editor.update(cx, |editor, window, cx| {
 9930        // Removing an excerpt causes the first selection to become degenerate.
 9931        assert_eq!(
 9932            editor.selections.ranges(cx),
 9933            [
 9934                Point::new(0, 0)..Point::new(0, 0),
 9935                Point::new(0, 1)..Point::new(0, 1)
 9936            ]
 9937        );
 9938
 9939        // Refreshing selections will relocate the first selection to the original buffer
 9940        // location.
 9941        editor.change_selections(None, window, cx, |s| s.refresh());
 9942        assert_eq!(
 9943            editor.selections.ranges(cx),
 9944            [
 9945                Point::new(0, 1)..Point::new(0, 1),
 9946                Point::new(0, 3)..Point::new(0, 3)
 9947            ]
 9948        );
 9949        assert!(editor.selections.pending_anchor().is_some());
 9950    });
 9951}
 9952
 9953#[gpui::test]
 9954fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
 9955    init_test(cx, |_| {});
 9956
 9957    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9958    let mut excerpt1_id = None;
 9959    let multibuffer = cx.new(|cx| {
 9960        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9961        excerpt1_id = multibuffer
 9962            .push_excerpts(
 9963                buffer.clone(),
 9964                [
 9965                    ExcerptRange {
 9966                        context: Point::new(0, 0)..Point::new(1, 4),
 9967                        primary: None,
 9968                    },
 9969                    ExcerptRange {
 9970                        context: Point::new(1, 0)..Point::new(2, 4),
 9971                        primary: None,
 9972                    },
 9973                ],
 9974                cx,
 9975            )
 9976            .into_iter()
 9977            .next();
 9978        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9979        multibuffer
 9980    });
 9981
 9982    let editor = cx.add_window(|window, cx| {
 9983        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9984        let snapshot = editor.snapshot(window, cx);
 9985        editor.begin_selection(
 9986            Point::new(1, 3).to_display_point(&snapshot),
 9987            false,
 9988            1,
 9989            window,
 9990            cx,
 9991        );
 9992        assert_eq!(
 9993            editor.selections.ranges(cx),
 9994            [Point::new(1, 3)..Point::new(1, 3)]
 9995        );
 9996        editor
 9997    });
 9998
 9999    multibuffer.update(cx, |multibuffer, cx| {
10000        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10001    });
10002    _ = editor.update(cx, |editor, window, cx| {
10003        assert_eq!(
10004            editor.selections.ranges(cx),
10005            [Point::new(0, 0)..Point::new(0, 0)]
10006        );
10007
10008        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10009        editor.change_selections(None, window, cx, |s| s.refresh());
10010        assert_eq!(
10011            editor.selections.ranges(cx),
10012            [Point::new(0, 3)..Point::new(0, 3)]
10013        );
10014        assert!(editor.selections.pending_anchor().is_some());
10015    });
10016}
10017
10018#[gpui::test]
10019async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10020    init_test(cx, |_| {});
10021
10022    let language = Arc::new(
10023        Language::new(
10024            LanguageConfig {
10025                brackets: BracketPairConfig {
10026                    pairs: vec![
10027                        BracketPair {
10028                            start: "{".to_string(),
10029                            end: "}".to_string(),
10030                            close: true,
10031                            surround: true,
10032                            newline: true,
10033                        },
10034                        BracketPair {
10035                            start: "/* ".to_string(),
10036                            end: " */".to_string(),
10037                            close: true,
10038                            surround: true,
10039                            newline: true,
10040                        },
10041                    ],
10042                    ..Default::default()
10043                },
10044                ..Default::default()
10045            },
10046            Some(tree_sitter_rust::LANGUAGE.into()),
10047        )
10048        .with_indents_query("")
10049        .unwrap(),
10050    );
10051
10052    let text = concat!(
10053        "{   }\n",     //
10054        "  x\n",       //
10055        "  /*   */\n", //
10056        "x\n",         //
10057        "{{} }\n",     //
10058    );
10059
10060    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10061    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10062    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10063    editor
10064        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10065        .await;
10066
10067    editor.update_in(cx, |editor, window, cx| {
10068        editor.change_selections(None, window, cx, |s| {
10069            s.select_display_ranges([
10070                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10071                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10072                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10073            ])
10074        });
10075        editor.newline(&Newline, window, cx);
10076
10077        assert_eq!(
10078            editor.buffer().read(cx).read(cx).text(),
10079            concat!(
10080                "{ \n",    // Suppress rustfmt
10081                "\n",      //
10082                "}\n",     //
10083                "  x\n",   //
10084                "  /* \n", //
10085                "  \n",    //
10086                "  */\n",  //
10087                "x\n",     //
10088                "{{} \n",  //
10089                "}\n",     //
10090            )
10091        );
10092    });
10093}
10094
10095#[gpui::test]
10096fn test_highlighted_ranges(cx: &mut TestAppContext) {
10097    init_test(cx, |_| {});
10098
10099    let editor = cx.add_window(|window, cx| {
10100        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10101        build_editor(buffer.clone(), window, cx)
10102    });
10103
10104    _ = editor.update(cx, |editor, window, cx| {
10105        struct Type1;
10106        struct Type2;
10107
10108        let buffer = editor.buffer.read(cx).snapshot(cx);
10109
10110        let anchor_range =
10111            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10112
10113        editor.highlight_background::<Type1>(
10114            &[
10115                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10116                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10117                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10118                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10119            ],
10120            |_| Hsla::red(),
10121            cx,
10122        );
10123        editor.highlight_background::<Type2>(
10124            &[
10125                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10126                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10127                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10128                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10129            ],
10130            |_| Hsla::green(),
10131            cx,
10132        );
10133
10134        let snapshot = editor.snapshot(window, cx);
10135        let mut highlighted_ranges = editor.background_highlights_in_range(
10136            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10137            &snapshot,
10138            cx.theme().colors(),
10139        );
10140        // Enforce a consistent ordering based on color without relying on the ordering of the
10141        // highlight's `TypeId` which is non-executor.
10142        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10143        assert_eq!(
10144            highlighted_ranges,
10145            &[
10146                (
10147                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10148                    Hsla::red(),
10149                ),
10150                (
10151                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10152                    Hsla::red(),
10153                ),
10154                (
10155                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10156                    Hsla::green(),
10157                ),
10158                (
10159                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10160                    Hsla::green(),
10161                ),
10162            ]
10163        );
10164        assert_eq!(
10165            editor.background_highlights_in_range(
10166                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10167                &snapshot,
10168                cx.theme().colors(),
10169            ),
10170            &[(
10171                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10172                Hsla::red(),
10173            )]
10174        );
10175    });
10176}
10177
10178#[gpui::test]
10179async fn test_following(cx: &mut gpui::TestAppContext) {
10180    init_test(cx, |_| {});
10181
10182    let fs = FakeFs::new(cx.executor());
10183    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10184
10185    let buffer = project.update(cx, |project, cx| {
10186        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10187        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10188    });
10189    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10190    let follower = cx.update(|cx| {
10191        cx.open_window(
10192            WindowOptions {
10193                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10194                    gpui::Point::new(px(0.), px(0.)),
10195                    gpui::Point::new(px(10.), px(80.)),
10196                ))),
10197                ..Default::default()
10198            },
10199            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10200        )
10201        .unwrap()
10202    });
10203
10204    let is_still_following = Rc::new(RefCell::new(true));
10205    let follower_edit_event_count = Rc::new(RefCell::new(0));
10206    let pending_update = Rc::new(RefCell::new(None));
10207    let leader_entity = leader.root(cx).unwrap();
10208    let follower_entity = follower.root(cx).unwrap();
10209    _ = follower.update(cx, {
10210        let update = pending_update.clone();
10211        let is_still_following = is_still_following.clone();
10212        let follower_edit_event_count = follower_edit_event_count.clone();
10213        |_, window, cx| {
10214            cx.subscribe_in(
10215                &leader_entity,
10216                window,
10217                move |_, leader, event, window, cx| {
10218                    leader.read(cx).add_event_to_update_proto(
10219                        event,
10220                        &mut update.borrow_mut(),
10221                        window,
10222                        cx,
10223                    );
10224                },
10225            )
10226            .detach();
10227
10228            cx.subscribe_in(
10229                &follower_entity,
10230                window,
10231                move |_, _, event: &EditorEvent, _window, _cx| {
10232                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10233                        *is_still_following.borrow_mut() = false;
10234                    }
10235
10236                    if let EditorEvent::BufferEdited = event {
10237                        *follower_edit_event_count.borrow_mut() += 1;
10238                    }
10239                },
10240            )
10241            .detach();
10242        }
10243    });
10244
10245    // Update the selections only
10246    _ = leader.update(cx, |leader, window, cx| {
10247        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10248    });
10249    follower
10250        .update(cx, |follower, window, cx| {
10251            follower.apply_update_proto(
10252                &project,
10253                pending_update.borrow_mut().take().unwrap(),
10254                window,
10255                cx,
10256            )
10257        })
10258        .unwrap()
10259        .await
10260        .unwrap();
10261    _ = follower.update(cx, |follower, _, cx| {
10262        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10263    });
10264    assert!(*is_still_following.borrow());
10265    assert_eq!(*follower_edit_event_count.borrow(), 0);
10266
10267    // Update the scroll position only
10268    _ = leader.update(cx, |leader, window, cx| {
10269        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10270    });
10271    follower
10272        .update(cx, |follower, window, cx| {
10273            follower.apply_update_proto(
10274                &project,
10275                pending_update.borrow_mut().take().unwrap(),
10276                window,
10277                cx,
10278            )
10279        })
10280        .unwrap()
10281        .await
10282        .unwrap();
10283    assert_eq!(
10284        follower
10285            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10286            .unwrap(),
10287        gpui::Point::new(1.5, 3.5)
10288    );
10289    assert!(*is_still_following.borrow());
10290    assert_eq!(*follower_edit_event_count.borrow(), 0);
10291
10292    // Update the selections and scroll position. The follower's scroll position is updated
10293    // via autoscroll, not via the leader's exact scroll position.
10294    _ = leader.update(cx, |leader, window, cx| {
10295        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10296        leader.request_autoscroll(Autoscroll::newest(), cx);
10297        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10298    });
10299    follower
10300        .update(cx, |follower, window, cx| {
10301            follower.apply_update_proto(
10302                &project,
10303                pending_update.borrow_mut().take().unwrap(),
10304                window,
10305                cx,
10306            )
10307        })
10308        .unwrap()
10309        .await
10310        .unwrap();
10311    _ = follower.update(cx, |follower, _, cx| {
10312        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10313        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10314    });
10315    assert!(*is_still_following.borrow());
10316
10317    // Creating a pending selection that precedes another selection
10318    _ = leader.update(cx, |leader, window, cx| {
10319        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10320        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10321    });
10322    follower
10323        .update(cx, |follower, window, cx| {
10324            follower.apply_update_proto(
10325                &project,
10326                pending_update.borrow_mut().take().unwrap(),
10327                window,
10328                cx,
10329            )
10330        })
10331        .unwrap()
10332        .await
10333        .unwrap();
10334    _ = follower.update(cx, |follower, _, cx| {
10335        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10336    });
10337    assert!(*is_still_following.borrow());
10338
10339    // Extend the pending selection so that it surrounds another selection
10340    _ = leader.update(cx, |leader, window, cx| {
10341        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10342    });
10343    follower
10344        .update(cx, |follower, window, cx| {
10345            follower.apply_update_proto(
10346                &project,
10347                pending_update.borrow_mut().take().unwrap(),
10348                window,
10349                cx,
10350            )
10351        })
10352        .unwrap()
10353        .await
10354        .unwrap();
10355    _ = follower.update(cx, |follower, _, cx| {
10356        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10357    });
10358
10359    // Scrolling locally breaks the follow
10360    _ = follower.update(cx, |follower, window, cx| {
10361        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10362        follower.set_scroll_anchor(
10363            ScrollAnchor {
10364                anchor: top_anchor,
10365                offset: gpui::Point::new(0.0, 0.5),
10366            },
10367            window,
10368            cx,
10369        );
10370    });
10371    assert!(!(*is_still_following.borrow()));
10372}
10373
10374#[gpui::test]
10375async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10376    init_test(cx, |_| {});
10377
10378    let fs = FakeFs::new(cx.executor());
10379    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10380    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10381    let pane = workspace
10382        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10383        .unwrap();
10384
10385    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10386
10387    let leader = pane.update_in(cx, |_, window, cx| {
10388        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10389        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10390    });
10391
10392    // Start following the editor when it has no excerpts.
10393    let mut state_message =
10394        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10395    let workspace_entity = workspace.root(cx).unwrap();
10396    let follower_1 = cx
10397        .update_window(*workspace.deref(), |_, window, cx| {
10398            Editor::from_state_proto(
10399                workspace_entity,
10400                ViewId {
10401                    creator: Default::default(),
10402                    id: 0,
10403                },
10404                &mut state_message,
10405                window,
10406                cx,
10407            )
10408        })
10409        .unwrap()
10410        .unwrap()
10411        .await
10412        .unwrap();
10413
10414    let update_message = Rc::new(RefCell::new(None));
10415    follower_1.update_in(cx, {
10416        let update = update_message.clone();
10417        |_, window, cx| {
10418            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10419                leader.read(cx).add_event_to_update_proto(
10420                    event,
10421                    &mut update.borrow_mut(),
10422                    window,
10423                    cx,
10424                );
10425            })
10426            .detach();
10427        }
10428    });
10429
10430    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10431        (
10432            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10433            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10434        )
10435    });
10436
10437    // Insert some excerpts.
10438    leader.update(cx, |leader, cx| {
10439        leader.buffer.update(cx, |multibuffer, cx| {
10440            let excerpt_ids = multibuffer.push_excerpts(
10441                buffer_1.clone(),
10442                [
10443                    ExcerptRange {
10444                        context: 1..6,
10445                        primary: None,
10446                    },
10447                    ExcerptRange {
10448                        context: 12..15,
10449                        primary: None,
10450                    },
10451                    ExcerptRange {
10452                        context: 0..3,
10453                        primary: None,
10454                    },
10455                ],
10456                cx,
10457            );
10458            multibuffer.insert_excerpts_after(
10459                excerpt_ids[0],
10460                buffer_2.clone(),
10461                [
10462                    ExcerptRange {
10463                        context: 8..12,
10464                        primary: None,
10465                    },
10466                    ExcerptRange {
10467                        context: 0..6,
10468                        primary: None,
10469                    },
10470                ],
10471                cx,
10472            );
10473        });
10474    });
10475
10476    // Apply the update of adding the excerpts.
10477    follower_1
10478        .update_in(cx, |follower, window, cx| {
10479            follower.apply_update_proto(
10480                &project,
10481                update_message.borrow().clone().unwrap(),
10482                window,
10483                cx,
10484            )
10485        })
10486        .await
10487        .unwrap();
10488    assert_eq!(
10489        follower_1.update(cx, |editor, cx| editor.text(cx)),
10490        leader.update(cx, |editor, cx| editor.text(cx))
10491    );
10492    update_message.borrow_mut().take();
10493
10494    // Start following separately after it already has excerpts.
10495    let mut state_message =
10496        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10497    let workspace_entity = workspace.root(cx).unwrap();
10498    let follower_2 = cx
10499        .update_window(*workspace.deref(), |_, window, cx| {
10500            Editor::from_state_proto(
10501                workspace_entity,
10502                ViewId {
10503                    creator: Default::default(),
10504                    id: 0,
10505                },
10506                &mut state_message,
10507                window,
10508                cx,
10509            )
10510        })
10511        .unwrap()
10512        .unwrap()
10513        .await
10514        .unwrap();
10515    assert_eq!(
10516        follower_2.update(cx, |editor, cx| editor.text(cx)),
10517        leader.update(cx, |editor, cx| editor.text(cx))
10518    );
10519
10520    // Remove some excerpts.
10521    leader.update(cx, |leader, cx| {
10522        leader.buffer.update(cx, |multibuffer, cx| {
10523            let excerpt_ids = multibuffer.excerpt_ids();
10524            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10525            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10526        });
10527    });
10528
10529    // Apply the update of removing the excerpts.
10530    follower_1
10531        .update_in(cx, |follower, window, cx| {
10532            follower.apply_update_proto(
10533                &project,
10534                update_message.borrow().clone().unwrap(),
10535                window,
10536                cx,
10537            )
10538        })
10539        .await
10540        .unwrap();
10541    follower_2
10542        .update_in(cx, |follower, window, cx| {
10543            follower.apply_update_proto(
10544                &project,
10545                update_message.borrow().clone().unwrap(),
10546                window,
10547                cx,
10548            )
10549        })
10550        .await
10551        .unwrap();
10552    update_message.borrow_mut().take();
10553    assert_eq!(
10554        follower_1.update(cx, |editor, cx| editor.text(cx)),
10555        leader.update(cx, |editor, cx| editor.text(cx))
10556    );
10557}
10558
10559#[gpui::test]
10560async fn go_to_prev_overlapping_diagnostic(
10561    executor: BackgroundExecutor,
10562    cx: &mut gpui::TestAppContext,
10563) {
10564    init_test(cx, |_| {});
10565
10566    let mut cx = EditorTestContext::new(cx).await;
10567    let lsp_store =
10568        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10569
10570    cx.set_state(indoc! {"
10571        ˇfn func(abc def: i32) -> u32 {
10572        }
10573    "});
10574
10575    cx.update(|_, cx| {
10576        lsp_store.update(cx, |lsp_store, cx| {
10577            lsp_store
10578                .update_diagnostics(
10579                    LanguageServerId(0),
10580                    lsp::PublishDiagnosticsParams {
10581                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10582                        version: None,
10583                        diagnostics: vec![
10584                            lsp::Diagnostic {
10585                                range: lsp::Range::new(
10586                                    lsp::Position::new(0, 11),
10587                                    lsp::Position::new(0, 12),
10588                                ),
10589                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10590                                ..Default::default()
10591                            },
10592                            lsp::Diagnostic {
10593                                range: lsp::Range::new(
10594                                    lsp::Position::new(0, 12),
10595                                    lsp::Position::new(0, 15),
10596                                ),
10597                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10598                                ..Default::default()
10599                            },
10600                            lsp::Diagnostic {
10601                                range: lsp::Range::new(
10602                                    lsp::Position::new(0, 25),
10603                                    lsp::Position::new(0, 28),
10604                                ),
10605                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10606                                ..Default::default()
10607                            },
10608                        ],
10609                    },
10610                    &[],
10611                    cx,
10612                )
10613                .unwrap()
10614        });
10615    });
10616
10617    executor.run_until_parked();
10618
10619    cx.update_editor(|editor, window, cx| {
10620        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10621    });
10622
10623    cx.assert_editor_state(indoc! {"
10624        fn func(abc def: i32) -> ˇu32 {
10625        }
10626    "});
10627
10628    cx.update_editor(|editor, window, cx| {
10629        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10630    });
10631
10632    cx.assert_editor_state(indoc! {"
10633        fn func(abc ˇdef: i32) -> u32 {
10634        }
10635    "});
10636
10637    cx.update_editor(|editor, window, cx| {
10638        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10639    });
10640
10641    cx.assert_editor_state(indoc! {"
10642        fn func(abcˇ def: i32) -> u32 {
10643        }
10644    "});
10645
10646    cx.update_editor(|editor, window, cx| {
10647        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10648    });
10649
10650    cx.assert_editor_state(indoc! {"
10651        fn func(abc def: i32) -> ˇu32 {
10652        }
10653    "});
10654}
10655
10656#[gpui::test]
10657async fn cycle_through_same_place_diagnostics(
10658    executor: BackgroundExecutor,
10659    cx: &mut gpui::TestAppContext,
10660) {
10661    init_test(cx, |_| {});
10662
10663    let mut cx = EditorTestContext::new(cx).await;
10664    let lsp_store =
10665        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10666
10667    cx.set_state(indoc! {"
10668        ˇfn func(abc def: i32) -> u32 {
10669        }
10670    "});
10671
10672    cx.update(|_, cx| {
10673        lsp_store.update(cx, |lsp_store, cx| {
10674            lsp_store
10675                .update_diagnostics(
10676                    LanguageServerId(0),
10677                    lsp::PublishDiagnosticsParams {
10678                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10679                        version: None,
10680                        diagnostics: vec![
10681                            lsp::Diagnostic {
10682                                range: lsp::Range::new(
10683                                    lsp::Position::new(0, 11),
10684                                    lsp::Position::new(0, 12),
10685                                ),
10686                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10687                                ..Default::default()
10688                            },
10689                            lsp::Diagnostic {
10690                                range: lsp::Range::new(
10691                                    lsp::Position::new(0, 12),
10692                                    lsp::Position::new(0, 15),
10693                                ),
10694                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10695                                ..Default::default()
10696                            },
10697                            lsp::Diagnostic {
10698                                range: lsp::Range::new(
10699                                    lsp::Position::new(0, 12),
10700                                    lsp::Position::new(0, 15),
10701                                ),
10702                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10703                                ..Default::default()
10704                            },
10705                            lsp::Diagnostic {
10706                                range: lsp::Range::new(
10707                                    lsp::Position::new(0, 25),
10708                                    lsp::Position::new(0, 28),
10709                                ),
10710                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10711                                ..Default::default()
10712                            },
10713                        ],
10714                    },
10715                    &[],
10716                    cx,
10717                )
10718                .unwrap()
10719        });
10720    });
10721    executor.run_until_parked();
10722
10723    //// Backward
10724
10725    // Fourth diagnostic
10726    cx.update_editor(|editor, window, cx| {
10727        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10728    });
10729    cx.assert_editor_state(indoc! {"
10730        fn func(abc def: i32) -> ˇu32 {
10731        }
10732    "});
10733
10734    // Third diagnostic
10735    cx.update_editor(|editor, window, cx| {
10736        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10737    });
10738    cx.assert_editor_state(indoc! {"
10739        fn func(abc ˇdef: i32) -> u32 {
10740        }
10741    "});
10742
10743    // Second diagnostic, same place
10744    cx.update_editor(|editor, window, cx| {
10745        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10746    });
10747    cx.assert_editor_state(indoc! {"
10748        fn func(abc ˇdef: i32) -> u32 {
10749        }
10750    "});
10751
10752    // First diagnostic
10753    cx.update_editor(|editor, window, cx| {
10754        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10755    });
10756    cx.assert_editor_state(indoc! {"
10757        fn func(abcˇ def: i32) -> u32 {
10758        }
10759    "});
10760
10761    // Wrapped over, fourth diagnostic
10762    cx.update_editor(|editor, window, cx| {
10763        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10764    });
10765    cx.assert_editor_state(indoc! {"
10766        fn func(abc def: i32) -> ˇu32 {
10767        }
10768    "});
10769
10770    cx.update_editor(|editor, window, cx| {
10771        editor.move_to_beginning(&MoveToBeginning, window, cx);
10772    });
10773    cx.assert_editor_state(indoc! {"
10774        ˇfn func(abc def: i32) -> u32 {
10775        }
10776    "});
10777
10778    //// Forward
10779
10780    // First diagnostic
10781    cx.update_editor(|editor, window, cx| {
10782        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10783    });
10784    cx.assert_editor_state(indoc! {"
10785        fn func(abcˇ def: i32) -> u32 {
10786        }
10787    "});
10788
10789    // Second diagnostic
10790    cx.update_editor(|editor, window, cx| {
10791        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10792    });
10793    cx.assert_editor_state(indoc! {"
10794        fn func(abc ˇdef: i32) -> u32 {
10795        }
10796    "});
10797
10798    // Third diagnostic, same place
10799    cx.update_editor(|editor, window, cx| {
10800        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10801    });
10802    cx.assert_editor_state(indoc! {"
10803        fn func(abc ˇdef: i32) -> u32 {
10804        }
10805    "});
10806
10807    // Fourth diagnostic
10808    cx.update_editor(|editor, window, cx| {
10809        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10810    });
10811    cx.assert_editor_state(indoc! {"
10812        fn func(abc def: i32) -> ˇu32 {
10813        }
10814    "});
10815
10816    // Wrapped around, first diagnostic
10817    cx.update_editor(|editor, window, cx| {
10818        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10819    });
10820    cx.assert_editor_state(indoc! {"
10821        fn func(abcˇ def: i32) -> u32 {
10822        }
10823    "});
10824}
10825
10826#[gpui::test]
10827async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10828    init_test(cx, |_| {});
10829
10830    let mut cx = EditorTestContext::new(cx).await;
10831
10832    cx.set_state(indoc! {"
10833        fn func(abˇc def: i32) -> u32 {
10834        }
10835    "});
10836    let lsp_store =
10837        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10838
10839    cx.update(|_, cx| {
10840        lsp_store.update(cx, |lsp_store, cx| {
10841            lsp_store.update_diagnostics(
10842                LanguageServerId(0),
10843                lsp::PublishDiagnosticsParams {
10844                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10845                    version: None,
10846                    diagnostics: vec![lsp::Diagnostic {
10847                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10848                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10849                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10850                        ..Default::default()
10851                    }],
10852                },
10853                &[],
10854                cx,
10855            )
10856        })
10857    }).unwrap();
10858    cx.run_until_parked();
10859    cx.update_editor(|editor, window, cx| {
10860        hover_popover::hover(editor, &Default::default(), window, cx)
10861    });
10862    cx.run_until_parked();
10863    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10864}
10865
10866#[gpui::test]
10867async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10868    init_test(cx, |_| {});
10869
10870    let mut cx = EditorTestContext::new(cx).await;
10871
10872    let diff_base = r#"
10873        use some::mod;
10874
10875        const A: u32 = 42;
10876
10877        fn main() {
10878            println!("hello");
10879
10880            println!("world");
10881        }
10882        "#
10883    .unindent();
10884
10885    // Edits are modified, removed, modified, added
10886    cx.set_state(
10887        &r#"
10888        use some::modified;
10889
10890        ˇ
10891        fn main() {
10892            println!("hello there");
10893
10894            println!("around the");
10895            println!("world");
10896        }
10897        "#
10898        .unindent(),
10899    );
10900
10901    cx.set_diff_base(&diff_base);
10902    executor.run_until_parked();
10903
10904    cx.update_editor(|editor, window, cx| {
10905        //Wrap around the bottom of the buffer
10906        for _ in 0..3 {
10907            editor.go_to_next_hunk(&GoToHunk, window, cx);
10908        }
10909    });
10910
10911    cx.assert_editor_state(
10912        &r#"
10913        ˇuse some::modified;
10914
10915
10916        fn main() {
10917            println!("hello there");
10918
10919            println!("around the");
10920            println!("world");
10921        }
10922        "#
10923        .unindent(),
10924    );
10925
10926    cx.update_editor(|editor, window, cx| {
10927        //Wrap around the top of the buffer
10928        for _ in 0..2 {
10929            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10930        }
10931    });
10932
10933    cx.assert_editor_state(
10934        &r#"
10935        use some::modified;
10936
10937
10938        fn main() {
10939        ˇ    println!("hello there");
10940
10941            println!("around the");
10942            println!("world");
10943        }
10944        "#
10945        .unindent(),
10946    );
10947
10948    cx.update_editor(|editor, window, cx| {
10949        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10950    });
10951
10952    cx.assert_editor_state(
10953        &r#"
10954        use some::modified;
10955
10956        ˇ
10957        fn main() {
10958            println!("hello there");
10959
10960            println!("around the");
10961            println!("world");
10962        }
10963        "#
10964        .unindent(),
10965    );
10966
10967    cx.update_editor(|editor, window, cx| {
10968        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10969    });
10970
10971    cx.assert_editor_state(
10972        &r#"
10973        ˇuse some::modified;
10974
10975
10976        fn main() {
10977            println!("hello there");
10978
10979            println!("around the");
10980            println!("world");
10981        }
10982        "#
10983        .unindent(),
10984    );
10985
10986    cx.update_editor(|editor, window, cx| {
10987        for _ in 0..2 {
10988            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
10989        }
10990    });
10991
10992    cx.assert_editor_state(
10993        &r#"
10994        use some::modified;
10995
10996
10997        fn main() {
10998        ˇ    println!("hello there");
10999
11000            println!("around the");
11001            println!("world");
11002        }
11003        "#
11004        .unindent(),
11005    );
11006
11007    cx.update_editor(|editor, window, cx| {
11008        editor.fold(&Fold, window, cx);
11009    });
11010
11011    cx.update_editor(|editor, window, cx| {
11012        editor.go_to_next_hunk(&GoToHunk, window, cx);
11013    });
11014
11015    cx.assert_editor_state(
11016        &r#"
11017        ˇuse some::modified;
11018
11019
11020        fn main() {
11021            println!("hello there");
11022
11023            println!("around the");
11024            println!("world");
11025        }
11026        "#
11027        .unindent(),
11028    );
11029}
11030
11031#[test]
11032fn test_split_words() {
11033    fn split(text: &str) -> Vec<&str> {
11034        split_words(text).collect()
11035    }
11036
11037    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11038    assert_eq!(split("hello_world"), &["hello_", "world"]);
11039    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11040    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11041    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11042    assert_eq!(split("helloworld"), &["helloworld"]);
11043
11044    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11045}
11046
11047#[gpui::test]
11048async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
11049    init_test(cx, |_| {});
11050
11051    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11052    let mut assert = |before, after| {
11053        let _state_context = cx.set_state(before);
11054        cx.run_until_parked();
11055        cx.update_editor(|editor, window, cx| {
11056            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11057        });
11058        cx.assert_editor_state(after);
11059    };
11060
11061    // Outside bracket jumps to outside of matching bracket
11062    assert("console.logˇ(var);", "console.log(var)ˇ;");
11063    assert("console.log(var)ˇ;", "console.logˇ(var);");
11064
11065    // Inside bracket jumps to inside of matching bracket
11066    assert("console.log(ˇvar);", "console.log(varˇ);");
11067    assert("console.log(varˇ);", "console.log(ˇvar);");
11068
11069    // When outside a bracket and inside, favor jumping to the inside bracket
11070    assert(
11071        "console.log('foo', [1, 2, 3]ˇ);",
11072        "console.log(ˇ'foo', [1, 2, 3]);",
11073    );
11074    assert(
11075        "console.log(ˇ'foo', [1, 2, 3]);",
11076        "console.log('foo', [1, 2, 3]ˇ);",
11077    );
11078
11079    // Bias forward if two options are equally likely
11080    assert(
11081        "let result = curried_fun()ˇ();",
11082        "let result = curried_fun()()ˇ;",
11083    );
11084
11085    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11086    assert(
11087        indoc! {"
11088            function test() {
11089                console.log('test')ˇ
11090            }"},
11091        indoc! {"
11092            function test() {
11093                console.logˇ('test')
11094            }"},
11095    );
11096}
11097
11098#[gpui::test]
11099async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
11100    init_test(cx, |_| {});
11101
11102    let fs = FakeFs::new(cx.executor());
11103    fs.insert_tree(
11104        path!("/a"),
11105        json!({
11106            "main.rs": "fn main() { let a = 5; }",
11107            "other.rs": "// Test file",
11108        }),
11109    )
11110    .await;
11111    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11112
11113    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11114    language_registry.add(Arc::new(Language::new(
11115        LanguageConfig {
11116            name: "Rust".into(),
11117            matcher: LanguageMatcher {
11118                path_suffixes: vec!["rs".to_string()],
11119                ..Default::default()
11120            },
11121            brackets: BracketPairConfig {
11122                pairs: vec![BracketPair {
11123                    start: "{".to_string(),
11124                    end: "}".to_string(),
11125                    close: true,
11126                    surround: true,
11127                    newline: true,
11128                }],
11129                disabled_scopes_by_bracket_ix: Vec::new(),
11130            },
11131            ..Default::default()
11132        },
11133        Some(tree_sitter_rust::LANGUAGE.into()),
11134    )));
11135    let mut fake_servers = language_registry.register_fake_lsp(
11136        "Rust",
11137        FakeLspAdapter {
11138            capabilities: lsp::ServerCapabilities {
11139                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11140                    first_trigger_character: "{".to_string(),
11141                    more_trigger_character: None,
11142                }),
11143                ..Default::default()
11144            },
11145            ..Default::default()
11146        },
11147    );
11148
11149    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11150
11151    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11152
11153    let worktree_id = workspace
11154        .update(cx, |workspace, _, cx| {
11155            workspace.project().update(cx, |project, cx| {
11156                project.worktrees(cx).next().unwrap().read(cx).id()
11157            })
11158        })
11159        .unwrap();
11160
11161    let buffer = project
11162        .update(cx, |project, cx| {
11163            project.open_local_buffer(path!("/a/main.rs"), cx)
11164        })
11165        .await
11166        .unwrap();
11167    let editor_handle = workspace
11168        .update(cx, |workspace, window, cx| {
11169            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11170        })
11171        .unwrap()
11172        .await
11173        .unwrap()
11174        .downcast::<Editor>()
11175        .unwrap();
11176
11177    cx.executor().start_waiting();
11178    let fake_server = fake_servers.next().await.unwrap();
11179
11180    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11181        assert_eq!(
11182            params.text_document_position.text_document.uri,
11183            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11184        );
11185        assert_eq!(
11186            params.text_document_position.position,
11187            lsp::Position::new(0, 21),
11188        );
11189
11190        Ok(Some(vec![lsp::TextEdit {
11191            new_text: "]".to_string(),
11192            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11193        }]))
11194    });
11195
11196    editor_handle.update_in(cx, |editor, window, cx| {
11197        window.focus(&editor.focus_handle(cx));
11198        editor.change_selections(None, window, cx, |s| {
11199            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11200        });
11201        editor.handle_input("{", window, cx);
11202    });
11203
11204    cx.executor().run_until_parked();
11205
11206    buffer.update(cx, |buffer, _| {
11207        assert_eq!(
11208            buffer.text(),
11209            "fn main() { let a = {5}; }",
11210            "No extra braces from on type formatting should appear in the buffer"
11211        )
11212    });
11213}
11214
11215#[gpui::test]
11216async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11217    init_test(cx, |_| {});
11218
11219    let fs = FakeFs::new(cx.executor());
11220    fs.insert_tree(
11221        path!("/a"),
11222        json!({
11223            "main.rs": "fn main() { let a = 5; }",
11224            "other.rs": "// Test file",
11225        }),
11226    )
11227    .await;
11228
11229    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11230
11231    let server_restarts = Arc::new(AtomicUsize::new(0));
11232    let closure_restarts = Arc::clone(&server_restarts);
11233    let language_server_name = "test language server";
11234    let language_name: LanguageName = "Rust".into();
11235
11236    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11237    language_registry.add(Arc::new(Language::new(
11238        LanguageConfig {
11239            name: language_name.clone(),
11240            matcher: LanguageMatcher {
11241                path_suffixes: vec!["rs".to_string()],
11242                ..Default::default()
11243            },
11244            ..Default::default()
11245        },
11246        Some(tree_sitter_rust::LANGUAGE.into()),
11247    )));
11248    let mut fake_servers = language_registry.register_fake_lsp(
11249        "Rust",
11250        FakeLspAdapter {
11251            name: language_server_name,
11252            initialization_options: Some(json!({
11253                "testOptionValue": true
11254            })),
11255            initializer: Some(Box::new(move |fake_server| {
11256                let task_restarts = Arc::clone(&closure_restarts);
11257                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11258                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11259                    futures::future::ready(Ok(()))
11260                });
11261            })),
11262            ..Default::default()
11263        },
11264    );
11265
11266    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11267    let _buffer = project
11268        .update(cx, |project, cx| {
11269            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11270        })
11271        .await
11272        .unwrap();
11273    let _fake_server = fake_servers.next().await.unwrap();
11274    update_test_language_settings(cx, |language_settings| {
11275        language_settings.languages.insert(
11276            language_name.clone(),
11277            LanguageSettingsContent {
11278                tab_size: NonZeroU32::new(8),
11279                ..Default::default()
11280            },
11281        );
11282    });
11283    cx.executor().run_until_parked();
11284    assert_eq!(
11285        server_restarts.load(atomic::Ordering::Acquire),
11286        0,
11287        "Should not restart LSP server on an unrelated change"
11288    );
11289
11290    update_test_project_settings(cx, |project_settings| {
11291        project_settings.lsp.insert(
11292            "Some other server name".into(),
11293            LspSettings {
11294                binary: None,
11295                settings: None,
11296                initialization_options: Some(json!({
11297                    "some other init value": false
11298                })),
11299            },
11300        );
11301    });
11302    cx.executor().run_until_parked();
11303    assert_eq!(
11304        server_restarts.load(atomic::Ordering::Acquire),
11305        0,
11306        "Should not restart LSP server on an unrelated LSP settings change"
11307    );
11308
11309    update_test_project_settings(cx, |project_settings| {
11310        project_settings.lsp.insert(
11311            language_server_name.into(),
11312            LspSettings {
11313                binary: None,
11314                settings: None,
11315                initialization_options: Some(json!({
11316                    "anotherInitValue": false
11317                })),
11318            },
11319        );
11320    });
11321    cx.executor().run_until_parked();
11322    assert_eq!(
11323        server_restarts.load(atomic::Ordering::Acquire),
11324        1,
11325        "Should restart LSP server on a related LSP settings change"
11326    );
11327
11328    update_test_project_settings(cx, |project_settings| {
11329        project_settings.lsp.insert(
11330            language_server_name.into(),
11331            LspSettings {
11332                binary: None,
11333                settings: None,
11334                initialization_options: Some(json!({
11335                    "anotherInitValue": false
11336                })),
11337            },
11338        );
11339    });
11340    cx.executor().run_until_parked();
11341    assert_eq!(
11342        server_restarts.load(atomic::Ordering::Acquire),
11343        1,
11344        "Should not restart LSP server on a related LSP settings change that is the same"
11345    );
11346
11347    update_test_project_settings(cx, |project_settings| {
11348        project_settings.lsp.insert(
11349            language_server_name.into(),
11350            LspSettings {
11351                binary: None,
11352                settings: None,
11353                initialization_options: None,
11354            },
11355        );
11356    });
11357    cx.executor().run_until_parked();
11358    assert_eq!(
11359        server_restarts.load(atomic::Ordering::Acquire),
11360        2,
11361        "Should restart LSP server on another related LSP settings change"
11362    );
11363}
11364
11365#[gpui::test]
11366async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11367    init_test(cx, |_| {});
11368
11369    let mut cx = EditorLspTestContext::new_rust(
11370        lsp::ServerCapabilities {
11371            completion_provider: Some(lsp::CompletionOptions {
11372                trigger_characters: Some(vec![".".to_string()]),
11373                resolve_provider: Some(true),
11374                ..Default::default()
11375            }),
11376            ..Default::default()
11377        },
11378        cx,
11379    )
11380    .await;
11381
11382    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11383    cx.simulate_keystroke(".");
11384    let completion_item = lsp::CompletionItem {
11385        label: "some".into(),
11386        kind: Some(lsp::CompletionItemKind::SNIPPET),
11387        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11388        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11389            kind: lsp::MarkupKind::Markdown,
11390            value: "```rust\nSome(2)\n```".to_string(),
11391        })),
11392        deprecated: Some(false),
11393        sort_text: Some("fffffff2".to_string()),
11394        filter_text: Some("some".to_string()),
11395        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11396        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11397            range: lsp::Range {
11398                start: lsp::Position {
11399                    line: 0,
11400                    character: 22,
11401                },
11402                end: lsp::Position {
11403                    line: 0,
11404                    character: 22,
11405                },
11406            },
11407            new_text: "Some(2)".to_string(),
11408        })),
11409        additional_text_edits: Some(vec![lsp::TextEdit {
11410            range: lsp::Range {
11411                start: lsp::Position {
11412                    line: 0,
11413                    character: 20,
11414                },
11415                end: lsp::Position {
11416                    line: 0,
11417                    character: 22,
11418                },
11419            },
11420            new_text: "".to_string(),
11421        }]),
11422        ..Default::default()
11423    };
11424
11425    let closure_completion_item = completion_item.clone();
11426    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11427        let task_completion_item = closure_completion_item.clone();
11428        async move {
11429            Ok(Some(lsp::CompletionResponse::Array(vec![
11430                task_completion_item,
11431            ])))
11432        }
11433    });
11434
11435    request.next().await;
11436
11437    cx.condition(|editor, _| editor.context_menu_visible())
11438        .await;
11439    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11440        editor
11441            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11442            .unwrap()
11443    });
11444    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11445
11446    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11447        let task_completion_item = completion_item.clone();
11448        async move { Ok(task_completion_item) }
11449    })
11450    .next()
11451    .await
11452    .unwrap();
11453    apply_additional_edits.await.unwrap();
11454    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11455}
11456
11457#[gpui::test]
11458async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11459    cx: &mut gpui::TestAppContext,
11460) {
11461    init_test(cx, |_| {});
11462
11463    let mut cx = EditorLspTestContext::new_rust(
11464        lsp::ServerCapabilities {
11465            completion_provider: Some(lsp::CompletionOptions {
11466                trigger_characters: Some(vec![".".to_string()]),
11467                resolve_provider: Some(true),
11468                ..Default::default()
11469            }),
11470            ..Default::default()
11471        },
11472        cx,
11473    )
11474    .await;
11475
11476    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11477    cx.simulate_keystroke(".");
11478
11479    let item1 = lsp::CompletionItem {
11480        label: "method id()".to_string(),
11481        filter_text: Some("id".to_string()),
11482        detail: None,
11483        documentation: None,
11484        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11485            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11486            new_text: ".id".to_string(),
11487        })),
11488        ..lsp::CompletionItem::default()
11489    };
11490
11491    let item2 = lsp::CompletionItem {
11492        label: "other".to_string(),
11493        filter_text: Some("other".to_string()),
11494        detail: None,
11495        documentation: None,
11496        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11497            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11498            new_text: ".other".to_string(),
11499        })),
11500        ..lsp::CompletionItem::default()
11501    };
11502
11503    let item1 = item1.clone();
11504    cx.handle_request::<lsp::request::Completion, _, _>({
11505        let item1 = item1.clone();
11506        move |_, _, _| {
11507            let item1 = item1.clone();
11508            let item2 = item2.clone();
11509            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11510        }
11511    })
11512    .next()
11513    .await;
11514
11515    cx.condition(|editor, _| editor.context_menu_visible())
11516        .await;
11517    cx.update_editor(|editor, _, _| {
11518        let context_menu = editor.context_menu.borrow_mut();
11519        let context_menu = context_menu
11520            .as_ref()
11521            .expect("Should have the context menu deployed");
11522        match context_menu {
11523            CodeContextMenu::Completions(completions_menu) => {
11524                let completions = completions_menu.completions.borrow_mut();
11525                assert_eq!(
11526                    completions
11527                        .iter()
11528                        .map(|completion| &completion.label.text)
11529                        .collect::<Vec<_>>(),
11530                    vec!["method id()", "other"]
11531                )
11532            }
11533            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11534        }
11535    });
11536
11537    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11538        let item1 = item1.clone();
11539        move |_, item_to_resolve, _| {
11540            let item1 = item1.clone();
11541            async move {
11542                if item1 == item_to_resolve {
11543                    Ok(lsp::CompletionItem {
11544                        label: "method id()".to_string(),
11545                        filter_text: Some("id".to_string()),
11546                        detail: Some("Now resolved!".to_string()),
11547                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11548                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11549                            range: lsp::Range::new(
11550                                lsp::Position::new(0, 22),
11551                                lsp::Position::new(0, 22),
11552                            ),
11553                            new_text: ".id".to_string(),
11554                        })),
11555                        ..lsp::CompletionItem::default()
11556                    })
11557                } else {
11558                    Ok(item_to_resolve)
11559                }
11560            }
11561        }
11562    })
11563    .next()
11564    .await
11565    .unwrap();
11566    cx.run_until_parked();
11567
11568    cx.update_editor(|editor, window, cx| {
11569        editor.context_menu_next(&Default::default(), window, cx);
11570    });
11571
11572    cx.update_editor(|editor, _, _| {
11573        let context_menu = editor.context_menu.borrow_mut();
11574        let context_menu = context_menu
11575            .as_ref()
11576            .expect("Should have the context menu deployed");
11577        match context_menu {
11578            CodeContextMenu::Completions(completions_menu) => {
11579                let completions = completions_menu.completions.borrow_mut();
11580                assert_eq!(
11581                    completions
11582                        .iter()
11583                        .map(|completion| &completion.label.text)
11584                        .collect::<Vec<_>>(),
11585                    vec!["method id() Now resolved!", "other"],
11586                    "Should update first completion label, but not second as the filter text did not match."
11587                );
11588            }
11589            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11590        }
11591    });
11592}
11593
11594#[gpui::test]
11595async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11596    init_test(cx, |_| {});
11597
11598    let mut cx = EditorLspTestContext::new_rust(
11599        lsp::ServerCapabilities {
11600            completion_provider: Some(lsp::CompletionOptions {
11601                trigger_characters: Some(vec![".".to_string()]),
11602                resolve_provider: Some(true),
11603                ..Default::default()
11604            }),
11605            ..Default::default()
11606        },
11607        cx,
11608    )
11609    .await;
11610
11611    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11612    cx.simulate_keystroke(".");
11613
11614    let unresolved_item_1 = lsp::CompletionItem {
11615        label: "id".to_string(),
11616        filter_text: Some("id".to_string()),
11617        detail: None,
11618        documentation: None,
11619        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11620            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11621            new_text: ".id".to_string(),
11622        })),
11623        ..lsp::CompletionItem::default()
11624    };
11625    let resolved_item_1 = lsp::CompletionItem {
11626        additional_text_edits: Some(vec![lsp::TextEdit {
11627            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11628            new_text: "!!".to_string(),
11629        }]),
11630        ..unresolved_item_1.clone()
11631    };
11632    let unresolved_item_2 = lsp::CompletionItem {
11633        label: "other".to_string(),
11634        filter_text: Some("other".to_string()),
11635        detail: None,
11636        documentation: None,
11637        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11638            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11639            new_text: ".other".to_string(),
11640        })),
11641        ..lsp::CompletionItem::default()
11642    };
11643    let resolved_item_2 = lsp::CompletionItem {
11644        additional_text_edits: Some(vec![lsp::TextEdit {
11645            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11646            new_text: "??".to_string(),
11647        }]),
11648        ..unresolved_item_2.clone()
11649    };
11650
11651    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11652    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11653    cx.lsp
11654        .server
11655        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11656            let unresolved_item_1 = unresolved_item_1.clone();
11657            let resolved_item_1 = resolved_item_1.clone();
11658            let unresolved_item_2 = unresolved_item_2.clone();
11659            let resolved_item_2 = resolved_item_2.clone();
11660            let resolve_requests_1 = resolve_requests_1.clone();
11661            let resolve_requests_2 = resolve_requests_2.clone();
11662            move |unresolved_request, _| {
11663                let unresolved_item_1 = unresolved_item_1.clone();
11664                let resolved_item_1 = resolved_item_1.clone();
11665                let unresolved_item_2 = unresolved_item_2.clone();
11666                let resolved_item_2 = resolved_item_2.clone();
11667                let resolve_requests_1 = resolve_requests_1.clone();
11668                let resolve_requests_2 = resolve_requests_2.clone();
11669                async move {
11670                    if unresolved_request == unresolved_item_1 {
11671                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11672                        Ok(resolved_item_1.clone())
11673                    } else if unresolved_request == unresolved_item_2 {
11674                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11675                        Ok(resolved_item_2.clone())
11676                    } else {
11677                        panic!("Unexpected completion item {unresolved_request:?}")
11678                    }
11679                }
11680            }
11681        })
11682        .detach();
11683
11684    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11685        let unresolved_item_1 = unresolved_item_1.clone();
11686        let unresolved_item_2 = unresolved_item_2.clone();
11687        async move {
11688            Ok(Some(lsp::CompletionResponse::Array(vec![
11689                unresolved_item_1,
11690                unresolved_item_2,
11691            ])))
11692        }
11693    })
11694    .next()
11695    .await;
11696
11697    cx.condition(|editor, _| editor.context_menu_visible())
11698        .await;
11699    cx.update_editor(|editor, _, _| {
11700        let context_menu = editor.context_menu.borrow_mut();
11701        let context_menu = context_menu
11702            .as_ref()
11703            .expect("Should have the context menu deployed");
11704        match context_menu {
11705            CodeContextMenu::Completions(completions_menu) => {
11706                let completions = completions_menu.completions.borrow_mut();
11707                assert_eq!(
11708                    completions
11709                        .iter()
11710                        .map(|completion| &completion.label.text)
11711                        .collect::<Vec<_>>(),
11712                    vec!["id", "other"]
11713                )
11714            }
11715            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11716        }
11717    });
11718    cx.run_until_parked();
11719
11720    cx.update_editor(|editor, window, cx| {
11721        editor.context_menu_next(&ContextMenuNext, window, cx);
11722    });
11723    cx.run_until_parked();
11724    cx.update_editor(|editor, window, cx| {
11725        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11726    });
11727    cx.run_until_parked();
11728    cx.update_editor(|editor, window, cx| {
11729        editor.context_menu_next(&ContextMenuNext, window, cx);
11730    });
11731    cx.run_until_parked();
11732    cx.update_editor(|editor, window, cx| {
11733        editor
11734            .compose_completion(&ComposeCompletion::default(), window, cx)
11735            .expect("No task returned")
11736    })
11737    .await
11738    .expect("Completion failed");
11739    cx.run_until_parked();
11740
11741    cx.update_editor(|editor, _, cx| {
11742        assert_eq!(
11743            resolve_requests_1.load(atomic::Ordering::Acquire),
11744            1,
11745            "Should always resolve once despite multiple selections"
11746        );
11747        assert_eq!(
11748            resolve_requests_2.load(atomic::Ordering::Acquire),
11749            1,
11750            "Should always resolve once after multiple selections and applying the completion"
11751        );
11752        assert_eq!(
11753            editor.text(cx),
11754            "fn main() { let a = ??.other; }",
11755            "Should use resolved data when applying the completion"
11756        );
11757    });
11758}
11759
11760#[gpui::test]
11761async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11762    init_test(cx, |_| {});
11763
11764    let item_0 = lsp::CompletionItem {
11765        label: "abs".into(),
11766        insert_text: Some("abs".into()),
11767        data: Some(json!({ "very": "special"})),
11768        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11769        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11770            lsp::InsertReplaceEdit {
11771                new_text: "abs".to_string(),
11772                insert: lsp::Range::default(),
11773                replace: lsp::Range::default(),
11774            },
11775        )),
11776        ..lsp::CompletionItem::default()
11777    };
11778    let items = iter::once(item_0.clone())
11779        .chain((11..51).map(|i| lsp::CompletionItem {
11780            label: format!("item_{}", i),
11781            insert_text: Some(format!("item_{}", i)),
11782            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11783            ..lsp::CompletionItem::default()
11784        }))
11785        .collect::<Vec<_>>();
11786
11787    let default_commit_characters = vec!["?".to_string()];
11788    let default_data = json!({ "default": "data"});
11789    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11790    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11791    let default_edit_range = lsp::Range {
11792        start: lsp::Position {
11793            line: 0,
11794            character: 5,
11795        },
11796        end: lsp::Position {
11797            line: 0,
11798            character: 5,
11799        },
11800    };
11801
11802    let item_0_out = lsp::CompletionItem {
11803        commit_characters: Some(default_commit_characters.clone()),
11804        insert_text_format: Some(default_insert_text_format),
11805        ..item_0
11806    };
11807    let items_out = iter::once(item_0_out)
11808        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11809            commit_characters: Some(default_commit_characters.clone()),
11810            data: Some(default_data.clone()),
11811            insert_text_mode: Some(default_insert_text_mode),
11812            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11813                range: default_edit_range,
11814                new_text: item.label.clone(),
11815            })),
11816            ..item.clone()
11817        }))
11818        .collect::<Vec<lsp::CompletionItem>>();
11819
11820    let mut cx = EditorLspTestContext::new_rust(
11821        lsp::ServerCapabilities {
11822            completion_provider: Some(lsp::CompletionOptions {
11823                trigger_characters: Some(vec![".".to_string()]),
11824                resolve_provider: Some(true),
11825                ..Default::default()
11826            }),
11827            ..Default::default()
11828        },
11829        cx,
11830    )
11831    .await;
11832
11833    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11834    cx.simulate_keystroke(".");
11835
11836    let completion_data = default_data.clone();
11837    let completion_characters = default_commit_characters.clone();
11838    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11839        let default_data = completion_data.clone();
11840        let default_commit_characters = completion_characters.clone();
11841        let items = items.clone();
11842        async move {
11843            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11844                items,
11845                item_defaults: Some(lsp::CompletionListItemDefaults {
11846                    data: Some(default_data.clone()),
11847                    commit_characters: Some(default_commit_characters.clone()),
11848                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11849                        default_edit_range,
11850                    )),
11851                    insert_text_format: Some(default_insert_text_format),
11852                    insert_text_mode: Some(default_insert_text_mode),
11853                }),
11854                ..lsp::CompletionList::default()
11855            })))
11856        }
11857    })
11858    .next()
11859    .await;
11860
11861    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11862    cx.lsp
11863        .server
11864        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11865            let closure_resolved_items = resolved_items.clone();
11866            move |item_to_resolve, _| {
11867                let closure_resolved_items = closure_resolved_items.clone();
11868                async move {
11869                    closure_resolved_items.lock().push(item_to_resolve.clone());
11870                    Ok(item_to_resolve)
11871                }
11872            }
11873        })
11874        .detach();
11875
11876    cx.condition(|editor, _| editor.context_menu_visible())
11877        .await;
11878    cx.run_until_parked();
11879    cx.update_editor(|editor, _, _| {
11880        let menu = editor.context_menu.borrow_mut();
11881        match menu.as_ref().expect("should have the completions menu") {
11882            CodeContextMenu::Completions(completions_menu) => {
11883                assert_eq!(
11884                    completions_menu
11885                        .entries
11886                        .borrow()
11887                        .iter()
11888                        .map(|mat| mat.string.clone())
11889                        .collect::<Vec<String>>(),
11890                    items_out
11891                        .iter()
11892                        .map(|completion| completion.label.clone())
11893                        .collect::<Vec<String>>()
11894                );
11895            }
11896            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11897        }
11898    });
11899    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11900    // with 4 from the end.
11901    assert_eq!(
11902        *resolved_items.lock(),
11903        [
11904            &items_out[0..16],
11905            &items_out[items_out.len() - 4..items_out.len()]
11906        ]
11907        .concat()
11908        .iter()
11909        .cloned()
11910        .collect::<Vec<lsp::CompletionItem>>()
11911    );
11912    resolved_items.lock().clear();
11913
11914    cx.update_editor(|editor, window, cx| {
11915        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11916    });
11917    cx.run_until_parked();
11918    // Completions that have already been resolved are skipped.
11919    assert_eq!(
11920        *resolved_items.lock(),
11921        items_out[items_out.len() - 16..items_out.len() - 4]
11922            .iter()
11923            .cloned()
11924            .collect::<Vec<lsp::CompletionItem>>()
11925    );
11926    resolved_items.lock().clear();
11927}
11928
11929#[gpui::test]
11930async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
11931    init_test(cx, |_| {});
11932
11933    let mut cx = EditorLspTestContext::new(
11934        Language::new(
11935            LanguageConfig {
11936                matcher: LanguageMatcher {
11937                    path_suffixes: vec!["jsx".into()],
11938                    ..Default::default()
11939                },
11940                overrides: [(
11941                    "element".into(),
11942                    LanguageConfigOverride {
11943                        word_characters: Override::Set(['-'].into_iter().collect()),
11944                        ..Default::default()
11945                    },
11946                )]
11947                .into_iter()
11948                .collect(),
11949                ..Default::default()
11950            },
11951            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11952        )
11953        .with_override_query("(jsx_self_closing_element) @element")
11954        .unwrap(),
11955        lsp::ServerCapabilities {
11956            completion_provider: Some(lsp::CompletionOptions {
11957                trigger_characters: Some(vec![":".to_string()]),
11958                ..Default::default()
11959            }),
11960            ..Default::default()
11961        },
11962        cx,
11963    )
11964    .await;
11965
11966    cx.lsp
11967        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11968            Ok(Some(lsp::CompletionResponse::Array(vec![
11969                lsp::CompletionItem {
11970                    label: "bg-blue".into(),
11971                    ..Default::default()
11972                },
11973                lsp::CompletionItem {
11974                    label: "bg-red".into(),
11975                    ..Default::default()
11976                },
11977                lsp::CompletionItem {
11978                    label: "bg-yellow".into(),
11979                    ..Default::default()
11980                },
11981            ])))
11982        });
11983
11984    cx.set_state(r#"<p class="bgˇ" />"#);
11985
11986    // Trigger completion when typing a dash, because the dash is an extra
11987    // word character in the 'element' scope, which contains the cursor.
11988    cx.simulate_keystroke("-");
11989    cx.executor().run_until_parked();
11990    cx.update_editor(|editor, _, _| {
11991        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11992        {
11993            assert_eq!(
11994                completion_menu_entries(&menu),
11995                &["bg-red", "bg-blue", "bg-yellow"]
11996            );
11997        } else {
11998            panic!("expected completion menu to be open");
11999        }
12000    });
12001
12002    cx.simulate_keystroke("l");
12003    cx.executor().run_until_parked();
12004    cx.update_editor(|editor, _, _| {
12005        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12006        {
12007            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12008        } else {
12009            panic!("expected completion menu to be open");
12010        }
12011    });
12012
12013    // When filtering completions, consider the character after the '-' to
12014    // be the start of a subword.
12015    cx.set_state(r#"<p class="yelˇ" />"#);
12016    cx.simulate_keystroke("l");
12017    cx.executor().run_until_parked();
12018    cx.update_editor(|editor, _, _| {
12019        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12020        {
12021            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12022        } else {
12023            panic!("expected completion menu to be open");
12024        }
12025    });
12026}
12027
12028fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12029    let entries = menu.entries.borrow();
12030    entries.iter().map(|mat| mat.string.clone()).collect()
12031}
12032
12033#[gpui::test]
12034async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
12035    init_test(cx, |settings| {
12036        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12037            FormatterList(vec![Formatter::Prettier].into()),
12038        ))
12039    });
12040
12041    let fs = FakeFs::new(cx.executor());
12042    fs.insert_file(path!("/file.ts"), Default::default()).await;
12043
12044    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12045    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12046
12047    language_registry.add(Arc::new(Language::new(
12048        LanguageConfig {
12049            name: "TypeScript".into(),
12050            matcher: LanguageMatcher {
12051                path_suffixes: vec!["ts".to_string()],
12052                ..Default::default()
12053            },
12054            ..Default::default()
12055        },
12056        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12057    )));
12058    update_test_language_settings(cx, |settings| {
12059        settings.defaults.prettier = Some(PrettierSettings {
12060            allowed: true,
12061            ..PrettierSettings::default()
12062        });
12063    });
12064
12065    let test_plugin = "test_plugin";
12066    let _ = language_registry.register_fake_lsp(
12067        "TypeScript",
12068        FakeLspAdapter {
12069            prettier_plugins: vec![test_plugin],
12070            ..Default::default()
12071        },
12072    );
12073
12074    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12075    let buffer = project
12076        .update(cx, |project, cx| {
12077            project.open_local_buffer(path!("/file.ts"), cx)
12078        })
12079        .await
12080        .unwrap();
12081
12082    let buffer_text = "one\ntwo\nthree\n";
12083    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12084    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12085    editor.update_in(cx, |editor, window, cx| {
12086        editor.set_text(buffer_text, window, cx)
12087    });
12088
12089    editor
12090        .update_in(cx, |editor, window, cx| {
12091            editor.perform_format(
12092                project.clone(),
12093                FormatTrigger::Manual,
12094                FormatTarget::Buffers,
12095                window,
12096                cx,
12097            )
12098        })
12099        .unwrap()
12100        .await;
12101    assert_eq!(
12102        editor.update(cx, |editor, cx| editor.text(cx)),
12103        buffer_text.to_string() + prettier_format_suffix,
12104        "Test prettier formatting was not applied to the original buffer text",
12105    );
12106
12107    update_test_language_settings(cx, |settings| {
12108        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12109    });
12110    let format = editor.update_in(cx, |editor, window, cx| {
12111        editor.perform_format(
12112            project.clone(),
12113            FormatTrigger::Manual,
12114            FormatTarget::Buffers,
12115            window,
12116            cx,
12117        )
12118    });
12119    format.await.unwrap();
12120    assert_eq!(
12121        editor.update(cx, |editor, cx| editor.text(cx)),
12122        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12123        "Autoformatting (via test prettier) was not applied to the original buffer text",
12124    );
12125}
12126
12127#[gpui::test]
12128async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
12129    init_test(cx, |_| {});
12130    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12131    let base_text = indoc! {r#"
12132        struct Row;
12133        struct Row1;
12134        struct Row2;
12135
12136        struct Row4;
12137        struct Row5;
12138        struct Row6;
12139
12140        struct Row8;
12141        struct Row9;
12142        struct Row10;"#};
12143
12144    // When addition hunks are not adjacent to carets, no hunk revert is performed
12145    assert_hunk_revert(
12146        indoc! {r#"struct Row;
12147                   struct Row1;
12148                   struct Row1.1;
12149                   struct Row1.2;
12150                   struct Row2;ˇ
12151
12152                   struct Row4;
12153                   struct Row5;
12154                   struct Row6;
12155
12156                   struct Row8;
12157                   ˇstruct Row9;
12158                   struct Row9.1;
12159                   struct Row9.2;
12160                   struct Row9.3;
12161                   struct Row10;"#},
12162        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12163        indoc! {r#"struct Row;
12164                   struct Row1;
12165                   struct Row1.1;
12166                   struct Row1.2;
12167                   struct Row2;ˇ
12168
12169                   struct Row4;
12170                   struct Row5;
12171                   struct Row6;
12172
12173                   struct Row8;
12174                   ˇstruct Row9;
12175                   struct Row9.1;
12176                   struct Row9.2;
12177                   struct Row9.3;
12178                   struct Row10;"#},
12179        base_text,
12180        &mut cx,
12181    );
12182    // Same for selections
12183    assert_hunk_revert(
12184        indoc! {r#"struct Row;
12185                   struct Row1;
12186                   struct Row2;
12187                   struct Row2.1;
12188                   struct Row2.2;
12189                   «ˇ
12190                   struct Row4;
12191                   struct» Row5;
12192                   «struct Row6;
12193                   ˇ»
12194                   struct Row9.1;
12195                   struct Row9.2;
12196                   struct Row9.3;
12197                   struct Row8;
12198                   struct Row9;
12199                   struct Row10;"#},
12200        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
12201        indoc! {r#"struct Row;
12202                   struct Row1;
12203                   struct Row2;
12204                   struct Row2.1;
12205                   struct Row2.2;
12206                   «ˇ
12207                   struct Row4;
12208                   struct» Row5;
12209                   «struct Row6;
12210                   ˇ»
12211                   struct Row9.1;
12212                   struct Row9.2;
12213                   struct Row9.3;
12214                   struct Row8;
12215                   struct Row9;
12216                   struct Row10;"#},
12217        base_text,
12218        &mut cx,
12219    );
12220
12221    // When carets and selections intersect the addition hunks, those are reverted.
12222    // Adjacent carets got merged.
12223    assert_hunk_revert(
12224        indoc! {r#"struct Row;
12225                   ˇ// something on the top
12226                   struct Row1;
12227                   struct Row2;
12228                   struct Roˇw3.1;
12229                   struct Row2.2;
12230                   struct Row2.3;ˇ
12231
12232                   struct Row4;
12233                   struct ˇRow5.1;
12234                   struct Row5.2;
12235                   struct «Rowˇ»5.3;
12236                   struct Row5;
12237                   struct Row6;
12238                   ˇ
12239                   struct Row9.1;
12240                   struct «Rowˇ»9.2;
12241                   struct «ˇRow»9.3;
12242                   struct Row8;
12243                   struct Row9;
12244                   «ˇ// something on bottom»
12245                   struct Row10;"#},
12246        vec![
12247            DiffHunkStatus::Added,
12248            DiffHunkStatus::Added,
12249            DiffHunkStatus::Added,
12250            DiffHunkStatus::Added,
12251            DiffHunkStatus::Added,
12252        ],
12253        indoc! {r#"struct Row;
12254                   ˇstruct Row1;
12255                   struct Row2;
12256                   ˇ
12257                   struct Row4;
12258                   ˇstruct Row5;
12259                   struct Row6;
12260                   ˇ
12261                   ˇstruct Row8;
12262                   struct Row9;
12263                   ˇstruct Row10;"#},
12264        base_text,
12265        &mut cx,
12266    );
12267}
12268
12269#[gpui::test]
12270async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12271    init_test(cx, |_| {});
12272    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12273    let base_text = indoc! {r#"
12274        struct Row;
12275        struct Row1;
12276        struct Row2;
12277
12278        struct Row4;
12279        struct Row5;
12280        struct Row6;
12281
12282        struct Row8;
12283        struct Row9;
12284        struct Row10;"#};
12285
12286    // Modification hunks behave the same as the addition ones.
12287    assert_hunk_revert(
12288        indoc! {r#"struct Row;
12289                   struct Row1;
12290                   struct Row33;
12291                   ˇ
12292                   struct Row4;
12293                   struct Row5;
12294                   struct Row6;
12295                   ˇ
12296                   struct Row99;
12297                   struct Row9;
12298                   struct Row10;"#},
12299        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12300        indoc! {r#"struct Row;
12301                   struct Row1;
12302                   struct Row33;
12303                   ˇ
12304                   struct Row4;
12305                   struct Row5;
12306                   struct Row6;
12307                   ˇ
12308                   struct Row99;
12309                   struct Row9;
12310                   struct Row10;"#},
12311        base_text,
12312        &mut cx,
12313    );
12314    assert_hunk_revert(
12315        indoc! {r#"struct Row;
12316                   struct Row1;
12317                   struct Row33;
12318                   «ˇ
12319                   struct Row4;
12320                   struct» Row5;
12321                   «struct Row6;
12322                   ˇ»
12323                   struct Row99;
12324                   struct Row9;
12325                   struct Row10;"#},
12326        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
12327        indoc! {r#"struct Row;
12328                   struct Row1;
12329                   struct Row33;
12330                   «ˇ
12331                   struct Row4;
12332                   struct» Row5;
12333                   «struct Row6;
12334                   ˇ»
12335                   struct Row99;
12336                   struct Row9;
12337                   struct Row10;"#},
12338        base_text,
12339        &mut cx,
12340    );
12341
12342    assert_hunk_revert(
12343        indoc! {r#"ˇstruct Row1.1;
12344                   struct Row1;
12345                   «ˇstr»uct Row22;
12346
12347                   struct ˇRow44;
12348                   struct Row5;
12349                   struct «Rˇ»ow66;ˇ
12350
12351                   «struˇ»ct Row88;
12352                   struct Row9;
12353                   struct Row1011;ˇ"#},
12354        vec![
12355            DiffHunkStatus::Modified,
12356            DiffHunkStatus::Modified,
12357            DiffHunkStatus::Modified,
12358            DiffHunkStatus::Modified,
12359            DiffHunkStatus::Modified,
12360            DiffHunkStatus::Modified,
12361        ],
12362        indoc! {r#"struct Row;
12363                   ˇstruct Row1;
12364                   struct Row2;
12365                   ˇ
12366                   struct Row4;
12367                   ˇstruct Row5;
12368                   struct Row6;
12369                   ˇ
12370                   struct Row8;
12371                   ˇstruct Row9;
12372                   struct Row10;ˇ"#},
12373        base_text,
12374        &mut cx,
12375    );
12376}
12377
12378#[gpui::test]
12379async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12380    init_test(cx, |_| {});
12381    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12382    let base_text = indoc! {r#"
12383        one
12384
12385        two
12386        three
12387        "#};
12388
12389    cx.set_diff_base(base_text);
12390    cx.set_state("\nˇ\n");
12391    cx.executor().run_until_parked();
12392    cx.update_editor(|editor, _window, cx| {
12393        editor.expand_selected_diff_hunks(cx);
12394    });
12395    cx.executor().run_until_parked();
12396    cx.update_editor(|editor, window, cx| {
12397        editor.backspace(&Default::default(), window, cx);
12398    });
12399    cx.run_until_parked();
12400    cx.assert_state_with_diff(
12401        indoc! {r#"
12402
12403        - two
12404        - threeˇ
12405        +
12406        "#}
12407        .to_string(),
12408    );
12409}
12410
12411#[gpui::test]
12412async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12413    init_test(cx, |_| {});
12414    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12415    let base_text = indoc! {r#"struct Row;
12416struct Row1;
12417struct Row2;
12418
12419struct Row4;
12420struct Row5;
12421struct Row6;
12422
12423struct Row8;
12424struct Row9;
12425struct Row10;"#};
12426
12427    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12428    assert_hunk_revert(
12429        indoc! {r#"struct Row;
12430                   struct Row2;
12431
12432                   ˇstruct Row4;
12433                   struct Row5;
12434                   struct Row6;
12435                   ˇ
12436                   struct Row8;
12437                   struct Row10;"#},
12438        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12439        indoc! {r#"struct Row;
12440                   struct Row2;
12441
12442                   ˇstruct Row4;
12443                   struct Row5;
12444                   struct Row6;
12445                   ˇ
12446                   struct Row8;
12447                   struct Row10;"#},
12448        base_text,
12449        &mut cx,
12450    );
12451    assert_hunk_revert(
12452        indoc! {r#"struct Row;
12453                   struct Row2;
12454
12455                   «ˇstruct Row4;
12456                   struct» Row5;
12457                   «struct Row6;
12458                   ˇ»
12459                   struct Row8;
12460                   struct Row10;"#},
12461        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12462        indoc! {r#"struct Row;
12463                   struct Row2;
12464
12465                   «ˇstruct Row4;
12466                   struct» Row5;
12467                   «struct Row6;
12468                   ˇ»
12469                   struct Row8;
12470                   struct Row10;"#},
12471        base_text,
12472        &mut cx,
12473    );
12474
12475    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12476    assert_hunk_revert(
12477        indoc! {r#"struct Row;
12478                   ˇstruct Row2;
12479
12480                   struct Row4;
12481                   struct Row5;
12482                   struct Row6;
12483
12484                   struct Row8;ˇ
12485                   struct Row10;"#},
12486        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
12487        indoc! {r#"struct Row;
12488                   struct Row1;
12489                   ˇstruct Row2;
12490
12491                   struct Row4;
12492                   struct Row5;
12493                   struct Row6;
12494
12495                   struct Row8;ˇ
12496                   struct Row9;
12497                   struct Row10;"#},
12498        base_text,
12499        &mut cx,
12500    );
12501    assert_hunk_revert(
12502        indoc! {r#"struct Row;
12503                   struct Row2«ˇ;
12504                   struct Row4;
12505                   struct» Row5;
12506                   «struct Row6;
12507
12508                   struct Row8;ˇ»
12509                   struct Row10;"#},
12510        vec![
12511            DiffHunkStatus::Removed,
12512            DiffHunkStatus::Removed,
12513            DiffHunkStatus::Removed,
12514        ],
12515        indoc! {r#"struct Row;
12516                   struct Row1;
12517                   struct Row2«ˇ;
12518
12519                   struct Row4;
12520                   struct» Row5;
12521                   «struct Row6;
12522
12523                   struct Row8;ˇ»
12524                   struct Row9;
12525                   struct Row10;"#},
12526        base_text,
12527        &mut cx,
12528    );
12529}
12530
12531#[gpui::test]
12532async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12533    init_test(cx, |_| {});
12534
12535    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12536    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12537    let base_text_3 =
12538        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12539
12540    let text_1 = edit_first_char_of_every_line(base_text_1);
12541    let text_2 = edit_first_char_of_every_line(base_text_2);
12542    let text_3 = edit_first_char_of_every_line(base_text_3);
12543
12544    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12545    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12546    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12547
12548    let multibuffer = cx.new(|cx| {
12549        let mut multibuffer = MultiBuffer::new(ReadWrite);
12550        multibuffer.push_excerpts(
12551            buffer_1.clone(),
12552            [
12553                ExcerptRange {
12554                    context: Point::new(0, 0)..Point::new(3, 0),
12555                    primary: None,
12556                },
12557                ExcerptRange {
12558                    context: Point::new(5, 0)..Point::new(7, 0),
12559                    primary: None,
12560                },
12561                ExcerptRange {
12562                    context: Point::new(9, 0)..Point::new(10, 4),
12563                    primary: None,
12564                },
12565            ],
12566            cx,
12567        );
12568        multibuffer.push_excerpts(
12569            buffer_2.clone(),
12570            [
12571                ExcerptRange {
12572                    context: Point::new(0, 0)..Point::new(3, 0),
12573                    primary: None,
12574                },
12575                ExcerptRange {
12576                    context: Point::new(5, 0)..Point::new(7, 0),
12577                    primary: None,
12578                },
12579                ExcerptRange {
12580                    context: Point::new(9, 0)..Point::new(10, 4),
12581                    primary: None,
12582                },
12583            ],
12584            cx,
12585        );
12586        multibuffer.push_excerpts(
12587            buffer_3.clone(),
12588            [
12589                ExcerptRange {
12590                    context: Point::new(0, 0)..Point::new(3, 0),
12591                    primary: None,
12592                },
12593                ExcerptRange {
12594                    context: Point::new(5, 0)..Point::new(7, 0),
12595                    primary: None,
12596                },
12597                ExcerptRange {
12598                    context: Point::new(9, 0)..Point::new(10, 4),
12599                    primary: None,
12600                },
12601            ],
12602            cx,
12603        );
12604        multibuffer
12605    });
12606
12607    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12608    editor.update_in(cx, |editor, _window, cx| {
12609        for (buffer, diff_base) in [
12610            (buffer_1.clone(), base_text_1),
12611            (buffer_2.clone(), base_text_2),
12612            (buffer_3.clone(), base_text_3),
12613        ] {
12614            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12615            editor
12616                .buffer
12617                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12618        }
12619    });
12620    cx.executor().run_until_parked();
12621
12622    editor.update_in(cx, |editor, window, cx| {
12623        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}");
12624        editor.select_all(&SelectAll, window, cx);
12625        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12626    });
12627    cx.executor().run_until_parked();
12628
12629    // When all ranges are selected, all buffer hunks are reverted.
12630    editor.update(cx, |editor, cx| {
12631        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");
12632    });
12633    buffer_1.update(cx, |buffer, _| {
12634        assert_eq!(buffer.text(), base_text_1);
12635    });
12636    buffer_2.update(cx, |buffer, _| {
12637        assert_eq!(buffer.text(), base_text_2);
12638    });
12639    buffer_3.update(cx, |buffer, _| {
12640        assert_eq!(buffer.text(), base_text_3);
12641    });
12642
12643    editor.update_in(cx, |editor, window, cx| {
12644        editor.undo(&Default::default(), window, cx);
12645    });
12646
12647    editor.update_in(cx, |editor, window, cx| {
12648        editor.change_selections(None, window, cx, |s| {
12649            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12650        });
12651        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12652    });
12653
12654    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12655    // but not affect buffer_2 and its related excerpts.
12656    editor.update(cx, |editor, cx| {
12657        assert_eq!(
12658            editor.text(cx),
12659            "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}"
12660        );
12661    });
12662    buffer_1.update(cx, |buffer, _| {
12663        assert_eq!(buffer.text(), base_text_1);
12664    });
12665    buffer_2.update(cx, |buffer, _| {
12666        assert_eq!(
12667            buffer.text(),
12668            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12669        );
12670    });
12671    buffer_3.update(cx, |buffer, _| {
12672        assert_eq!(
12673            buffer.text(),
12674            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12675        );
12676    });
12677
12678    fn edit_first_char_of_every_line(text: &str) -> String {
12679        text.split('\n')
12680            .map(|line| format!("X{}", &line[1..]))
12681            .collect::<Vec<_>>()
12682            .join("\n")
12683    }
12684}
12685
12686#[gpui::test]
12687async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12688    init_test(cx, |_| {});
12689
12690    let cols = 4;
12691    let rows = 10;
12692    let sample_text_1 = sample_text(rows, cols, 'a');
12693    assert_eq!(
12694        sample_text_1,
12695        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12696    );
12697    let sample_text_2 = sample_text(rows, cols, 'l');
12698    assert_eq!(
12699        sample_text_2,
12700        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12701    );
12702    let sample_text_3 = sample_text(rows, cols, 'v');
12703    assert_eq!(
12704        sample_text_3,
12705        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12706    );
12707
12708    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12709    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12710    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12711
12712    let multi_buffer = cx.new(|cx| {
12713        let mut multibuffer = MultiBuffer::new(ReadWrite);
12714        multibuffer.push_excerpts(
12715            buffer_1.clone(),
12716            [
12717                ExcerptRange {
12718                    context: Point::new(0, 0)..Point::new(3, 0),
12719                    primary: None,
12720                },
12721                ExcerptRange {
12722                    context: Point::new(5, 0)..Point::new(7, 0),
12723                    primary: None,
12724                },
12725                ExcerptRange {
12726                    context: Point::new(9, 0)..Point::new(10, 4),
12727                    primary: None,
12728                },
12729            ],
12730            cx,
12731        );
12732        multibuffer.push_excerpts(
12733            buffer_2.clone(),
12734            [
12735                ExcerptRange {
12736                    context: Point::new(0, 0)..Point::new(3, 0),
12737                    primary: None,
12738                },
12739                ExcerptRange {
12740                    context: Point::new(5, 0)..Point::new(7, 0),
12741                    primary: None,
12742                },
12743                ExcerptRange {
12744                    context: Point::new(9, 0)..Point::new(10, 4),
12745                    primary: None,
12746                },
12747            ],
12748            cx,
12749        );
12750        multibuffer.push_excerpts(
12751            buffer_3.clone(),
12752            [
12753                ExcerptRange {
12754                    context: Point::new(0, 0)..Point::new(3, 0),
12755                    primary: None,
12756                },
12757                ExcerptRange {
12758                    context: Point::new(5, 0)..Point::new(7, 0),
12759                    primary: None,
12760                },
12761                ExcerptRange {
12762                    context: Point::new(9, 0)..Point::new(10, 4),
12763                    primary: None,
12764                },
12765            ],
12766            cx,
12767        );
12768        multibuffer
12769    });
12770
12771    let fs = FakeFs::new(cx.executor());
12772    fs.insert_tree(
12773        "/a",
12774        json!({
12775            "main.rs": sample_text_1,
12776            "other.rs": sample_text_2,
12777            "lib.rs": sample_text_3,
12778        }),
12779    )
12780    .await;
12781    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12782    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12783    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12784    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12785        Editor::new(
12786            EditorMode::Full,
12787            multi_buffer,
12788            Some(project.clone()),
12789            true,
12790            window,
12791            cx,
12792        )
12793    });
12794    let multibuffer_item_id = workspace
12795        .update(cx, |workspace, window, cx| {
12796            assert!(
12797                workspace.active_item(cx).is_none(),
12798                "active item should be None before the first item is added"
12799            );
12800            workspace.add_item_to_active_pane(
12801                Box::new(multi_buffer_editor.clone()),
12802                None,
12803                true,
12804                window,
12805                cx,
12806            );
12807            let active_item = workspace
12808                .active_item(cx)
12809                .expect("should have an active item after adding the multi buffer");
12810            assert!(
12811                !active_item.is_singleton(cx),
12812                "A multi buffer was expected to active after adding"
12813            );
12814            active_item.item_id()
12815        })
12816        .unwrap();
12817    cx.executor().run_until_parked();
12818
12819    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12820        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12821            s.select_ranges(Some(1..2))
12822        });
12823        editor.open_excerpts(&OpenExcerpts, window, cx);
12824    });
12825    cx.executor().run_until_parked();
12826    let first_item_id = workspace
12827        .update(cx, |workspace, window, cx| {
12828            let active_item = workspace
12829                .active_item(cx)
12830                .expect("should have an active item after navigating into the 1st buffer");
12831            let first_item_id = active_item.item_id();
12832            assert_ne!(
12833                first_item_id, multibuffer_item_id,
12834                "Should navigate into the 1st buffer and activate it"
12835            );
12836            assert!(
12837                active_item.is_singleton(cx),
12838                "New active item should be a singleton buffer"
12839            );
12840            assert_eq!(
12841                active_item
12842                    .act_as::<Editor>(cx)
12843                    .expect("should have navigated into an editor for the 1st buffer")
12844                    .read(cx)
12845                    .text(cx),
12846                sample_text_1
12847            );
12848
12849            workspace
12850                .go_back(workspace.active_pane().downgrade(), window, cx)
12851                .detach_and_log_err(cx);
12852
12853            first_item_id
12854        })
12855        .unwrap();
12856    cx.executor().run_until_parked();
12857    workspace
12858        .update(cx, |workspace, _, cx| {
12859            let active_item = workspace
12860                .active_item(cx)
12861                .expect("should have an active item after navigating back");
12862            assert_eq!(
12863                active_item.item_id(),
12864                multibuffer_item_id,
12865                "Should navigate back to the multi buffer"
12866            );
12867            assert!(!active_item.is_singleton(cx));
12868        })
12869        .unwrap();
12870
12871    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12872        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12873            s.select_ranges(Some(39..40))
12874        });
12875        editor.open_excerpts(&OpenExcerpts, window, cx);
12876    });
12877    cx.executor().run_until_parked();
12878    let second_item_id = workspace
12879        .update(cx, |workspace, window, cx| {
12880            let active_item = workspace
12881                .active_item(cx)
12882                .expect("should have an active item after navigating into the 2nd buffer");
12883            let second_item_id = active_item.item_id();
12884            assert_ne!(
12885                second_item_id, multibuffer_item_id,
12886                "Should navigate away from the multibuffer"
12887            );
12888            assert_ne!(
12889                second_item_id, first_item_id,
12890                "Should navigate into the 2nd buffer and activate it"
12891            );
12892            assert!(
12893                active_item.is_singleton(cx),
12894                "New active item should be a singleton buffer"
12895            );
12896            assert_eq!(
12897                active_item
12898                    .act_as::<Editor>(cx)
12899                    .expect("should have navigated into an editor")
12900                    .read(cx)
12901                    .text(cx),
12902                sample_text_2
12903            );
12904
12905            workspace
12906                .go_back(workspace.active_pane().downgrade(), window, cx)
12907                .detach_and_log_err(cx);
12908
12909            second_item_id
12910        })
12911        .unwrap();
12912    cx.executor().run_until_parked();
12913    workspace
12914        .update(cx, |workspace, _, cx| {
12915            let active_item = workspace
12916                .active_item(cx)
12917                .expect("should have an active item after navigating back from the 2nd buffer");
12918            assert_eq!(
12919                active_item.item_id(),
12920                multibuffer_item_id,
12921                "Should navigate back from the 2nd buffer to the multi buffer"
12922            );
12923            assert!(!active_item.is_singleton(cx));
12924        })
12925        .unwrap();
12926
12927    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12928        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12929            s.select_ranges(Some(70..70))
12930        });
12931        editor.open_excerpts(&OpenExcerpts, window, cx);
12932    });
12933    cx.executor().run_until_parked();
12934    workspace
12935        .update(cx, |workspace, window, cx| {
12936            let active_item = workspace
12937                .active_item(cx)
12938                .expect("should have an active item after navigating into the 3rd buffer");
12939            let third_item_id = active_item.item_id();
12940            assert_ne!(
12941                third_item_id, multibuffer_item_id,
12942                "Should navigate into the 3rd buffer and activate it"
12943            );
12944            assert_ne!(third_item_id, first_item_id);
12945            assert_ne!(third_item_id, second_item_id);
12946            assert!(
12947                active_item.is_singleton(cx),
12948                "New active item should be a singleton buffer"
12949            );
12950            assert_eq!(
12951                active_item
12952                    .act_as::<Editor>(cx)
12953                    .expect("should have navigated into an editor")
12954                    .read(cx)
12955                    .text(cx),
12956                sample_text_3
12957            );
12958
12959            workspace
12960                .go_back(workspace.active_pane().downgrade(), window, cx)
12961                .detach_and_log_err(cx);
12962        })
12963        .unwrap();
12964    cx.executor().run_until_parked();
12965    workspace
12966        .update(cx, |workspace, _, cx| {
12967            let active_item = workspace
12968                .active_item(cx)
12969                .expect("should have an active item after navigating back from the 3rd buffer");
12970            assert_eq!(
12971                active_item.item_id(),
12972                multibuffer_item_id,
12973                "Should navigate back from the 3rd buffer to the multi buffer"
12974            );
12975            assert!(!active_item.is_singleton(cx));
12976        })
12977        .unwrap();
12978}
12979
12980#[gpui::test]
12981async fn test_toggle_selected_diff_hunks(
12982    executor: BackgroundExecutor,
12983    cx: &mut gpui::TestAppContext,
12984) {
12985    init_test(cx, |_| {});
12986
12987    let mut cx = EditorTestContext::new(cx).await;
12988
12989    let diff_base = r#"
12990        use some::mod;
12991
12992        const A: u32 = 42;
12993
12994        fn main() {
12995            println!("hello");
12996
12997            println!("world");
12998        }
12999        "#
13000    .unindent();
13001
13002    cx.set_state(
13003        &r#"
13004        use some::modified;
13005
13006        ˇ
13007        fn main() {
13008            println!("hello there");
13009
13010            println!("around the");
13011            println!("world");
13012        }
13013        "#
13014        .unindent(),
13015    );
13016
13017    cx.set_diff_base(&diff_base);
13018    executor.run_until_parked();
13019
13020    cx.update_editor(|editor, window, cx| {
13021        editor.go_to_next_hunk(&GoToHunk, window, cx);
13022        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13023    });
13024    executor.run_until_parked();
13025    cx.assert_state_with_diff(
13026        r#"
13027          use some::modified;
13028
13029
13030          fn main() {
13031        -     println!("hello");
13032        + ˇ    println!("hello there");
13033
13034              println!("around the");
13035              println!("world");
13036          }
13037        "#
13038        .unindent(),
13039    );
13040
13041    cx.update_editor(|editor, window, cx| {
13042        for _ in 0..2 {
13043            editor.go_to_next_hunk(&GoToHunk, window, cx);
13044            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13045        }
13046    });
13047    executor.run_until_parked();
13048    cx.assert_state_with_diff(
13049        r#"
13050        - use some::mod;
13051        + ˇuse some::modified;
13052
13053
13054          fn main() {
13055        -     println!("hello");
13056        +     println!("hello there");
13057
13058        +     println!("around the");
13059              println!("world");
13060          }
13061        "#
13062        .unindent(),
13063    );
13064
13065    cx.update_editor(|editor, window, cx| {
13066        editor.go_to_next_hunk(&GoToHunk, window, cx);
13067        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13068    });
13069    executor.run_until_parked();
13070    cx.assert_state_with_diff(
13071        r#"
13072        - use some::mod;
13073        + use some::modified;
13074
13075        - const A: u32 = 42;
13076          ˇ
13077          fn main() {
13078        -     println!("hello");
13079        +     println!("hello there");
13080
13081        +     println!("around the");
13082              println!("world");
13083          }
13084        "#
13085        .unindent(),
13086    );
13087
13088    cx.update_editor(|editor, window, cx| {
13089        editor.cancel(&Cancel, window, cx);
13090    });
13091
13092    cx.assert_state_with_diff(
13093        r#"
13094          use some::modified;
13095
13096          ˇ
13097          fn main() {
13098              println!("hello there");
13099
13100              println!("around the");
13101              println!("world");
13102          }
13103        "#
13104        .unindent(),
13105    );
13106}
13107
13108#[gpui::test]
13109async fn test_diff_base_change_with_expanded_diff_hunks(
13110    executor: BackgroundExecutor,
13111    cx: &mut gpui::TestAppContext,
13112) {
13113    init_test(cx, |_| {});
13114
13115    let mut cx = EditorTestContext::new(cx).await;
13116
13117    let diff_base = r#"
13118        use some::mod1;
13119        use some::mod2;
13120
13121        const A: u32 = 42;
13122        const B: u32 = 42;
13123        const C: u32 = 42;
13124
13125        fn main() {
13126            println!("hello");
13127
13128            println!("world");
13129        }
13130        "#
13131    .unindent();
13132
13133    cx.set_state(
13134        &r#"
13135        use some::mod2;
13136
13137        const A: u32 = 42;
13138        const C: u32 = 42;
13139
13140        fn main(ˇ) {
13141            //println!("hello");
13142
13143            println!("world");
13144            //
13145            //
13146        }
13147        "#
13148        .unindent(),
13149    );
13150
13151    cx.set_diff_base(&diff_base);
13152    executor.run_until_parked();
13153
13154    cx.update_editor(|editor, window, cx| {
13155        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13156    });
13157    executor.run_until_parked();
13158    cx.assert_state_with_diff(
13159        r#"
13160        - use some::mod1;
13161          use some::mod2;
13162
13163          const A: u32 = 42;
13164        - const B: u32 = 42;
13165          const C: u32 = 42;
13166
13167          fn main(ˇ) {
13168        -     println!("hello");
13169        +     //println!("hello");
13170
13171              println!("world");
13172        +     //
13173        +     //
13174          }
13175        "#
13176        .unindent(),
13177    );
13178
13179    cx.set_diff_base("new diff base!");
13180    executor.run_until_parked();
13181    cx.assert_state_with_diff(
13182        r#"
13183          use some::mod2;
13184
13185          const A: u32 = 42;
13186          const C: u32 = 42;
13187
13188          fn main(ˇ) {
13189              //println!("hello");
13190
13191              println!("world");
13192              //
13193              //
13194          }
13195        "#
13196        .unindent(),
13197    );
13198
13199    cx.update_editor(|editor, window, cx| {
13200        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13201    });
13202    executor.run_until_parked();
13203    cx.assert_state_with_diff(
13204        r#"
13205        - new diff base!
13206        + use some::mod2;
13207        +
13208        + const A: u32 = 42;
13209        + const C: u32 = 42;
13210        +
13211        + fn main(ˇ) {
13212        +     //println!("hello");
13213        +
13214        +     println!("world");
13215        +     //
13216        +     //
13217        + }
13218        "#
13219        .unindent(),
13220    );
13221}
13222
13223#[gpui::test]
13224async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13225    init_test(cx, |_| {});
13226
13227    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13228    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13229    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13230    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13231    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13232    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13233
13234    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13235    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13236    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13237
13238    let multi_buffer = cx.new(|cx| {
13239        let mut multibuffer = MultiBuffer::new(ReadWrite);
13240        multibuffer.push_excerpts(
13241            buffer_1.clone(),
13242            [
13243                ExcerptRange {
13244                    context: Point::new(0, 0)..Point::new(3, 0),
13245                    primary: None,
13246                },
13247                ExcerptRange {
13248                    context: Point::new(5, 0)..Point::new(7, 0),
13249                    primary: None,
13250                },
13251                ExcerptRange {
13252                    context: Point::new(9, 0)..Point::new(10, 3),
13253                    primary: None,
13254                },
13255            ],
13256            cx,
13257        );
13258        multibuffer.push_excerpts(
13259            buffer_2.clone(),
13260            [
13261                ExcerptRange {
13262                    context: Point::new(0, 0)..Point::new(3, 0),
13263                    primary: None,
13264                },
13265                ExcerptRange {
13266                    context: Point::new(5, 0)..Point::new(7, 0),
13267                    primary: None,
13268                },
13269                ExcerptRange {
13270                    context: Point::new(9, 0)..Point::new(10, 3),
13271                    primary: None,
13272                },
13273            ],
13274            cx,
13275        );
13276        multibuffer.push_excerpts(
13277            buffer_3.clone(),
13278            [
13279                ExcerptRange {
13280                    context: Point::new(0, 0)..Point::new(3, 0),
13281                    primary: None,
13282                },
13283                ExcerptRange {
13284                    context: Point::new(5, 0)..Point::new(7, 0),
13285                    primary: None,
13286                },
13287                ExcerptRange {
13288                    context: Point::new(9, 0)..Point::new(10, 3),
13289                    primary: None,
13290                },
13291            ],
13292            cx,
13293        );
13294        multibuffer
13295    });
13296
13297    let editor = cx.add_window(|window, cx| {
13298        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13299    });
13300    editor
13301        .update(cx, |editor, _window, cx| {
13302            for (buffer, diff_base) in [
13303                (buffer_1.clone(), file_1_old),
13304                (buffer_2.clone(), file_2_old),
13305                (buffer_3.clone(), file_3_old),
13306            ] {
13307                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13308                editor
13309                    .buffer
13310                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13311            }
13312        })
13313        .unwrap();
13314
13315    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13316    cx.run_until_parked();
13317
13318    cx.assert_editor_state(
13319        &"
13320            ˇaaa
13321            ccc
13322            ddd
13323
13324            ggg
13325            hhh
13326
13327
13328            lll
13329            mmm
13330            NNN
13331
13332            qqq
13333            rrr
13334
13335            uuu
13336            111
13337            222
13338            333
13339
13340            666
13341            777
13342
13343            000
13344            !!!"
13345        .unindent(),
13346    );
13347
13348    cx.update_editor(|editor, window, cx| {
13349        editor.select_all(&SelectAll, window, cx);
13350        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13351    });
13352    cx.executor().run_until_parked();
13353
13354    cx.assert_state_with_diff(
13355        "
13356            «aaa
13357          - bbb
13358            ccc
13359            ddd
13360
13361            ggg
13362            hhh
13363
13364
13365            lll
13366            mmm
13367          - nnn
13368          + NNN
13369
13370            qqq
13371            rrr
13372
13373            uuu
13374            111
13375            222
13376            333
13377
13378          + 666
13379            777
13380
13381            000
13382            !!!ˇ»"
13383            .unindent(),
13384    );
13385}
13386
13387#[gpui::test]
13388async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13389    init_test(cx, |_| {});
13390
13391    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13392    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13393
13394    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13395    let multi_buffer = cx.new(|cx| {
13396        let mut multibuffer = MultiBuffer::new(ReadWrite);
13397        multibuffer.push_excerpts(
13398            buffer.clone(),
13399            [
13400                ExcerptRange {
13401                    context: Point::new(0, 0)..Point::new(2, 0),
13402                    primary: None,
13403                },
13404                ExcerptRange {
13405                    context: Point::new(4, 0)..Point::new(7, 0),
13406                    primary: None,
13407                },
13408                ExcerptRange {
13409                    context: Point::new(9, 0)..Point::new(10, 0),
13410                    primary: None,
13411                },
13412            ],
13413            cx,
13414        );
13415        multibuffer
13416    });
13417
13418    let editor = cx.add_window(|window, cx| {
13419        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13420    });
13421    editor
13422        .update(cx, |editor, _window, cx| {
13423            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13424            editor
13425                .buffer
13426                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13427        })
13428        .unwrap();
13429
13430    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13431    cx.run_until_parked();
13432
13433    cx.update_editor(|editor, window, cx| {
13434        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13435    });
13436    cx.executor().run_until_parked();
13437
13438    // When the start of a hunk coincides with the start of its excerpt,
13439    // the hunk is expanded. When the start of a a hunk is earlier than
13440    // the start of its excerpt, the hunk is not expanded.
13441    cx.assert_state_with_diff(
13442        "
13443            ˇaaa
13444          - bbb
13445          + BBB
13446
13447          - ddd
13448          - eee
13449          + DDD
13450          + EEE
13451            fff
13452
13453            iii
13454        "
13455        .unindent(),
13456    );
13457}
13458
13459#[gpui::test]
13460async fn test_edits_around_expanded_insertion_hunks(
13461    executor: BackgroundExecutor,
13462    cx: &mut gpui::TestAppContext,
13463) {
13464    init_test(cx, |_| {});
13465
13466    let mut cx = EditorTestContext::new(cx).await;
13467
13468    let diff_base = r#"
13469        use some::mod1;
13470        use some::mod2;
13471
13472        const A: u32 = 42;
13473
13474        fn main() {
13475            println!("hello");
13476
13477            println!("world");
13478        }
13479        "#
13480    .unindent();
13481    executor.run_until_parked();
13482    cx.set_state(
13483        &r#"
13484        use some::mod1;
13485        use some::mod2;
13486
13487        const A: u32 = 42;
13488        const B: u32 = 42;
13489        const C: u32 = 42;
13490        ˇ
13491
13492        fn main() {
13493            println!("hello");
13494
13495            println!("world");
13496        }
13497        "#
13498        .unindent(),
13499    );
13500
13501    cx.set_diff_base(&diff_base);
13502    executor.run_until_parked();
13503
13504    cx.update_editor(|editor, window, cx| {
13505        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13506    });
13507    executor.run_until_parked();
13508
13509    cx.assert_state_with_diff(
13510        r#"
13511        use some::mod1;
13512        use some::mod2;
13513
13514        const A: u32 = 42;
13515      + const B: u32 = 42;
13516      + const C: u32 = 42;
13517      + ˇ
13518
13519        fn main() {
13520            println!("hello");
13521
13522            println!("world");
13523        }
13524      "#
13525        .unindent(),
13526    );
13527
13528    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13529    executor.run_until_parked();
13530
13531    cx.assert_state_with_diff(
13532        r#"
13533        use some::mod1;
13534        use some::mod2;
13535
13536        const A: u32 = 42;
13537      + const B: u32 = 42;
13538      + const C: u32 = 42;
13539      + const D: u32 = 42;
13540      + ˇ
13541
13542        fn main() {
13543            println!("hello");
13544
13545            println!("world");
13546        }
13547      "#
13548        .unindent(),
13549    );
13550
13551    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13552    executor.run_until_parked();
13553
13554    cx.assert_state_with_diff(
13555        r#"
13556        use some::mod1;
13557        use some::mod2;
13558
13559        const A: u32 = 42;
13560      + const B: u32 = 42;
13561      + const C: u32 = 42;
13562      + const D: u32 = 42;
13563      + const E: u32 = 42;
13564      + ˇ
13565
13566        fn main() {
13567            println!("hello");
13568
13569            println!("world");
13570        }
13571      "#
13572        .unindent(),
13573    );
13574
13575    cx.update_editor(|editor, window, cx| {
13576        editor.delete_line(&DeleteLine, window, cx);
13577    });
13578    executor.run_until_parked();
13579
13580    cx.assert_state_with_diff(
13581        r#"
13582        use some::mod1;
13583        use some::mod2;
13584
13585        const A: u32 = 42;
13586      + const B: u32 = 42;
13587      + const C: u32 = 42;
13588      + const D: u32 = 42;
13589      + const E: u32 = 42;
13590        ˇ
13591        fn main() {
13592            println!("hello");
13593
13594            println!("world");
13595        }
13596      "#
13597        .unindent(),
13598    );
13599
13600    cx.update_editor(|editor, window, cx| {
13601        editor.move_up(&MoveUp, window, cx);
13602        editor.delete_line(&DeleteLine, window, cx);
13603        editor.move_up(&MoveUp, window, cx);
13604        editor.delete_line(&DeleteLine, window, cx);
13605        editor.move_up(&MoveUp, window, cx);
13606        editor.delete_line(&DeleteLine, window, cx);
13607    });
13608    executor.run_until_parked();
13609    cx.assert_state_with_diff(
13610        r#"
13611        use some::mod1;
13612        use some::mod2;
13613
13614        const A: u32 = 42;
13615      + const B: u32 = 42;
13616        ˇ
13617        fn main() {
13618            println!("hello");
13619
13620            println!("world");
13621        }
13622      "#
13623        .unindent(),
13624    );
13625
13626    cx.update_editor(|editor, window, cx| {
13627        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13628        editor.delete_line(&DeleteLine, window, cx);
13629    });
13630    executor.run_until_parked();
13631    cx.assert_state_with_diff(
13632        r#"
13633        ˇ
13634        fn main() {
13635            println!("hello");
13636
13637            println!("world");
13638        }
13639      "#
13640        .unindent(),
13641    );
13642}
13643
13644#[gpui::test]
13645async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13646    init_test(cx, |_| {});
13647
13648    let mut cx = EditorTestContext::new(cx).await;
13649    cx.set_diff_base(indoc! { "
13650        one
13651        two
13652        three
13653        four
13654        five
13655        "
13656    });
13657    cx.set_state(indoc! { "
13658        one
13659        ˇthree
13660        five
13661    "});
13662    cx.run_until_parked();
13663    cx.update_editor(|editor, window, cx| {
13664        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13665    });
13666    cx.assert_state_with_diff(
13667        indoc! { "
13668        one
13669      - two
13670        ˇthree
13671      - four
13672        five
13673    "}
13674        .to_string(),
13675    );
13676    cx.update_editor(|editor, window, cx| {
13677        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13678    });
13679
13680    cx.assert_state_with_diff(
13681        indoc! { "
13682        one
13683        ˇthree
13684        five
13685    "}
13686        .to_string(),
13687    );
13688
13689    cx.set_state(indoc! { "
13690        one
13691        ˇTWO
13692        three
13693        four
13694        five
13695    "});
13696    cx.run_until_parked();
13697    cx.update_editor(|editor, window, cx| {
13698        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13699    });
13700
13701    cx.assert_state_with_diff(
13702        indoc! { "
13703            one
13704          - two
13705          + ˇTWO
13706            three
13707            four
13708            five
13709        "}
13710        .to_string(),
13711    );
13712    cx.update_editor(|editor, window, cx| {
13713        editor.move_up(&Default::default(), window, cx);
13714        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13715    });
13716    cx.assert_state_with_diff(
13717        indoc! { "
13718            one
13719            ˇTWO
13720            three
13721            four
13722            five
13723        "}
13724        .to_string(),
13725    );
13726}
13727
13728#[gpui::test]
13729async fn test_edits_around_expanded_deletion_hunks(
13730    executor: BackgroundExecutor,
13731    cx: &mut gpui::TestAppContext,
13732) {
13733    init_test(cx, |_| {});
13734
13735    let mut cx = EditorTestContext::new(cx).await;
13736
13737    let diff_base = r#"
13738        use some::mod1;
13739        use some::mod2;
13740
13741        const A: u32 = 42;
13742        const B: u32 = 42;
13743        const C: u32 = 42;
13744
13745
13746        fn main() {
13747            println!("hello");
13748
13749            println!("world");
13750        }
13751    "#
13752    .unindent();
13753    executor.run_until_parked();
13754    cx.set_state(
13755        &r#"
13756        use some::mod1;
13757        use some::mod2;
13758
13759        ˇconst B: u32 = 42;
13760        const C: u32 = 42;
13761
13762
13763        fn main() {
13764            println!("hello");
13765
13766            println!("world");
13767        }
13768        "#
13769        .unindent(),
13770    );
13771
13772    cx.set_diff_base(&diff_base);
13773    executor.run_until_parked();
13774
13775    cx.update_editor(|editor, window, cx| {
13776        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13777    });
13778    executor.run_until_parked();
13779
13780    cx.assert_state_with_diff(
13781        r#"
13782        use some::mod1;
13783        use some::mod2;
13784
13785      - const A: u32 = 42;
13786        ˇconst B: u32 = 42;
13787        const C: u32 = 42;
13788
13789
13790        fn main() {
13791            println!("hello");
13792
13793            println!("world");
13794        }
13795      "#
13796        .unindent(),
13797    );
13798
13799    cx.update_editor(|editor, window, cx| {
13800        editor.delete_line(&DeleteLine, window, cx);
13801    });
13802    executor.run_until_parked();
13803    cx.assert_state_with_diff(
13804        r#"
13805        use some::mod1;
13806        use some::mod2;
13807
13808      - const A: u32 = 42;
13809      - const B: u32 = 42;
13810        ˇconst C: u32 = 42;
13811
13812
13813        fn main() {
13814            println!("hello");
13815
13816            println!("world");
13817        }
13818      "#
13819        .unindent(),
13820    );
13821
13822    cx.update_editor(|editor, window, cx| {
13823        editor.delete_line(&DeleteLine, window, cx);
13824    });
13825    executor.run_until_parked();
13826    cx.assert_state_with_diff(
13827        r#"
13828        use some::mod1;
13829        use some::mod2;
13830
13831      - const A: u32 = 42;
13832      - const B: u32 = 42;
13833      - const C: u32 = 42;
13834        ˇ
13835
13836        fn main() {
13837            println!("hello");
13838
13839            println!("world");
13840        }
13841      "#
13842        .unindent(),
13843    );
13844
13845    cx.update_editor(|editor, window, cx| {
13846        editor.handle_input("replacement", window, cx);
13847    });
13848    executor.run_until_parked();
13849    cx.assert_state_with_diff(
13850        r#"
13851        use some::mod1;
13852        use some::mod2;
13853
13854      - const A: u32 = 42;
13855      - const B: u32 = 42;
13856      - const C: u32 = 42;
13857      -
13858      + replacementˇ
13859
13860        fn main() {
13861            println!("hello");
13862
13863            println!("world");
13864        }
13865      "#
13866        .unindent(),
13867    );
13868}
13869
13870#[gpui::test]
13871async fn test_backspace_after_deletion_hunk(
13872    executor: BackgroundExecutor,
13873    cx: &mut gpui::TestAppContext,
13874) {
13875    init_test(cx, |_| {});
13876
13877    let mut cx = EditorTestContext::new(cx).await;
13878
13879    let base_text = r#"
13880        one
13881        two
13882        three
13883        four
13884        five
13885    "#
13886    .unindent();
13887    executor.run_until_parked();
13888    cx.set_state(
13889        &r#"
13890        one
13891        two
13892        fˇour
13893        five
13894        "#
13895        .unindent(),
13896    );
13897
13898    cx.set_diff_base(&base_text);
13899    executor.run_until_parked();
13900
13901    cx.update_editor(|editor, window, cx| {
13902        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13903    });
13904    executor.run_until_parked();
13905
13906    cx.assert_state_with_diff(
13907        r#"
13908          one
13909          two
13910        - three
13911          fˇour
13912          five
13913        "#
13914        .unindent(),
13915    );
13916
13917    cx.update_editor(|editor, window, cx| {
13918        editor.backspace(&Backspace, window, cx);
13919        editor.backspace(&Backspace, window, cx);
13920    });
13921    executor.run_until_parked();
13922    cx.assert_state_with_diff(
13923        r#"
13924          one
13925          two
13926        - threeˇ
13927        - four
13928        + our
13929          five
13930        "#
13931        .unindent(),
13932    );
13933}
13934
13935#[gpui::test]
13936async fn test_edit_after_expanded_modification_hunk(
13937    executor: BackgroundExecutor,
13938    cx: &mut gpui::TestAppContext,
13939) {
13940    init_test(cx, |_| {});
13941
13942    let mut cx = EditorTestContext::new(cx).await;
13943
13944    let diff_base = r#"
13945        use some::mod1;
13946        use some::mod2;
13947
13948        const A: u32 = 42;
13949        const B: u32 = 42;
13950        const C: u32 = 42;
13951        const D: u32 = 42;
13952
13953
13954        fn main() {
13955            println!("hello");
13956
13957            println!("world");
13958        }"#
13959    .unindent();
13960
13961    cx.set_state(
13962        &r#"
13963        use some::mod1;
13964        use some::mod2;
13965
13966        const A: u32 = 42;
13967        const B: u32 = 42;
13968        const C: u32 = 43ˇ
13969        const D: u32 = 42;
13970
13971
13972        fn main() {
13973            println!("hello");
13974
13975            println!("world");
13976        }"#
13977        .unindent(),
13978    );
13979
13980    cx.set_diff_base(&diff_base);
13981    executor.run_until_parked();
13982    cx.update_editor(|editor, window, cx| {
13983        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13984    });
13985    executor.run_until_parked();
13986
13987    cx.assert_state_with_diff(
13988        r#"
13989        use some::mod1;
13990        use some::mod2;
13991
13992        const A: u32 = 42;
13993        const B: u32 = 42;
13994      - const C: u32 = 42;
13995      + const C: u32 = 43ˇ
13996        const D: u32 = 42;
13997
13998
13999        fn main() {
14000            println!("hello");
14001
14002            println!("world");
14003        }"#
14004        .unindent(),
14005    );
14006
14007    cx.update_editor(|editor, window, cx| {
14008        editor.handle_input("\nnew_line\n", window, cx);
14009    });
14010    executor.run_until_parked();
14011
14012    cx.assert_state_with_diff(
14013        r#"
14014        use some::mod1;
14015        use some::mod2;
14016
14017        const A: u32 = 42;
14018        const B: u32 = 42;
14019      - const C: u32 = 42;
14020      + const C: u32 = 43
14021      + new_line
14022      + ˇ
14023        const D: u32 = 42;
14024
14025
14026        fn main() {
14027            println!("hello");
14028
14029            println!("world");
14030        }"#
14031        .unindent(),
14032    );
14033}
14034
14035async fn setup_indent_guides_editor(
14036    text: &str,
14037    cx: &mut gpui::TestAppContext,
14038) -> (BufferId, EditorTestContext) {
14039    init_test(cx, |_| {});
14040
14041    let mut cx = EditorTestContext::new(cx).await;
14042
14043    let buffer_id = cx.update_editor(|editor, window, cx| {
14044        editor.set_text(text, window, cx);
14045        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14046
14047        buffer_ids[0]
14048    });
14049
14050    (buffer_id, cx)
14051}
14052
14053fn assert_indent_guides(
14054    range: Range<u32>,
14055    expected: Vec<IndentGuide>,
14056    active_indices: Option<Vec<usize>>,
14057    cx: &mut EditorTestContext,
14058) {
14059    let indent_guides = cx.update_editor(|editor, window, cx| {
14060        let snapshot = editor.snapshot(window, cx).display_snapshot;
14061        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14062            editor,
14063            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14064            true,
14065            &snapshot,
14066            cx,
14067        );
14068
14069        indent_guides.sort_by(|a, b| {
14070            a.depth.cmp(&b.depth).then(
14071                a.start_row
14072                    .cmp(&b.start_row)
14073                    .then(a.end_row.cmp(&b.end_row)),
14074            )
14075        });
14076        indent_guides
14077    });
14078
14079    if let Some(expected) = active_indices {
14080        let active_indices = cx.update_editor(|editor, window, cx| {
14081            let snapshot = editor.snapshot(window, cx).display_snapshot;
14082            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14083        });
14084
14085        assert_eq!(
14086            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14087            expected,
14088            "Active indent guide indices do not match"
14089        );
14090    }
14091
14092    assert_eq!(indent_guides, expected, "Indent guides do not match");
14093}
14094
14095fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14096    IndentGuide {
14097        buffer_id,
14098        start_row: MultiBufferRow(start_row),
14099        end_row: MultiBufferRow(end_row),
14100        depth,
14101        tab_size: 4,
14102        settings: IndentGuideSettings {
14103            enabled: true,
14104            line_width: 1,
14105            active_line_width: 1,
14106            ..Default::default()
14107        },
14108    }
14109}
14110
14111#[gpui::test]
14112async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14113    let (buffer_id, mut cx) = setup_indent_guides_editor(
14114        &"
14115    fn main() {
14116        let a = 1;
14117    }"
14118        .unindent(),
14119        cx,
14120    )
14121    .await;
14122
14123    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14124}
14125
14126#[gpui::test]
14127async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
14128    let (buffer_id, mut cx) = setup_indent_guides_editor(
14129        &"
14130    fn main() {
14131        let a = 1;
14132        let b = 2;
14133    }"
14134        .unindent(),
14135        cx,
14136    )
14137    .await;
14138
14139    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14140}
14141
14142#[gpui::test]
14143async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
14144    let (buffer_id, mut cx) = setup_indent_guides_editor(
14145        &"
14146    fn main() {
14147        let a = 1;
14148        if a == 3 {
14149            let b = 2;
14150        } else {
14151            let c = 3;
14152        }
14153    }"
14154        .unindent(),
14155        cx,
14156    )
14157    .await;
14158
14159    assert_indent_guides(
14160        0..8,
14161        vec![
14162            indent_guide(buffer_id, 1, 6, 0),
14163            indent_guide(buffer_id, 3, 3, 1),
14164            indent_guide(buffer_id, 5, 5, 1),
14165        ],
14166        None,
14167        &mut cx,
14168    );
14169}
14170
14171#[gpui::test]
14172async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
14173    let (buffer_id, mut cx) = setup_indent_guides_editor(
14174        &"
14175    fn main() {
14176        let a = 1;
14177            let b = 2;
14178        let c = 3;
14179    }"
14180        .unindent(),
14181        cx,
14182    )
14183    .await;
14184
14185    assert_indent_guides(
14186        0..5,
14187        vec![
14188            indent_guide(buffer_id, 1, 3, 0),
14189            indent_guide(buffer_id, 2, 2, 1),
14190        ],
14191        None,
14192        &mut cx,
14193    );
14194}
14195
14196#[gpui::test]
14197async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14198    let (buffer_id, mut cx) = setup_indent_guides_editor(
14199        &"
14200        fn main() {
14201            let a = 1;
14202
14203            let c = 3;
14204        }"
14205        .unindent(),
14206        cx,
14207    )
14208    .await;
14209
14210    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14211}
14212
14213#[gpui::test]
14214async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14215    let (buffer_id, mut cx) = setup_indent_guides_editor(
14216        &"
14217        fn main() {
14218            let a = 1;
14219
14220            let c = 3;
14221
14222            if a == 3 {
14223                let b = 2;
14224            } else {
14225                let c = 3;
14226            }
14227        }"
14228        .unindent(),
14229        cx,
14230    )
14231    .await;
14232
14233    assert_indent_guides(
14234        0..11,
14235        vec![
14236            indent_guide(buffer_id, 1, 9, 0),
14237            indent_guide(buffer_id, 6, 6, 1),
14238            indent_guide(buffer_id, 8, 8, 1),
14239        ],
14240        None,
14241        &mut cx,
14242    );
14243}
14244
14245#[gpui::test]
14246async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14247    let (buffer_id, mut cx) = setup_indent_guides_editor(
14248        &"
14249        fn main() {
14250            let a = 1;
14251
14252            let c = 3;
14253
14254            if a == 3 {
14255                let b = 2;
14256            } else {
14257                let c = 3;
14258            }
14259        }"
14260        .unindent(),
14261        cx,
14262    )
14263    .await;
14264
14265    assert_indent_guides(
14266        1..11,
14267        vec![
14268            indent_guide(buffer_id, 1, 9, 0),
14269            indent_guide(buffer_id, 6, 6, 1),
14270            indent_guide(buffer_id, 8, 8, 1),
14271        ],
14272        None,
14273        &mut cx,
14274    );
14275}
14276
14277#[gpui::test]
14278async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14279    let (buffer_id, mut cx) = setup_indent_guides_editor(
14280        &"
14281        fn main() {
14282            let a = 1;
14283
14284            let c = 3;
14285
14286            if a == 3 {
14287                let b = 2;
14288            } else {
14289                let c = 3;
14290            }
14291        }"
14292        .unindent(),
14293        cx,
14294    )
14295    .await;
14296
14297    assert_indent_guides(
14298        1..10,
14299        vec![
14300            indent_guide(buffer_id, 1, 9, 0),
14301            indent_guide(buffer_id, 6, 6, 1),
14302            indent_guide(buffer_id, 8, 8, 1),
14303        ],
14304        None,
14305        &mut cx,
14306    );
14307}
14308
14309#[gpui::test]
14310async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14311    let (buffer_id, mut cx) = setup_indent_guides_editor(
14312        &"
14313        block1
14314            block2
14315                block3
14316                    block4
14317            block2
14318        block1
14319        block1"
14320            .unindent(),
14321        cx,
14322    )
14323    .await;
14324
14325    assert_indent_guides(
14326        1..10,
14327        vec![
14328            indent_guide(buffer_id, 1, 4, 0),
14329            indent_guide(buffer_id, 2, 3, 1),
14330            indent_guide(buffer_id, 3, 3, 2),
14331        ],
14332        None,
14333        &mut cx,
14334    );
14335}
14336
14337#[gpui::test]
14338async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14339    let (buffer_id, mut cx) = setup_indent_guides_editor(
14340        &"
14341        block1
14342            block2
14343                block3
14344
14345        block1
14346        block1"
14347            .unindent(),
14348        cx,
14349    )
14350    .await;
14351
14352    assert_indent_guides(
14353        0..6,
14354        vec![
14355            indent_guide(buffer_id, 1, 2, 0),
14356            indent_guide(buffer_id, 2, 2, 1),
14357        ],
14358        None,
14359        &mut cx,
14360    );
14361}
14362
14363#[gpui::test]
14364async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14365    let (buffer_id, mut cx) = setup_indent_guides_editor(
14366        &"
14367        block1
14368
14369
14370
14371            block2
14372        "
14373        .unindent(),
14374        cx,
14375    )
14376    .await;
14377
14378    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14379}
14380
14381#[gpui::test]
14382async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14383    let (buffer_id, mut cx) = setup_indent_guides_editor(
14384        &"
14385        def a:
14386        \tb = 3
14387        \tif True:
14388        \t\tc = 4
14389        \t\td = 5
14390        \tprint(b)
14391        "
14392        .unindent(),
14393        cx,
14394    )
14395    .await;
14396
14397    assert_indent_guides(
14398        0..6,
14399        vec![
14400            indent_guide(buffer_id, 1, 6, 0),
14401            indent_guide(buffer_id, 3, 4, 1),
14402        ],
14403        None,
14404        &mut cx,
14405    );
14406}
14407
14408#[gpui::test]
14409async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14410    let (buffer_id, mut cx) = setup_indent_guides_editor(
14411        &"
14412    fn main() {
14413        let a = 1;
14414    }"
14415        .unindent(),
14416        cx,
14417    )
14418    .await;
14419
14420    cx.update_editor(|editor, window, cx| {
14421        editor.change_selections(None, window, cx, |s| {
14422            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14423        });
14424    });
14425
14426    assert_indent_guides(
14427        0..3,
14428        vec![indent_guide(buffer_id, 1, 1, 0)],
14429        Some(vec![0]),
14430        &mut cx,
14431    );
14432}
14433
14434#[gpui::test]
14435async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14436    let (buffer_id, mut cx) = setup_indent_guides_editor(
14437        &"
14438    fn main() {
14439        if 1 == 2 {
14440            let a = 1;
14441        }
14442    }"
14443        .unindent(),
14444        cx,
14445    )
14446    .await;
14447
14448    cx.update_editor(|editor, window, cx| {
14449        editor.change_selections(None, window, cx, |s| {
14450            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14451        });
14452    });
14453
14454    assert_indent_guides(
14455        0..4,
14456        vec![
14457            indent_guide(buffer_id, 1, 3, 0),
14458            indent_guide(buffer_id, 2, 2, 1),
14459        ],
14460        Some(vec![1]),
14461        &mut cx,
14462    );
14463
14464    cx.update_editor(|editor, window, cx| {
14465        editor.change_selections(None, window, cx, |s| {
14466            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14467        });
14468    });
14469
14470    assert_indent_guides(
14471        0..4,
14472        vec![
14473            indent_guide(buffer_id, 1, 3, 0),
14474            indent_guide(buffer_id, 2, 2, 1),
14475        ],
14476        Some(vec![1]),
14477        &mut cx,
14478    );
14479
14480    cx.update_editor(|editor, window, cx| {
14481        editor.change_selections(None, window, cx, |s| {
14482            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14483        });
14484    });
14485
14486    assert_indent_guides(
14487        0..4,
14488        vec![
14489            indent_guide(buffer_id, 1, 3, 0),
14490            indent_guide(buffer_id, 2, 2, 1),
14491        ],
14492        Some(vec![0]),
14493        &mut cx,
14494    );
14495}
14496
14497#[gpui::test]
14498async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14499    let (buffer_id, mut cx) = setup_indent_guides_editor(
14500        &"
14501    fn main() {
14502        let a = 1;
14503
14504        let b = 2;
14505    }"
14506        .unindent(),
14507        cx,
14508    )
14509    .await;
14510
14511    cx.update_editor(|editor, window, cx| {
14512        editor.change_selections(None, window, cx, |s| {
14513            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14514        });
14515    });
14516
14517    assert_indent_guides(
14518        0..5,
14519        vec![indent_guide(buffer_id, 1, 3, 0)],
14520        Some(vec![0]),
14521        &mut cx,
14522    );
14523}
14524
14525#[gpui::test]
14526async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14527    let (buffer_id, mut cx) = setup_indent_guides_editor(
14528        &"
14529    def m:
14530        a = 1
14531        pass"
14532            .unindent(),
14533        cx,
14534    )
14535    .await;
14536
14537    cx.update_editor(|editor, window, cx| {
14538        editor.change_selections(None, window, cx, |s| {
14539            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14540        });
14541    });
14542
14543    assert_indent_guides(
14544        0..3,
14545        vec![indent_guide(buffer_id, 1, 2, 0)],
14546        Some(vec![0]),
14547        &mut cx,
14548    );
14549}
14550
14551#[gpui::test]
14552async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14553    init_test(cx, |_| {});
14554    let mut cx = EditorTestContext::new(cx).await;
14555    let text = indoc! {
14556        "
14557        impl A {
14558            fn b() {
14559                0;
14560                3;
14561                5;
14562                6;
14563                7;
14564            }
14565        }
14566        "
14567    };
14568    let base_text = indoc! {
14569        "
14570        impl A {
14571            fn b() {
14572                0;
14573                1;
14574                2;
14575                3;
14576                4;
14577            }
14578            fn c() {
14579                5;
14580                6;
14581                7;
14582            }
14583        }
14584        "
14585    };
14586
14587    cx.update_editor(|editor, window, cx| {
14588        editor.set_text(text, window, cx);
14589
14590        editor.buffer().update(cx, |multibuffer, cx| {
14591            let buffer = multibuffer.as_singleton().unwrap();
14592            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14593
14594            multibuffer.set_all_diff_hunks_expanded(cx);
14595            multibuffer.add_diff(diff, cx);
14596
14597            buffer.read(cx).remote_id()
14598        })
14599    });
14600    cx.run_until_parked();
14601
14602    cx.assert_state_with_diff(
14603        indoc! { "
14604          impl A {
14605              fn b() {
14606                  0;
14607        -         1;
14608        -         2;
14609                  3;
14610        -         4;
14611        -     }
14612        -     fn c() {
14613                  5;
14614                  6;
14615                  7;
14616              }
14617          }
14618          ˇ"
14619        }
14620        .to_string(),
14621    );
14622
14623    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14624        editor
14625            .snapshot(window, cx)
14626            .buffer_snapshot
14627            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14628            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14629            .collect::<Vec<_>>()
14630    });
14631    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14632    assert_eq!(
14633        actual_guides,
14634        vec![
14635            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14636            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14637            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14638        ]
14639    );
14640}
14641
14642#[gpui::test]
14643fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14644    init_test(cx, |_| {});
14645
14646    let editor = cx.add_window(|window, cx| {
14647        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14648        build_editor(buffer, window, cx)
14649    });
14650
14651    let render_args = Arc::new(Mutex::new(None));
14652    let snapshot = editor
14653        .update(cx, |editor, window, cx| {
14654            let snapshot = editor.buffer().read(cx).snapshot(cx);
14655            let range =
14656                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14657
14658            struct RenderArgs {
14659                row: MultiBufferRow,
14660                folded: bool,
14661                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14662            }
14663
14664            let crease = Crease::inline(
14665                range,
14666                FoldPlaceholder::test(),
14667                {
14668                    let toggle_callback = render_args.clone();
14669                    move |row, folded, callback, _window, _cx| {
14670                        *toggle_callback.lock() = Some(RenderArgs {
14671                            row,
14672                            folded,
14673                            callback,
14674                        });
14675                        div()
14676                    }
14677                },
14678                |_row, _folded, _window, _cx| div(),
14679            );
14680
14681            editor.insert_creases(Some(crease), cx);
14682            let snapshot = editor.snapshot(window, cx);
14683            let _div = snapshot.render_crease_toggle(
14684                MultiBufferRow(1),
14685                false,
14686                cx.entity().clone(),
14687                window,
14688                cx,
14689            );
14690            snapshot
14691        })
14692        .unwrap();
14693
14694    let render_args = render_args.lock().take().unwrap();
14695    assert_eq!(render_args.row, MultiBufferRow(1));
14696    assert!(!render_args.folded);
14697    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14698
14699    cx.update_window(*editor, |_, window, cx| {
14700        (render_args.callback)(true, window, cx)
14701    })
14702    .unwrap();
14703    let snapshot = editor
14704        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14705        .unwrap();
14706    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14707
14708    cx.update_window(*editor, |_, window, cx| {
14709        (render_args.callback)(false, window, cx)
14710    })
14711    .unwrap();
14712    let snapshot = editor
14713        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14714        .unwrap();
14715    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14716}
14717
14718#[gpui::test]
14719async fn test_input_text(cx: &mut gpui::TestAppContext) {
14720    init_test(cx, |_| {});
14721    let mut cx = EditorTestContext::new(cx).await;
14722
14723    cx.set_state(
14724        &r#"ˇone
14725        two
14726
14727        three
14728        fourˇ
14729        five
14730
14731        siˇx"#
14732            .unindent(),
14733    );
14734
14735    cx.dispatch_action(HandleInput(String::new()));
14736    cx.assert_editor_state(
14737        &r#"ˇone
14738        two
14739
14740        three
14741        fourˇ
14742        five
14743
14744        siˇx"#
14745            .unindent(),
14746    );
14747
14748    cx.dispatch_action(HandleInput("AAAA".to_string()));
14749    cx.assert_editor_state(
14750        &r#"AAAAˇone
14751        two
14752
14753        three
14754        fourAAAAˇ
14755        five
14756
14757        siAAAAˇx"#
14758            .unindent(),
14759    );
14760}
14761
14762#[gpui::test]
14763async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14764    init_test(cx, |_| {});
14765
14766    let mut cx = EditorTestContext::new(cx).await;
14767    cx.set_state(
14768        r#"let foo = 1;
14769let foo = 2;
14770let foo = 3;
14771let fooˇ = 4;
14772let foo = 5;
14773let foo = 6;
14774let foo = 7;
14775let foo = 8;
14776let foo = 9;
14777let foo = 10;
14778let foo = 11;
14779let foo = 12;
14780let foo = 13;
14781let foo = 14;
14782let foo = 15;"#,
14783    );
14784
14785    cx.update_editor(|e, window, cx| {
14786        assert_eq!(
14787            e.next_scroll_position,
14788            NextScrollCursorCenterTopBottom::Center,
14789            "Default next scroll direction is center",
14790        );
14791
14792        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14793        assert_eq!(
14794            e.next_scroll_position,
14795            NextScrollCursorCenterTopBottom::Top,
14796            "After center, next scroll direction should be top",
14797        );
14798
14799        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14800        assert_eq!(
14801            e.next_scroll_position,
14802            NextScrollCursorCenterTopBottom::Bottom,
14803            "After top, next scroll direction should be bottom",
14804        );
14805
14806        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14807        assert_eq!(
14808            e.next_scroll_position,
14809            NextScrollCursorCenterTopBottom::Center,
14810            "After bottom, scrolling should start over",
14811        );
14812
14813        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14814        assert_eq!(
14815            e.next_scroll_position,
14816            NextScrollCursorCenterTopBottom::Top,
14817            "Scrolling continues if retriggered fast enough"
14818        );
14819    });
14820
14821    cx.executor()
14822        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14823    cx.executor().run_until_parked();
14824    cx.update_editor(|e, _, _| {
14825        assert_eq!(
14826            e.next_scroll_position,
14827            NextScrollCursorCenterTopBottom::Center,
14828            "If scrolling is not triggered fast enough, it should reset"
14829        );
14830    });
14831}
14832
14833#[gpui::test]
14834async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14835    init_test(cx, |_| {});
14836    let mut cx = EditorLspTestContext::new_rust(
14837        lsp::ServerCapabilities {
14838            definition_provider: Some(lsp::OneOf::Left(true)),
14839            references_provider: Some(lsp::OneOf::Left(true)),
14840            ..lsp::ServerCapabilities::default()
14841        },
14842        cx,
14843    )
14844    .await;
14845
14846    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14847        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14848            move |params, _| async move {
14849                if empty_go_to_definition {
14850                    Ok(None)
14851                } else {
14852                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14853                        uri: params.text_document_position_params.text_document.uri,
14854                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14855                    })))
14856                }
14857            },
14858        );
14859        let references =
14860            cx.lsp
14861                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14862                    Ok(Some(vec![lsp::Location {
14863                        uri: params.text_document_position.text_document.uri,
14864                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14865                    }]))
14866                });
14867        (go_to_definition, references)
14868    };
14869
14870    cx.set_state(
14871        &r#"fn one() {
14872            let mut a = ˇtwo();
14873        }
14874
14875        fn two() {}"#
14876            .unindent(),
14877    );
14878    set_up_lsp_handlers(false, &mut cx);
14879    let navigated = cx
14880        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14881        .await
14882        .expect("Failed to navigate to definition");
14883    assert_eq!(
14884        navigated,
14885        Navigated::Yes,
14886        "Should have navigated to definition from the GetDefinition response"
14887    );
14888    cx.assert_editor_state(
14889        &r#"fn one() {
14890            let mut a = two();
14891        }
14892
14893        fn «twoˇ»() {}"#
14894            .unindent(),
14895    );
14896
14897    let editors = cx.update_workspace(|workspace, _, cx| {
14898        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14899    });
14900    cx.update_editor(|_, _, test_editor_cx| {
14901        assert_eq!(
14902            editors.len(),
14903            1,
14904            "Initially, only one, test, editor should be open in the workspace"
14905        );
14906        assert_eq!(
14907            test_editor_cx.entity(),
14908            editors.last().expect("Asserted len is 1").clone()
14909        );
14910    });
14911
14912    set_up_lsp_handlers(true, &mut cx);
14913    let navigated = cx
14914        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
14915        .await
14916        .expect("Failed to navigate to lookup references");
14917    assert_eq!(
14918        navigated,
14919        Navigated::Yes,
14920        "Should have navigated to references as a fallback after empty GoToDefinition response"
14921    );
14922    // We should not change the selections in the existing file,
14923    // if opening another milti buffer with the references
14924    cx.assert_editor_state(
14925        &r#"fn one() {
14926            let mut a = two();
14927        }
14928
14929        fn «twoˇ»() {}"#
14930            .unindent(),
14931    );
14932    let editors = cx.update_workspace(|workspace, _, cx| {
14933        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
14934    });
14935    cx.update_editor(|_, _, test_editor_cx| {
14936        assert_eq!(
14937            editors.len(),
14938            2,
14939            "After falling back to references search, we open a new editor with the results"
14940        );
14941        let references_fallback_text = editors
14942            .into_iter()
14943            .find(|new_editor| *new_editor != test_editor_cx.entity())
14944            .expect("Should have one non-test editor now")
14945            .read(test_editor_cx)
14946            .text(test_editor_cx);
14947        assert_eq!(
14948            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
14949            "Should use the range from the references response and not the GoToDefinition one"
14950        );
14951    });
14952}
14953
14954#[gpui::test]
14955async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
14956    init_test(cx, |_| {});
14957
14958    let language = Arc::new(Language::new(
14959        LanguageConfig::default(),
14960        Some(tree_sitter_rust::LANGUAGE.into()),
14961    ));
14962
14963    let text = r#"
14964        #[cfg(test)]
14965        mod tests() {
14966            #[test]
14967            fn runnable_1() {
14968                let a = 1;
14969            }
14970
14971            #[test]
14972            fn runnable_2() {
14973                let a = 1;
14974                let b = 2;
14975            }
14976        }
14977    "#
14978    .unindent();
14979
14980    let fs = FakeFs::new(cx.executor());
14981    fs.insert_file("/file.rs", Default::default()).await;
14982
14983    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14984    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14985    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14986    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
14987    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
14988
14989    let editor = cx.new_window_entity(|window, cx| {
14990        Editor::new(
14991            EditorMode::Full,
14992            multi_buffer,
14993            Some(project.clone()),
14994            true,
14995            window,
14996            cx,
14997        )
14998    });
14999
15000    editor.update_in(cx, |editor, window, cx| {
15001        editor.tasks.insert(
15002            (buffer.read(cx).remote_id(), 3),
15003            RunnableTasks {
15004                templates: vec![],
15005                offset: MultiBufferOffset(43),
15006                column: 0,
15007                extra_variables: HashMap::default(),
15008                context_range: BufferOffset(43)..BufferOffset(85),
15009            },
15010        );
15011        editor.tasks.insert(
15012            (buffer.read(cx).remote_id(), 8),
15013            RunnableTasks {
15014                templates: vec![],
15015                offset: MultiBufferOffset(86),
15016                column: 0,
15017                extra_variables: HashMap::default(),
15018                context_range: BufferOffset(86)..BufferOffset(191),
15019            },
15020        );
15021
15022        // Test finding task when cursor is inside function body
15023        editor.change_selections(None, window, cx, |s| {
15024            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15025        });
15026        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15027        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15028
15029        // Test finding task when cursor is on function name
15030        editor.change_selections(None, window, cx, |s| {
15031            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15032        });
15033        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15034        assert_eq!(row, 8, "Should find task when cursor is on function name");
15035    });
15036}
15037
15038#[gpui::test]
15039async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
15040    init_test(cx, |_| {});
15041
15042    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15043    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15044    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15045
15046    let fs = FakeFs::new(cx.executor());
15047    fs.insert_tree(
15048        path!("/a"),
15049        json!({
15050            "first.rs": sample_text_1,
15051            "second.rs": sample_text_2,
15052            "third.rs": sample_text_3,
15053        }),
15054    )
15055    .await;
15056    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15057    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15058    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15059    let worktree = project.update(cx, |project, cx| {
15060        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15061        assert_eq!(worktrees.len(), 1);
15062        worktrees.pop().unwrap()
15063    });
15064    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15065
15066    let buffer_1 = project
15067        .update(cx, |project, cx| {
15068            project.open_buffer((worktree_id, "first.rs"), cx)
15069        })
15070        .await
15071        .unwrap();
15072    let buffer_2 = project
15073        .update(cx, |project, cx| {
15074            project.open_buffer((worktree_id, "second.rs"), cx)
15075        })
15076        .await
15077        .unwrap();
15078    let buffer_3 = project
15079        .update(cx, |project, cx| {
15080            project.open_buffer((worktree_id, "third.rs"), cx)
15081        })
15082        .await
15083        .unwrap();
15084
15085    let multi_buffer = cx.new(|cx| {
15086        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15087        multi_buffer.push_excerpts(
15088            buffer_1.clone(),
15089            [
15090                ExcerptRange {
15091                    context: Point::new(0, 0)..Point::new(3, 0),
15092                    primary: None,
15093                },
15094                ExcerptRange {
15095                    context: Point::new(5, 0)..Point::new(7, 0),
15096                    primary: None,
15097                },
15098                ExcerptRange {
15099                    context: Point::new(9, 0)..Point::new(10, 4),
15100                    primary: None,
15101                },
15102            ],
15103            cx,
15104        );
15105        multi_buffer.push_excerpts(
15106            buffer_2.clone(),
15107            [
15108                ExcerptRange {
15109                    context: Point::new(0, 0)..Point::new(3, 0),
15110                    primary: None,
15111                },
15112                ExcerptRange {
15113                    context: Point::new(5, 0)..Point::new(7, 0),
15114                    primary: None,
15115                },
15116                ExcerptRange {
15117                    context: Point::new(9, 0)..Point::new(10, 4),
15118                    primary: None,
15119                },
15120            ],
15121            cx,
15122        );
15123        multi_buffer.push_excerpts(
15124            buffer_3.clone(),
15125            [
15126                ExcerptRange {
15127                    context: Point::new(0, 0)..Point::new(3, 0),
15128                    primary: None,
15129                },
15130                ExcerptRange {
15131                    context: Point::new(5, 0)..Point::new(7, 0),
15132                    primary: None,
15133                },
15134                ExcerptRange {
15135                    context: Point::new(9, 0)..Point::new(10, 4),
15136                    primary: None,
15137                },
15138            ],
15139            cx,
15140        );
15141        multi_buffer
15142    });
15143    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15144        Editor::new(
15145            EditorMode::Full,
15146            multi_buffer,
15147            Some(project.clone()),
15148            true,
15149            window,
15150            cx,
15151        )
15152    });
15153
15154    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";
15155    assert_eq!(
15156        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15157        full_text,
15158    );
15159
15160    multi_buffer_editor.update(cx, |editor, cx| {
15161        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15162    });
15163    assert_eq!(
15164        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15165        "\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",
15166        "After folding the first buffer, its text should not be displayed"
15167    );
15168
15169    multi_buffer_editor.update(cx, |editor, cx| {
15170        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15171    });
15172    assert_eq!(
15173        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15174        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15175        "After folding the second buffer, its text should not be displayed"
15176    );
15177
15178    multi_buffer_editor.update(cx, |editor, cx| {
15179        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15180    });
15181    assert_eq!(
15182        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15183        "\n\n\n\n\n",
15184        "After folding the third buffer, its text should not be displayed"
15185    );
15186
15187    // Emulate selection inside the fold logic, that should work
15188    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15189        editor
15190            .snapshot(window, cx)
15191            .next_line_boundary(Point::new(0, 4));
15192    });
15193
15194    multi_buffer_editor.update(cx, |editor, cx| {
15195        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15196    });
15197    assert_eq!(
15198        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15199        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15200        "After unfolding the second buffer, its text should be displayed"
15201    );
15202
15203    multi_buffer_editor.update(cx, |editor, cx| {
15204        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15205    });
15206    assert_eq!(
15207        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15208        "\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",
15209        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15210    );
15211
15212    multi_buffer_editor.update(cx, |editor, cx| {
15213        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15214    });
15215    assert_eq!(
15216        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15217        full_text,
15218        "After unfolding the all buffers, all original text should be displayed"
15219    );
15220}
15221
15222#[gpui::test]
15223async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15224    init_test(cx, |_| {});
15225
15226    let sample_text_1 = "1111\n2222\n3333".to_string();
15227    let sample_text_2 = "4444\n5555\n6666".to_string();
15228    let sample_text_3 = "7777\n8888\n9999".to_string();
15229
15230    let fs = FakeFs::new(cx.executor());
15231    fs.insert_tree(
15232        path!("/a"),
15233        json!({
15234            "first.rs": sample_text_1,
15235            "second.rs": sample_text_2,
15236            "third.rs": sample_text_3,
15237        }),
15238    )
15239    .await;
15240    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15241    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15242    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15243    let worktree = project.update(cx, |project, cx| {
15244        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15245        assert_eq!(worktrees.len(), 1);
15246        worktrees.pop().unwrap()
15247    });
15248    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15249
15250    let buffer_1 = project
15251        .update(cx, |project, cx| {
15252            project.open_buffer((worktree_id, "first.rs"), cx)
15253        })
15254        .await
15255        .unwrap();
15256    let buffer_2 = project
15257        .update(cx, |project, cx| {
15258            project.open_buffer((worktree_id, "second.rs"), cx)
15259        })
15260        .await
15261        .unwrap();
15262    let buffer_3 = project
15263        .update(cx, |project, cx| {
15264            project.open_buffer((worktree_id, "third.rs"), cx)
15265        })
15266        .await
15267        .unwrap();
15268
15269    let multi_buffer = cx.new(|cx| {
15270        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15271        multi_buffer.push_excerpts(
15272            buffer_1.clone(),
15273            [ExcerptRange {
15274                context: Point::new(0, 0)..Point::new(3, 0),
15275                primary: None,
15276            }],
15277            cx,
15278        );
15279        multi_buffer.push_excerpts(
15280            buffer_2.clone(),
15281            [ExcerptRange {
15282                context: Point::new(0, 0)..Point::new(3, 0),
15283                primary: None,
15284            }],
15285            cx,
15286        );
15287        multi_buffer.push_excerpts(
15288            buffer_3.clone(),
15289            [ExcerptRange {
15290                context: Point::new(0, 0)..Point::new(3, 0),
15291                primary: None,
15292            }],
15293            cx,
15294        );
15295        multi_buffer
15296    });
15297
15298    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15299        Editor::new(
15300            EditorMode::Full,
15301            multi_buffer,
15302            Some(project.clone()),
15303            true,
15304            window,
15305            cx,
15306        )
15307    });
15308
15309    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15310    assert_eq!(
15311        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15312        full_text,
15313    );
15314
15315    multi_buffer_editor.update(cx, |editor, cx| {
15316        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15317    });
15318    assert_eq!(
15319        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15320        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15321        "After folding the first buffer, its text should not be displayed"
15322    );
15323
15324    multi_buffer_editor.update(cx, |editor, cx| {
15325        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15326    });
15327
15328    assert_eq!(
15329        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15330        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15331        "After folding the second buffer, its text should not be displayed"
15332    );
15333
15334    multi_buffer_editor.update(cx, |editor, cx| {
15335        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15336    });
15337    assert_eq!(
15338        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15339        "\n\n\n\n\n",
15340        "After folding the third buffer, its text should not be displayed"
15341    );
15342
15343    multi_buffer_editor.update(cx, |editor, cx| {
15344        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15345    });
15346    assert_eq!(
15347        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15348        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15349        "After unfolding the second buffer, its text should be displayed"
15350    );
15351
15352    multi_buffer_editor.update(cx, |editor, cx| {
15353        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15354    });
15355    assert_eq!(
15356        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15357        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15358        "After unfolding the first buffer, its text should be displayed"
15359    );
15360
15361    multi_buffer_editor.update(cx, |editor, cx| {
15362        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15363    });
15364    assert_eq!(
15365        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15366        full_text,
15367        "After unfolding all buffers, all original text should be displayed"
15368    );
15369}
15370
15371#[gpui::test]
15372async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15373    init_test(cx, |_| {});
15374
15375    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15376
15377    let fs = FakeFs::new(cx.executor());
15378    fs.insert_tree(
15379        path!("/a"),
15380        json!({
15381            "main.rs": sample_text,
15382        }),
15383    )
15384    .await;
15385    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15386    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15387    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15388    let worktree = project.update(cx, |project, cx| {
15389        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15390        assert_eq!(worktrees.len(), 1);
15391        worktrees.pop().unwrap()
15392    });
15393    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15394
15395    let buffer_1 = project
15396        .update(cx, |project, cx| {
15397            project.open_buffer((worktree_id, "main.rs"), cx)
15398        })
15399        .await
15400        .unwrap();
15401
15402    let multi_buffer = cx.new(|cx| {
15403        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15404        multi_buffer.push_excerpts(
15405            buffer_1.clone(),
15406            [ExcerptRange {
15407                context: Point::new(0, 0)
15408                    ..Point::new(
15409                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15410                        0,
15411                    ),
15412                primary: None,
15413            }],
15414            cx,
15415        );
15416        multi_buffer
15417    });
15418    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15419        Editor::new(
15420            EditorMode::Full,
15421            multi_buffer,
15422            Some(project.clone()),
15423            true,
15424            window,
15425            cx,
15426        )
15427    });
15428
15429    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15430    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15431        enum TestHighlight {}
15432        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15433        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15434        editor.highlight_text::<TestHighlight>(
15435            vec![highlight_range.clone()],
15436            HighlightStyle::color(Hsla::green()),
15437            cx,
15438        );
15439        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15440    });
15441
15442    let full_text = format!("\n\n\n{sample_text}\n");
15443    assert_eq!(
15444        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15445        full_text,
15446    );
15447}
15448
15449#[gpui::test]
15450async fn test_inline_completion_text(cx: &mut TestAppContext) {
15451    init_test(cx, |_| {});
15452
15453    // Simple insertion
15454    assert_highlighted_edits(
15455        "Hello, world!",
15456        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15457        true,
15458        cx,
15459        |highlighted_edits, cx| {
15460            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15461            assert_eq!(highlighted_edits.highlights.len(), 1);
15462            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15463            assert_eq!(
15464                highlighted_edits.highlights[0].1.background_color,
15465                Some(cx.theme().status().created_background)
15466            );
15467        },
15468    )
15469    .await;
15470
15471    // Replacement
15472    assert_highlighted_edits(
15473        "This is a test.",
15474        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15475        false,
15476        cx,
15477        |highlighted_edits, cx| {
15478            assert_eq!(highlighted_edits.text, "That is a test.");
15479            assert_eq!(highlighted_edits.highlights.len(), 1);
15480            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15481            assert_eq!(
15482                highlighted_edits.highlights[0].1.background_color,
15483                Some(cx.theme().status().created_background)
15484            );
15485        },
15486    )
15487    .await;
15488
15489    // Multiple edits
15490    assert_highlighted_edits(
15491        "Hello, world!",
15492        vec![
15493            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15494            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15495        ],
15496        false,
15497        cx,
15498        |highlighted_edits, cx| {
15499            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15500            assert_eq!(highlighted_edits.highlights.len(), 2);
15501            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15502            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15503            assert_eq!(
15504                highlighted_edits.highlights[0].1.background_color,
15505                Some(cx.theme().status().created_background)
15506            );
15507            assert_eq!(
15508                highlighted_edits.highlights[1].1.background_color,
15509                Some(cx.theme().status().created_background)
15510            );
15511        },
15512    )
15513    .await;
15514
15515    // Multiple lines with edits
15516    assert_highlighted_edits(
15517        "First line\nSecond line\nThird line\nFourth line",
15518        vec![
15519            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15520            (
15521                Point::new(2, 0)..Point::new(2, 10),
15522                "New third line".to_string(),
15523            ),
15524            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15525        ],
15526        false,
15527        cx,
15528        |highlighted_edits, cx| {
15529            assert_eq!(
15530                highlighted_edits.text,
15531                "Second modified\nNew third line\nFourth updated line"
15532            );
15533            assert_eq!(highlighted_edits.highlights.len(), 3);
15534            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15535            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15536            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15537            for highlight in &highlighted_edits.highlights {
15538                assert_eq!(
15539                    highlight.1.background_color,
15540                    Some(cx.theme().status().created_background)
15541                );
15542            }
15543        },
15544    )
15545    .await;
15546}
15547
15548#[gpui::test]
15549async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15550    init_test(cx, |_| {});
15551
15552    // Deletion
15553    assert_highlighted_edits(
15554        "Hello, world!",
15555        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15556        true,
15557        cx,
15558        |highlighted_edits, cx| {
15559            assert_eq!(highlighted_edits.text, "Hello, world!");
15560            assert_eq!(highlighted_edits.highlights.len(), 1);
15561            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15562            assert_eq!(
15563                highlighted_edits.highlights[0].1.background_color,
15564                Some(cx.theme().status().deleted_background)
15565            );
15566        },
15567    )
15568    .await;
15569
15570    // Insertion
15571    assert_highlighted_edits(
15572        "Hello, world!",
15573        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15574        true,
15575        cx,
15576        |highlighted_edits, cx| {
15577            assert_eq!(highlighted_edits.highlights.len(), 1);
15578            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15579            assert_eq!(
15580                highlighted_edits.highlights[0].1.background_color,
15581                Some(cx.theme().status().created_background)
15582            );
15583        },
15584    )
15585    .await;
15586}
15587
15588async fn assert_highlighted_edits(
15589    text: &str,
15590    edits: Vec<(Range<Point>, String)>,
15591    include_deletions: bool,
15592    cx: &mut TestAppContext,
15593    assertion_fn: impl Fn(HighlightedText, &App),
15594) {
15595    let window = cx.add_window(|window, cx| {
15596        let buffer = MultiBuffer::build_simple(text, cx);
15597        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15598    });
15599    let cx = &mut VisualTestContext::from_window(*window, cx);
15600
15601    let (buffer, snapshot) = window
15602        .update(cx, |editor, _window, cx| {
15603            (
15604                editor.buffer().clone(),
15605                editor.buffer().read(cx).snapshot(cx),
15606            )
15607        })
15608        .unwrap();
15609
15610    let edits = edits
15611        .into_iter()
15612        .map(|(range, edit)| {
15613            (
15614                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15615                edit,
15616            )
15617        })
15618        .collect::<Vec<_>>();
15619
15620    let text_anchor_edits = edits
15621        .clone()
15622        .into_iter()
15623        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15624        .collect::<Vec<_>>();
15625
15626    let edit_preview = window
15627        .update(cx, |_, _window, cx| {
15628            buffer
15629                .read(cx)
15630                .as_singleton()
15631                .unwrap()
15632                .read(cx)
15633                .preview_edits(text_anchor_edits.into(), cx)
15634        })
15635        .unwrap()
15636        .await;
15637
15638    cx.update(|_window, cx| {
15639        let highlighted_edits = inline_completion_edit_text(
15640            &snapshot.as_singleton().unwrap().2,
15641            &edits,
15642            &edit_preview,
15643            include_deletions,
15644            cx,
15645        );
15646        assertion_fn(highlighted_edits, cx)
15647    });
15648}
15649
15650#[gpui::test]
15651async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15652    init_test(cx, |_| {});
15653    let capabilities = lsp::ServerCapabilities {
15654        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15655            prepare_provider: Some(true),
15656            work_done_progress_options: Default::default(),
15657        })),
15658        ..Default::default()
15659    };
15660    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15661
15662    cx.set_state(indoc! {"
15663        struct Fˇoo {}
15664    "});
15665
15666    cx.update_editor(|editor, _, cx| {
15667        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15668        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15669        editor.highlight_background::<DocumentHighlightRead>(
15670            &[highlight_range],
15671            |c| c.editor_document_highlight_read_background,
15672            cx,
15673        );
15674    });
15675
15676    let mut prepare_rename_handler =
15677        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15678            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15679                start: lsp::Position {
15680                    line: 0,
15681                    character: 7,
15682                },
15683                end: lsp::Position {
15684                    line: 0,
15685                    character: 10,
15686                },
15687            })))
15688        });
15689    let prepare_rename_task = cx
15690        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15691        .expect("Prepare rename was not started");
15692    prepare_rename_handler.next().await.unwrap();
15693    prepare_rename_task.await.expect("Prepare rename failed");
15694
15695    let mut rename_handler =
15696        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15697            let edit = lsp::TextEdit {
15698                range: lsp::Range {
15699                    start: lsp::Position {
15700                        line: 0,
15701                        character: 7,
15702                    },
15703                    end: lsp::Position {
15704                        line: 0,
15705                        character: 10,
15706                    },
15707                },
15708                new_text: "FooRenamed".to_string(),
15709            };
15710            Ok(Some(lsp::WorkspaceEdit::new(
15711                // Specify the same edit twice
15712                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15713            )))
15714        });
15715    let rename_task = cx
15716        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15717        .expect("Confirm rename was not started");
15718    rename_handler.next().await.unwrap();
15719    rename_task.await.expect("Confirm rename failed");
15720    cx.run_until_parked();
15721
15722    // Despite two edits, only one is actually applied as those are identical
15723    cx.assert_editor_state(indoc! {"
15724        struct FooRenamedˇ {}
15725    "});
15726}
15727
15728#[gpui::test]
15729async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15730    init_test(cx, |_| {});
15731    // These capabilities indicate that the server does not support prepare rename.
15732    let capabilities = lsp::ServerCapabilities {
15733        rename_provider: Some(lsp::OneOf::Left(true)),
15734        ..Default::default()
15735    };
15736    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15737
15738    cx.set_state(indoc! {"
15739        struct Fˇoo {}
15740    "});
15741
15742    cx.update_editor(|editor, _window, cx| {
15743        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15744        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15745        editor.highlight_background::<DocumentHighlightRead>(
15746            &[highlight_range],
15747            |c| c.editor_document_highlight_read_background,
15748            cx,
15749        );
15750    });
15751
15752    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15753        .expect("Prepare rename was not started")
15754        .await
15755        .expect("Prepare rename failed");
15756
15757    let mut rename_handler =
15758        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15759            let edit = lsp::TextEdit {
15760                range: lsp::Range {
15761                    start: lsp::Position {
15762                        line: 0,
15763                        character: 7,
15764                    },
15765                    end: lsp::Position {
15766                        line: 0,
15767                        character: 10,
15768                    },
15769                },
15770                new_text: "FooRenamed".to_string(),
15771            };
15772            Ok(Some(lsp::WorkspaceEdit::new(
15773                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15774            )))
15775        });
15776    let rename_task = cx
15777        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15778        .expect("Confirm rename was not started");
15779    rename_handler.next().await.unwrap();
15780    rename_task.await.expect("Confirm rename failed");
15781    cx.run_until_parked();
15782
15783    // Correct range is renamed, as `surrounding_word` is used to find it.
15784    cx.assert_editor_state(indoc! {"
15785        struct FooRenamedˇ {}
15786    "});
15787}
15788
15789fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15790    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15791    point..point
15792}
15793
15794fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15795    let (text, ranges) = marked_text_ranges(marked_text, true);
15796    assert_eq!(editor.text(cx), text);
15797    assert_eq!(
15798        editor.selections.ranges(cx),
15799        ranges,
15800        "Assert selections are {}",
15801        marked_text
15802    );
15803}
15804
15805pub fn handle_signature_help_request(
15806    cx: &mut EditorLspTestContext,
15807    mocked_response: lsp::SignatureHelp,
15808) -> impl Future<Output = ()> {
15809    let mut request =
15810        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15811            let mocked_response = mocked_response.clone();
15812            async move { Ok(Some(mocked_response)) }
15813        });
15814
15815    async move {
15816        request.next().await;
15817    }
15818}
15819
15820/// Handle completion request passing a marked string specifying where the completion
15821/// should be triggered from using '|' character, what range should be replaced, and what completions
15822/// should be returned using '<' and '>' to delimit the range
15823pub fn handle_completion_request(
15824    cx: &mut EditorLspTestContext,
15825    marked_string: &str,
15826    completions: Vec<&'static str>,
15827    counter: Arc<AtomicUsize>,
15828) -> impl Future<Output = ()> {
15829    let complete_from_marker: TextRangeMarker = '|'.into();
15830    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15831    let (_, mut marked_ranges) = marked_text_ranges_by(
15832        marked_string,
15833        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15834    );
15835
15836    let complete_from_position =
15837        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15838    let replace_range =
15839        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15840
15841    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15842        let completions = completions.clone();
15843        counter.fetch_add(1, atomic::Ordering::Release);
15844        async move {
15845            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15846            assert_eq!(
15847                params.text_document_position.position,
15848                complete_from_position
15849            );
15850            Ok(Some(lsp::CompletionResponse::Array(
15851                completions
15852                    .iter()
15853                    .map(|completion_text| lsp::CompletionItem {
15854                        label: completion_text.to_string(),
15855                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15856                            range: replace_range,
15857                            new_text: completion_text.to_string(),
15858                        })),
15859                        ..Default::default()
15860                    })
15861                    .collect(),
15862            )))
15863        }
15864    });
15865
15866    async move {
15867        request.next().await;
15868    }
15869}
15870
15871fn handle_resolve_completion_request(
15872    cx: &mut EditorLspTestContext,
15873    edits: Option<Vec<(&'static str, &'static str)>>,
15874) -> impl Future<Output = ()> {
15875    let edits = edits.map(|edits| {
15876        edits
15877            .iter()
15878            .map(|(marked_string, new_text)| {
15879                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
15880                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
15881                lsp::TextEdit::new(replace_range, new_text.to_string())
15882            })
15883            .collect::<Vec<_>>()
15884    });
15885
15886    let mut request =
15887        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
15888            let edits = edits.clone();
15889            async move {
15890                Ok(lsp::CompletionItem {
15891                    additional_text_edits: edits,
15892                    ..Default::default()
15893                })
15894            }
15895        });
15896
15897    async move {
15898        request.next().await;
15899    }
15900}
15901
15902pub(crate) fn update_test_language_settings(
15903    cx: &mut TestAppContext,
15904    f: impl Fn(&mut AllLanguageSettingsContent),
15905) {
15906    cx.update(|cx| {
15907        SettingsStore::update_global(cx, |store, cx| {
15908            store.update_user_settings::<AllLanguageSettings>(cx, f);
15909        });
15910    });
15911}
15912
15913pub(crate) fn update_test_project_settings(
15914    cx: &mut TestAppContext,
15915    f: impl Fn(&mut ProjectSettings),
15916) {
15917    cx.update(|cx| {
15918        SettingsStore::update_global(cx, |store, cx| {
15919            store.update_user_settings::<ProjectSettings>(cx, f);
15920        });
15921    });
15922}
15923
15924pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
15925    cx.update(|cx| {
15926        assets::Assets.load_test_fonts(cx);
15927        let store = SettingsStore::test(cx);
15928        cx.set_global(store);
15929        theme::init(theme::LoadThemes::JustBase, cx);
15930        release_channel::init(SemanticVersion::default(), cx);
15931        client::init_settings(cx);
15932        language::init(cx);
15933        Project::init_settings(cx);
15934        workspace::init_settings(cx);
15935        crate::init(cx);
15936    });
15937
15938    update_test_language_settings(cx, f);
15939}
15940
15941#[track_caller]
15942fn assert_hunk_revert(
15943    not_reverted_text_with_selections: &str,
15944    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
15945    expected_reverted_text_with_selections: &str,
15946    base_text: &str,
15947    cx: &mut EditorLspTestContext,
15948) {
15949    cx.set_state(not_reverted_text_with_selections);
15950    cx.set_diff_base(base_text);
15951    cx.executor().run_until_parked();
15952
15953    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
15954        let snapshot = editor.snapshot(window, cx);
15955        let reverted_hunk_statuses = snapshot
15956            .buffer_snapshot
15957            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
15958            .map(|hunk| hunk.status())
15959            .collect::<Vec<_>>();
15960
15961        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
15962        reverted_hunk_statuses
15963    });
15964    cx.executor().run_until_parked();
15965    cx.assert_editor_state(expected_reverted_text_with_selections);
15966    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
15967}