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