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