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