editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    linked_editing_ranges::LinkedEditingRanges,
    5    scroll::scroll_amount::ScrollAmount,
    6    test::{
    7        assert_text_with_selections, build_editor,
    8        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    9        editor_test_context::EditorTestContext,
   10        select_ranges,
   11    },
   12};
   13use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   14use futures::StreamExt;
   15use gpui::{
   16    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   17    VisualTestContext, WindowBounds, WindowOptions, div,
   18};
   19use indoc::indoc;
   20use language::{
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25    language_settings::{
   26        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   27        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   28    },
   29};
   30use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   31use lsp::CompletionParams;
   32use multi_buffer::{IndentGuide, PathKey};
   33use parking_lot::Mutex;
   34use pretty_assertions::{assert_eq, assert_ne};
   35use project::{
   36    FakeFs,
   37    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   38    project_settings::{LspSettings, ProjectSettings},
   39};
   40use serde_json::{self, json};
   41use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   42use std::{
   43    iter,
   44    sync::atomic::{self, AtomicUsize},
   45};
   46use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   47use text::ToPoint as _;
   48use unindent::Unindent;
   49use util::{
   50    assert_set_eq, path,
   51    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   52    uri,
   53};
   54use workspace::{
   55    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   56    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   57};
   58
   59#[gpui::test]
   60fn test_edit_events(cx: &mut TestAppContext) {
   61    init_test(cx, |_| {});
   62
   63    let buffer = cx.new(|cx| {
   64        let mut buffer = language::Buffer::local("123456", cx);
   65        buffer.set_group_interval(Duration::from_secs(1));
   66        buffer
   67    });
   68
   69    let events = Rc::new(RefCell::new(Vec::new()));
   70    let editor1 = cx.add_window({
   71        let events = events.clone();
   72        |window, cx| {
   73            let entity = cx.entity().clone();
   74            cx.subscribe_in(
   75                &entity,
   76                window,
   77                move |_, _, event: &EditorEvent, _, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor1", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, window, cx)
   87        }
   88    });
   89
   90    let editor2 = cx.add_window({
   91        let events = events.clone();
   92        |window, cx| {
   93            cx.subscribe_in(
   94                &cx.entity().clone(),
   95                window,
   96                move |_, _, event: &EditorEvent, _, _| match event {
   97                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   98                    EditorEvent::BufferEdited => {
   99                        events.borrow_mut().push(("editor2", "buffer edited"))
  100                    }
  101                    _ => {}
  102                },
  103            )
  104            .detach();
  105            Editor::for_buffer(buffer.clone(), None, window, cx)
  106        }
  107    });
  108
  109    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  110
  111    // Mutating editor 1 will emit an `Edited` event only for that editor.
  112    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  113    assert_eq!(
  114        mem::take(&mut *events.borrow_mut()),
  115        [
  116            ("editor1", "edited"),
  117            ("editor1", "buffer edited"),
  118            ("editor2", "buffer edited"),
  119        ]
  120    );
  121
  122    // Mutating editor 2 will emit an `Edited` event only for that editor.
  123    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  124    assert_eq!(
  125        mem::take(&mut *events.borrow_mut()),
  126        [
  127            ("editor2", "edited"),
  128            ("editor1", "buffer edited"),
  129            ("editor2", "buffer edited"),
  130        ]
  131    );
  132
  133    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  134    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  135    assert_eq!(
  136        mem::take(&mut *events.borrow_mut()),
  137        [
  138            ("editor1", "edited"),
  139            ("editor1", "buffer edited"),
  140            ("editor2", "buffer edited"),
  141        ]
  142    );
  143
  144    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  145    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  146    assert_eq!(
  147        mem::take(&mut *events.borrow_mut()),
  148        [
  149            ("editor1", "edited"),
  150            ("editor1", "buffer edited"),
  151            ("editor2", "buffer edited"),
  152        ]
  153    );
  154
  155    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  156    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  157    assert_eq!(
  158        mem::take(&mut *events.borrow_mut()),
  159        [
  160            ("editor2", "edited"),
  161            ("editor1", "buffer edited"),
  162            ("editor2", "buffer edited"),
  163        ]
  164    );
  165
  166    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  167    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  168    assert_eq!(
  169        mem::take(&mut *events.borrow_mut()),
  170        [
  171            ("editor2", "edited"),
  172            ("editor1", "buffer edited"),
  173            ("editor2", "buffer edited"),
  174        ]
  175    );
  176
  177    // No event is emitted when the mutation is a no-op.
  178    _ = editor2.update(cx, |editor, window, cx| {
  179        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  180
  181        editor.backspace(&Backspace, window, cx);
  182    });
  183    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  184}
  185
  186#[gpui::test]
  187fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  188    init_test(cx, |_| {});
  189
  190    let mut now = Instant::now();
  191    let group_interval = Duration::from_millis(1);
  192    let buffer = cx.new(|cx| {
  193        let mut buf = language::Buffer::local("123456", cx);
  194        buf.set_group_interval(group_interval);
  195        buf
  196    });
  197    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  198    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  199
  200    _ = editor.update(cx, |editor, window, cx| {
  201        editor.start_transaction_at(now, window, cx);
  202        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  203
  204        editor.insert("cd", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cd56");
  207        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  208
  209        editor.start_transaction_at(now, window, cx);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  211        editor.insert("e", window, cx);
  212        editor.end_transaction_at(now, cx);
  213        assert_eq!(editor.text(cx), "12cde6");
  214        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  215
  216        now += group_interval + Duration::from_millis(1);
  217        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  218
  219        // Simulate an edit in another editor
  220        buffer.update(cx, |buffer, cx| {
  221            buffer.start_transaction_at(now, cx);
  222            buffer.edit([(0..1, "a")], None, cx);
  223            buffer.edit([(1..1, "b")], None, cx);
  224            buffer.end_transaction_at(now, cx);
  225        });
  226
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  229
  230        // Last transaction happened past the group interval in a different editor.
  231        // Undo it individually and don't restore selections.
  232        editor.undo(&Undo, window, cx);
  233        assert_eq!(editor.text(cx), "12cde6");
  234        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  235
  236        // First two transactions happened within the group interval in this editor.
  237        // Undo them together and restore selections.
  238        editor.undo(&Undo, window, cx);
  239        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  240        assert_eq!(editor.text(cx), "123456");
  241        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  242
  243        // Redo the first two transactions together.
  244        editor.redo(&Redo, window, cx);
  245        assert_eq!(editor.text(cx), "12cde6");
  246        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  247
  248        // Redo the last transaction on its own.
  249        editor.redo(&Redo, window, cx);
  250        assert_eq!(editor.text(cx), "ab2cde6");
  251        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  252
  253        // Test empty transactions.
  254        editor.start_transaction_at(now, window, cx);
  255        editor.end_transaction_at(now, cx);
  256        editor.undo(&Undo, window, cx);
  257        assert_eq!(editor.text(cx), "12cde6");
  258    });
  259}
  260
  261#[gpui::test]
  262fn test_ime_composition(cx: &mut TestAppContext) {
  263    init_test(cx, |_| {});
  264
  265    let buffer = cx.new(|cx| {
  266        let mut buffer = language::Buffer::local("abcde", cx);
  267        // Ensure automatic grouping doesn't occur.
  268        buffer.set_group_interval(Duration::ZERO);
  269        buffer
  270    });
  271
  272    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  273    cx.add_window(|window, cx| {
  274        let mut editor = build_editor(buffer.clone(), window, cx);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  279        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  280        assert_eq!(editor.text(cx), "äbcde");
  281        assert_eq!(
  282            editor.marked_text_ranges(cx),
  283            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  284        );
  285
  286        // Finalize IME composition.
  287        editor.replace_text_in_range(None, "ā", window, cx);
  288        assert_eq!(editor.text(cx), "ābcde");
  289        assert_eq!(editor.marked_text_ranges(cx), None);
  290
  291        // IME composition edits are grouped and are undone/redone at once.
  292        editor.undo(&Default::default(), window, cx);
  293        assert_eq!(editor.text(cx), "abcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295        editor.redo(&Default::default(), window, cx);
  296        assert_eq!(editor.text(cx), "ābcde");
  297        assert_eq!(editor.marked_text_ranges(cx), None);
  298
  299        // Start a new IME composition.
  300        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  301        assert_eq!(
  302            editor.marked_text_ranges(cx),
  303            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  304        );
  305
  306        // Undoing during an IME composition cancels it.
  307        editor.undo(&Default::default(), window, cx);
  308        assert_eq!(editor.text(cx), "ābcde");
  309        assert_eq!(editor.marked_text_ranges(cx), None);
  310
  311        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  312        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  313        assert_eq!(editor.text(cx), "ābcdè");
  314        assert_eq!(
  315            editor.marked_text_ranges(cx),
  316            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  317        );
  318
  319        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  320        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  321        assert_eq!(editor.text(cx), "ābcdę");
  322        assert_eq!(editor.marked_text_ranges(cx), None);
  323
  324        // Start a new IME composition with multiple cursors.
  325        editor.change_selections(None, window, cx, |s| {
  326            s.select_ranges([
  327                OffsetUtf16(1)..OffsetUtf16(1),
  328                OffsetUtf16(3)..OffsetUtf16(3),
  329                OffsetUtf16(5)..OffsetUtf16(5),
  330            ])
  331        });
  332        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  333        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  334        assert_eq!(
  335            editor.marked_text_ranges(cx),
  336            Some(vec![
  337                OffsetUtf16(0)..OffsetUtf16(3),
  338                OffsetUtf16(4)..OffsetUtf16(7),
  339                OffsetUtf16(8)..OffsetUtf16(11)
  340            ])
  341        );
  342
  343        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  344        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  345        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  346        assert_eq!(
  347            editor.marked_text_ranges(cx),
  348            Some(vec![
  349                OffsetUtf16(1)..OffsetUtf16(2),
  350                OffsetUtf16(5)..OffsetUtf16(6),
  351                OffsetUtf16(9)..OffsetUtf16(10)
  352            ])
  353        );
  354
  355        // Finalize IME composition with multiple cursors.
  356        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  357        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  358        assert_eq!(editor.marked_text_ranges(cx), None);
  359
  360        editor
  361    });
  362}
  363
  364#[gpui::test]
  365fn test_selection_with_mouse(cx: &mut TestAppContext) {
  366    init_test(cx, |_| {});
  367
  368    let editor = cx.add_window(|window, cx| {
  369        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  370        build_editor(buffer, window, cx)
  371    });
  372
  373    _ = editor.update(cx, |editor, window, cx| {
  374        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  375    });
  376    assert_eq!(
  377        editor
  378            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  379            .unwrap(),
  380        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  381    );
  382
  383    _ = editor.update(cx, |editor, window, cx| {
  384        editor.update_selection(
  385            DisplayPoint::new(DisplayRow(3), 3),
  386            0,
  387            gpui::Point::<f32>::default(),
  388            window,
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  398    );
  399
  400    _ = editor.update(cx, |editor, window, cx| {
  401        editor.update_selection(
  402            DisplayPoint::new(DisplayRow(1), 1),
  403            0,
  404            gpui::Point::<f32>::default(),
  405            window,
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |editor, window, cx| {
  418        editor.end_selection(window, cx);
  419        editor.update_selection(
  420            DisplayPoint::new(DisplayRow(3), 3),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            window,
  424            cx,
  425        );
  426    });
  427
  428    assert_eq!(
  429        editor
  430            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  431            .unwrap(),
  432        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  433    );
  434
  435    _ = editor.update(cx, |editor, window, cx| {
  436        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  437        editor.update_selection(
  438            DisplayPoint::new(DisplayRow(0), 0),
  439            0,
  440            gpui::Point::<f32>::default(),
  441            window,
  442            cx,
  443        );
  444    });
  445
  446    assert_eq!(
  447        editor
  448            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  449            .unwrap(),
  450        [
  451            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  452            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  453        ]
  454    );
  455
  456    _ = editor.update(cx, |editor, window, cx| {
  457        editor.end_selection(window, cx);
  458    });
  459
  460    assert_eq!(
  461        editor
  462            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  463            .unwrap(),
  464        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  465    );
  466}
  467
  468#[gpui::test]
  469fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  470    init_test(cx, |_| {});
  471
  472    let editor = cx.add_window(|window, cx| {
  473        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  474        build_editor(buffer, window, cx)
  475    });
  476
  477    _ = editor.update(cx, |editor, window, cx| {
  478        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  479    });
  480
  481    _ = editor.update(cx, |editor, window, cx| {
  482        editor.end_selection(window, cx);
  483    });
  484
  485    _ = editor.update(cx, |editor, window, cx| {
  486        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  487    });
  488
  489    _ = editor.update(cx, |editor, window, cx| {
  490        editor.end_selection(window, cx);
  491    });
  492
  493    assert_eq!(
  494        editor
  495            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  496            .unwrap(),
  497        [
  498            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  499            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  500        ]
  501    );
  502
  503    _ = editor.update(cx, |editor, window, cx| {
  504        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  505    });
  506
  507    _ = editor.update(cx, |editor, window, cx| {
  508        editor.end_selection(window, cx);
  509    });
  510
  511    assert_eq!(
  512        editor
  513            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  514            .unwrap(),
  515        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  516    );
  517}
  518
  519#[gpui::test]
  520fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  521    init_test(cx, |_| {});
  522
  523    let editor = cx.add_window(|window, cx| {
  524        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  525        build_editor(buffer, window, cx)
  526    });
  527
  528    _ = editor.update(cx, |editor, window, cx| {
  529        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  530        assert_eq!(
  531            editor.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  533        );
  534    });
  535
  536    _ = editor.update(cx, |editor, window, cx| {
  537        editor.update_selection(
  538            DisplayPoint::new(DisplayRow(3), 3),
  539            0,
  540            gpui::Point::<f32>::default(),
  541            window,
  542            cx,
  543        );
  544        assert_eq!(
  545            editor.selections.display_ranges(cx),
  546            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  547        );
  548    });
  549
  550    _ = editor.update(cx, |editor, window, cx| {
  551        editor.cancel(&Cancel, window, cx);
  552        editor.update_selection(
  553            DisplayPoint::new(DisplayRow(1), 1),
  554            0,
  555            gpui::Point::<f32>::default(),
  556            window,
  557            cx,
  558        );
  559        assert_eq!(
  560            editor.selections.display_ranges(cx),
  561            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  562        );
  563    });
  564}
  565
  566#[gpui::test]
  567fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  568    init_test(cx, |_| {});
  569
  570    let editor = cx.add_window(|window, cx| {
  571        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  572        build_editor(buffer, window, cx)
  573    });
  574
  575    _ = editor.update(cx, |editor, window, cx| {
  576        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  577        assert_eq!(
  578            editor.selections.display_ranges(cx),
  579            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  580        );
  581
  582        editor.move_down(&Default::default(), window, cx);
  583        assert_eq!(
  584            editor.selections.display_ranges(cx),
  585            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  586        );
  587
  588        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  589        assert_eq!(
  590            editor.selections.display_ranges(cx),
  591            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  592        );
  593
  594        editor.move_up(&Default::default(), window, cx);
  595        assert_eq!(
  596            editor.selections.display_ranges(cx),
  597            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  598        );
  599    });
  600}
  601
  602#[gpui::test]
  603fn test_clone(cx: &mut TestAppContext) {
  604    init_test(cx, |_| {});
  605
  606    let (text, selection_ranges) = marked_text_ranges(
  607        indoc! {"
  608            one
  609            two
  610            threeˇ
  611            four
  612            fiveˇ
  613        "},
  614        true,
  615    );
  616
  617    let editor = cx.add_window(|window, cx| {
  618        let buffer = MultiBuffer::build_simple(&text, cx);
  619        build_editor(buffer, window, cx)
  620    });
  621
  622    _ = editor.update(cx, |editor, window, cx| {
  623        editor.change_selections(None, window, cx, |s| {
  624            s.select_ranges(selection_ranges.clone())
  625        });
  626        editor.fold_creases(
  627            vec![
  628                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  629                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  630            ],
  631            true,
  632            window,
  633            cx,
  634        );
  635    });
  636
  637    let cloned_editor = editor
  638        .update(cx, |editor, _, cx| {
  639            cx.open_window(Default::default(), |window, cx| {
  640                cx.new(|cx| editor.clone(window, cx))
  641            })
  642        })
  643        .unwrap()
  644        .unwrap();
  645
  646    let snapshot = editor
  647        .update(cx, |e, window, cx| e.snapshot(window, cx))
  648        .unwrap();
  649    let cloned_snapshot = cloned_editor
  650        .update(cx, |e, window, cx| e.snapshot(window, cx))
  651        .unwrap();
  652
  653    assert_eq!(
  654        cloned_editor
  655            .update(cx, |e, _, cx| e.display_text(cx))
  656            .unwrap(),
  657        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  658    );
  659    assert_eq!(
  660        cloned_snapshot
  661            .folds_in_range(0..text.len())
  662            .collect::<Vec<_>>(),
  663        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  664    );
  665    assert_set_eq!(
  666        cloned_editor
  667            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  668            .unwrap(),
  669        editor
  670            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  671            .unwrap()
  672    );
  673    assert_set_eq!(
  674        cloned_editor
  675            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  676            .unwrap(),
  677        editor
  678            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  679            .unwrap()
  680    );
  681}
  682
  683#[gpui::test]
  684async fn test_navigation_history(cx: &mut TestAppContext) {
  685    init_test(cx, |_| {});
  686
  687    use workspace::item::Item;
  688
  689    let fs = FakeFs::new(cx.executor());
  690    let project = Project::test(fs, [], cx).await;
  691    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  692    let pane = workspace
  693        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  694        .unwrap();
  695
  696    _ = workspace.update(cx, |_v, window, cx| {
  697        cx.new(|cx| {
  698            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  699            let mut editor = build_editor(buffer.clone(), window, cx);
  700            let handle = cx.entity();
  701            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  702
  703            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  704                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  705            }
  706
  707            // Move the cursor a small distance.
  708            // Nothing is added to the navigation history.
  709            editor.change_selections(None, window, cx, |s| {
  710                s.select_display_ranges([
  711                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  712                ])
  713            });
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  717                ])
  718            });
  719            assert!(pop_history(&mut editor, cx).is_none());
  720
  721            // Move the cursor a large distance.
  722            // The history can jump back to the previous position.
  723            editor.change_selections(None, window, cx, |s| {
  724                s.select_display_ranges([
  725                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  726                ])
  727            });
  728            let nav_entry = pop_history(&mut editor, cx).unwrap();
  729            editor.navigate(nav_entry.data.unwrap(), window, cx);
  730            assert_eq!(nav_entry.item.id(), cx.entity_id());
  731            assert_eq!(
  732                editor.selections.display_ranges(cx),
  733                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  734            );
  735            assert!(pop_history(&mut editor, cx).is_none());
  736
  737            // Move the cursor a small distance via the mouse.
  738            // Nothing is added to the navigation history.
  739            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  740            editor.end_selection(window, cx);
  741            assert_eq!(
  742                editor.selections.display_ranges(cx),
  743                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  744            );
  745            assert!(pop_history(&mut editor, cx).is_none());
  746
  747            // Move the cursor a large distance via the mouse.
  748            // The history can jump back to the previous position.
  749            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  750            editor.end_selection(window, cx);
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  754            );
  755            let nav_entry = pop_history(&mut editor, cx).unwrap();
  756            editor.navigate(nav_entry.data.unwrap(), window, cx);
  757            assert_eq!(nav_entry.item.id(), cx.entity_id());
  758            assert_eq!(
  759                editor.selections.display_ranges(cx),
  760                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  761            );
  762            assert!(pop_history(&mut editor, cx).is_none());
  763
  764            // Set scroll position to check later
  765            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  766            let original_scroll_position = editor.scroll_manager.anchor();
  767
  768            // Jump to the end of the document and adjust scroll
  769            editor.move_to_end(&MoveToEnd, window, cx);
  770            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  771            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  772
  773            let nav_entry = pop_history(&mut editor, cx).unwrap();
  774            editor.navigate(nav_entry.data.unwrap(), window, cx);
  775            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  776
  777            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  778            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  779            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  780            let invalid_point = Point::new(9999, 0);
  781            editor.navigate(
  782                Box::new(NavigationData {
  783                    cursor_anchor: invalid_anchor,
  784                    cursor_position: invalid_point,
  785                    scroll_anchor: ScrollAnchor {
  786                        anchor: invalid_anchor,
  787                        offset: Default::default(),
  788                    },
  789                    scroll_top_row: invalid_point.row,
  790                }),
  791                window,
  792                cx,
  793            );
  794            assert_eq!(
  795                editor.selections.display_ranges(cx),
  796                &[editor.max_point(cx)..editor.max_point(cx)]
  797            );
  798            assert_eq!(
  799                editor.scroll_position(cx),
  800                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  801            );
  802
  803            editor
  804        })
  805    });
  806}
  807
  808#[gpui::test]
  809fn test_cancel(cx: &mut TestAppContext) {
  810    init_test(cx, |_| {});
  811
  812    let editor = cx.add_window(|window, cx| {
  813        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  814        build_editor(buffer, window, cx)
  815    });
  816
  817    _ = editor.update(cx, |editor, window, cx| {
  818        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  819        editor.update_selection(
  820            DisplayPoint::new(DisplayRow(1), 1),
  821            0,
  822            gpui::Point::<f32>::default(),
  823            window,
  824            cx,
  825        );
  826        editor.end_selection(window, cx);
  827
  828        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  829        editor.update_selection(
  830            DisplayPoint::new(DisplayRow(0), 3),
  831            0,
  832            gpui::Point::<f32>::default(),
  833            window,
  834            cx,
  835        );
  836        editor.end_selection(window, cx);
  837        assert_eq!(
  838            editor.selections.display_ranges(cx),
  839            [
  840                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  841                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  842            ]
  843        );
  844    });
  845
  846    _ = editor.update(cx, |editor, window, cx| {
  847        editor.cancel(&Cancel, window, cx);
  848        assert_eq!(
  849            editor.selections.display_ranges(cx),
  850            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  851        );
  852    });
  853
  854    _ = editor.update(cx, |editor, window, cx| {
  855        editor.cancel(&Cancel, window, cx);
  856        assert_eq!(
  857            editor.selections.display_ranges(cx),
  858            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  859        );
  860    });
  861}
  862
  863#[gpui::test]
  864fn test_fold_action(cx: &mut TestAppContext) {
  865    init_test(cx, |_| {});
  866
  867    let editor = cx.add_window(|window, cx| {
  868        let buffer = MultiBuffer::build_simple(
  869            &"
  870                impl Foo {
  871                    // Hello!
  872
  873                    fn a() {
  874                        1
  875                    }
  876
  877                    fn b() {
  878                        2
  879                    }
  880
  881                    fn c() {
  882                        3
  883                    }
  884                }
  885            "
  886            .unindent(),
  887            cx,
  888        );
  889        build_editor(buffer.clone(), window, cx)
  890    });
  891
  892    _ = editor.update(cx, |editor, window, cx| {
  893        editor.change_selections(None, window, cx, |s| {
  894            s.select_display_ranges([
  895                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  896            ]);
  897        });
  898        editor.fold(&Fold, window, cx);
  899        assert_eq!(
  900            editor.display_text(cx),
  901            "
  902                impl Foo {
  903                    // Hello!
  904
  905                    fn a() {
  906                        1
  907                    }
  908
  909                    fn b() {⋯
  910                    }
  911
  912                    fn c() {⋯
  913                    }
  914                }
  915            "
  916            .unindent(),
  917        );
  918
  919        editor.fold(&Fold, window, cx);
  920        assert_eq!(
  921            editor.display_text(cx),
  922            "
  923                impl Foo {⋯
  924                }
  925            "
  926            .unindent(),
  927        );
  928
  929        editor.unfold_lines(&UnfoldLines, window, cx);
  930        assert_eq!(
  931            editor.display_text(cx),
  932            "
  933                impl Foo {
  934                    // Hello!
  935
  936                    fn a() {
  937                        1
  938                    }
  939
  940                    fn b() {⋯
  941                    }
  942
  943                    fn c() {⋯
  944                    }
  945                }
  946            "
  947            .unindent(),
  948        );
  949
  950        editor.unfold_lines(&UnfoldLines, window, cx);
  951        assert_eq!(
  952            editor.display_text(cx),
  953            editor.buffer.read(cx).read(cx).text()
  954        );
  955    });
  956}
  957
  958#[gpui::test]
  959fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  960    init_test(cx, |_| {});
  961
  962    let editor = cx.add_window(|window, cx| {
  963        let buffer = MultiBuffer::build_simple(
  964            &"
  965                class Foo:
  966                    # Hello!
  967
  968                    def a():
  969                        print(1)
  970
  971                    def b():
  972                        print(2)
  973
  974                    def c():
  975                        print(3)
  976            "
  977            .unindent(),
  978            cx,
  979        );
  980        build_editor(buffer.clone(), window, cx)
  981    });
  982
  983    _ = editor.update(cx, |editor, window, cx| {
  984        editor.change_selections(None, window, cx, |s| {
  985            s.select_display_ranges([
  986                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  987            ]);
  988        });
  989        editor.fold(&Fold, window, cx);
  990        assert_eq!(
  991            editor.display_text(cx),
  992            "
  993                class Foo:
  994                    # Hello!
  995
  996                    def a():
  997                        print(1)
  998
  999                    def b():⋯
 1000
 1001                    def c():⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.fold(&Fold, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:⋯
 1011            "
 1012            .unindent(),
 1013        );
 1014
 1015        editor.unfold_lines(&UnfoldLines, window, cx);
 1016        assert_eq!(
 1017            editor.display_text(cx),
 1018            "
 1019                class Foo:
 1020                    # Hello!
 1021
 1022                    def a():
 1023                        print(1)
 1024
 1025                    def b():⋯
 1026
 1027                    def c():⋯
 1028            "
 1029            .unindent(),
 1030        );
 1031
 1032        editor.unfold_lines(&UnfoldLines, window, cx);
 1033        assert_eq!(
 1034            editor.display_text(cx),
 1035            editor.buffer.read(cx).read(cx).text()
 1036        );
 1037    });
 1038}
 1039
 1040#[gpui::test]
 1041fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1042    init_test(cx, |_| {});
 1043
 1044    let editor = cx.add_window(|window, cx| {
 1045        let buffer = MultiBuffer::build_simple(
 1046            &"
 1047                class Foo:
 1048                    # Hello!
 1049
 1050                    def a():
 1051                        print(1)
 1052
 1053                    def b():
 1054                        print(2)
 1055
 1056
 1057                    def c():
 1058                        print(3)
 1059
 1060
 1061            "
 1062            .unindent(),
 1063            cx,
 1064        );
 1065        build_editor(buffer.clone(), window, cx)
 1066    });
 1067
 1068    _ = editor.update(cx, |editor, window, cx| {
 1069        editor.change_selections(None, window, cx, |s| {
 1070            s.select_display_ranges([
 1071                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1072            ]);
 1073        });
 1074        editor.fold(&Fold, window, cx);
 1075        assert_eq!(
 1076            editor.display_text(cx),
 1077            "
 1078                class Foo:
 1079                    # Hello!
 1080
 1081                    def a():
 1082                        print(1)
 1083
 1084                    def b():⋯
 1085
 1086
 1087                    def c():⋯
 1088
 1089
 1090            "
 1091            .unindent(),
 1092        );
 1093
 1094        editor.fold(&Fold, window, cx);
 1095        assert_eq!(
 1096            editor.display_text(cx),
 1097            "
 1098                class Foo:⋯
 1099
 1100
 1101            "
 1102            .unindent(),
 1103        );
 1104
 1105        editor.unfold_lines(&UnfoldLines, window, cx);
 1106        assert_eq!(
 1107            editor.display_text(cx),
 1108            "
 1109                class Foo:
 1110                    # Hello!
 1111
 1112                    def a():
 1113                        print(1)
 1114
 1115                    def b():⋯
 1116
 1117
 1118                    def c():⋯
 1119
 1120
 1121            "
 1122            .unindent(),
 1123        );
 1124
 1125        editor.unfold_lines(&UnfoldLines, window, cx);
 1126        assert_eq!(
 1127            editor.display_text(cx),
 1128            editor.buffer.read(cx).read(cx).text()
 1129        );
 1130    });
 1131}
 1132
 1133#[gpui::test]
 1134fn test_fold_at_level(cx: &mut TestAppContext) {
 1135    init_test(cx, |_| {});
 1136
 1137    let editor = cx.add_window(|window, cx| {
 1138        let buffer = MultiBuffer::build_simple(
 1139            &"
 1140                class Foo:
 1141                    # Hello!
 1142
 1143                    def a():
 1144                        print(1)
 1145
 1146                    def b():
 1147                        print(2)
 1148
 1149
 1150                class Bar:
 1151                    # World!
 1152
 1153                    def a():
 1154                        print(1)
 1155
 1156                    def b():
 1157                        print(2)
 1158
 1159
 1160            "
 1161            .unindent(),
 1162            cx,
 1163        );
 1164        build_editor(buffer.clone(), window, cx)
 1165    });
 1166
 1167    _ = editor.update(cx, |editor, window, cx| {
 1168        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1169        assert_eq!(
 1170            editor.display_text(cx),
 1171            "
 1172                class Foo:
 1173                    # Hello!
 1174
 1175                    def a():⋯
 1176
 1177                    def b():⋯
 1178
 1179
 1180                class Bar:
 1181                    # World!
 1182
 1183                    def a():⋯
 1184
 1185                    def b():⋯
 1186
 1187
 1188            "
 1189            .unindent(),
 1190        );
 1191
 1192        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1193        assert_eq!(
 1194            editor.display_text(cx),
 1195            "
 1196                class Foo:⋯
 1197
 1198
 1199                class Bar:⋯
 1200
 1201
 1202            "
 1203            .unindent(),
 1204        );
 1205
 1206        editor.unfold_all(&UnfoldAll, window, cx);
 1207        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1208        assert_eq!(
 1209            editor.display_text(cx),
 1210            "
 1211                class Foo:
 1212                    # Hello!
 1213
 1214                    def a():
 1215                        print(1)
 1216
 1217                    def b():
 1218                        print(2)
 1219
 1220
 1221                class Bar:
 1222                    # World!
 1223
 1224                    def a():
 1225                        print(1)
 1226
 1227                    def b():
 1228                        print(2)
 1229
 1230
 1231            "
 1232            .unindent(),
 1233        );
 1234
 1235        assert_eq!(
 1236            editor.display_text(cx),
 1237            editor.buffer.read(cx).read(cx).text()
 1238        );
 1239    });
 1240}
 1241
 1242#[gpui::test]
 1243fn test_move_cursor(cx: &mut TestAppContext) {
 1244    init_test(cx, |_| {});
 1245
 1246    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1247    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1248
 1249    buffer.update(cx, |buffer, cx| {
 1250        buffer.edit(
 1251            vec![
 1252                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1253                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1254            ],
 1255            None,
 1256            cx,
 1257        );
 1258    });
 1259    _ = editor.update(cx, |editor, window, cx| {
 1260        assert_eq!(
 1261            editor.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1263        );
 1264
 1265        editor.move_down(&MoveDown, window, cx);
 1266        assert_eq!(
 1267            editor.selections.display_ranges(cx),
 1268            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1269        );
 1270
 1271        editor.move_right(&MoveRight, window, cx);
 1272        assert_eq!(
 1273            editor.selections.display_ranges(cx),
 1274            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1275        );
 1276
 1277        editor.move_left(&MoveLeft, window, cx);
 1278        assert_eq!(
 1279            editor.selections.display_ranges(cx),
 1280            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1281        );
 1282
 1283        editor.move_up(&MoveUp, window, cx);
 1284        assert_eq!(
 1285            editor.selections.display_ranges(cx),
 1286            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1287        );
 1288
 1289        editor.move_to_end(&MoveToEnd, window, cx);
 1290        assert_eq!(
 1291            editor.selections.display_ranges(cx),
 1292            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1293        );
 1294
 1295        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1296        assert_eq!(
 1297            editor.selections.display_ranges(cx),
 1298            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1299        );
 1300
 1301        editor.change_selections(None, window, cx, |s| {
 1302            s.select_display_ranges([
 1303                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1304            ]);
 1305        });
 1306        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1307        assert_eq!(
 1308            editor.selections.display_ranges(cx),
 1309            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1310        );
 1311
 1312        editor.select_to_end(&SelectToEnd, window, cx);
 1313        assert_eq!(
 1314            editor.selections.display_ranges(cx),
 1315            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1316        );
 1317    });
 1318}
 1319
 1320#[gpui::test]
 1321fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1322    init_test(cx, |_| {});
 1323
 1324    let editor = cx.add_window(|window, cx| {
 1325        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1326        build_editor(buffer.clone(), window, cx)
 1327    });
 1328
 1329    assert_eq!('🟥'.len_utf8(), 4);
 1330    assert_eq!('α'.len_utf8(), 2);
 1331
 1332    _ = editor.update(cx, |editor, window, cx| {
 1333        editor.fold_creases(
 1334            vec![
 1335                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1338            ],
 1339            true,
 1340            window,
 1341            cx,
 1342        );
 1343        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1344
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧".len())]
 1354        );
 1355        editor.move_right(&MoveRight, window, cx);
 1356        assert_eq!(
 1357            editor.selections.display_ranges(cx),
 1358            &[empty_range(0, "🟥🟧⋯".len())]
 1359        );
 1360
 1361        editor.move_down(&MoveDown, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "ab".len())]
 1375        );
 1376        editor.move_left(&MoveLeft, window, cx);
 1377        assert_eq!(
 1378            editor.selections.display_ranges(cx),
 1379            &[empty_range(1, "a".len())]
 1380        );
 1381
 1382        editor.move_down(&MoveDown, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "α".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯".len())]
 1396        );
 1397        editor.move_right(&MoveRight, window, cx);
 1398        assert_eq!(
 1399            editor.selections.display_ranges(cx),
 1400            &[empty_range(2, "αβ⋯ε".len())]
 1401        );
 1402
 1403        editor.move_up(&MoveUp, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(1, "ab⋯e".len())]
 1407        );
 1408        editor.move_down(&MoveDown, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(2, "αβ⋯ε".len())]
 1412        );
 1413        editor.move_up(&MoveUp, window, cx);
 1414        assert_eq!(
 1415            editor.selections.display_ranges(cx),
 1416            &[empty_range(1, "ab⋯e".len())]
 1417        );
 1418
 1419        editor.move_up(&MoveUp, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥🟧".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "🟥".len())]
 1428        );
 1429        editor.move_left(&MoveLeft, window, cx);
 1430        assert_eq!(
 1431            editor.selections.display_ranges(cx),
 1432            &[empty_range(0, "".len())]
 1433        );
 1434    });
 1435}
 1436
 1437#[gpui::test]
 1438fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1439    init_test(cx, |_| {});
 1440
 1441    let editor = cx.add_window(|window, cx| {
 1442        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1443        build_editor(buffer.clone(), window, cx)
 1444    });
 1445    _ = editor.update(cx, |editor, window, cx| {
 1446        editor.change_selections(None, window, cx, |s| {
 1447            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1448        });
 1449
 1450        // moving above start of document should move selection to start of document,
 1451        // but the next move down should still be at the original goal_x
 1452        editor.move_up(&MoveUp, window, cx);
 1453        assert_eq!(
 1454            editor.selections.display_ranges(cx),
 1455            &[empty_range(0, "".len())]
 1456        );
 1457
 1458        editor.move_down(&MoveDown, window, cx);
 1459        assert_eq!(
 1460            editor.selections.display_ranges(cx),
 1461            &[empty_range(1, "abcd".len())]
 1462        );
 1463
 1464        editor.move_down(&MoveDown, window, cx);
 1465        assert_eq!(
 1466            editor.selections.display_ranges(cx),
 1467            &[empty_range(2, "αβγ".len())]
 1468        );
 1469
 1470        editor.move_down(&MoveDown, window, cx);
 1471        assert_eq!(
 1472            editor.selections.display_ranges(cx),
 1473            &[empty_range(3, "abcd".len())]
 1474        );
 1475
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1480        );
 1481
 1482        // moving past end of document should not change goal_x
 1483        editor.move_down(&MoveDown, window, cx);
 1484        assert_eq!(
 1485            editor.selections.display_ranges(cx),
 1486            &[empty_range(5, "".len())]
 1487        );
 1488
 1489        editor.move_down(&MoveDown, window, cx);
 1490        assert_eq!(
 1491            editor.selections.display_ranges(cx),
 1492            &[empty_range(5, "".len())]
 1493        );
 1494
 1495        editor.move_up(&MoveUp, window, cx);
 1496        assert_eq!(
 1497            editor.selections.display_ranges(cx),
 1498            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1499        );
 1500
 1501        editor.move_up(&MoveUp, window, cx);
 1502        assert_eq!(
 1503            editor.selections.display_ranges(cx),
 1504            &[empty_range(3, "abcd".len())]
 1505        );
 1506
 1507        editor.move_up(&MoveUp, window, cx);
 1508        assert_eq!(
 1509            editor.selections.display_ranges(cx),
 1510            &[empty_range(2, "αβγ".len())]
 1511        );
 1512    });
 1513}
 1514
 1515#[gpui::test]
 1516fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1517    init_test(cx, |_| {});
 1518    let move_to_beg = MoveToBeginningOfLine {
 1519        stop_at_soft_wraps: true,
 1520        stop_at_indent: true,
 1521    };
 1522
 1523    let delete_to_beg = DeleteToBeginningOfLine {
 1524        stop_at_indent: false,
 1525    };
 1526
 1527    let move_to_end = MoveToEndOfLine {
 1528        stop_at_soft_wraps: true,
 1529    };
 1530
 1531    let editor = cx.add_window(|window, cx| {
 1532        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1533        build_editor(buffer, window, cx)
 1534    });
 1535    _ = editor.update(cx, |editor, window, cx| {
 1536        editor.change_selections(None, window, cx, |s| {
 1537            s.select_display_ranges([
 1538                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1539                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1540            ]);
 1541        });
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1572                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1573            ]
 1574        );
 1575    });
 1576
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    // Moving to the end of line again is a no-op.
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_to_end_of_line(&move_to_end, window, cx);
 1591        assert_eq!(
 1592            editor.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1595                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = editor.update(cx, |editor, window, cx| {
 1601        editor.move_left(&MoveLeft, window, cx);
 1602        editor.select_to_beginning_of_line(
 1603            &SelectToBeginningOfLine {
 1604                stop_at_soft_wraps: true,
 1605                stop_at_indent: true,
 1606            },
 1607            window,
 1608            cx,
 1609        );
 1610        assert_eq!(
 1611            editor.selections.display_ranges(cx),
 1612            &[
 1613                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1614                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1615            ]
 1616        );
 1617    });
 1618
 1619    _ = editor.update(cx, |editor, window, cx| {
 1620        editor.select_to_beginning_of_line(
 1621            &SelectToBeginningOfLine {
 1622                stop_at_soft_wraps: true,
 1623                stop_at_indent: true,
 1624            },
 1625            window,
 1626            cx,
 1627        );
 1628        assert_eq!(
 1629            editor.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636
 1637    _ = editor.update(cx, |editor, window, cx| {
 1638        editor.select_to_beginning_of_line(
 1639            &SelectToBeginningOfLine {
 1640                stop_at_soft_wraps: true,
 1641                stop_at_indent: true,
 1642            },
 1643            window,
 1644            cx,
 1645        );
 1646        assert_eq!(
 1647            editor.selections.display_ranges(cx),
 1648            &[
 1649                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1650                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1651            ]
 1652        );
 1653    });
 1654
 1655    _ = editor.update(cx, |editor, window, cx| {
 1656        editor.select_to_end_of_line(
 1657            &SelectToEndOfLine {
 1658                stop_at_soft_wraps: true,
 1659            },
 1660            window,
 1661            cx,
 1662        );
 1663        assert_eq!(
 1664            editor.selections.display_ranges(cx),
 1665            &[
 1666                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1667                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1668            ]
 1669        );
 1670    });
 1671
 1672    _ = editor.update(cx, |editor, window, cx| {
 1673        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1674        assert_eq!(editor.display_text(cx), "ab\n  de");
 1675        assert_eq!(
 1676            editor.selections.display_ranges(cx),
 1677            &[
 1678                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1679                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1680            ]
 1681        );
 1682    });
 1683
 1684    _ = editor.update(cx, |editor, window, cx| {
 1685        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1686        assert_eq!(editor.display_text(cx), "\n");
 1687        assert_eq!(
 1688            editor.selections.display_ranges(cx),
 1689            &[
 1690                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1691                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1692            ]
 1693        );
 1694    });
 1695}
 1696
 1697#[gpui::test]
 1698fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1699    init_test(cx, |_| {});
 1700    let move_to_beg = MoveToBeginningOfLine {
 1701        stop_at_soft_wraps: false,
 1702        stop_at_indent: false,
 1703    };
 1704
 1705    let move_to_end = MoveToEndOfLine {
 1706        stop_at_soft_wraps: false,
 1707    };
 1708
 1709    let editor = cx.add_window(|window, cx| {
 1710        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1711        build_editor(buffer, window, cx)
 1712    });
 1713
 1714    _ = editor.update(cx, |editor, window, cx| {
 1715        editor.set_wrap_width(Some(140.0.into()), cx);
 1716
 1717        // We expect the following lines after wrapping
 1718        // ```
 1719        // thequickbrownfox
 1720        // jumpedoverthelazydo
 1721        // gs
 1722        // ```
 1723        // The final `gs` was soft-wrapped onto a new line.
 1724        assert_eq!(
 1725            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1726            editor.display_text(cx),
 1727        );
 1728
 1729        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1730        // Start the cursor at the `k` on the first line
 1731        editor.change_selections(None, window, cx, |s| {
 1732            s.select_display_ranges([
 1733                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1734            ]);
 1735        });
 1736
 1737        // Moving to the beginning of the line should put us at the beginning of the line.
 1738        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1739        assert_eq!(
 1740            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1741            editor.selections.display_ranges(cx)
 1742        );
 1743
 1744        // Moving to the end of the line should put us at the end of the line.
 1745        editor.move_to_end_of_line(&move_to_end, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1752        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1753        editor.change_selections(None, window, cx, |s| {
 1754            s.select_display_ranges([
 1755                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1756            ]);
 1757        });
 1758
 1759        // Moving to the beginning of the line should put us at the start of the second line of
 1760        // display text, i.e., the `j`.
 1761        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the beginning of the line again should be a no-op.
 1768        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773
 1774        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1775        // next display line.
 1776        editor.move_to_end_of_line(&move_to_end, window, cx);
 1777        assert_eq!(
 1778            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1779            editor.selections.display_ranges(cx)
 1780        );
 1781
 1782        // Moving to the end of the line again should be a no-op.
 1783        editor.move_to_end_of_line(&move_to_end, window, cx);
 1784        assert_eq!(
 1785            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1786            editor.selections.display_ranges(cx)
 1787        );
 1788    });
 1789}
 1790
 1791#[gpui::test]
 1792fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1793    init_test(cx, |_| {});
 1794
 1795    let move_to_beg = MoveToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let select_to_beg = SelectToBeginningOfLine {
 1801        stop_at_soft_wraps: true,
 1802        stop_at_indent: true,
 1803    };
 1804
 1805    let delete_to_beg = DeleteToBeginningOfLine {
 1806        stop_at_indent: true,
 1807    };
 1808
 1809    let move_to_end = MoveToEndOfLine {
 1810        stop_at_soft_wraps: false,
 1811    };
 1812
 1813    let editor = cx.add_window(|window, cx| {
 1814        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1815        build_editor(buffer, window, cx)
 1816    });
 1817
 1818    _ = editor.update(cx, |editor, window, cx| {
 1819        editor.change_selections(None, window, cx, |s| {
 1820            s.select_display_ranges([
 1821                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1822                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1823            ]);
 1824        });
 1825
 1826        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1827        // and the second cursor at the first non-whitespace character in the line.
 1828        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1829        assert_eq!(
 1830            editor.selections.display_ranges(cx),
 1831            &[
 1832                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1834            ]
 1835        );
 1836
 1837        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1838        // and should move the second cursor to the beginning of the line.
 1839        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1840        assert_eq!(
 1841            editor.selections.display_ranges(cx),
 1842            &[
 1843                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1844                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1845            ]
 1846        );
 1847
 1848        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1849        // and should move the second cursor back to the first non-whitespace character in the line.
 1850        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1851        assert_eq!(
 1852            editor.selections.display_ranges(cx),
 1853            &[
 1854                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1855                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1856            ]
 1857        );
 1858
 1859        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1860        // and to the first non-whitespace character in the line for the second cursor.
 1861        editor.move_to_end_of_line(&move_to_end, window, cx);
 1862        editor.move_left(&MoveLeft, window, cx);
 1863        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1864        assert_eq!(
 1865            editor.selections.display_ranges(cx),
 1866            &[
 1867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1868                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1869            ]
 1870        );
 1871
 1872        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1873        // and should select to the beginning of the line for the second cursor.
 1874        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[
 1878                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1879                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1880            ]
 1881        );
 1882
 1883        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1884        // and should delete to the first non-whitespace character in the line for the second cursor.
 1885        editor.move_to_end_of_line(&move_to_end, window, cx);
 1886        editor.move_left(&MoveLeft, window, cx);
 1887        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1888        assert_eq!(editor.text(cx), "c\n  f");
 1889    });
 1890}
 1891
 1892#[gpui::test]
 1893fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1894    init_test(cx, |_| {});
 1895
 1896    let editor = cx.add_window(|window, cx| {
 1897        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1898        build_editor(buffer, window, cx)
 1899    });
 1900    _ = editor.update(cx, |editor, window, cx| {
 1901        editor.change_selections(None, window, cx, |s| {
 1902            s.select_display_ranges([
 1903                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1904                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1905            ])
 1906        });
 1907
 1908        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1909        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1918        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1921        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1924        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1925
 1926        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1927        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1928
 1929        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1930        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1931
 1932        editor.move_right(&MoveRight, window, cx);
 1933        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1934        assert_selection_ranges(
 1935            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1936            editor,
 1937            cx,
 1938        );
 1939
 1940        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1941        assert_selection_ranges(
 1942            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1943            editor,
 1944            cx,
 1945        );
 1946
 1947        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1948        assert_selection_ranges(
 1949            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1950            editor,
 1951            cx,
 1952        );
 1953    });
 1954}
 1955
 1956#[gpui::test]
 1957fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1958    init_test(cx, |_| {});
 1959
 1960    let editor = cx.add_window(|window, cx| {
 1961        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1962        build_editor(buffer, window, cx)
 1963    });
 1964
 1965    _ = editor.update(cx, |editor, window, cx| {
 1966        editor.set_wrap_width(Some(140.0.into()), cx);
 1967        assert_eq!(
 1968            editor.display_text(cx),
 1969            "use one::{\n    two::three::\n    four::five\n};"
 1970        );
 1971
 1972        editor.change_selections(None, window, cx, |s| {
 1973            s.select_display_ranges([
 1974                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1975            ]);
 1976        });
 1977
 1978        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1979        assert_eq!(
 1980            editor.selections.display_ranges(cx),
 1981            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1982        );
 1983
 1984        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1985        assert_eq!(
 1986            editor.selections.display_ranges(cx),
 1987            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1988        );
 1989
 1990        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1991        assert_eq!(
 1992            editor.selections.display_ranges(cx),
 1993            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1994        );
 1995
 1996        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1997        assert_eq!(
 1998            editor.selections.display_ranges(cx),
 1999            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2000        );
 2001
 2002        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2003        assert_eq!(
 2004            editor.selections.display_ranges(cx),
 2005            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2006        );
 2007
 2008        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2009        assert_eq!(
 2010            editor.selections.display_ranges(cx),
 2011            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2012        );
 2013    });
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2029
 2030    cx.set_state(
 2031        &r#"ˇone
 2032        two
 2033
 2034        three
 2035        fourˇ
 2036        five
 2037
 2038        six"#
 2039            .unindent(),
 2040    );
 2041
 2042    cx.update_editor(|editor, window, cx| {
 2043        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2044    });
 2045    cx.assert_editor_state(
 2046        &r#"one
 2047        two
 2048        ˇ
 2049        three
 2050        four
 2051        five
 2052        ˇ
 2053        six"#
 2054            .unindent(),
 2055    );
 2056
 2057    cx.update_editor(|editor, window, cx| {
 2058        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2059    });
 2060    cx.assert_editor_state(
 2061        &r#"one
 2062        two
 2063
 2064        three
 2065        four
 2066        five
 2067        ˇ
 2068        sixˇ"#
 2069            .unindent(),
 2070    );
 2071
 2072    cx.update_editor(|editor, window, cx| {
 2073        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2074    });
 2075    cx.assert_editor_state(
 2076        &r#"one
 2077        two
 2078
 2079        three
 2080        four
 2081        five
 2082
 2083        sixˇ"#
 2084            .unindent(),
 2085    );
 2086
 2087    cx.update_editor(|editor, window, cx| {
 2088        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2089    });
 2090    cx.assert_editor_state(
 2091        &r#"one
 2092        two
 2093
 2094        three
 2095        four
 2096        five
 2097        ˇ
 2098        six"#
 2099            .unindent(),
 2100    );
 2101
 2102    cx.update_editor(|editor, window, cx| {
 2103        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2104    });
 2105    cx.assert_editor_state(
 2106        &r#"one
 2107        two
 2108        ˇ
 2109        three
 2110        four
 2111        five
 2112
 2113        six"#
 2114            .unindent(),
 2115    );
 2116
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2119    });
 2120    cx.assert_editor_state(
 2121        &r#"ˇone
 2122        two
 2123
 2124        three
 2125        four
 2126        five
 2127
 2128        six"#
 2129            .unindent(),
 2130    );
 2131}
 2132
 2133#[gpui::test]
 2134async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136    let mut cx = EditorTestContext::new(cx).await;
 2137    let line_height = cx.editor(|editor, window, _| {
 2138        editor
 2139            .style()
 2140            .unwrap()
 2141            .text
 2142            .line_height_in_pixels(window.rem_size())
 2143    });
 2144    let window = cx.window;
 2145    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2146
 2147    cx.set_state(
 2148        r#"ˇone
 2149        two
 2150        three
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#,
 2159    );
 2160
 2161    cx.update_editor(|editor, window, cx| {
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 0.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 3.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 6.)
 2175        );
 2176        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2177        assert_eq!(
 2178            editor.snapshot(window, cx).scroll_position(),
 2179            gpui::Point::new(0., 3.)
 2180        );
 2181
 2182        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 1.)
 2186        );
 2187        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2188        assert_eq!(
 2189            editor.snapshot(window, cx).scroll_position(),
 2190            gpui::Point::new(0., 3.)
 2191        );
 2192    });
 2193}
 2194
 2195#[gpui::test]
 2196async fn test_autoscroll(cx: &mut TestAppContext) {
 2197    init_test(cx, |_| {});
 2198    let mut cx = EditorTestContext::new(cx).await;
 2199
 2200    let line_height = cx.update_editor(|editor, window, cx| {
 2201        editor.set_vertical_scroll_margin(2, cx);
 2202        editor
 2203            .style()
 2204            .unwrap()
 2205            .text
 2206            .line_height_in_pixels(window.rem_size())
 2207    });
 2208    let window = cx.window;
 2209    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2210
 2211    cx.set_state(
 2212        r#"ˇone
 2213            two
 2214            three
 2215            four
 2216            five
 2217            six
 2218            seven
 2219            eight
 2220            nine
 2221            ten
 2222        "#,
 2223    );
 2224    cx.update_editor(|editor, window, cx| {
 2225        assert_eq!(
 2226            editor.snapshot(window, cx).scroll_position(),
 2227            gpui::Point::new(0., 0.0)
 2228        );
 2229    });
 2230
 2231    // Add a cursor below the visible area. Since both cursors cannot fit
 2232    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2233    // allows the vertical scroll margin below that cursor.
 2234    cx.update_editor(|editor, window, cx| {
 2235        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2236            selections.select_ranges([
 2237                Point::new(0, 0)..Point::new(0, 0),
 2238                Point::new(6, 0)..Point::new(6, 0),
 2239            ]);
 2240        })
 2241    });
 2242    cx.update_editor(|editor, window, cx| {
 2243        assert_eq!(
 2244            editor.snapshot(window, cx).scroll_position(),
 2245            gpui::Point::new(0., 3.0)
 2246        );
 2247    });
 2248
 2249    // Move down. The editor cursor scrolls down to track the newest cursor.
 2250    cx.update_editor(|editor, window, cx| {
 2251        editor.move_down(&Default::default(), window, cx);
 2252    });
 2253    cx.update_editor(|editor, window, cx| {
 2254        assert_eq!(
 2255            editor.snapshot(window, cx).scroll_position(),
 2256            gpui::Point::new(0., 4.0)
 2257        );
 2258    });
 2259
 2260    // Add a cursor above the visible area. Since both cursors fit on screen,
 2261    // the editor scrolls to show both.
 2262    cx.update_editor(|editor, window, cx| {
 2263        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2264            selections.select_ranges([
 2265                Point::new(1, 0)..Point::new(1, 0),
 2266                Point::new(6, 0)..Point::new(6, 0),
 2267            ]);
 2268        })
 2269    });
 2270    cx.update_editor(|editor, window, cx| {
 2271        assert_eq!(
 2272            editor.snapshot(window, cx).scroll_position(),
 2273            gpui::Point::new(0., 1.0)
 2274        );
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281    let mut cx = EditorTestContext::new(cx).await;
 2282
 2283    let line_height = cx.editor(|editor, window, _cx| {
 2284        editor
 2285            .style()
 2286            .unwrap()
 2287            .text
 2288            .line_height_in_pixels(window.rem_size())
 2289    });
 2290    let window = cx.window;
 2291    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2292    cx.set_state(
 2293        &r#"
 2294        ˇone
 2295        two
 2296        threeˇ
 2297        four
 2298        five
 2299        six
 2300        seven
 2301        eight
 2302        nine
 2303        ten
 2304        "#
 2305        .unindent(),
 2306    );
 2307
 2308    cx.update_editor(|editor, window, cx| {
 2309        editor.move_page_down(&MovePageDown::default(), window, cx)
 2310    });
 2311    cx.assert_editor_state(
 2312        &r#"
 2313        one
 2314        two
 2315        three
 2316        ˇfour
 2317        five
 2318        sixˇ
 2319        seven
 2320        eight
 2321        nine
 2322        ten
 2323        "#
 2324        .unindent(),
 2325    );
 2326
 2327    cx.update_editor(|editor, window, cx| {
 2328        editor.move_page_down(&MovePageDown::default(), window, cx)
 2329    });
 2330    cx.assert_editor_state(
 2331        &r#"
 2332        one
 2333        two
 2334        three
 2335        four
 2336        five
 2337        six
 2338        ˇseven
 2339        eight
 2340        nineˇ
 2341        ten
 2342        "#
 2343        .unindent(),
 2344    );
 2345
 2346    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2347    cx.assert_editor_state(
 2348        &r#"
 2349        one
 2350        two
 2351        three
 2352        ˇfour
 2353        five
 2354        sixˇ
 2355        seven
 2356        eight
 2357        nine
 2358        ten
 2359        "#
 2360        .unindent(),
 2361    );
 2362
 2363    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2364    cx.assert_editor_state(
 2365        &r#"
 2366        ˇone
 2367        two
 2368        threeˇ
 2369        four
 2370        five
 2371        six
 2372        seven
 2373        eight
 2374        nine
 2375        ten
 2376        "#
 2377        .unindent(),
 2378    );
 2379
 2380    // Test select collapsing
 2381    cx.update_editor(|editor, window, cx| {
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385    });
 2386    cx.assert_editor_state(
 2387        &r#"
 2388        one
 2389        two
 2390        three
 2391        four
 2392        five
 2393        six
 2394        seven
 2395        eight
 2396        nine
 2397        ˇten
 2398        ˇ"#
 2399        .unindent(),
 2400    );
 2401}
 2402
 2403#[gpui::test]
 2404async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2405    init_test(cx, |_| {});
 2406    let mut cx = EditorTestContext::new(cx).await;
 2407    cx.set_state("one «two threeˇ» four");
 2408    cx.update_editor(|editor, window, cx| {
 2409        editor.delete_to_beginning_of_line(
 2410            &DeleteToBeginningOfLine {
 2411                stop_at_indent: false,
 2412            },
 2413            window,
 2414            cx,
 2415        );
 2416        assert_eq!(editor.text(cx), " four");
 2417    });
 2418}
 2419
 2420#[gpui::test]
 2421fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2422    init_test(cx, |_| {});
 2423
 2424    let editor = cx.add_window(|window, cx| {
 2425        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2426        build_editor(buffer.clone(), window, cx)
 2427    });
 2428
 2429    _ = editor.update(cx, |editor, window, cx| {
 2430        editor.change_selections(None, window, cx, |s| {
 2431            s.select_display_ranges([
 2432                // an empty selection - the preceding word fragment is deleted
 2433                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2434                // characters selected - they are deleted
 2435                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2436            ])
 2437        });
 2438        editor.delete_to_previous_word_start(
 2439            &DeleteToPreviousWordStart {
 2440                ignore_newlines: false,
 2441            },
 2442            window,
 2443            cx,
 2444        );
 2445        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2446    });
 2447
 2448    _ = editor.update(cx, |editor, window, cx| {
 2449        editor.change_selections(None, window, cx, |s| {
 2450            s.select_display_ranges([
 2451                // an empty selection - the following word fragment is deleted
 2452                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2453                // characters selected - they are deleted
 2454                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2455            ])
 2456        });
 2457        editor.delete_to_next_word_end(
 2458            &DeleteToNextWordEnd {
 2459                ignore_newlines: false,
 2460            },
 2461            window,
 2462            cx,
 2463        );
 2464        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2465    });
 2466}
 2467
 2468#[gpui::test]
 2469fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2470    init_test(cx, |_| {});
 2471
 2472    let editor = cx.add_window(|window, cx| {
 2473        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2474        build_editor(buffer.clone(), window, cx)
 2475    });
 2476    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2477        ignore_newlines: false,
 2478    };
 2479    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2480        ignore_newlines: true,
 2481    };
 2482
 2483    _ = editor.update(cx, |editor, window, cx| {
 2484        editor.change_selections(None, window, cx, |s| {
 2485            s.select_display_ranges([
 2486                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2487            ])
 2488        });
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2501    });
 2502}
 2503
 2504#[gpui::test]
 2505fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2506    init_test(cx, |_| {});
 2507
 2508    let editor = cx.add_window(|window, cx| {
 2509        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2510        build_editor(buffer.clone(), window, cx)
 2511    });
 2512    let del_to_next_word_end = DeleteToNextWordEnd {
 2513        ignore_newlines: false,
 2514    };
 2515    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2516        ignore_newlines: true,
 2517    };
 2518
 2519    _ = editor.update(cx, |editor, window, cx| {
 2520        editor.change_selections(None, window, cx, |s| {
 2521            s.select_display_ranges([
 2522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2523            ])
 2524        });
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "one\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "\n   two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(
 2537            editor.buffer.read(cx).read(cx).text(),
 2538            "two\nthree\n   four"
 2539        );
 2540        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2541        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2542        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2546    });
 2547}
 2548
 2549#[gpui::test]
 2550fn test_newline(cx: &mut TestAppContext) {
 2551    init_test(cx, |_| {});
 2552
 2553    let editor = cx.add_window(|window, cx| {
 2554        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2555        build_editor(buffer.clone(), window, cx)
 2556    });
 2557
 2558    _ = editor.update(cx, |editor, window, cx| {
 2559        editor.change_selections(None, window, cx, |s| {
 2560            s.select_display_ranges([
 2561                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2563                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2564            ])
 2565        });
 2566
 2567        editor.newline(&Newline, window, cx);
 2568        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2569    });
 2570}
 2571
 2572#[gpui::test]
 2573fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2574    init_test(cx, |_| {});
 2575
 2576    let editor = cx.add_window(|window, cx| {
 2577        let buffer = MultiBuffer::build_simple(
 2578            "
 2579                a
 2580                b(
 2581                    X
 2582                )
 2583                c(
 2584                    X
 2585                )
 2586            "
 2587            .unindent()
 2588            .as_str(),
 2589            cx,
 2590        );
 2591        let mut editor = build_editor(buffer.clone(), window, cx);
 2592        editor.change_selections(None, window, cx, |s| {
 2593            s.select_ranges([
 2594                Point::new(2, 4)..Point::new(2, 5),
 2595                Point::new(5, 4)..Point::new(5, 5),
 2596            ])
 2597        });
 2598        editor
 2599    });
 2600
 2601    _ = editor.update(cx, |editor, window, cx| {
 2602        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2603        editor.buffer.update(cx, |buffer, cx| {
 2604            buffer.edit(
 2605                [
 2606                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2607                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2608                ],
 2609                None,
 2610                cx,
 2611            );
 2612            assert_eq!(
 2613                buffer.read(cx).text(),
 2614                "
 2615                    a
 2616                    b()
 2617                    c()
 2618                "
 2619                .unindent()
 2620            );
 2621        });
 2622        assert_eq!(
 2623            editor.selections.ranges(cx),
 2624            &[
 2625                Point::new(1, 2)..Point::new(1, 2),
 2626                Point::new(2, 2)..Point::new(2, 2),
 2627            ],
 2628        );
 2629
 2630        editor.newline(&Newline, window, cx);
 2631        assert_eq!(
 2632            editor.text(cx),
 2633            "
 2634                a
 2635                b(
 2636                )
 2637                c(
 2638                )
 2639            "
 2640            .unindent()
 2641        );
 2642
 2643        // The selections are moved after the inserted newlines
 2644        assert_eq!(
 2645            editor.selections.ranges(cx),
 2646            &[
 2647                Point::new(2, 0)..Point::new(2, 0),
 2648                Point::new(4, 0)..Point::new(4, 0),
 2649            ],
 2650        );
 2651    });
 2652}
 2653
 2654#[gpui::test]
 2655async fn test_newline_above(cx: &mut TestAppContext) {
 2656    init_test(cx, |settings| {
 2657        settings.defaults.tab_size = NonZeroU32::new(4)
 2658    });
 2659
 2660    let language = Arc::new(
 2661        Language::new(
 2662            LanguageConfig::default(),
 2663            Some(tree_sitter_rust::LANGUAGE.into()),
 2664        )
 2665        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2666        .unwrap(),
 2667    );
 2668
 2669    let mut cx = EditorTestContext::new(cx).await;
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671    cx.set_state(indoc! {"
 2672        const a: ˇA = (
 2673 2674                «const_functionˇ»(ˇ),
 2675                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2676 2677        ˇ);ˇ
 2678    "});
 2679
 2680    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2681    cx.assert_editor_state(indoc! {"
 2682        ˇ
 2683        const a: A = (
 2684            ˇ
 2685            (
 2686                ˇ
 2687                ˇ
 2688                const_function(),
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                something_else,
 2694                ˇ
 2695            )
 2696            ˇ
 2697            ˇ
 2698        );
 2699    "});
 2700}
 2701
 2702#[gpui::test]
 2703async fn test_newline_below(cx: &mut TestAppContext) {
 2704    init_test(cx, |settings| {
 2705        settings.defaults.tab_size = NonZeroU32::new(4)
 2706    });
 2707
 2708    let language = Arc::new(
 2709        Language::new(
 2710            LanguageConfig::default(),
 2711            Some(tree_sitter_rust::LANGUAGE.into()),
 2712        )
 2713        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2714        .unwrap(),
 2715    );
 2716
 2717    let mut cx = EditorTestContext::new(cx).await;
 2718    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2719    cx.set_state(indoc! {"
 2720        const a: ˇA = (
 2721 2722                «const_functionˇ»(ˇ),
 2723                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2724 2725        ˇ);ˇ
 2726    "});
 2727
 2728    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2729    cx.assert_editor_state(indoc! {"
 2730        const a: A = (
 2731            ˇ
 2732            (
 2733                ˇ
 2734                const_function(),
 2735                ˇ
 2736                ˇ
 2737                something_else,
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742            )
 2743            ˇ
 2744        );
 2745        ˇ
 2746        ˇ
 2747    "});
 2748}
 2749
 2750#[gpui::test]
 2751async fn test_newline_comments(cx: &mut TestAppContext) {
 2752    init_test(cx, |settings| {
 2753        settings.defaults.tab_size = NonZeroU32::new(4)
 2754    });
 2755
 2756    let language = Arc::new(Language::new(
 2757        LanguageConfig {
 2758            line_comments: vec!["//".into()],
 2759            ..LanguageConfig::default()
 2760        },
 2761        None,
 2762    ));
 2763    {
 2764        let mut cx = EditorTestContext::new(cx).await;
 2765        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2766        cx.set_state(indoc! {"
 2767        // Fooˇ
 2768    "});
 2769
 2770        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2771        cx.assert_editor_state(indoc! {"
 2772        // Foo
 2773        //ˇ
 2774    "});
 2775        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2776        cx.set_state(indoc! {"
 2777        ˇ// Foo
 2778    "});
 2779        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2780        cx.assert_editor_state(indoc! {"
 2781
 2782        ˇ// Foo
 2783    "});
 2784    }
 2785    // Ensure that comment continuations can be disabled.
 2786    update_test_language_settings(cx, |settings| {
 2787        settings.defaults.extend_comment_on_newline = Some(false);
 2788    });
 2789    let mut cx = EditorTestContext::new(cx).await;
 2790    cx.set_state(indoc! {"
 2791        // Fooˇ
 2792    "});
 2793    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794    cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        ˇ
 2797    "});
 2798}
 2799
 2800#[gpui::test]
 2801fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2802    init_test(cx, |_| {});
 2803
 2804    let editor = cx.add_window(|window, cx| {
 2805        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2806        let mut editor = build_editor(buffer.clone(), window, cx);
 2807        editor.change_selections(None, window, cx, |s| {
 2808            s.select_ranges([3..4, 11..12, 19..20])
 2809        });
 2810        editor
 2811    });
 2812
 2813    _ = editor.update(cx, |editor, window, cx| {
 2814        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2815        editor.buffer.update(cx, |buffer, cx| {
 2816            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2817            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2818        });
 2819        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2820
 2821        editor.insert("Z", window, cx);
 2822        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2823
 2824        // The selections are moved after the inserted characters
 2825        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2826    });
 2827}
 2828
 2829#[gpui::test]
 2830async fn test_tab(cx: &mut TestAppContext) {
 2831    init_test(cx, |settings| {
 2832        settings.defaults.tab_size = NonZeroU32::new(3)
 2833    });
 2834
 2835    let mut cx = EditorTestContext::new(cx).await;
 2836    cx.set_state(indoc! {"
 2837        ˇabˇc
 2838        ˇ🏀ˇ🏀ˇefg
 2839 2840    "});
 2841    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843           ˇab ˇc
 2844           ˇ🏀  ˇ🏀  ˇefg
 2845        d  ˇ
 2846    "});
 2847
 2848    cx.set_state(indoc! {"
 2849        a
 2850        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        a
 2855           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2856    "});
 2857}
 2858
 2859#[gpui::test]
 2860async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2861    init_test(cx, |_| {});
 2862
 2863    let mut cx = EditorTestContext::new(cx).await;
 2864    let language = Arc::new(
 2865        Language::new(
 2866            LanguageConfig::default(),
 2867            Some(tree_sitter_rust::LANGUAGE.into()),
 2868        )
 2869        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2870        .unwrap(),
 2871    );
 2872    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2873
 2874    // when all cursors are to the left of the suggested indent, then auto-indent all.
 2875    cx.set_state(indoc! {"
 2876        const a: B = (
 2877            c(
 2878        ˇ
 2879        ˇ    )
 2880        );
 2881    "});
 2882    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884        const a: B = (
 2885            c(
 2886                ˇ
 2887            ˇ)
 2888        );
 2889    "});
 2890
 2891    // cursors that are already at the suggested indent level do not move
 2892    // until other cursors that are to the left of the suggested indent
 2893    // auto-indent.
 2894    cx.set_state(indoc! {"
 2895        ˇ
 2896        const a: B = (
 2897            c(
 2898                d(
 2899        ˇ
 2900                )
 2901        ˇ
 2902        ˇ    )
 2903        );
 2904    "});
 2905    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2906    cx.assert_editor_state(indoc! {"
 2907        ˇ
 2908        const a: B = (
 2909            c(
 2910                d(
 2911                    ˇ
 2912                )
 2913                ˇ
 2914            ˇ)
 2915        );
 2916    "});
 2917    // once all multi-cursors are at the suggested
 2918    // indent level, they all insert a soft tab together.
 2919    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2920    cx.assert_editor_state(indoc! {"
 2921            ˇ
 2922        const a: B = (
 2923            c(
 2924                d(
 2925                        ˇ
 2926                )
 2927                    ˇ
 2928                ˇ)
 2929        );
 2930    "});
 2931
 2932    // handle auto-indent when there are multiple cursors on the same line
 2933    cx.set_state(indoc! {"
 2934        const a: B = (
 2935            c(
 2936        ˇ    ˇ
 2937        ˇ    )
 2938        );
 2939    "});
 2940    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2941    cx.assert_editor_state(indoc! {"
 2942        const a: B = (
 2943            c(
 2944                ˇ
 2945            ˇ)
 2946        );
 2947    "});
 2948}
 2949
 2950#[gpui::test]
 2951async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2952    init_test(cx, |settings| {
 2953        settings.defaults.tab_size = NonZeroU32::new(3)
 2954    });
 2955
 2956    let mut cx = EditorTestContext::new(cx).await;
 2957    cx.set_state(indoc! {"
 2958         ˇ
 2959        \t ˇ
 2960        \t  ˇ
 2961        \t   ˇ
 2962         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2963    "});
 2964
 2965    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2966    cx.assert_editor_state(indoc! {"
 2967           ˇ
 2968        \t   ˇ
 2969        \t   ˇ
 2970        \t      ˇ
 2971         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2972    "});
 2973}
 2974
 2975#[gpui::test]
 2976async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2977    init_test(cx, |settings| {
 2978        settings.defaults.tab_size = NonZeroU32::new(4)
 2979    });
 2980
 2981    let language = Arc::new(
 2982        Language::new(
 2983            LanguageConfig::default(),
 2984            Some(tree_sitter_rust::LANGUAGE.into()),
 2985        )
 2986        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2987        .unwrap(),
 2988    );
 2989
 2990    let mut cx = EditorTestContext::new(cx).await;
 2991    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2992    cx.set_state(indoc! {"
 2993        fn a() {
 2994            if b {
 2995        \t ˇc
 2996            }
 2997        }
 2998    "});
 2999
 3000    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3001    cx.assert_editor_state(indoc! {"
 3002        fn a() {
 3003            if b {
 3004                ˇc
 3005            }
 3006        }
 3007    "});
 3008}
 3009
 3010#[gpui::test]
 3011async fn test_indent_outdent(cx: &mut TestAppContext) {
 3012    init_test(cx, |settings| {
 3013        settings.defaults.tab_size = NonZeroU32::new(4);
 3014    });
 3015
 3016    let mut cx = EditorTestContext::new(cx).await;
 3017
 3018    cx.set_state(indoc! {"
 3019          «oneˇ» «twoˇ»
 3020        three
 3021         four
 3022    "});
 3023    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3024    cx.assert_editor_state(indoc! {"
 3025            «oneˇ» «twoˇ»
 3026        three
 3027         four
 3028    "});
 3029
 3030    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3031    cx.assert_editor_state(indoc! {"
 3032        «oneˇ» «twoˇ»
 3033        three
 3034         four
 3035    "});
 3036
 3037    // select across line ending
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        t«hree
 3041        ˇ» four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046            t«hree
 3047        ˇ» four
 3048    "});
 3049
 3050    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        one two
 3053        t«hree
 3054        ˇ» four
 3055    "});
 3056
 3057    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3058    cx.set_state(indoc! {"
 3059        one two
 3060        ˇthree
 3061            four
 3062    "});
 3063    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3064    cx.assert_editor_state(indoc! {"
 3065        one two
 3066            ˇthree
 3067            four
 3068    "});
 3069
 3070    cx.set_state(indoc! {"
 3071        one two
 3072        ˇ    three
 3073            four
 3074    "});
 3075    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3076    cx.assert_editor_state(indoc! {"
 3077        one two
 3078        ˇthree
 3079            four
 3080    "});
 3081}
 3082
 3083#[gpui::test]
 3084async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3085    init_test(cx, |settings| {
 3086        settings.defaults.hard_tabs = Some(true);
 3087    });
 3088
 3089    let mut cx = EditorTestContext::new(cx).await;
 3090
 3091    // select two ranges on one line
 3092    cx.set_state(indoc! {"
 3093        «oneˇ» «twoˇ»
 3094        three
 3095        four
 3096    "});
 3097    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        \t«oneˇ» «twoˇ»
 3100        three
 3101        four
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        \t\t«oneˇ» «twoˇ»
 3106        three
 3107        four
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        \t«oneˇ» «twoˇ»
 3112        three
 3113        four
 3114    "});
 3115    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3116    cx.assert_editor_state(indoc! {"
 3117        «oneˇ» «twoˇ»
 3118        three
 3119        four
 3120    "});
 3121
 3122    // select across a line ending
 3123    cx.set_state(indoc! {"
 3124        one two
 3125        t«hree
 3126        ˇ»four
 3127    "});
 3128    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3129    cx.assert_editor_state(indoc! {"
 3130        one two
 3131        \tt«hree
 3132        ˇ»four
 3133    "});
 3134    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3135    cx.assert_editor_state(indoc! {"
 3136        one two
 3137        \t\tt«hree
 3138        ˇ»four
 3139    "});
 3140    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3141    cx.assert_editor_state(indoc! {"
 3142        one two
 3143        \tt«hree
 3144        ˇ»four
 3145    "});
 3146    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3147    cx.assert_editor_state(indoc! {"
 3148        one two
 3149        t«hree
 3150        ˇ»four
 3151    "});
 3152
 3153    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3154    cx.set_state(indoc! {"
 3155        one two
 3156        ˇthree
 3157        four
 3158    "});
 3159    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3160    cx.assert_editor_state(indoc! {"
 3161        one two
 3162        ˇthree
 3163        four
 3164    "});
 3165    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3166    cx.assert_editor_state(indoc! {"
 3167        one two
 3168        \tˇthree
 3169        four
 3170    "});
 3171    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3172    cx.assert_editor_state(indoc! {"
 3173        one two
 3174        ˇthree
 3175        four
 3176    "});
 3177}
 3178
 3179#[gpui::test]
 3180fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3181    init_test(cx, |settings| {
 3182        settings.languages.extend([
 3183            (
 3184                "TOML".into(),
 3185                LanguageSettingsContent {
 3186                    tab_size: NonZeroU32::new(2),
 3187                    ..Default::default()
 3188                },
 3189            ),
 3190            (
 3191                "Rust".into(),
 3192                LanguageSettingsContent {
 3193                    tab_size: NonZeroU32::new(4),
 3194                    ..Default::default()
 3195                },
 3196            ),
 3197        ]);
 3198    });
 3199
 3200    let toml_language = Arc::new(Language::new(
 3201        LanguageConfig {
 3202            name: "TOML".into(),
 3203            ..Default::default()
 3204        },
 3205        None,
 3206    ));
 3207    let rust_language = Arc::new(Language::new(
 3208        LanguageConfig {
 3209            name: "Rust".into(),
 3210            ..Default::default()
 3211        },
 3212        None,
 3213    ));
 3214
 3215    let toml_buffer =
 3216        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3217    let rust_buffer =
 3218        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3219    let multibuffer = cx.new(|cx| {
 3220        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3221        multibuffer.push_excerpts(
 3222            toml_buffer.clone(),
 3223            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3224            cx,
 3225        );
 3226        multibuffer.push_excerpts(
 3227            rust_buffer.clone(),
 3228            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3229            cx,
 3230        );
 3231        multibuffer
 3232    });
 3233
 3234    cx.add_window(|window, cx| {
 3235        let mut editor = build_editor(multibuffer, window, cx);
 3236
 3237        assert_eq!(
 3238            editor.text(cx),
 3239            indoc! {"
 3240                a = 1
 3241                b = 2
 3242
 3243                const c: usize = 3;
 3244            "}
 3245        );
 3246
 3247        select_ranges(
 3248            &mut editor,
 3249            indoc! {"
 3250                «aˇ» = 1
 3251                b = 2
 3252
 3253                «const c:ˇ» usize = 3;
 3254            "},
 3255            window,
 3256            cx,
 3257        );
 3258
 3259        editor.tab(&Tab, window, cx);
 3260        assert_text_with_selections(
 3261            &mut editor,
 3262            indoc! {"
 3263                  «aˇ» = 1
 3264                b = 2
 3265
 3266                    «const c:ˇ» usize = 3;
 3267            "},
 3268            cx,
 3269        );
 3270        editor.backtab(&Backtab, window, cx);
 3271        assert_text_with_selections(
 3272            &mut editor,
 3273            indoc! {"
 3274                «aˇ» = 1
 3275                b = 2
 3276
 3277                «const c:ˇ» usize = 3;
 3278            "},
 3279            cx,
 3280        );
 3281
 3282        editor
 3283    });
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_backspace(cx: &mut TestAppContext) {
 3288    init_test(cx, |_| {});
 3289
 3290    let mut cx = EditorTestContext::new(cx).await;
 3291
 3292    // Basic backspace
 3293    cx.set_state(indoc! {"
 3294        onˇe two three
 3295        fou«rˇ» five six
 3296        seven «ˇeight nine
 3297        »ten
 3298    "});
 3299    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3300    cx.assert_editor_state(indoc! {"
 3301        oˇe two three
 3302        fouˇ five six
 3303        seven ˇten
 3304    "});
 3305
 3306    // Test backspace inside and around indents
 3307    cx.set_state(indoc! {"
 3308        zero
 3309            ˇone
 3310                ˇtwo
 3311            ˇ ˇ ˇ  three
 3312        ˇ  ˇ  four
 3313    "});
 3314    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3315    cx.assert_editor_state(indoc! {"
 3316        zero
 3317        ˇone
 3318            ˇtwo
 3319        ˇ  threeˇ  four
 3320    "});
 3321}
 3322
 3323#[gpui::test]
 3324async fn test_delete(cx: &mut TestAppContext) {
 3325    init_test(cx, |_| {});
 3326
 3327    let mut cx = EditorTestContext::new(cx).await;
 3328    cx.set_state(indoc! {"
 3329        onˇe two three
 3330        fou«rˇ» five six
 3331        seven «ˇeight nine
 3332        »ten
 3333    "});
 3334    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3335    cx.assert_editor_state(indoc! {"
 3336        onˇ two three
 3337        fouˇ five six
 3338        seven ˇten
 3339    "});
 3340}
 3341
 3342#[gpui::test]
 3343fn test_delete_line(cx: &mut TestAppContext) {
 3344    init_test(cx, |_| {});
 3345
 3346    let editor = cx.add_window(|window, cx| {
 3347        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3348        build_editor(buffer, window, cx)
 3349    });
 3350    _ = editor.update(cx, |editor, window, cx| {
 3351        editor.change_selections(None, window, cx, |s| {
 3352            s.select_display_ranges([
 3353                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3354                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3355                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3356            ])
 3357        });
 3358        editor.delete_line(&DeleteLine, window, cx);
 3359        assert_eq!(editor.display_text(cx), "ghi");
 3360        assert_eq!(
 3361            editor.selections.display_ranges(cx),
 3362            vec![
 3363                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3364                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3365            ]
 3366        );
 3367    });
 3368
 3369    let editor = cx.add_window(|window, cx| {
 3370        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3371        build_editor(buffer, window, cx)
 3372    });
 3373    _ = editor.update(cx, |editor, window, cx| {
 3374        editor.change_selections(None, window, cx, |s| {
 3375            s.select_display_ranges([
 3376                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3377            ])
 3378        });
 3379        editor.delete_line(&DeleteLine, window, cx);
 3380        assert_eq!(editor.display_text(cx), "ghi\n");
 3381        assert_eq!(
 3382            editor.selections.display_ranges(cx),
 3383            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3384        );
 3385    });
 3386}
 3387
 3388#[gpui::test]
 3389fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3390    init_test(cx, |_| {});
 3391
 3392    cx.add_window(|window, cx| {
 3393        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3394        let mut editor = build_editor(buffer.clone(), window, cx);
 3395        let buffer = buffer.read(cx).as_singleton().unwrap();
 3396
 3397        assert_eq!(
 3398            editor.selections.ranges::<Point>(cx),
 3399            &[Point::new(0, 0)..Point::new(0, 0)]
 3400        );
 3401
 3402        // When on single line, replace newline at end by space
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            &[Point::new(0, 3)..Point::new(0, 3)]
 3408        );
 3409
 3410        // When multiple lines are selected, remove newlines that are spanned by the selection
 3411        editor.change_selections(None, window, cx, |s| {
 3412            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3413        });
 3414        editor.join_lines(&JoinLines, window, cx);
 3415        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3416        assert_eq!(
 3417            editor.selections.ranges::<Point>(cx),
 3418            &[Point::new(0, 11)..Point::new(0, 11)]
 3419        );
 3420
 3421        // Undo should be transactional
 3422        editor.undo(&Undo, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3424        assert_eq!(
 3425            editor.selections.ranges::<Point>(cx),
 3426            &[Point::new(0, 5)..Point::new(2, 2)]
 3427        );
 3428
 3429        // When joining an empty line don't insert a space
 3430        editor.change_selections(None, window, cx, |s| {
 3431            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3432        });
 3433        editor.join_lines(&JoinLines, window, cx);
 3434        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3435        assert_eq!(
 3436            editor.selections.ranges::<Point>(cx),
 3437            [Point::new(2, 3)..Point::new(2, 3)]
 3438        );
 3439
 3440        // We can remove trailing newlines
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3443        assert_eq!(
 3444            editor.selections.ranges::<Point>(cx),
 3445            [Point::new(2, 3)..Point::new(2, 3)]
 3446        );
 3447
 3448        // We don't blow up on the last line
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3451        assert_eq!(
 3452            editor.selections.ranges::<Point>(cx),
 3453            [Point::new(2, 3)..Point::new(2, 3)]
 3454        );
 3455
 3456        // reset to test indentation
 3457        editor.buffer.update(cx, |buffer, cx| {
 3458            buffer.edit(
 3459                [
 3460                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3461                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3462                ],
 3463                None,
 3464                cx,
 3465            )
 3466        });
 3467
 3468        // We remove any leading spaces
 3469        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3470        editor.change_selections(None, window, cx, |s| {
 3471            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3472        });
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3475
 3476        // We don't insert a space for a line containing only spaces
 3477        editor.join_lines(&JoinLines, window, cx);
 3478        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3479
 3480        // We ignore any leading tabs
 3481        editor.join_lines(&JoinLines, window, cx);
 3482        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3483
 3484        editor
 3485    });
 3486}
 3487
 3488#[gpui::test]
 3489fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3490    init_test(cx, |_| {});
 3491
 3492    cx.add_window(|window, cx| {
 3493        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3494        let mut editor = build_editor(buffer.clone(), window, cx);
 3495        let buffer = buffer.read(cx).as_singleton().unwrap();
 3496
 3497        editor.change_selections(None, window, cx, |s| {
 3498            s.select_ranges([
 3499                Point::new(0, 2)..Point::new(1, 1),
 3500                Point::new(1, 2)..Point::new(1, 2),
 3501                Point::new(3, 1)..Point::new(3, 2),
 3502            ])
 3503        });
 3504
 3505        editor.join_lines(&JoinLines, window, cx);
 3506        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3507
 3508        assert_eq!(
 3509            editor.selections.ranges::<Point>(cx),
 3510            [
 3511                Point::new(0, 7)..Point::new(0, 7),
 3512                Point::new(1, 3)..Point::new(1, 3)
 3513            ]
 3514        );
 3515        editor
 3516    });
 3517}
 3518
 3519#[gpui::test]
 3520async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3521    init_test(cx, |_| {});
 3522
 3523    let mut cx = EditorTestContext::new(cx).await;
 3524
 3525    let diff_base = r#"
 3526        Line 0
 3527        Line 1
 3528        Line 2
 3529        Line 3
 3530        "#
 3531    .unindent();
 3532
 3533    cx.set_state(
 3534        &r#"
 3535        ˇLine 0
 3536        Line 1
 3537        Line 2
 3538        Line 3
 3539        "#
 3540        .unindent(),
 3541    );
 3542
 3543    cx.set_head_text(&diff_base);
 3544    executor.run_until_parked();
 3545
 3546    // Join lines
 3547    cx.update_editor(|editor, window, cx| {
 3548        editor.join_lines(&JoinLines, window, cx);
 3549    });
 3550    executor.run_until_parked();
 3551
 3552    cx.assert_editor_state(
 3553        &r#"
 3554        Line 0ˇ Line 1
 3555        Line 2
 3556        Line 3
 3557        "#
 3558        .unindent(),
 3559    );
 3560    // Join again
 3561    cx.update_editor(|editor, window, cx| {
 3562        editor.join_lines(&JoinLines, window, cx);
 3563    });
 3564    executor.run_until_parked();
 3565
 3566    cx.assert_editor_state(
 3567        &r#"
 3568        Line 0 Line 1ˇ Line 2
 3569        Line 3
 3570        "#
 3571        .unindent(),
 3572    );
 3573}
 3574
 3575#[gpui::test]
 3576async fn test_custom_newlines_cause_no_false_positive_diffs(
 3577    executor: BackgroundExecutor,
 3578    cx: &mut TestAppContext,
 3579) {
 3580    init_test(cx, |_| {});
 3581    let mut cx = EditorTestContext::new(cx).await;
 3582    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3583    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3584    executor.run_until_parked();
 3585
 3586    cx.update_editor(|editor, window, cx| {
 3587        let snapshot = editor.snapshot(window, cx);
 3588        assert_eq!(
 3589            snapshot
 3590                .buffer_snapshot
 3591                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3592                .collect::<Vec<_>>(),
 3593            Vec::new(),
 3594            "Should not have any diffs for files with custom newlines"
 3595        );
 3596    });
 3597}
 3598
 3599#[gpui::test]
 3600async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3601    init_test(cx, |_| {});
 3602
 3603    let mut cx = EditorTestContext::new(cx).await;
 3604
 3605    // Test sort_lines_case_insensitive()
 3606    cx.set_state(indoc! {"
 3607        «z
 3608        y
 3609        x
 3610        Z
 3611        Y
 3612        Xˇ»
 3613    "});
 3614    cx.update_editor(|e, window, cx| {
 3615        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3616    });
 3617    cx.assert_editor_state(indoc! {"
 3618        «x
 3619        X
 3620        y
 3621        Y
 3622        z
 3623        Zˇ»
 3624    "});
 3625
 3626    // Test reverse_lines()
 3627    cx.set_state(indoc! {"
 3628        «5
 3629        4
 3630        3
 3631        2
 3632        1ˇ»
 3633    "});
 3634    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3635    cx.assert_editor_state(indoc! {"
 3636        «1
 3637        2
 3638        3
 3639        4
 3640        5ˇ»
 3641    "});
 3642
 3643    // Skip testing shuffle_line()
 3644
 3645    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3646    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3647
 3648    // Don't manipulate when cursor is on single line, but expand the selection
 3649    cx.set_state(indoc! {"
 3650        ddˇdd
 3651        ccc
 3652        bb
 3653        a
 3654    "});
 3655    cx.update_editor(|e, window, cx| {
 3656        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3657    });
 3658    cx.assert_editor_state(indoc! {"
 3659        «ddddˇ»
 3660        ccc
 3661        bb
 3662        a
 3663    "});
 3664
 3665    // Basic manipulate case
 3666    // Start selection moves to column 0
 3667    // End of selection shrinks to fit shorter line
 3668    cx.set_state(indoc! {"
 3669        dd«d
 3670        ccc
 3671        bb
 3672        aaaaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «aaaaa
 3679        bb
 3680        ccc
 3681        dddˇ»
 3682    "});
 3683
 3684    // Manipulate case with newlines
 3685    cx.set_state(indoc! {"
 3686        dd«d
 3687        ccc
 3688
 3689        bb
 3690        aaaaa
 3691
 3692        ˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3696    });
 3697    cx.assert_editor_state(indoc! {"
 3698        «
 3699
 3700        aaaaa
 3701        bb
 3702        ccc
 3703        dddˇ»
 3704
 3705    "});
 3706
 3707    // Adding new line
 3708    cx.set_state(indoc! {"
 3709        aa«a
 3710        bbˇ»b
 3711    "});
 3712    cx.update_editor(|e, window, cx| {
 3713        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        «aaa
 3717        bbb
 3718        added_lineˇ»
 3719    "});
 3720
 3721    // Removing line
 3722    cx.set_state(indoc! {"
 3723        aa«a
 3724        bbbˇ»
 3725    "});
 3726    cx.update_editor(|e, window, cx| {
 3727        e.manipulate_lines(window, cx, |lines| {
 3728            lines.pop();
 3729        })
 3730    });
 3731    cx.assert_editor_state(indoc! {"
 3732        «aaaˇ»
 3733    "});
 3734
 3735    // Removing all lines
 3736    cx.set_state(indoc! {"
 3737        aa«a
 3738        bbbˇ»
 3739    "});
 3740    cx.update_editor(|e, window, cx| {
 3741        e.manipulate_lines(window, cx, |lines| {
 3742            lines.drain(..);
 3743        })
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        ˇ
 3747    "});
 3748}
 3749
 3750#[gpui::test]
 3751async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3752    init_test(cx, |_| {});
 3753
 3754    let mut cx = EditorTestContext::new(cx).await;
 3755
 3756    // Consider continuous selection as single selection
 3757    cx.set_state(indoc! {"
 3758        Aaa«aa
 3759        cˇ»c«c
 3760        bb
 3761        aaaˇ»aa
 3762    "});
 3763    cx.update_editor(|e, window, cx| {
 3764        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3765    });
 3766    cx.assert_editor_state(indoc! {"
 3767        «Aaaaa
 3768        ccc
 3769        bb
 3770        aaaaaˇ»
 3771    "});
 3772
 3773    cx.set_state(indoc! {"
 3774        Aaa«aa
 3775        cˇ»c«c
 3776        bb
 3777        aaaˇ»aa
 3778    "});
 3779    cx.update_editor(|e, window, cx| {
 3780        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3781    });
 3782    cx.assert_editor_state(indoc! {"
 3783        «Aaaaa
 3784        ccc
 3785        bbˇ»
 3786    "});
 3787
 3788    // Consider non continuous selection as distinct dedup operations
 3789    cx.set_state(indoc! {"
 3790        «aaaaa
 3791        bb
 3792        aaaaa
 3793        aaaaaˇ»
 3794
 3795        aaa«aaˇ»
 3796    "});
 3797    cx.update_editor(|e, window, cx| {
 3798        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3799    });
 3800    cx.assert_editor_state(indoc! {"
 3801        «aaaaa
 3802        bbˇ»
 3803
 3804        «aaaaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    cx.set_state(indoc! {"
 3815        «Aaa
 3816        aAa
 3817        Aaaˇ»
 3818    "});
 3819    cx.update_editor(|e, window, cx| {
 3820        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3821    });
 3822    cx.assert_editor_state(indoc! {"
 3823        «Aaa
 3824        aAaˇ»
 3825    "});
 3826
 3827    cx.set_state(indoc! {"
 3828        «Aaa
 3829        aAa
 3830        aaAˇ»
 3831    "});
 3832    cx.update_editor(|e, window, cx| {
 3833        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3834    });
 3835    cx.assert_editor_state(indoc! {"
 3836        «Aaaˇ»
 3837    "});
 3838}
 3839
 3840#[gpui::test]
 3841async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3842    init_test(cx, |_| {});
 3843
 3844    let mut cx = EditorTestContext::new(cx).await;
 3845
 3846    // Manipulate with multiple selections on a single line
 3847    cx.set_state(indoc! {"
 3848        dd«dd
 3849        cˇ»c«c
 3850        bb
 3851        aaaˇ»aa
 3852    "});
 3853    cx.update_editor(|e, window, cx| {
 3854        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3855    });
 3856    cx.assert_editor_state(indoc! {"
 3857        «aaaaa
 3858        bb
 3859        ccc
 3860        ddddˇ»
 3861    "});
 3862
 3863    // Manipulate with multiple disjoin selections
 3864    cx.set_state(indoc! {"
 3865 3866        4
 3867        3
 3868        2
 3869        1ˇ»
 3870
 3871        dd«dd
 3872        ccc
 3873        bb
 3874        aaaˇ»aa
 3875    "});
 3876    cx.update_editor(|e, window, cx| {
 3877        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3878    });
 3879    cx.assert_editor_state(indoc! {"
 3880        «1
 3881        2
 3882        3
 3883        4
 3884        5ˇ»
 3885
 3886        «aaaaa
 3887        bb
 3888        ccc
 3889        ddddˇ»
 3890    "});
 3891
 3892    // Adding lines on each selection
 3893    cx.set_state(indoc! {"
 3894 3895        1ˇ»
 3896
 3897        bb«bb
 3898        aaaˇ»aa
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «2
 3905        1
 3906        added lineˇ»
 3907
 3908        «bbbb
 3909        aaaaa
 3910        added lineˇ»
 3911    "});
 3912
 3913    // Removing lines on each selection
 3914    cx.set_state(indoc! {"
 3915 3916        1ˇ»
 3917
 3918        bb«bb
 3919        aaaˇ»aa
 3920    "});
 3921    cx.update_editor(|e, window, cx| {
 3922        e.manipulate_lines(window, cx, |lines| {
 3923            lines.pop();
 3924        })
 3925    });
 3926    cx.assert_editor_state(indoc! {"
 3927        «2ˇ»
 3928
 3929        «bbbbˇ»
 3930    "});
 3931}
 3932
 3933#[gpui::test]
 3934async fn test_toggle_case(cx: &mut TestAppContext) {
 3935    init_test(cx, |_| {});
 3936
 3937    let mut cx = EditorTestContext::new(cx).await;
 3938
 3939    // If all lower case -> upper case
 3940    cx.set_state(indoc! {"
 3941        «hello worldˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3944    cx.assert_editor_state(indoc! {"
 3945        «HELLO WORLDˇ»
 3946    "});
 3947
 3948    // If all upper case -> lower case
 3949    cx.set_state(indoc! {"
 3950        «HELLO WORLDˇ»
 3951    "});
 3952    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3953    cx.assert_editor_state(indoc! {"
 3954        «hello worldˇ»
 3955    "});
 3956
 3957    // If any upper case characters are identified -> lower case
 3958    // This matches JetBrains IDEs
 3959    cx.set_state(indoc! {"
 3960        «hEllo worldˇ»
 3961    "});
 3962    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3963    cx.assert_editor_state(indoc! {"
 3964        «hello worldˇ»
 3965    "});
 3966}
 3967
 3968#[gpui::test]
 3969async fn test_manipulate_text(cx: &mut TestAppContext) {
 3970    init_test(cx, |_| {});
 3971
 3972    let mut cx = EditorTestContext::new(cx).await;
 3973
 3974    // Test convert_to_upper_case()
 3975    cx.set_state(indoc! {"
 3976        «hello worldˇ»
 3977    "});
 3978    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3979    cx.assert_editor_state(indoc! {"
 3980        «HELLO WORLDˇ»
 3981    "});
 3982
 3983    // Test convert_to_lower_case()
 3984    cx.set_state(indoc! {"
 3985        «HELLO WORLDˇ»
 3986    "});
 3987    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3988    cx.assert_editor_state(indoc! {"
 3989        «hello worldˇ»
 3990    "});
 3991
 3992    // Test multiple line, single selection case
 3993    cx.set_state(indoc! {"
 3994        «The quick brown
 3995        fox jumps over
 3996        the lazy dogˇ»
 3997    "});
 3998    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3999    cx.assert_editor_state(indoc! {"
 4000        «The Quick Brown
 4001        Fox Jumps Over
 4002        The Lazy Dogˇ»
 4003    "});
 4004
 4005    // Test multiple line, single selection case
 4006    cx.set_state(indoc! {"
 4007        «The quick brown
 4008        fox jumps over
 4009        the lazy dogˇ»
 4010    "});
 4011    cx.update_editor(|e, window, cx| {
 4012        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4013    });
 4014    cx.assert_editor_state(indoc! {"
 4015        «TheQuickBrown
 4016        FoxJumpsOver
 4017        TheLazyDogˇ»
 4018    "});
 4019
 4020    // From here on out, test more complex cases of manipulate_text()
 4021
 4022    // Test no selection case - should affect words cursors are in
 4023    // Cursor at beginning, middle, and end of word
 4024    cx.set_state(indoc! {"
 4025        ˇhello big beauˇtiful worldˇ
 4026    "});
 4027    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4028    cx.assert_editor_state(indoc! {"
 4029        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4030    "});
 4031
 4032    // Test multiple selections on a single line and across multiple lines
 4033    cx.set_state(indoc! {"
 4034        «Theˇ» quick «brown
 4035        foxˇ» jumps «overˇ»
 4036        the «lazyˇ» dog
 4037    "});
 4038    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4039    cx.assert_editor_state(indoc! {"
 4040        «THEˇ» quick «BROWN
 4041        FOXˇ» jumps «OVERˇ»
 4042        the «LAZYˇ» dog
 4043    "});
 4044
 4045    // Test case where text length grows
 4046    cx.set_state(indoc! {"
 4047        «tschüߡ»
 4048    "});
 4049    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        «TSCHÜSSˇ»
 4052    "});
 4053
 4054    // Test to make sure we don't crash when text shrinks
 4055    cx.set_state(indoc! {"
 4056        aaa_bbbˇ
 4057    "});
 4058    cx.update_editor(|e, window, cx| {
 4059        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4060    });
 4061    cx.assert_editor_state(indoc! {"
 4062        «aaaBbbˇ»
 4063    "});
 4064
 4065    // Test to make sure we all aware of the fact that each word can grow and shrink
 4066    // Final selections should be aware of this fact
 4067    cx.set_state(indoc! {"
 4068        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4069    "});
 4070    cx.update_editor(|e, window, cx| {
 4071        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4072    });
 4073    cx.assert_editor_state(indoc! {"
 4074        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4075    "});
 4076
 4077    cx.set_state(indoc! {"
 4078        «hElLo, WoRld!ˇ»
 4079    "});
 4080    cx.update_editor(|e, window, cx| {
 4081        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4082    });
 4083    cx.assert_editor_state(indoc! {"
 4084        «HeLlO, wOrLD!ˇ»
 4085    "});
 4086}
 4087
 4088#[gpui::test]
 4089fn test_duplicate_line(cx: &mut TestAppContext) {
 4090    init_test(cx, |_| {});
 4091
 4092    let editor = cx.add_window(|window, cx| {
 4093        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4094        build_editor(buffer, window, cx)
 4095    });
 4096    _ = editor.update(cx, |editor, window, cx| {
 4097        editor.change_selections(None, window, cx, |s| {
 4098            s.select_display_ranges([
 4099                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4101                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4102                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4103            ])
 4104        });
 4105        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4106        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4107        assert_eq!(
 4108            editor.selections.display_ranges(cx),
 4109            vec![
 4110                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4111                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4112                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4113                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4114            ]
 4115        );
 4116    });
 4117
 4118    let editor = cx.add_window(|window, cx| {
 4119        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4120        build_editor(buffer, window, cx)
 4121    });
 4122    _ = editor.update(cx, |editor, window, cx| {
 4123        editor.change_selections(None, window, cx, |s| {
 4124            s.select_display_ranges([
 4125                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4126                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4127            ])
 4128        });
 4129        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4130        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4131        assert_eq!(
 4132            editor.selections.display_ranges(cx),
 4133            vec![
 4134                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4135                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4136            ]
 4137        );
 4138    });
 4139
 4140    // With `move_upwards` the selections stay in place, except for
 4141    // the lines inserted above them
 4142    let editor = cx.add_window(|window, cx| {
 4143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4144        build_editor(buffer, window, cx)
 4145    });
 4146    _ = editor.update(cx, |editor, window, cx| {
 4147        editor.change_selections(None, window, cx, |s| {
 4148            s.select_display_ranges([
 4149                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4150                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4151                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4152                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4153            ])
 4154        });
 4155        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4156        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4157        assert_eq!(
 4158            editor.selections.display_ranges(cx),
 4159            vec![
 4160                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4161                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4162                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4163                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4164            ]
 4165        );
 4166    });
 4167
 4168    let editor = cx.add_window(|window, cx| {
 4169        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4170        build_editor(buffer, window, cx)
 4171    });
 4172    _ = editor.update(cx, |editor, window, cx| {
 4173        editor.change_selections(None, window, cx, |s| {
 4174            s.select_display_ranges([
 4175                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4176                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4177            ])
 4178        });
 4179        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4180        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4181        assert_eq!(
 4182            editor.selections.display_ranges(cx),
 4183            vec![
 4184                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4185                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4186            ]
 4187        );
 4188    });
 4189
 4190    let editor = cx.add_window(|window, cx| {
 4191        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4192        build_editor(buffer, window, cx)
 4193    });
 4194    _ = editor.update(cx, |editor, window, cx| {
 4195        editor.change_selections(None, window, cx, |s| {
 4196            s.select_display_ranges([
 4197                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4198                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4199            ])
 4200        });
 4201        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4202        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4203        assert_eq!(
 4204            editor.selections.display_ranges(cx),
 4205            vec![
 4206                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4207                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4208            ]
 4209        );
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214fn test_move_line_up_down(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let editor = cx.add_window(|window, cx| {
 4218        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4219        build_editor(buffer, window, cx)
 4220    });
 4221    _ = editor.update(cx, |editor, window, cx| {
 4222        editor.fold_creases(
 4223            vec![
 4224                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4225                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4226                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4227            ],
 4228            true,
 4229            window,
 4230            cx,
 4231        );
 4232        editor.change_selections(None, window, cx, |s| {
 4233            s.select_display_ranges([
 4234                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4235                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4236                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4237                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4238            ])
 4239        });
 4240        assert_eq!(
 4241            editor.display_text(cx),
 4242            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4243        );
 4244
 4245        editor.move_line_up(&MoveLineUp, window, cx);
 4246        assert_eq!(
 4247            editor.display_text(cx),
 4248            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4249        );
 4250        assert_eq!(
 4251            editor.selections.display_ranges(cx),
 4252            vec![
 4253                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4254                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4255                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4256                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4257            ]
 4258        );
 4259    });
 4260
 4261    _ = editor.update(cx, |editor, window, cx| {
 4262        editor.move_line_down(&MoveLineDown, window, cx);
 4263        assert_eq!(
 4264            editor.display_text(cx),
 4265            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4266        );
 4267        assert_eq!(
 4268            editor.selections.display_ranges(cx),
 4269            vec![
 4270                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4271                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4272                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4273                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4274            ]
 4275        );
 4276    });
 4277
 4278    _ = editor.update(cx, |editor, window, cx| {
 4279        editor.move_line_down(&MoveLineDown, window, cx);
 4280        assert_eq!(
 4281            editor.display_text(cx),
 4282            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4283        );
 4284        assert_eq!(
 4285            editor.selections.display_ranges(cx),
 4286            vec![
 4287                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4288                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4289                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4290                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4291            ]
 4292        );
 4293    });
 4294
 4295    _ = editor.update(cx, |editor, window, cx| {
 4296        editor.move_line_up(&MoveLineUp, window, cx);
 4297        assert_eq!(
 4298            editor.display_text(cx),
 4299            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4300        );
 4301        assert_eq!(
 4302            editor.selections.display_ranges(cx),
 4303            vec![
 4304                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4305                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4306                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4307                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4308            ]
 4309        );
 4310    });
 4311}
 4312
 4313#[gpui::test]
 4314fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4315    init_test(cx, |_| {});
 4316
 4317    let editor = cx.add_window(|window, cx| {
 4318        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4319        build_editor(buffer, window, cx)
 4320    });
 4321    _ = editor.update(cx, |editor, window, cx| {
 4322        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4323        editor.insert_blocks(
 4324            [BlockProperties {
 4325                style: BlockStyle::Fixed,
 4326                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4327                height: Some(1),
 4328                render: Arc::new(|_| div().into_any()),
 4329                priority: 0,
 4330            }],
 4331            Some(Autoscroll::fit()),
 4332            cx,
 4333        );
 4334        editor.change_selections(None, window, cx, |s| {
 4335            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4336        });
 4337        editor.move_line_down(&MoveLineDown, window, cx);
 4338    });
 4339}
 4340
 4341#[gpui::test]
 4342async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4343    init_test(cx, |_| {});
 4344
 4345    let mut cx = EditorTestContext::new(cx).await;
 4346    cx.set_state(
 4347        &"
 4348            ˇzero
 4349            one
 4350            two
 4351            three
 4352            four
 4353            five
 4354        "
 4355        .unindent(),
 4356    );
 4357
 4358    // Create a four-line block that replaces three lines of text.
 4359    cx.update_editor(|editor, window, cx| {
 4360        let snapshot = editor.snapshot(window, cx);
 4361        let snapshot = &snapshot.buffer_snapshot;
 4362        let placement = BlockPlacement::Replace(
 4363            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4364        );
 4365        editor.insert_blocks(
 4366            [BlockProperties {
 4367                placement,
 4368                height: Some(4),
 4369                style: BlockStyle::Sticky,
 4370                render: Arc::new(|_| gpui::div().into_any_element()),
 4371                priority: 0,
 4372            }],
 4373            None,
 4374            cx,
 4375        );
 4376    });
 4377
 4378    // Move down so that the cursor touches the block.
 4379    cx.update_editor(|editor, window, cx| {
 4380        editor.move_down(&Default::default(), window, cx);
 4381    });
 4382    cx.assert_editor_state(
 4383        &"
 4384            zero
 4385            «one
 4386            two
 4387            threeˇ»
 4388            four
 4389            five
 4390        "
 4391        .unindent(),
 4392    );
 4393
 4394    // Move down past the block.
 4395    cx.update_editor(|editor, window, cx| {
 4396        editor.move_down(&Default::default(), window, cx);
 4397    });
 4398    cx.assert_editor_state(
 4399        &"
 4400            zero
 4401            one
 4402            two
 4403            three
 4404            ˇfour
 4405            five
 4406        "
 4407        .unindent(),
 4408    );
 4409}
 4410
 4411#[gpui::test]
 4412fn test_transpose(cx: &mut TestAppContext) {
 4413    init_test(cx, |_| {});
 4414
 4415    _ = cx.add_window(|window, cx| {
 4416        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4417        editor.set_style(EditorStyle::default(), window, cx);
 4418        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "bac");
 4421        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4422
 4423        editor.transpose(&Default::default(), window, cx);
 4424        assert_eq!(editor.text(cx), "bca");
 4425        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4426
 4427        editor.transpose(&Default::default(), window, cx);
 4428        assert_eq!(editor.text(cx), "bac");
 4429        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4430
 4431        editor
 4432    });
 4433
 4434    _ = cx.add_window(|window, cx| {
 4435        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4436        editor.set_style(EditorStyle::default(), window, cx);
 4437        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "acb\nde");
 4440        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4441
 4442        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4443        editor.transpose(&Default::default(), window, cx);
 4444        assert_eq!(editor.text(cx), "acbd\ne");
 4445        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4446
 4447        editor.transpose(&Default::default(), window, cx);
 4448        assert_eq!(editor.text(cx), "acbde\n");
 4449        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4450
 4451        editor.transpose(&Default::default(), window, cx);
 4452        assert_eq!(editor.text(cx), "acbd\ne");
 4453        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4454
 4455        editor
 4456    });
 4457
 4458    _ = cx.add_window(|window, cx| {
 4459        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4460        editor.set_style(EditorStyle::default(), window, cx);
 4461        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4462        editor.transpose(&Default::default(), window, cx);
 4463        assert_eq!(editor.text(cx), "bacd\ne");
 4464        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4465
 4466        editor.transpose(&Default::default(), window, cx);
 4467        assert_eq!(editor.text(cx), "bcade\n");
 4468        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4469
 4470        editor.transpose(&Default::default(), window, cx);
 4471        assert_eq!(editor.text(cx), "bcda\ne");
 4472        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4473
 4474        editor.transpose(&Default::default(), window, cx);
 4475        assert_eq!(editor.text(cx), "bcade\n");
 4476        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4477
 4478        editor.transpose(&Default::default(), window, cx);
 4479        assert_eq!(editor.text(cx), "bcaed\n");
 4480        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4481
 4482        editor
 4483    });
 4484
 4485    _ = cx.add_window(|window, cx| {
 4486        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4487        editor.set_style(EditorStyle::default(), window, cx);
 4488        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4489        editor.transpose(&Default::default(), window, cx);
 4490        assert_eq!(editor.text(cx), "🏀🍐✋");
 4491        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4492
 4493        editor.transpose(&Default::default(), window, cx);
 4494        assert_eq!(editor.text(cx), "🏀✋🍐");
 4495        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4496
 4497        editor.transpose(&Default::default(), window, cx);
 4498        assert_eq!(editor.text(cx), "🏀🍐✋");
 4499        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4500
 4501        editor
 4502    });
 4503}
 4504
 4505#[gpui::test]
 4506async fn test_rewrap(cx: &mut TestAppContext) {
 4507    init_test(cx, |settings| {
 4508        settings.languages.extend([
 4509            (
 4510                "Markdown".into(),
 4511                LanguageSettingsContent {
 4512                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4513                    ..Default::default()
 4514                },
 4515            ),
 4516            (
 4517                "Plain Text".into(),
 4518                LanguageSettingsContent {
 4519                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4520                    ..Default::default()
 4521                },
 4522            ),
 4523        ])
 4524    });
 4525
 4526    let mut cx = EditorTestContext::new(cx).await;
 4527
 4528    let language_with_c_comments = Arc::new(Language::new(
 4529        LanguageConfig {
 4530            line_comments: vec!["// ".into()],
 4531            ..LanguageConfig::default()
 4532        },
 4533        None,
 4534    ));
 4535    let language_with_pound_comments = Arc::new(Language::new(
 4536        LanguageConfig {
 4537            line_comments: vec!["# ".into()],
 4538            ..LanguageConfig::default()
 4539        },
 4540        None,
 4541    ));
 4542    let markdown_language = Arc::new(Language::new(
 4543        LanguageConfig {
 4544            name: "Markdown".into(),
 4545            ..LanguageConfig::default()
 4546        },
 4547        None,
 4548    ));
 4549    let language_with_doc_comments = Arc::new(Language::new(
 4550        LanguageConfig {
 4551            line_comments: vec!["// ".into(), "/// ".into()],
 4552            ..LanguageConfig::default()
 4553        },
 4554        Some(tree_sitter_rust::LANGUAGE.into()),
 4555    ));
 4556
 4557    let plaintext_language = Arc::new(Language::new(
 4558        LanguageConfig {
 4559            name: "Plain Text".into(),
 4560            ..LanguageConfig::default()
 4561        },
 4562        None,
 4563    ));
 4564
 4565    assert_rewrap(
 4566        indoc! {"
 4567            // ˇ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.
 4568        "},
 4569        indoc! {"
 4570            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4571            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4572            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4573            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4574            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4575            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4576            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4577            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4578            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4579            // porttitor id. Aliquam id accumsan eros.
 4580        "},
 4581        language_with_c_comments.clone(),
 4582        &mut cx,
 4583    );
 4584
 4585    // Test that rewrapping works inside of a selection
 4586    assert_rewrap(
 4587        indoc! {"
 4588            «// 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.ˇ»
 4589        "},
 4590        indoc! {"
 4591            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4592            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4593            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4594            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4595            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4596            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4597            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4598            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4599            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4600            // porttitor id. Aliquam id accumsan eros.ˇ»
 4601        "},
 4602        language_with_c_comments.clone(),
 4603        &mut cx,
 4604    );
 4605
 4606    // Test that cursors that expand to the same region are collapsed.
 4607    assert_rewrap(
 4608        indoc! {"
 4609            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4610            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4611            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4612            // ˇ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.
 4613        "},
 4614        indoc! {"
 4615            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4616            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4617            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4618            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4619            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4620            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4621            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4622            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4623            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4624            // porttitor id. Aliquam id accumsan eros.
 4625        "},
 4626        language_with_c_comments.clone(),
 4627        &mut cx,
 4628    );
 4629
 4630    // Test that non-contiguous selections are treated separately.
 4631    assert_rewrap(
 4632        indoc! {"
 4633            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4634            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4635            //
 4636            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4637            // ˇ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.
 4638        "},
 4639        indoc! {"
 4640            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4641            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4642            // auctor, eu lacinia sapien scelerisque.
 4643            //
 4644            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4645            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4646            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4647            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4648            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4649            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4650            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4651        "},
 4652        language_with_c_comments.clone(),
 4653        &mut cx,
 4654    );
 4655
 4656    // Test that different comment prefixes are supported.
 4657    assert_rewrap(
 4658        indoc! {"
 4659            # ˇ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.
 4660        "},
 4661        indoc! {"
 4662            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4663            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4664            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4665            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4666            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4667            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4668            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4669            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4670            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4671            # accumsan eros.
 4672        "},
 4673        language_with_pound_comments.clone(),
 4674        &mut cx,
 4675    );
 4676
 4677    // Test that rewrapping is ignored outside of comments in most languages.
 4678    assert_rewrap(
 4679        indoc! {"
 4680            /// Adds two numbers.
 4681            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4682            fn add(a: u32, b: u32) -> u32 {
 4683                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ˇ
 4684            }
 4685        "},
 4686        indoc! {"
 4687            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4688            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4689            fn add(a: u32, b: u32) -> u32 {
 4690                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ˇ
 4691            }
 4692        "},
 4693        language_with_doc_comments.clone(),
 4694        &mut cx,
 4695    );
 4696
 4697    // Test that rewrapping works in Markdown and Plain Text languages.
 4698    assert_rewrap(
 4699        indoc! {"
 4700            # Hello
 4701
 4702            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.
 4703        "},
 4704        indoc! {"
 4705            # Hello
 4706
 4707            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4708            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4709            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4710            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4711            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4712            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4713            Integer sit amet scelerisque nisi.
 4714        "},
 4715        markdown_language,
 4716        &mut cx,
 4717    );
 4718
 4719    assert_rewrap(
 4720        indoc! {"
 4721            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.
 4722        "},
 4723        indoc! {"
 4724            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4725            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4726            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4727            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4728            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4729            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4730            Integer sit amet scelerisque nisi.
 4731        "},
 4732        plaintext_language,
 4733        &mut cx,
 4734    );
 4735
 4736    // Test rewrapping unaligned comments in a selection.
 4737    assert_rewrap(
 4738        indoc! {"
 4739            fn foo() {
 4740                if true {
 4741            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4742            // Praesent semper egestas tellus id dignissim.ˇ»
 4743                    do_something();
 4744                } else {
 4745                    //
 4746                }
 4747            }
 4748        "},
 4749        indoc! {"
 4750            fn foo() {
 4751                if true {
 4752            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4753                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4754                    // egestas tellus id dignissim.ˇ»
 4755                    do_something();
 4756                } else {
 4757                    //
 4758                }
 4759            }
 4760        "},
 4761        language_with_doc_comments.clone(),
 4762        &mut cx,
 4763    );
 4764
 4765    assert_rewrap(
 4766        indoc! {"
 4767            fn foo() {
 4768                if true {
 4769            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4770            // Praesent semper egestas tellus id dignissim.»
 4771                    do_something();
 4772                } else {
 4773                    //
 4774                }
 4775
 4776            }
 4777        "},
 4778        indoc! {"
 4779            fn foo() {
 4780                if true {
 4781            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4782                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4783                    // egestas tellus id dignissim.»
 4784                    do_something();
 4785                } else {
 4786                    //
 4787                }
 4788
 4789            }
 4790        "},
 4791        language_with_doc_comments.clone(),
 4792        &mut cx,
 4793    );
 4794
 4795    #[track_caller]
 4796    fn assert_rewrap(
 4797        unwrapped_text: &str,
 4798        wrapped_text: &str,
 4799        language: Arc<Language>,
 4800        cx: &mut EditorTestContext,
 4801    ) {
 4802        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4803        cx.set_state(unwrapped_text);
 4804        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4805        cx.assert_editor_state(wrapped_text);
 4806    }
 4807}
 4808
 4809#[gpui::test]
 4810async fn test_hard_wrap(cx: &mut TestAppContext) {
 4811    init_test(cx, |_| {});
 4812    let mut cx = EditorTestContext::new(cx).await;
 4813
 4814    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4815    cx.update_editor(|editor, _, cx| {
 4816        editor.set_hard_wrap(Some(14), cx);
 4817    });
 4818
 4819    cx.set_state(indoc!(
 4820        "
 4821        one two three ˇ
 4822        "
 4823    ));
 4824    cx.simulate_input("four");
 4825    cx.run_until_parked();
 4826
 4827    cx.assert_editor_state(indoc!(
 4828        "
 4829        one two three
 4830        fourˇ
 4831        "
 4832    ));
 4833
 4834    cx.update_editor(|editor, window, cx| {
 4835        editor.newline(&Default::default(), window, cx);
 4836    });
 4837    cx.run_until_parked();
 4838    cx.assert_editor_state(indoc!(
 4839        "
 4840        one two three
 4841        four
 4842        ˇ
 4843        "
 4844    ));
 4845
 4846    cx.simulate_input("five");
 4847    cx.run_until_parked();
 4848    cx.assert_editor_state(indoc!(
 4849        "
 4850        one two three
 4851        four
 4852        fiveˇ
 4853        "
 4854    ));
 4855
 4856    cx.update_editor(|editor, window, cx| {
 4857        editor.newline(&Default::default(), window, cx);
 4858    });
 4859    cx.run_until_parked();
 4860    cx.simulate_input("# ");
 4861    cx.run_until_parked();
 4862    cx.assert_editor_state(indoc!(
 4863        "
 4864        one two three
 4865        four
 4866        five
 4867        # ˇ
 4868        "
 4869    ));
 4870
 4871    cx.update_editor(|editor, window, cx| {
 4872        editor.newline(&Default::default(), window, cx);
 4873    });
 4874    cx.run_until_parked();
 4875    cx.assert_editor_state(indoc!(
 4876        "
 4877        one two three
 4878        four
 4879        five
 4880        #\x20
 4881 4882        "
 4883    ));
 4884
 4885    cx.simulate_input(" 6");
 4886    cx.run_until_parked();
 4887    cx.assert_editor_state(indoc!(
 4888        "
 4889        one two three
 4890        four
 4891        five
 4892        #
 4893        # 6ˇ
 4894        "
 4895    ));
 4896}
 4897
 4898#[gpui::test]
 4899async fn test_clipboard(cx: &mut TestAppContext) {
 4900    init_test(cx, |_| {});
 4901
 4902    let mut cx = EditorTestContext::new(cx).await;
 4903
 4904    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4905    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4906    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4907
 4908    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4909    cx.set_state("two ˇfour ˇsix ˇ");
 4910    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4911    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4912
 4913    // Paste again but with only two cursors. Since the number of cursors doesn't
 4914    // match the number of slices in the clipboard, the entire clipboard text
 4915    // is pasted at each cursor.
 4916    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4917    cx.update_editor(|e, window, cx| {
 4918        e.handle_input("( ", window, cx);
 4919        e.paste(&Paste, window, cx);
 4920        e.handle_input(") ", window, cx);
 4921    });
 4922    cx.assert_editor_state(
 4923        &([
 4924            "( one✅ ",
 4925            "three ",
 4926            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4927            "three ",
 4928            "five ) ˇ",
 4929        ]
 4930        .join("\n")),
 4931    );
 4932
 4933    // Cut with three selections, one of which is full-line.
 4934    cx.set_state(indoc! {"
 4935        1«2ˇ»3
 4936        4ˇ567
 4937        «8ˇ»9"});
 4938    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4939    cx.assert_editor_state(indoc! {"
 4940        1ˇ3
 4941        ˇ9"});
 4942
 4943    // Paste with three selections, noticing how the copied selection that was full-line
 4944    // gets inserted before the second cursor.
 4945    cx.set_state(indoc! {"
 4946        1ˇ3
 4947 4948        «oˇ»ne"});
 4949    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4950    cx.assert_editor_state(indoc! {"
 4951        12ˇ3
 4952        4567
 4953 4954        8ˇne"});
 4955
 4956    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4957    cx.set_state(indoc! {"
 4958        The quick brown
 4959        fox juˇmps over
 4960        the lazy dog"});
 4961    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4962    assert_eq!(
 4963        cx.read_from_clipboard()
 4964            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4965        Some("fox jumps over\n".to_string())
 4966    );
 4967
 4968    // Paste with three selections, noticing how the copied full-line selection is inserted
 4969    // before the empty selections but replaces the selection that is non-empty.
 4970    cx.set_state(indoc! {"
 4971        Tˇhe quick brown
 4972        «foˇ»x jumps over
 4973        tˇhe lazy dog"});
 4974    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4975    cx.assert_editor_state(indoc! {"
 4976        fox jumps over
 4977        Tˇhe quick brown
 4978        fox jumps over
 4979        ˇx jumps over
 4980        fox jumps over
 4981        tˇhe lazy dog"});
 4982}
 4983
 4984#[gpui::test]
 4985async fn test_copy_trim(cx: &mut TestAppContext) {
 4986    init_test(cx, |_| {});
 4987
 4988    let mut cx = EditorTestContext::new(cx).await;
 4989    cx.set_state(
 4990        r#"            «for selection in selections.iter() {
 4991            let mut start = selection.start;
 4992            let mut end = selection.end;
 4993            let is_entire_line = selection.is_empty();
 4994            if is_entire_line {
 4995                start = Point::new(start.row, 0);ˇ»
 4996                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4997            }
 4998        "#,
 4999    );
 5000    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5001    assert_eq!(
 5002        cx.read_from_clipboard()
 5003            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5004        Some(
 5005            "for selection in selections.iter() {
 5006            let mut start = selection.start;
 5007            let mut end = selection.end;
 5008            let is_entire_line = selection.is_empty();
 5009            if is_entire_line {
 5010                start = Point::new(start.row, 0);"
 5011                .to_string()
 5012        ),
 5013        "Regular copying preserves all indentation selected",
 5014    );
 5015    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5016    assert_eq!(
 5017        cx.read_from_clipboard()
 5018            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5019        Some(
 5020            "for selection in selections.iter() {
 5021let mut start = selection.start;
 5022let mut end = selection.end;
 5023let is_entire_line = selection.is_empty();
 5024if is_entire_line {
 5025    start = Point::new(start.row, 0);"
 5026                .to_string()
 5027        ),
 5028        "Copying with stripping should strip all leading whitespaces"
 5029    );
 5030
 5031    cx.set_state(
 5032        r#"       «     for selection in selections.iter() {
 5033            let mut start = selection.start;
 5034            let mut end = selection.end;
 5035            let is_entire_line = selection.is_empty();
 5036            if is_entire_line {
 5037                start = Point::new(start.row, 0);ˇ»
 5038                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5039            }
 5040        "#,
 5041    );
 5042    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5043    assert_eq!(
 5044        cx.read_from_clipboard()
 5045            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5046        Some(
 5047            "     for selection in selections.iter() {
 5048            let mut start = selection.start;
 5049            let mut end = selection.end;
 5050            let is_entire_line = selection.is_empty();
 5051            if is_entire_line {
 5052                start = Point::new(start.row, 0);"
 5053                .to_string()
 5054        ),
 5055        "Regular copying preserves all indentation selected",
 5056    );
 5057    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5058    assert_eq!(
 5059        cx.read_from_clipboard()
 5060            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5061        Some(
 5062            "for selection in selections.iter() {
 5063let mut start = selection.start;
 5064let mut end = selection.end;
 5065let is_entire_line = selection.is_empty();
 5066if is_entire_line {
 5067    start = Point::new(start.row, 0);"
 5068                .to_string()
 5069        ),
 5070        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5071    );
 5072
 5073    cx.set_state(
 5074        r#"       «ˇ     for selection in selections.iter() {
 5075            let mut start = selection.start;
 5076            let mut end = selection.end;
 5077            let is_entire_line = selection.is_empty();
 5078            if is_entire_line {
 5079                start = Point::new(start.row, 0);»
 5080                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5081            }
 5082        "#,
 5083    );
 5084    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5085    assert_eq!(
 5086        cx.read_from_clipboard()
 5087            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5088        Some(
 5089            "     for selection in selections.iter() {
 5090            let mut start = selection.start;
 5091            let mut end = selection.end;
 5092            let is_entire_line = selection.is_empty();
 5093            if is_entire_line {
 5094                start = Point::new(start.row, 0);"
 5095                .to_string()
 5096        ),
 5097        "Regular copying for reverse selection works the same",
 5098    );
 5099    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5100    assert_eq!(
 5101        cx.read_from_clipboard()
 5102            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5103        Some(
 5104            "for selection in selections.iter() {
 5105let mut start = selection.start;
 5106let mut end = selection.end;
 5107let is_entire_line = selection.is_empty();
 5108if is_entire_line {
 5109    start = Point::new(start.row, 0);"
 5110                .to_string()
 5111        ),
 5112        "Copying with stripping for reverse selection works the same"
 5113    );
 5114
 5115    cx.set_state(
 5116        r#"            for selection «in selections.iter() {
 5117            let mut start = selection.start;
 5118            let mut end = selection.end;
 5119            let is_entire_line = selection.is_empty();
 5120            if is_entire_line {
 5121                start = Point::new(start.row, 0);ˇ»
 5122                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5123            }
 5124        "#,
 5125    );
 5126    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5127    assert_eq!(
 5128        cx.read_from_clipboard()
 5129            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5130        Some(
 5131            "in selections.iter() {
 5132            let mut start = selection.start;
 5133            let mut end = selection.end;
 5134            let is_entire_line = selection.is_empty();
 5135            if is_entire_line {
 5136                start = Point::new(start.row, 0);"
 5137                .to_string()
 5138        ),
 5139        "When selecting past the indent, the copying works as usual",
 5140    );
 5141    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5142    assert_eq!(
 5143        cx.read_from_clipboard()
 5144            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5145        Some(
 5146            "in selections.iter() {
 5147            let mut start = selection.start;
 5148            let mut end = selection.end;
 5149            let is_entire_line = selection.is_empty();
 5150            if is_entire_line {
 5151                start = Point::new(start.row, 0);"
 5152                .to_string()
 5153        ),
 5154        "When selecting past the indent, nothing is trimmed"
 5155    );
 5156
 5157    cx.set_state(
 5158        r#"            «for selection in selections.iter() {
 5159            let mut start = selection.start;
 5160
 5161            let mut end = selection.end;
 5162            let is_entire_line = selection.is_empty();
 5163            if is_entire_line {
 5164                start = Point::new(start.row, 0);
 5165ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5166            }
 5167        "#,
 5168    );
 5169    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5170    assert_eq!(
 5171        cx.read_from_clipboard()
 5172            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5173        Some(
 5174            "for selection in selections.iter() {
 5175let mut start = selection.start;
 5176
 5177let mut end = selection.end;
 5178let is_entire_line = selection.is_empty();
 5179if is_entire_line {
 5180    start = Point::new(start.row, 0);
 5181"
 5182            .to_string()
 5183        ),
 5184        "Copying with stripping should ignore empty lines"
 5185    );
 5186}
 5187
 5188#[gpui::test]
 5189async fn test_paste_multiline(cx: &mut TestAppContext) {
 5190    init_test(cx, |_| {});
 5191
 5192    let mut cx = EditorTestContext::new(cx).await;
 5193    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5194
 5195    // Cut an indented block, without the leading whitespace.
 5196    cx.set_state(indoc! {"
 5197        const a: B = (
 5198            c(),
 5199            «d(
 5200                e,
 5201                f
 5202            )ˇ»
 5203        );
 5204    "});
 5205    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5206    cx.assert_editor_state(indoc! {"
 5207        const a: B = (
 5208            c(),
 5209            ˇ
 5210        );
 5211    "});
 5212
 5213    // Paste it at the same position.
 5214    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5215    cx.assert_editor_state(indoc! {"
 5216        const a: B = (
 5217            c(),
 5218            d(
 5219                e,
 5220                f
 5221 5222        );
 5223    "});
 5224
 5225    // Paste it at a line with a lower indent level.
 5226    cx.set_state(indoc! {"
 5227        ˇ
 5228        const a: B = (
 5229            c(),
 5230        );
 5231    "});
 5232    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5233    cx.assert_editor_state(indoc! {"
 5234        d(
 5235            e,
 5236            f
 5237 5238        const a: B = (
 5239            c(),
 5240        );
 5241    "});
 5242
 5243    // Cut an indented block, with the leading whitespace.
 5244    cx.set_state(indoc! {"
 5245        const a: B = (
 5246            c(),
 5247        «    d(
 5248                e,
 5249                f
 5250            )
 5251        ˇ»);
 5252    "});
 5253    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5254    cx.assert_editor_state(indoc! {"
 5255        const a: B = (
 5256            c(),
 5257        ˇ);
 5258    "});
 5259
 5260    // Paste it at the same position.
 5261    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5262    cx.assert_editor_state(indoc! {"
 5263        const a: B = (
 5264            c(),
 5265            d(
 5266                e,
 5267                f
 5268            )
 5269        ˇ);
 5270    "});
 5271
 5272    // Paste it at a line with a higher indent level.
 5273    cx.set_state(indoc! {"
 5274        const a: B = (
 5275            c(),
 5276            d(
 5277                e,
 5278 5279            )
 5280        );
 5281    "});
 5282    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5283    cx.assert_editor_state(indoc! {"
 5284        const a: B = (
 5285            c(),
 5286            d(
 5287                e,
 5288                f    d(
 5289                    e,
 5290                    f
 5291                )
 5292        ˇ
 5293            )
 5294        );
 5295    "});
 5296
 5297    // Copy an indented block, starting mid-line
 5298    cx.set_state(indoc! {"
 5299        const a: B = (
 5300            c(),
 5301            somethin«g(
 5302                e,
 5303                f
 5304            )ˇ»
 5305        );
 5306    "});
 5307    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5308
 5309    // Paste it on a line with a lower indent level
 5310    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5311    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5312    cx.assert_editor_state(indoc! {"
 5313        const a: B = (
 5314            c(),
 5315            something(
 5316                e,
 5317                f
 5318            )
 5319        );
 5320        g(
 5321            e,
 5322            f
 5323"});
 5324}
 5325
 5326#[gpui::test]
 5327async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5328    init_test(cx, |_| {});
 5329
 5330    cx.write_to_clipboard(ClipboardItem::new_string(
 5331        "    d(\n        e\n    );\n".into(),
 5332    ));
 5333
 5334    let mut cx = EditorTestContext::new(cx).await;
 5335    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5336
 5337    cx.set_state(indoc! {"
 5338        fn a() {
 5339            b();
 5340            if c() {
 5341                ˇ
 5342            }
 5343        }
 5344    "});
 5345
 5346    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5347    cx.assert_editor_state(indoc! {"
 5348        fn a() {
 5349            b();
 5350            if c() {
 5351                d(
 5352                    e
 5353                );
 5354        ˇ
 5355            }
 5356        }
 5357    "});
 5358
 5359    cx.set_state(indoc! {"
 5360        fn a() {
 5361            b();
 5362            ˇ
 5363        }
 5364    "});
 5365
 5366    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5367    cx.assert_editor_state(indoc! {"
 5368        fn a() {
 5369            b();
 5370            d(
 5371                e
 5372            );
 5373        ˇ
 5374        }
 5375    "});
 5376}
 5377
 5378#[gpui::test]
 5379fn test_select_all(cx: &mut TestAppContext) {
 5380    init_test(cx, |_| {});
 5381
 5382    let editor = cx.add_window(|window, cx| {
 5383        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5384        build_editor(buffer, window, cx)
 5385    });
 5386    _ = editor.update(cx, |editor, window, cx| {
 5387        editor.select_all(&SelectAll, window, cx);
 5388        assert_eq!(
 5389            editor.selections.display_ranges(cx),
 5390            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5391        );
 5392    });
 5393}
 5394
 5395#[gpui::test]
 5396fn test_select_line(cx: &mut TestAppContext) {
 5397    init_test(cx, |_| {});
 5398
 5399    let editor = cx.add_window(|window, cx| {
 5400        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5401        build_editor(buffer, window, cx)
 5402    });
 5403    _ = editor.update(cx, |editor, window, cx| {
 5404        editor.change_selections(None, window, cx, |s| {
 5405            s.select_display_ranges([
 5406                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5407                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5408                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5409                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5410            ])
 5411        });
 5412        editor.select_line(&SelectLine, window, cx);
 5413        assert_eq!(
 5414            editor.selections.display_ranges(cx),
 5415            vec![
 5416                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5417                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5418            ]
 5419        );
 5420    });
 5421
 5422    _ = editor.update(cx, |editor, window, cx| {
 5423        editor.select_line(&SelectLine, window, cx);
 5424        assert_eq!(
 5425            editor.selections.display_ranges(cx),
 5426            vec![
 5427                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5428                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5429            ]
 5430        );
 5431    });
 5432
 5433    _ = editor.update(cx, |editor, window, cx| {
 5434        editor.select_line(&SelectLine, window, cx);
 5435        assert_eq!(
 5436            editor.selections.display_ranges(cx),
 5437            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5438        );
 5439    });
 5440}
 5441
 5442#[gpui::test]
 5443async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5444    init_test(cx, |_| {});
 5445    let mut cx = EditorTestContext::new(cx).await;
 5446
 5447    #[track_caller]
 5448    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5449        cx.set_state(initial_state);
 5450        cx.update_editor(|e, window, cx| {
 5451            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5452        });
 5453        cx.assert_editor_state(expected_state);
 5454    }
 5455
 5456    // Selection starts and ends at the middle of lines, left-to-right
 5457    test(
 5458        &mut cx,
 5459        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5460        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5461    );
 5462    // Same thing, right-to-left
 5463    test(
 5464        &mut cx,
 5465        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5466        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5467    );
 5468
 5469    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5470    test(
 5471        &mut cx,
 5472        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5473        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5474    );
 5475    // Same thing, right-to-left
 5476    test(
 5477        &mut cx,
 5478        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5479        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5480    );
 5481
 5482    // Whole buffer, left-to-right, last line ends with newline
 5483    test(
 5484        &mut cx,
 5485        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5486        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5487    );
 5488    // Same thing, right-to-left
 5489    test(
 5490        &mut cx,
 5491        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5492        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5493    );
 5494
 5495    // Starts at the end of a line, ends at the start of another
 5496    test(
 5497        &mut cx,
 5498        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5499        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5500    );
 5501}
 5502
 5503#[gpui::test]
 5504async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5505    init_test(cx, |_| {});
 5506
 5507    let editor = cx.add_window(|window, cx| {
 5508        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5509        build_editor(buffer, window, cx)
 5510    });
 5511
 5512    // setup
 5513    _ = editor.update(cx, |editor, window, cx| {
 5514        editor.fold_creases(
 5515            vec![
 5516                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5517                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5518                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5519            ],
 5520            true,
 5521            window,
 5522            cx,
 5523        );
 5524        assert_eq!(
 5525            editor.display_text(cx),
 5526            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5527        );
 5528    });
 5529
 5530    _ = editor.update(cx, |editor, window, cx| {
 5531        editor.change_selections(None, window, cx, |s| {
 5532            s.select_display_ranges([
 5533                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5534                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5535                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5536                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5537            ])
 5538        });
 5539        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5540        assert_eq!(
 5541            editor.display_text(cx),
 5542            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5543        );
 5544    });
 5545    EditorTestContext::for_editor(editor, cx)
 5546        .await
 5547        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5548
 5549    _ = editor.update(cx, |editor, window, cx| {
 5550        editor.change_selections(None, window, cx, |s| {
 5551            s.select_display_ranges([
 5552                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5553            ])
 5554        });
 5555        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5556        assert_eq!(
 5557            editor.display_text(cx),
 5558            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5559        );
 5560        assert_eq!(
 5561            editor.selections.display_ranges(cx),
 5562            [
 5563                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5564                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5565                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5566                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5567                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5568                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5569                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5570            ]
 5571        );
 5572    });
 5573    EditorTestContext::for_editor(editor, cx)
 5574        .await
 5575        .assert_editor_state(
 5576            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5577        );
 5578}
 5579
 5580#[gpui::test]
 5581async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5582    init_test(cx, |_| {});
 5583
 5584    let mut cx = EditorTestContext::new(cx).await;
 5585
 5586    cx.set_state(indoc!(
 5587        r#"abc
 5588           defˇghi
 5589
 5590           jk
 5591           nlmo
 5592           "#
 5593    ));
 5594
 5595    cx.update_editor(|editor, window, cx| {
 5596        editor.add_selection_above(&Default::default(), window, cx);
 5597    });
 5598
 5599    cx.assert_editor_state(indoc!(
 5600        r#"abcˇ
 5601           defˇghi
 5602
 5603           jk
 5604           nlmo
 5605           "#
 5606    ));
 5607
 5608    cx.update_editor(|editor, window, cx| {
 5609        editor.add_selection_above(&Default::default(), window, cx);
 5610    });
 5611
 5612    cx.assert_editor_state(indoc!(
 5613        r#"abcˇ
 5614            defˇghi
 5615
 5616            jk
 5617            nlmo
 5618            "#
 5619    ));
 5620
 5621    cx.update_editor(|editor, window, cx| {
 5622        editor.add_selection_below(&Default::default(), window, cx);
 5623    });
 5624
 5625    cx.assert_editor_state(indoc!(
 5626        r#"abc
 5627           defˇghi
 5628
 5629           jk
 5630           nlmo
 5631           "#
 5632    ));
 5633
 5634    cx.update_editor(|editor, window, cx| {
 5635        editor.undo_selection(&Default::default(), window, cx);
 5636    });
 5637
 5638    cx.assert_editor_state(indoc!(
 5639        r#"abcˇ
 5640           defˇghi
 5641
 5642           jk
 5643           nlmo
 5644           "#
 5645    ));
 5646
 5647    cx.update_editor(|editor, window, cx| {
 5648        editor.redo_selection(&Default::default(), window, cx);
 5649    });
 5650
 5651    cx.assert_editor_state(indoc!(
 5652        r#"abc
 5653           defˇghi
 5654
 5655           jk
 5656           nlmo
 5657           "#
 5658    ));
 5659
 5660    cx.update_editor(|editor, window, cx| {
 5661        editor.add_selection_below(&Default::default(), window, cx);
 5662    });
 5663
 5664    cx.assert_editor_state(indoc!(
 5665        r#"abc
 5666           defˇghi
 5667
 5668           jk
 5669           nlmˇo
 5670           "#
 5671    ));
 5672
 5673    cx.update_editor(|editor, window, cx| {
 5674        editor.add_selection_below(&Default::default(), window, cx);
 5675    });
 5676
 5677    cx.assert_editor_state(indoc!(
 5678        r#"abc
 5679           defˇghi
 5680
 5681           jk
 5682           nlmˇo
 5683           "#
 5684    ));
 5685
 5686    // change selections
 5687    cx.set_state(indoc!(
 5688        r#"abc
 5689           def«ˇg»hi
 5690
 5691           jk
 5692           nlmo
 5693           "#
 5694    ));
 5695
 5696    cx.update_editor(|editor, window, cx| {
 5697        editor.add_selection_below(&Default::default(), window, cx);
 5698    });
 5699
 5700    cx.assert_editor_state(indoc!(
 5701        r#"abc
 5702           def«ˇg»hi
 5703
 5704           jk
 5705           nlm«ˇo»
 5706           "#
 5707    ));
 5708
 5709    cx.update_editor(|editor, window, cx| {
 5710        editor.add_selection_below(&Default::default(), window, cx);
 5711    });
 5712
 5713    cx.assert_editor_state(indoc!(
 5714        r#"abc
 5715           def«ˇg»hi
 5716
 5717           jk
 5718           nlm«ˇo»
 5719           "#
 5720    ));
 5721
 5722    cx.update_editor(|editor, window, cx| {
 5723        editor.add_selection_above(&Default::default(), window, cx);
 5724    });
 5725
 5726    cx.assert_editor_state(indoc!(
 5727        r#"abc
 5728           def«ˇg»hi
 5729
 5730           jk
 5731           nlmo
 5732           "#
 5733    ));
 5734
 5735    cx.update_editor(|editor, window, cx| {
 5736        editor.add_selection_above(&Default::default(), window, cx);
 5737    });
 5738
 5739    cx.assert_editor_state(indoc!(
 5740        r#"abc
 5741           def«ˇg»hi
 5742
 5743           jk
 5744           nlmo
 5745           "#
 5746    ));
 5747
 5748    // Change selections again
 5749    cx.set_state(indoc!(
 5750        r#"a«bc
 5751           defgˇ»hi
 5752
 5753           jk
 5754           nlmo
 5755           "#
 5756    ));
 5757
 5758    cx.update_editor(|editor, window, cx| {
 5759        editor.add_selection_below(&Default::default(), window, cx);
 5760    });
 5761
 5762    cx.assert_editor_state(indoc!(
 5763        r#"a«bcˇ»
 5764           d«efgˇ»hi
 5765
 5766           j«kˇ»
 5767           nlmo
 5768           "#
 5769    ));
 5770
 5771    cx.update_editor(|editor, window, cx| {
 5772        editor.add_selection_below(&Default::default(), window, cx);
 5773    });
 5774    cx.assert_editor_state(indoc!(
 5775        r#"a«bcˇ»
 5776           d«efgˇ»hi
 5777
 5778           j«kˇ»
 5779           n«lmoˇ»
 5780           "#
 5781    ));
 5782    cx.update_editor(|editor, window, cx| {
 5783        editor.add_selection_above(&Default::default(), window, cx);
 5784    });
 5785
 5786    cx.assert_editor_state(indoc!(
 5787        r#"a«bcˇ»
 5788           d«efgˇ»hi
 5789
 5790           j«kˇ»
 5791           nlmo
 5792           "#
 5793    ));
 5794
 5795    // Change selections again
 5796    cx.set_state(indoc!(
 5797        r#"abc
 5798           d«ˇefghi
 5799
 5800           jk
 5801           nlm»o
 5802           "#
 5803    ));
 5804
 5805    cx.update_editor(|editor, window, cx| {
 5806        editor.add_selection_above(&Default::default(), window, cx);
 5807    });
 5808
 5809    cx.assert_editor_state(indoc!(
 5810        r#"a«ˇbc»
 5811           d«ˇef»ghi
 5812
 5813           j«ˇk»
 5814           n«ˇlm»o
 5815           "#
 5816    ));
 5817
 5818    cx.update_editor(|editor, window, cx| {
 5819        editor.add_selection_below(&Default::default(), window, cx);
 5820    });
 5821
 5822    cx.assert_editor_state(indoc!(
 5823        r#"abc
 5824           d«ˇef»ghi
 5825
 5826           j«ˇk»
 5827           n«ˇlm»o
 5828           "#
 5829    ));
 5830}
 5831
 5832#[gpui::test]
 5833async fn test_select_next(cx: &mut TestAppContext) {
 5834    init_test(cx, |_| {});
 5835
 5836    let mut cx = EditorTestContext::new(cx).await;
 5837    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5838
 5839    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5840        .unwrap();
 5841    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5842
 5843    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5844        .unwrap();
 5845    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5846
 5847    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5848    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5849
 5850    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5851    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5852
 5853    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5854        .unwrap();
 5855    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5856
 5857    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5858        .unwrap();
 5859    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5860
 5861    // Test selection direction should be preserved
 5862    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5863
 5864    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5865        .unwrap();
 5866    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5867}
 5868
 5869#[gpui::test]
 5870async fn test_select_all_matches(cx: &mut TestAppContext) {
 5871    init_test(cx, |_| {});
 5872
 5873    let mut cx = EditorTestContext::new(cx).await;
 5874
 5875    // Test caret-only selections
 5876    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5877    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5878        .unwrap();
 5879    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5880
 5881    // Test left-to-right selections
 5882    cx.set_state("abc\n«abcˇ»\nabc");
 5883    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5884        .unwrap();
 5885    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5886
 5887    // Test right-to-left selections
 5888    cx.set_state("abc\n«ˇabc»\nabc");
 5889    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5890        .unwrap();
 5891    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5892
 5893    // Test selecting whitespace with caret selection
 5894    cx.set_state("abc\nˇ   abc\nabc");
 5895    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5896        .unwrap();
 5897    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5898
 5899    // Test selecting whitespace with left-to-right selection
 5900    cx.set_state("abc\n«ˇ  »abc\nabc");
 5901    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5902        .unwrap();
 5903    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5904
 5905    // Test no matches with right-to-left selection
 5906    cx.set_state("abc\n«  ˇ»abc\nabc");
 5907    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5908        .unwrap();
 5909    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5910}
 5911
 5912#[gpui::test]
 5913async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5914    init_test(cx, |_| {});
 5915
 5916    let mut cx = EditorTestContext::new(cx).await;
 5917
 5918    let large_body_1 = "\nd".repeat(200);
 5919    let large_body_2 = "\ne".repeat(200);
 5920
 5921    cx.set_state(&format!(
 5922        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5923    ));
 5924    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5925        let scroll_position = editor.scroll_position(cx);
 5926        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5927        scroll_position
 5928    });
 5929
 5930    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5931        .unwrap();
 5932    cx.assert_editor_state(&format!(
 5933        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5934    ));
 5935    let scroll_position_after_selection =
 5936        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5937    assert_eq!(
 5938        initial_scroll_position, scroll_position_after_selection,
 5939        "Scroll position should not change after selecting all matches"
 5940    );
 5941}
 5942
 5943#[gpui::test]
 5944async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5945    init_test(cx, |_| {});
 5946
 5947    let mut cx = EditorLspTestContext::new_rust(
 5948        lsp::ServerCapabilities {
 5949            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5950            ..Default::default()
 5951        },
 5952        cx,
 5953    )
 5954    .await;
 5955
 5956    cx.set_state(indoc! {"
 5957        line 1
 5958        line 2
 5959        linˇe 3
 5960        line 4
 5961        line 5
 5962    "});
 5963
 5964    // Make an edit
 5965    cx.update_editor(|editor, window, cx| {
 5966        editor.handle_input("X", window, cx);
 5967    });
 5968
 5969    // Move cursor to a different position
 5970    cx.update_editor(|editor, window, cx| {
 5971        editor.change_selections(None, window, cx, |s| {
 5972            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5973        });
 5974    });
 5975
 5976    cx.assert_editor_state(indoc! {"
 5977        line 1
 5978        line 2
 5979        linXe 3
 5980        line 4
 5981        liˇne 5
 5982    "});
 5983
 5984    cx.lsp
 5985        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5986            Ok(Some(vec![lsp::TextEdit::new(
 5987                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5988                "PREFIX ".to_string(),
 5989            )]))
 5990        });
 5991
 5992    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5993        .unwrap()
 5994        .await
 5995        .unwrap();
 5996
 5997    cx.assert_editor_state(indoc! {"
 5998        PREFIX line 1
 5999        line 2
 6000        linXe 3
 6001        line 4
 6002        liˇne 5
 6003    "});
 6004
 6005    // Undo formatting
 6006    cx.update_editor(|editor, window, cx| {
 6007        editor.undo(&Default::default(), window, cx);
 6008    });
 6009
 6010    // Verify cursor moved back to position after edit
 6011    cx.assert_editor_state(indoc! {"
 6012        line 1
 6013        line 2
 6014        linXˇe 3
 6015        line 4
 6016        line 5
 6017    "});
 6018}
 6019
 6020#[gpui::test]
 6021async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6022    init_test(cx, |_| {});
 6023
 6024    let mut cx = EditorTestContext::new(cx).await;
 6025    cx.set_state(
 6026        r#"let foo = 2;
 6027lˇet foo = 2;
 6028let fooˇ = 2;
 6029let foo = 2;
 6030let foo = ˇ2;"#,
 6031    );
 6032
 6033    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6034        .unwrap();
 6035    cx.assert_editor_state(
 6036        r#"let foo = 2;
 6037«letˇ» foo = 2;
 6038let «fooˇ» = 2;
 6039let foo = 2;
 6040let foo = «2ˇ»;"#,
 6041    );
 6042
 6043    // noop for multiple selections with different contents
 6044    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6045        .unwrap();
 6046    cx.assert_editor_state(
 6047        r#"let foo = 2;
 6048«letˇ» foo = 2;
 6049let «fooˇ» = 2;
 6050let foo = 2;
 6051let foo = «2ˇ»;"#,
 6052    );
 6053
 6054    // Test last selection direction should be preserved
 6055    cx.set_state(
 6056        r#"let foo = 2;
 6057let foo = 2;
 6058let «fooˇ» = 2;
 6059let «ˇfoo» = 2;
 6060let foo = 2;"#,
 6061    );
 6062
 6063    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6064        .unwrap();
 6065    cx.assert_editor_state(
 6066        r#"let foo = 2;
 6067let foo = 2;
 6068let «fooˇ» = 2;
 6069let «ˇfoo» = 2;
 6070let «ˇfoo» = 2;"#,
 6071    );
 6072}
 6073
 6074#[gpui::test]
 6075async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6076    init_test(cx, |_| {});
 6077
 6078    let mut cx =
 6079        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6080
 6081    cx.assert_editor_state(indoc! {"
 6082        ˇbbb
 6083        ccc
 6084
 6085        bbb
 6086        ccc
 6087        "});
 6088    cx.dispatch_action(SelectPrevious::default());
 6089    cx.assert_editor_state(indoc! {"
 6090                «bbbˇ»
 6091                ccc
 6092
 6093                bbb
 6094                ccc
 6095                "});
 6096    cx.dispatch_action(SelectPrevious::default());
 6097    cx.assert_editor_state(indoc! {"
 6098                «bbbˇ»
 6099                ccc
 6100
 6101                «bbbˇ»
 6102                ccc
 6103                "});
 6104}
 6105
 6106#[gpui::test]
 6107async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6108    init_test(cx, |_| {});
 6109
 6110    let mut cx = EditorTestContext::new(cx).await;
 6111    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6112
 6113    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6114        .unwrap();
 6115    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6116
 6117    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6118        .unwrap();
 6119    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6120
 6121    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6122    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6123
 6124    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6125    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6126
 6127    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6128        .unwrap();
 6129    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6130
 6131    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6132        .unwrap();
 6133    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6134}
 6135
 6136#[gpui::test]
 6137async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6138    init_test(cx, |_| {});
 6139
 6140    let mut cx = EditorTestContext::new(cx).await;
 6141    cx.set_state("");
 6142
 6143    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6144        .unwrap();
 6145    cx.assert_editor_state("«aˇ»");
 6146    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6147        .unwrap();
 6148    cx.assert_editor_state("«aˇ»");
 6149}
 6150
 6151#[gpui::test]
 6152async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6153    init_test(cx, |_| {});
 6154
 6155    let mut cx = EditorTestContext::new(cx).await;
 6156    cx.set_state(
 6157        r#"let foo = 2;
 6158lˇet foo = 2;
 6159let fooˇ = 2;
 6160let foo = 2;
 6161let foo = ˇ2;"#,
 6162    );
 6163
 6164    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6165        .unwrap();
 6166    cx.assert_editor_state(
 6167        r#"let foo = 2;
 6168«letˇ» foo = 2;
 6169let «fooˇ» = 2;
 6170let foo = 2;
 6171let foo = «2ˇ»;"#,
 6172    );
 6173
 6174    // noop for multiple selections with different contents
 6175    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6176        .unwrap();
 6177    cx.assert_editor_state(
 6178        r#"let foo = 2;
 6179«letˇ» foo = 2;
 6180let «fooˇ» = 2;
 6181let foo = 2;
 6182let foo = «2ˇ»;"#,
 6183    );
 6184}
 6185
 6186#[gpui::test]
 6187async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6188    init_test(cx, |_| {});
 6189
 6190    let mut cx = EditorTestContext::new(cx).await;
 6191    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6192
 6193    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6194        .unwrap();
 6195    // selection direction is preserved
 6196    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6197
 6198    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6199        .unwrap();
 6200    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6201
 6202    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6203    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6204
 6205    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6206    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6207
 6208    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6209        .unwrap();
 6210    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6211
 6212    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6213        .unwrap();
 6214    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6215}
 6216
 6217#[gpui::test]
 6218async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6219    init_test(cx, |_| {});
 6220
 6221    let language = Arc::new(Language::new(
 6222        LanguageConfig::default(),
 6223        Some(tree_sitter_rust::LANGUAGE.into()),
 6224    ));
 6225
 6226    let text = r#"
 6227        use mod1::mod2::{mod3, mod4};
 6228
 6229        fn fn_1(param1: bool, param2: &str) {
 6230            let var1 = "text";
 6231        }
 6232    "#
 6233    .unindent();
 6234
 6235    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6236    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6237    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6238
 6239    editor
 6240        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6241        .await;
 6242
 6243    editor.update_in(cx, |editor, window, cx| {
 6244        editor.change_selections(None, window, cx, |s| {
 6245            s.select_display_ranges([
 6246                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6247                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6248                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6249            ]);
 6250        });
 6251        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6252    });
 6253    editor.update(cx, |editor, cx| {
 6254        assert_text_with_selections(
 6255            editor,
 6256            indoc! {r#"
 6257                use mod1::mod2::{mod3, «mod4ˇ»};
 6258
 6259                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6260                    let var1 = "«ˇtext»";
 6261                }
 6262            "#},
 6263            cx,
 6264        );
 6265    });
 6266
 6267    editor.update_in(cx, |editor, window, cx| {
 6268        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6269    });
 6270    editor.update(cx, |editor, cx| {
 6271        assert_text_with_selections(
 6272            editor,
 6273            indoc! {r#"
 6274                use mod1::mod2::«{mod3, mod4}ˇ»;
 6275
 6276                «ˇfn fn_1(param1: bool, param2: &str) {
 6277                    let var1 = "text";
 6278 6279            "#},
 6280            cx,
 6281        );
 6282    });
 6283
 6284    editor.update_in(cx, |editor, window, cx| {
 6285        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6286    });
 6287    assert_eq!(
 6288        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6289        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6290    );
 6291
 6292    // Trying to expand the selected syntax node one more time has no effect.
 6293    editor.update_in(cx, |editor, window, cx| {
 6294        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6295    });
 6296    assert_eq!(
 6297        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6298        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6299    );
 6300
 6301    editor.update_in(cx, |editor, window, cx| {
 6302        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6303    });
 6304    editor.update(cx, |editor, cx| {
 6305        assert_text_with_selections(
 6306            editor,
 6307            indoc! {r#"
 6308                use mod1::mod2::«{mod3, mod4}ˇ»;
 6309
 6310                «ˇfn fn_1(param1: bool, param2: &str) {
 6311                    let var1 = "text";
 6312 6313            "#},
 6314            cx,
 6315        );
 6316    });
 6317
 6318    editor.update_in(cx, |editor, window, cx| {
 6319        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6320    });
 6321    editor.update(cx, |editor, cx| {
 6322        assert_text_with_selections(
 6323            editor,
 6324            indoc! {r#"
 6325                use mod1::mod2::{mod3, «mod4ˇ»};
 6326
 6327                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6328                    let var1 = "«ˇtext»";
 6329                }
 6330            "#},
 6331            cx,
 6332        );
 6333    });
 6334
 6335    editor.update_in(cx, |editor, window, cx| {
 6336        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6337    });
 6338    editor.update(cx, |editor, cx| {
 6339        assert_text_with_selections(
 6340            editor,
 6341            indoc! {r#"
 6342                use mod1::mod2::{mod3, mo«ˇ»d4};
 6343
 6344                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6345                    let var1 = "te«ˇ»xt";
 6346                }
 6347            "#},
 6348            cx,
 6349        );
 6350    });
 6351
 6352    // Trying to shrink the selected syntax node one more time has no effect.
 6353    editor.update_in(cx, |editor, window, cx| {
 6354        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6355    });
 6356    editor.update_in(cx, |editor, _, cx| {
 6357        assert_text_with_selections(
 6358            editor,
 6359            indoc! {r#"
 6360                use mod1::mod2::{mod3, mo«ˇ»d4};
 6361
 6362                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6363                    let var1 = "te«ˇ»xt";
 6364                }
 6365            "#},
 6366            cx,
 6367        );
 6368    });
 6369
 6370    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6371    // a fold.
 6372    editor.update_in(cx, |editor, window, cx| {
 6373        editor.fold_creases(
 6374            vec![
 6375                Crease::simple(
 6376                    Point::new(0, 21)..Point::new(0, 24),
 6377                    FoldPlaceholder::test(),
 6378                ),
 6379                Crease::simple(
 6380                    Point::new(3, 20)..Point::new(3, 22),
 6381                    FoldPlaceholder::test(),
 6382                ),
 6383            ],
 6384            true,
 6385            window,
 6386            cx,
 6387        );
 6388        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6389    });
 6390    editor.update(cx, |editor, cx| {
 6391        assert_text_with_selections(
 6392            editor,
 6393            indoc! {r#"
 6394                use mod1::mod2::«{mod3, mod4}ˇ»;
 6395
 6396                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6397                    let var1 = "«ˇtext»";
 6398                }
 6399            "#},
 6400            cx,
 6401        );
 6402    });
 6403}
 6404
 6405#[gpui::test]
 6406async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6407    init_test(cx, |_| {});
 6408
 6409    let language = Arc::new(Language::new(
 6410        LanguageConfig::default(),
 6411        Some(tree_sitter_rust::LANGUAGE.into()),
 6412    ));
 6413
 6414    let text = "let a = 2;";
 6415
 6416    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6417    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6418    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6419
 6420    editor
 6421        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6422        .await;
 6423
 6424    // Test case 1: Cursor at end of word
 6425    editor.update_in(cx, |editor, window, cx| {
 6426        editor.change_selections(None, window, cx, |s| {
 6427            s.select_display_ranges([
 6428                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6429            ]);
 6430        });
 6431    });
 6432    editor.update(cx, |editor, cx| {
 6433        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6434    });
 6435    editor.update_in(cx, |editor, window, cx| {
 6436        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6437    });
 6438    editor.update(cx, |editor, cx| {
 6439        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6440    });
 6441    editor.update_in(cx, |editor, window, cx| {
 6442        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6443    });
 6444    editor.update(cx, |editor, cx| {
 6445        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6446    });
 6447
 6448    // Test case 2: Cursor at end of statement
 6449    editor.update_in(cx, |editor, window, cx| {
 6450        editor.change_selections(None, window, cx, |s| {
 6451            s.select_display_ranges([
 6452                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6453            ]);
 6454        });
 6455    });
 6456    editor.update(cx, |editor, cx| {
 6457        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6458    });
 6459    editor.update_in(cx, |editor, window, cx| {
 6460        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6461    });
 6462    editor.update(cx, |editor, cx| {
 6463        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6464    });
 6465}
 6466
 6467#[gpui::test]
 6468async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6469    init_test(cx, |_| {});
 6470
 6471    let language = Arc::new(Language::new(
 6472        LanguageConfig::default(),
 6473        Some(tree_sitter_rust::LANGUAGE.into()),
 6474    ));
 6475
 6476    let text = r#"
 6477        use mod1::mod2::{mod3, mod4};
 6478
 6479        fn fn_1(param1: bool, param2: &str) {
 6480            let var1 = "hello world";
 6481        }
 6482    "#
 6483    .unindent();
 6484
 6485    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6486    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6487    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6488
 6489    editor
 6490        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6491        .await;
 6492
 6493    // Test 1: Cursor on a letter of a string word
 6494    editor.update_in(cx, |editor, window, cx| {
 6495        editor.change_selections(None, window, cx, |s| {
 6496            s.select_display_ranges([
 6497                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6498            ]);
 6499        });
 6500    });
 6501    editor.update_in(cx, |editor, window, cx| {
 6502        assert_text_with_selections(
 6503            editor,
 6504            indoc! {r#"
 6505                use mod1::mod2::{mod3, mod4};
 6506
 6507                fn fn_1(param1: bool, param2: &str) {
 6508                    let var1 = "hˇello world";
 6509                }
 6510            "#},
 6511            cx,
 6512        );
 6513        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6514        assert_text_with_selections(
 6515            editor,
 6516            indoc! {r#"
 6517                use mod1::mod2::{mod3, mod4};
 6518
 6519                fn fn_1(param1: bool, param2: &str) {
 6520                    let var1 = "«ˇhello» world";
 6521                }
 6522            "#},
 6523            cx,
 6524        );
 6525    });
 6526
 6527    // Test 2: Partial selection within a word
 6528    editor.update_in(cx, |editor, window, cx| {
 6529        editor.change_selections(None, window, cx, |s| {
 6530            s.select_display_ranges([
 6531                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6532            ]);
 6533        });
 6534    });
 6535    editor.update_in(cx, |editor, window, cx| {
 6536        assert_text_with_selections(
 6537            editor,
 6538            indoc! {r#"
 6539                use mod1::mod2::{mod3, mod4};
 6540
 6541                fn fn_1(param1: bool, param2: &str) {
 6542                    let var1 = "h«elˇ»lo world";
 6543                }
 6544            "#},
 6545            cx,
 6546        );
 6547        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6548        assert_text_with_selections(
 6549            editor,
 6550            indoc! {r#"
 6551                use mod1::mod2::{mod3, mod4};
 6552
 6553                fn fn_1(param1: bool, param2: &str) {
 6554                    let var1 = "«ˇhello» world";
 6555                }
 6556            "#},
 6557            cx,
 6558        );
 6559    });
 6560
 6561    // Test 3: Complete word already selected
 6562    editor.update_in(cx, |editor, window, cx| {
 6563        editor.change_selections(None, window, cx, |s| {
 6564            s.select_display_ranges([
 6565                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6566            ]);
 6567        });
 6568    });
 6569    editor.update_in(cx, |editor, window, cx| {
 6570        assert_text_with_selections(
 6571            editor,
 6572            indoc! {r#"
 6573                use mod1::mod2::{mod3, mod4};
 6574
 6575                fn fn_1(param1: bool, param2: &str) {
 6576                    let var1 = "«helloˇ» world";
 6577                }
 6578            "#},
 6579            cx,
 6580        );
 6581        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6582        assert_text_with_selections(
 6583            editor,
 6584            indoc! {r#"
 6585                use mod1::mod2::{mod3, mod4};
 6586
 6587                fn fn_1(param1: bool, param2: &str) {
 6588                    let var1 = "«hello worldˇ»";
 6589                }
 6590            "#},
 6591            cx,
 6592        );
 6593    });
 6594
 6595    // Test 4: Selection spanning across words
 6596    editor.update_in(cx, |editor, window, cx| {
 6597        editor.change_selections(None, window, cx, |s| {
 6598            s.select_display_ranges([
 6599                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6600            ]);
 6601        });
 6602    });
 6603    editor.update_in(cx, |editor, window, cx| {
 6604        assert_text_with_selections(
 6605            editor,
 6606            indoc! {r#"
 6607                use mod1::mod2::{mod3, mod4};
 6608
 6609                fn fn_1(param1: bool, param2: &str) {
 6610                    let var1 = "hel«lo woˇ»rld";
 6611                }
 6612            "#},
 6613            cx,
 6614        );
 6615        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6616        assert_text_with_selections(
 6617            editor,
 6618            indoc! {r#"
 6619                use mod1::mod2::{mod3, mod4};
 6620
 6621                fn fn_1(param1: bool, param2: &str) {
 6622                    let var1 = "«ˇhello world»";
 6623                }
 6624            "#},
 6625            cx,
 6626        );
 6627    });
 6628
 6629    // Test 5: Expansion beyond string
 6630    editor.update_in(cx, |editor, window, cx| {
 6631        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6632        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6633        assert_text_with_selections(
 6634            editor,
 6635            indoc! {r#"
 6636                use mod1::mod2::{mod3, mod4};
 6637
 6638                fn fn_1(param1: bool, param2: &str) {
 6639                    «ˇlet var1 = "hello world";»
 6640                }
 6641            "#},
 6642            cx,
 6643        );
 6644    });
 6645}
 6646
 6647#[gpui::test]
 6648async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6649    init_test(cx, |_| {});
 6650
 6651    let base_text = r#"
 6652        impl A {
 6653            // this is an uncommitted comment
 6654
 6655            fn b() {
 6656                c();
 6657            }
 6658
 6659            // this is another uncommitted comment
 6660
 6661            fn d() {
 6662                // e
 6663                // f
 6664            }
 6665        }
 6666
 6667        fn g() {
 6668            // h
 6669        }
 6670    "#
 6671    .unindent();
 6672
 6673    let text = r#"
 6674        ˇimpl A {
 6675
 6676            fn b() {
 6677                c();
 6678            }
 6679
 6680            fn d() {
 6681                // e
 6682                // f
 6683            }
 6684        }
 6685
 6686        fn g() {
 6687            // h
 6688        }
 6689    "#
 6690    .unindent();
 6691
 6692    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6693    cx.set_state(&text);
 6694    cx.set_head_text(&base_text);
 6695    cx.update_editor(|editor, window, cx| {
 6696        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6697    });
 6698
 6699    cx.assert_state_with_diff(
 6700        "
 6701        ˇimpl A {
 6702      -     // this is an uncommitted comment
 6703
 6704            fn b() {
 6705                c();
 6706            }
 6707
 6708      -     // this is another uncommitted comment
 6709      -
 6710            fn d() {
 6711                // e
 6712                // f
 6713            }
 6714        }
 6715
 6716        fn g() {
 6717            // h
 6718        }
 6719    "
 6720        .unindent(),
 6721    );
 6722
 6723    let expected_display_text = "
 6724        impl A {
 6725            // this is an uncommitted comment
 6726
 6727            fn b() {
 6728 6729            }
 6730
 6731            // this is another uncommitted comment
 6732
 6733            fn d() {
 6734 6735            }
 6736        }
 6737
 6738        fn g() {
 6739 6740        }
 6741        "
 6742    .unindent();
 6743
 6744    cx.update_editor(|editor, window, cx| {
 6745        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6746        assert_eq!(editor.display_text(cx), expected_display_text);
 6747    });
 6748}
 6749
 6750#[gpui::test]
 6751async fn test_autoindent(cx: &mut TestAppContext) {
 6752    init_test(cx, |_| {});
 6753
 6754    let language = Arc::new(
 6755        Language::new(
 6756            LanguageConfig {
 6757                brackets: BracketPairConfig {
 6758                    pairs: vec![
 6759                        BracketPair {
 6760                            start: "{".to_string(),
 6761                            end: "}".to_string(),
 6762                            close: false,
 6763                            surround: false,
 6764                            newline: true,
 6765                        },
 6766                        BracketPair {
 6767                            start: "(".to_string(),
 6768                            end: ")".to_string(),
 6769                            close: false,
 6770                            surround: false,
 6771                            newline: true,
 6772                        },
 6773                    ],
 6774                    ..Default::default()
 6775                },
 6776                ..Default::default()
 6777            },
 6778            Some(tree_sitter_rust::LANGUAGE.into()),
 6779        )
 6780        .with_indents_query(
 6781            r#"
 6782                (_ "(" ")" @end) @indent
 6783                (_ "{" "}" @end) @indent
 6784            "#,
 6785        )
 6786        .unwrap(),
 6787    );
 6788
 6789    let text = "fn a() {}";
 6790
 6791    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6792    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6793    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6794    editor
 6795        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6796        .await;
 6797
 6798    editor.update_in(cx, |editor, window, cx| {
 6799        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6800        editor.newline(&Newline, window, cx);
 6801        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6802        assert_eq!(
 6803            editor.selections.ranges(cx),
 6804            &[
 6805                Point::new(1, 4)..Point::new(1, 4),
 6806                Point::new(3, 4)..Point::new(3, 4),
 6807                Point::new(5, 0)..Point::new(5, 0)
 6808            ]
 6809        );
 6810    });
 6811}
 6812
 6813#[gpui::test]
 6814async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6815    init_test(cx, |_| {});
 6816
 6817    {
 6818        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6819        cx.set_state(indoc! {"
 6820            impl A {
 6821
 6822                fn b() {}
 6823
 6824            «fn c() {
 6825
 6826            }ˇ»
 6827            }
 6828        "});
 6829
 6830        cx.update_editor(|editor, window, cx| {
 6831            editor.autoindent(&Default::default(), window, cx);
 6832        });
 6833
 6834        cx.assert_editor_state(indoc! {"
 6835            impl A {
 6836
 6837                fn b() {}
 6838
 6839                «fn c() {
 6840
 6841                }ˇ»
 6842            }
 6843        "});
 6844    }
 6845
 6846    {
 6847        let mut cx = EditorTestContext::new_multibuffer(
 6848            cx,
 6849            [indoc! { "
 6850                impl A {
 6851                «
 6852                // a
 6853                fn b(){}
 6854                »
 6855                «
 6856                    }
 6857                    fn c(){}
 6858                »
 6859            "}],
 6860        );
 6861
 6862        let buffer = cx.update_editor(|editor, _, cx| {
 6863            let buffer = editor.buffer().update(cx, |buffer, _| {
 6864                buffer.all_buffers().iter().next().unwrap().clone()
 6865            });
 6866            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6867            buffer
 6868        });
 6869
 6870        cx.run_until_parked();
 6871        cx.update_editor(|editor, window, cx| {
 6872            editor.select_all(&Default::default(), window, cx);
 6873            editor.autoindent(&Default::default(), window, cx)
 6874        });
 6875        cx.run_until_parked();
 6876
 6877        cx.update(|_, cx| {
 6878            assert_eq!(
 6879                buffer.read(cx).text(),
 6880                indoc! { "
 6881                    impl A {
 6882
 6883                        // a
 6884                        fn b(){}
 6885
 6886
 6887                    }
 6888                    fn c(){}
 6889
 6890                " }
 6891            )
 6892        });
 6893    }
 6894}
 6895
 6896#[gpui::test]
 6897async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6898    init_test(cx, |_| {});
 6899
 6900    let mut cx = EditorTestContext::new(cx).await;
 6901
 6902    let language = Arc::new(Language::new(
 6903        LanguageConfig {
 6904            brackets: BracketPairConfig {
 6905                pairs: vec![
 6906                    BracketPair {
 6907                        start: "{".to_string(),
 6908                        end: "}".to_string(),
 6909                        close: true,
 6910                        surround: true,
 6911                        newline: true,
 6912                    },
 6913                    BracketPair {
 6914                        start: "(".to_string(),
 6915                        end: ")".to_string(),
 6916                        close: true,
 6917                        surround: true,
 6918                        newline: true,
 6919                    },
 6920                    BracketPair {
 6921                        start: "/*".to_string(),
 6922                        end: " */".to_string(),
 6923                        close: true,
 6924                        surround: true,
 6925                        newline: true,
 6926                    },
 6927                    BracketPair {
 6928                        start: "[".to_string(),
 6929                        end: "]".to_string(),
 6930                        close: false,
 6931                        surround: false,
 6932                        newline: true,
 6933                    },
 6934                    BracketPair {
 6935                        start: "\"".to_string(),
 6936                        end: "\"".to_string(),
 6937                        close: true,
 6938                        surround: true,
 6939                        newline: false,
 6940                    },
 6941                    BracketPair {
 6942                        start: "<".to_string(),
 6943                        end: ">".to_string(),
 6944                        close: false,
 6945                        surround: true,
 6946                        newline: true,
 6947                    },
 6948                ],
 6949                ..Default::default()
 6950            },
 6951            autoclose_before: "})]".to_string(),
 6952            ..Default::default()
 6953        },
 6954        Some(tree_sitter_rust::LANGUAGE.into()),
 6955    ));
 6956
 6957    cx.language_registry().add(language.clone());
 6958    cx.update_buffer(|buffer, cx| {
 6959        buffer.set_language(Some(language), cx);
 6960    });
 6961
 6962    cx.set_state(
 6963        &r#"
 6964            🏀ˇ
 6965            εˇ
 6966            ❤️ˇ
 6967        "#
 6968        .unindent(),
 6969    );
 6970
 6971    // autoclose multiple nested brackets at multiple cursors
 6972    cx.update_editor(|editor, window, cx| {
 6973        editor.handle_input("{", window, cx);
 6974        editor.handle_input("{", window, cx);
 6975        editor.handle_input("{", window, cx);
 6976    });
 6977    cx.assert_editor_state(
 6978        &"
 6979            🏀{{{ˇ}}}
 6980            ε{{{ˇ}}}
 6981            ❤️{{{ˇ}}}
 6982        "
 6983        .unindent(),
 6984    );
 6985
 6986    // insert a different closing bracket
 6987    cx.update_editor(|editor, window, cx| {
 6988        editor.handle_input(")", window, cx);
 6989    });
 6990    cx.assert_editor_state(
 6991        &"
 6992            🏀{{{)ˇ}}}
 6993            ε{{{)ˇ}}}
 6994            ❤️{{{)ˇ}}}
 6995        "
 6996        .unindent(),
 6997    );
 6998
 6999    // skip over the auto-closed brackets when typing a closing bracket
 7000    cx.update_editor(|editor, window, cx| {
 7001        editor.move_right(&MoveRight, window, cx);
 7002        editor.handle_input("}", window, cx);
 7003        editor.handle_input("}", window, cx);
 7004        editor.handle_input("}", window, cx);
 7005    });
 7006    cx.assert_editor_state(
 7007        &"
 7008            🏀{{{)}}}}ˇ
 7009            ε{{{)}}}}ˇ
 7010            ❤️{{{)}}}}ˇ
 7011        "
 7012        .unindent(),
 7013    );
 7014
 7015    // autoclose multi-character pairs
 7016    cx.set_state(
 7017        &"
 7018            ˇ
 7019            ˇ
 7020        "
 7021        .unindent(),
 7022    );
 7023    cx.update_editor(|editor, window, cx| {
 7024        editor.handle_input("/", window, cx);
 7025        editor.handle_input("*", window, cx);
 7026    });
 7027    cx.assert_editor_state(
 7028        &"
 7029            /*ˇ */
 7030            /*ˇ */
 7031        "
 7032        .unindent(),
 7033    );
 7034
 7035    // one cursor autocloses a multi-character pair, one cursor
 7036    // does not autoclose.
 7037    cx.set_state(
 7038        &"
 7039 7040            ˇ
 7041        "
 7042        .unindent(),
 7043    );
 7044    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7045    cx.assert_editor_state(
 7046        &"
 7047            /*ˇ */
 7048 7049        "
 7050        .unindent(),
 7051    );
 7052
 7053    // Don't autoclose if the next character isn't whitespace and isn't
 7054    // listed in the language's "autoclose_before" section.
 7055    cx.set_state("ˇa b");
 7056    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7057    cx.assert_editor_state("{ˇa b");
 7058
 7059    // Don't autoclose if `close` is false for the bracket pair
 7060    cx.set_state("ˇ");
 7061    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7062    cx.assert_editor_state("");
 7063
 7064    // Surround with brackets if text is selected
 7065    cx.set_state("«aˇ» b");
 7066    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7067    cx.assert_editor_state("{«aˇ»} b");
 7068
 7069    // Autoclose when not immediately after a word character
 7070    cx.set_state("a ˇ");
 7071    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7072    cx.assert_editor_state("a \"ˇ\"");
 7073
 7074    // Autoclose pair where the start and end characters are the same
 7075    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7076    cx.assert_editor_state("a \"\"ˇ");
 7077
 7078    // Don't autoclose when immediately after a word character
 7079    cx.set_state("");
 7080    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7081    cx.assert_editor_state("a\"ˇ");
 7082
 7083    // Do autoclose when after a non-word character
 7084    cx.set_state("");
 7085    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7086    cx.assert_editor_state("{\"ˇ\"");
 7087
 7088    // Non identical pairs autoclose regardless of preceding character
 7089    cx.set_state("");
 7090    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7091    cx.assert_editor_state("a{ˇ}");
 7092
 7093    // Don't autoclose pair if autoclose is disabled
 7094    cx.set_state("ˇ");
 7095    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7096    cx.assert_editor_state("");
 7097
 7098    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7099    cx.set_state("«aˇ» b");
 7100    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7101    cx.assert_editor_state("<«aˇ»> b");
 7102}
 7103
 7104#[gpui::test]
 7105async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7106    init_test(cx, |settings| {
 7107        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7108    });
 7109
 7110    let mut cx = EditorTestContext::new(cx).await;
 7111
 7112    let language = Arc::new(Language::new(
 7113        LanguageConfig {
 7114            brackets: BracketPairConfig {
 7115                pairs: vec![
 7116                    BracketPair {
 7117                        start: "{".to_string(),
 7118                        end: "}".to_string(),
 7119                        close: true,
 7120                        surround: true,
 7121                        newline: true,
 7122                    },
 7123                    BracketPair {
 7124                        start: "(".to_string(),
 7125                        end: ")".to_string(),
 7126                        close: true,
 7127                        surround: true,
 7128                        newline: true,
 7129                    },
 7130                    BracketPair {
 7131                        start: "[".to_string(),
 7132                        end: "]".to_string(),
 7133                        close: false,
 7134                        surround: false,
 7135                        newline: true,
 7136                    },
 7137                ],
 7138                ..Default::default()
 7139            },
 7140            autoclose_before: "})]".to_string(),
 7141            ..Default::default()
 7142        },
 7143        Some(tree_sitter_rust::LANGUAGE.into()),
 7144    ));
 7145
 7146    cx.language_registry().add(language.clone());
 7147    cx.update_buffer(|buffer, cx| {
 7148        buffer.set_language(Some(language), cx);
 7149    });
 7150
 7151    cx.set_state(
 7152        &"
 7153            ˇ
 7154            ˇ
 7155            ˇ
 7156        "
 7157        .unindent(),
 7158    );
 7159
 7160    // ensure only matching closing brackets are skipped over
 7161    cx.update_editor(|editor, window, cx| {
 7162        editor.handle_input("}", window, cx);
 7163        editor.move_left(&MoveLeft, window, cx);
 7164        editor.handle_input(")", window, cx);
 7165        editor.move_left(&MoveLeft, window, cx);
 7166    });
 7167    cx.assert_editor_state(
 7168        &"
 7169            ˇ)}
 7170            ˇ)}
 7171            ˇ)}
 7172        "
 7173        .unindent(),
 7174    );
 7175
 7176    // skip-over closing brackets at multiple cursors
 7177    cx.update_editor(|editor, window, cx| {
 7178        editor.handle_input(")", window, cx);
 7179        editor.handle_input("}", window, cx);
 7180    });
 7181    cx.assert_editor_state(
 7182        &"
 7183            )}ˇ
 7184            )}ˇ
 7185            )}ˇ
 7186        "
 7187        .unindent(),
 7188    );
 7189
 7190    // ignore non-close brackets
 7191    cx.update_editor(|editor, window, cx| {
 7192        editor.handle_input("]", window, cx);
 7193        editor.move_left(&MoveLeft, window, cx);
 7194        editor.handle_input("]", window, cx);
 7195    });
 7196    cx.assert_editor_state(
 7197        &"
 7198            )}]ˇ]
 7199            )}]ˇ]
 7200            )}]ˇ]
 7201        "
 7202        .unindent(),
 7203    );
 7204}
 7205
 7206#[gpui::test]
 7207async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7208    init_test(cx, |_| {});
 7209
 7210    let mut cx = EditorTestContext::new(cx).await;
 7211
 7212    let html_language = Arc::new(
 7213        Language::new(
 7214            LanguageConfig {
 7215                name: "HTML".into(),
 7216                brackets: BracketPairConfig {
 7217                    pairs: vec![
 7218                        BracketPair {
 7219                            start: "<".into(),
 7220                            end: ">".into(),
 7221                            close: true,
 7222                            ..Default::default()
 7223                        },
 7224                        BracketPair {
 7225                            start: "{".into(),
 7226                            end: "}".into(),
 7227                            close: true,
 7228                            ..Default::default()
 7229                        },
 7230                        BracketPair {
 7231                            start: "(".into(),
 7232                            end: ")".into(),
 7233                            close: true,
 7234                            ..Default::default()
 7235                        },
 7236                    ],
 7237                    ..Default::default()
 7238                },
 7239                autoclose_before: "})]>".into(),
 7240                ..Default::default()
 7241            },
 7242            Some(tree_sitter_html::LANGUAGE.into()),
 7243        )
 7244        .with_injection_query(
 7245            r#"
 7246            (script_element
 7247                (raw_text) @injection.content
 7248                (#set! injection.language "javascript"))
 7249            "#,
 7250        )
 7251        .unwrap(),
 7252    );
 7253
 7254    let javascript_language = Arc::new(Language::new(
 7255        LanguageConfig {
 7256            name: "JavaScript".into(),
 7257            brackets: BracketPairConfig {
 7258                pairs: vec![
 7259                    BracketPair {
 7260                        start: "/*".into(),
 7261                        end: " */".into(),
 7262                        close: true,
 7263                        ..Default::default()
 7264                    },
 7265                    BracketPair {
 7266                        start: "{".into(),
 7267                        end: "}".into(),
 7268                        close: true,
 7269                        ..Default::default()
 7270                    },
 7271                    BracketPair {
 7272                        start: "(".into(),
 7273                        end: ")".into(),
 7274                        close: true,
 7275                        ..Default::default()
 7276                    },
 7277                ],
 7278                ..Default::default()
 7279            },
 7280            autoclose_before: "})]>".into(),
 7281            ..Default::default()
 7282        },
 7283        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7284    ));
 7285
 7286    cx.language_registry().add(html_language.clone());
 7287    cx.language_registry().add(javascript_language.clone());
 7288
 7289    cx.update_buffer(|buffer, cx| {
 7290        buffer.set_language(Some(html_language), cx);
 7291    });
 7292
 7293    cx.set_state(
 7294        &r#"
 7295            <body>ˇ
 7296                <script>
 7297                    var x = 1;ˇ
 7298                </script>
 7299            </body>ˇ
 7300        "#
 7301        .unindent(),
 7302    );
 7303
 7304    // Precondition: different languages are active at different locations.
 7305    cx.update_editor(|editor, window, cx| {
 7306        let snapshot = editor.snapshot(window, cx);
 7307        let cursors = editor.selections.ranges::<usize>(cx);
 7308        let languages = cursors
 7309            .iter()
 7310            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7311            .collect::<Vec<_>>();
 7312        assert_eq!(
 7313            languages,
 7314            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7315        );
 7316    });
 7317
 7318    // Angle brackets autoclose in HTML, but not JavaScript.
 7319    cx.update_editor(|editor, window, cx| {
 7320        editor.handle_input("<", window, cx);
 7321        editor.handle_input("a", window, cx);
 7322    });
 7323    cx.assert_editor_state(
 7324        &r#"
 7325            <body><aˇ>
 7326                <script>
 7327                    var x = 1;<aˇ
 7328                </script>
 7329            </body><aˇ>
 7330        "#
 7331        .unindent(),
 7332    );
 7333
 7334    // Curly braces and parens autoclose in both HTML and JavaScript.
 7335    cx.update_editor(|editor, window, cx| {
 7336        editor.handle_input(" b=", window, cx);
 7337        editor.handle_input("{", window, cx);
 7338        editor.handle_input("c", window, cx);
 7339        editor.handle_input("(", window, cx);
 7340    });
 7341    cx.assert_editor_state(
 7342        &r#"
 7343            <body><a b={c(ˇ)}>
 7344                <script>
 7345                    var x = 1;<a b={c(ˇ)}
 7346                </script>
 7347            </body><a b={c(ˇ)}>
 7348        "#
 7349        .unindent(),
 7350    );
 7351
 7352    // Brackets that were already autoclosed are skipped.
 7353    cx.update_editor(|editor, window, cx| {
 7354        editor.handle_input(")", window, cx);
 7355        editor.handle_input("d", window, cx);
 7356        editor.handle_input("}", window, cx);
 7357    });
 7358    cx.assert_editor_state(
 7359        &r#"
 7360            <body><a b={c()d}ˇ>
 7361                <script>
 7362                    var x = 1;<a b={c()d}ˇ
 7363                </script>
 7364            </body><a b={c()d}ˇ>
 7365        "#
 7366        .unindent(),
 7367    );
 7368    cx.update_editor(|editor, window, cx| {
 7369        editor.handle_input(">", window, cx);
 7370    });
 7371    cx.assert_editor_state(
 7372        &r#"
 7373            <body><a b={c()d}>ˇ
 7374                <script>
 7375                    var x = 1;<a b={c()d}>ˇ
 7376                </script>
 7377            </body><a b={c()d}>ˇ
 7378        "#
 7379        .unindent(),
 7380    );
 7381
 7382    // Reset
 7383    cx.set_state(
 7384        &r#"
 7385            <body>ˇ
 7386                <script>
 7387                    var x = 1;ˇ
 7388                </script>
 7389            </body>ˇ
 7390        "#
 7391        .unindent(),
 7392    );
 7393
 7394    cx.update_editor(|editor, window, cx| {
 7395        editor.handle_input("<", window, cx);
 7396    });
 7397    cx.assert_editor_state(
 7398        &r#"
 7399            <body><ˇ>
 7400                <script>
 7401                    var x = 1;<ˇ
 7402                </script>
 7403            </body><ˇ>
 7404        "#
 7405        .unindent(),
 7406    );
 7407
 7408    // When backspacing, the closing angle brackets are removed.
 7409    cx.update_editor(|editor, window, cx| {
 7410        editor.backspace(&Backspace, window, cx);
 7411    });
 7412    cx.assert_editor_state(
 7413        &r#"
 7414            <body>ˇ
 7415                <script>
 7416                    var x = 1;ˇ
 7417                </script>
 7418            </body>ˇ
 7419        "#
 7420        .unindent(),
 7421    );
 7422
 7423    // Block comments autoclose in JavaScript, but not HTML.
 7424    cx.update_editor(|editor, window, cx| {
 7425        editor.handle_input("/", window, cx);
 7426        editor.handle_input("*", window, cx);
 7427    });
 7428    cx.assert_editor_state(
 7429        &r#"
 7430            <body>/*ˇ
 7431                <script>
 7432                    var x = 1;/*ˇ */
 7433                </script>
 7434            </body>/*ˇ
 7435        "#
 7436        .unindent(),
 7437    );
 7438}
 7439
 7440#[gpui::test]
 7441async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7442    init_test(cx, |_| {});
 7443
 7444    let mut cx = EditorTestContext::new(cx).await;
 7445
 7446    let rust_language = Arc::new(
 7447        Language::new(
 7448            LanguageConfig {
 7449                name: "Rust".into(),
 7450                brackets: serde_json::from_value(json!([
 7451                    { "start": "{", "end": "}", "close": true, "newline": true },
 7452                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7453                ]))
 7454                .unwrap(),
 7455                autoclose_before: "})]>".into(),
 7456                ..Default::default()
 7457            },
 7458            Some(tree_sitter_rust::LANGUAGE.into()),
 7459        )
 7460        .with_override_query("(string_literal) @string")
 7461        .unwrap(),
 7462    );
 7463
 7464    cx.language_registry().add(rust_language.clone());
 7465    cx.update_buffer(|buffer, cx| {
 7466        buffer.set_language(Some(rust_language), cx);
 7467    });
 7468
 7469    cx.set_state(
 7470        &r#"
 7471            let x = ˇ
 7472        "#
 7473        .unindent(),
 7474    );
 7475
 7476    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7477    cx.update_editor(|editor, window, cx| {
 7478        editor.handle_input("\"", window, cx);
 7479    });
 7480    cx.assert_editor_state(
 7481        &r#"
 7482            let x = "ˇ"
 7483        "#
 7484        .unindent(),
 7485    );
 7486
 7487    // Inserting another quotation mark. The cursor moves across the existing
 7488    // automatically-inserted quotation mark.
 7489    cx.update_editor(|editor, window, cx| {
 7490        editor.handle_input("\"", window, cx);
 7491    });
 7492    cx.assert_editor_state(
 7493        &r#"
 7494            let x = ""ˇ
 7495        "#
 7496        .unindent(),
 7497    );
 7498
 7499    // Reset
 7500    cx.set_state(
 7501        &r#"
 7502            let x = ˇ
 7503        "#
 7504        .unindent(),
 7505    );
 7506
 7507    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7508    cx.update_editor(|editor, window, cx| {
 7509        editor.handle_input("\"", window, cx);
 7510        editor.handle_input(" ", window, cx);
 7511        editor.move_left(&Default::default(), window, cx);
 7512        editor.handle_input("\\", window, cx);
 7513        editor.handle_input("\"", window, cx);
 7514    });
 7515    cx.assert_editor_state(
 7516        &r#"
 7517            let x = "\"ˇ "
 7518        "#
 7519        .unindent(),
 7520    );
 7521
 7522    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7523    // mark. Nothing is inserted.
 7524    cx.update_editor(|editor, window, cx| {
 7525        editor.move_right(&Default::default(), window, cx);
 7526        editor.handle_input("\"", window, cx);
 7527    });
 7528    cx.assert_editor_state(
 7529        &r#"
 7530            let x = "\" "ˇ
 7531        "#
 7532        .unindent(),
 7533    );
 7534}
 7535
 7536#[gpui::test]
 7537async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7538    init_test(cx, |_| {});
 7539
 7540    let language = Arc::new(Language::new(
 7541        LanguageConfig {
 7542            brackets: BracketPairConfig {
 7543                pairs: vec![
 7544                    BracketPair {
 7545                        start: "{".to_string(),
 7546                        end: "}".to_string(),
 7547                        close: true,
 7548                        surround: true,
 7549                        newline: true,
 7550                    },
 7551                    BracketPair {
 7552                        start: "/* ".to_string(),
 7553                        end: "*/".to_string(),
 7554                        close: true,
 7555                        surround: true,
 7556                        ..Default::default()
 7557                    },
 7558                ],
 7559                ..Default::default()
 7560            },
 7561            ..Default::default()
 7562        },
 7563        Some(tree_sitter_rust::LANGUAGE.into()),
 7564    ));
 7565
 7566    let text = r#"
 7567        a
 7568        b
 7569        c
 7570    "#
 7571    .unindent();
 7572
 7573    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7574    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7575    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7576    editor
 7577        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7578        .await;
 7579
 7580    editor.update_in(cx, |editor, window, cx| {
 7581        editor.change_selections(None, window, cx, |s| {
 7582            s.select_display_ranges([
 7583                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7584                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7585                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7586            ])
 7587        });
 7588
 7589        editor.handle_input("{", window, cx);
 7590        editor.handle_input("{", window, cx);
 7591        editor.handle_input("{", window, cx);
 7592        assert_eq!(
 7593            editor.text(cx),
 7594            "
 7595                {{{a}}}
 7596                {{{b}}}
 7597                {{{c}}}
 7598            "
 7599            .unindent()
 7600        );
 7601        assert_eq!(
 7602            editor.selections.display_ranges(cx),
 7603            [
 7604                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7605                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7606                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7607            ]
 7608        );
 7609
 7610        editor.undo(&Undo, window, cx);
 7611        editor.undo(&Undo, window, cx);
 7612        editor.undo(&Undo, window, cx);
 7613        assert_eq!(
 7614            editor.text(cx),
 7615            "
 7616                a
 7617                b
 7618                c
 7619            "
 7620            .unindent()
 7621        );
 7622        assert_eq!(
 7623            editor.selections.display_ranges(cx),
 7624            [
 7625                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7626                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7627                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7628            ]
 7629        );
 7630
 7631        // Ensure inserting the first character of a multi-byte bracket pair
 7632        // doesn't surround the selections with the bracket.
 7633        editor.handle_input("/", window, cx);
 7634        assert_eq!(
 7635            editor.text(cx),
 7636            "
 7637                /
 7638                /
 7639                /
 7640            "
 7641            .unindent()
 7642        );
 7643        assert_eq!(
 7644            editor.selections.display_ranges(cx),
 7645            [
 7646                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7647                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7648                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7649            ]
 7650        );
 7651
 7652        editor.undo(&Undo, window, cx);
 7653        assert_eq!(
 7654            editor.text(cx),
 7655            "
 7656                a
 7657                b
 7658                c
 7659            "
 7660            .unindent()
 7661        );
 7662        assert_eq!(
 7663            editor.selections.display_ranges(cx),
 7664            [
 7665                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7666                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7667                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7668            ]
 7669        );
 7670
 7671        // Ensure inserting the last character of a multi-byte bracket pair
 7672        // doesn't surround the selections with the bracket.
 7673        editor.handle_input("*", window, cx);
 7674        assert_eq!(
 7675            editor.text(cx),
 7676            "
 7677                *
 7678                *
 7679                *
 7680            "
 7681            .unindent()
 7682        );
 7683        assert_eq!(
 7684            editor.selections.display_ranges(cx),
 7685            [
 7686                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7687                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7688                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7689            ]
 7690        );
 7691    });
 7692}
 7693
 7694#[gpui::test]
 7695async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7696    init_test(cx, |_| {});
 7697
 7698    let language = Arc::new(Language::new(
 7699        LanguageConfig {
 7700            brackets: BracketPairConfig {
 7701                pairs: vec![BracketPair {
 7702                    start: "{".to_string(),
 7703                    end: "}".to_string(),
 7704                    close: true,
 7705                    surround: true,
 7706                    newline: true,
 7707                }],
 7708                ..Default::default()
 7709            },
 7710            autoclose_before: "}".to_string(),
 7711            ..Default::default()
 7712        },
 7713        Some(tree_sitter_rust::LANGUAGE.into()),
 7714    ));
 7715
 7716    let text = r#"
 7717        a
 7718        b
 7719        c
 7720    "#
 7721    .unindent();
 7722
 7723    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7724    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7725    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7726    editor
 7727        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7728        .await;
 7729
 7730    editor.update_in(cx, |editor, window, cx| {
 7731        editor.change_selections(None, window, cx, |s| {
 7732            s.select_ranges([
 7733                Point::new(0, 1)..Point::new(0, 1),
 7734                Point::new(1, 1)..Point::new(1, 1),
 7735                Point::new(2, 1)..Point::new(2, 1),
 7736            ])
 7737        });
 7738
 7739        editor.handle_input("{", window, cx);
 7740        editor.handle_input("{", window, cx);
 7741        editor.handle_input("_", window, cx);
 7742        assert_eq!(
 7743            editor.text(cx),
 7744            "
 7745                a{{_}}
 7746                b{{_}}
 7747                c{{_}}
 7748            "
 7749            .unindent()
 7750        );
 7751        assert_eq!(
 7752            editor.selections.ranges::<Point>(cx),
 7753            [
 7754                Point::new(0, 4)..Point::new(0, 4),
 7755                Point::new(1, 4)..Point::new(1, 4),
 7756                Point::new(2, 4)..Point::new(2, 4)
 7757            ]
 7758        );
 7759
 7760        editor.backspace(&Default::default(), window, cx);
 7761        editor.backspace(&Default::default(), window, cx);
 7762        assert_eq!(
 7763            editor.text(cx),
 7764            "
 7765                a{}
 7766                b{}
 7767                c{}
 7768            "
 7769            .unindent()
 7770        );
 7771        assert_eq!(
 7772            editor.selections.ranges::<Point>(cx),
 7773            [
 7774                Point::new(0, 2)..Point::new(0, 2),
 7775                Point::new(1, 2)..Point::new(1, 2),
 7776                Point::new(2, 2)..Point::new(2, 2)
 7777            ]
 7778        );
 7779
 7780        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7781        assert_eq!(
 7782            editor.text(cx),
 7783            "
 7784                a
 7785                b
 7786                c
 7787            "
 7788            .unindent()
 7789        );
 7790        assert_eq!(
 7791            editor.selections.ranges::<Point>(cx),
 7792            [
 7793                Point::new(0, 1)..Point::new(0, 1),
 7794                Point::new(1, 1)..Point::new(1, 1),
 7795                Point::new(2, 1)..Point::new(2, 1)
 7796            ]
 7797        );
 7798    });
 7799}
 7800
 7801#[gpui::test]
 7802async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7803    init_test(cx, |settings| {
 7804        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7805    });
 7806
 7807    let mut cx = EditorTestContext::new(cx).await;
 7808
 7809    let language = Arc::new(Language::new(
 7810        LanguageConfig {
 7811            brackets: BracketPairConfig {
 7812                pairs: vec![
 7813                    BracketPair {
 7814                        start: "{".to_string(),
 7815                        end: "}".to_string(),
 7816                        close: true,
 7817                        surround: true,
 7818                        newline: true,
 7819                    },
 7820                    BracketPair {
 7821                        start: "(".to_string(),
 7822                        end: ")".to_string(),
 7823                        close: true,
 7824                        surround: true,
 7825                        newline: true,
 7826                    },
 7827                    BracketPair {
 7828                        start: "[".to_string(),
 7829                        end: "]".to_string(),
 7830                        close: false,
 7831                        surround: true,
 7832                        newline: true,
 7833                    },
 7834                ],
 7835                ..Default::default()
 7836            },
 7837            autoclose_before: "})]".to_string(),
 7838            ..Default::default()
 7839        },
 7840        Some(tree_sitter_rust::LANGUAGE.into()),
 7841    ));
 7842
 7843    cx.language_registry().add(language.clone());
 7844    cx.update_buffer(|buffer, cx| {
 7845        buffer.set_language(Some(language), cx);
 7846    });
 7847
 7848    cx.set_state(
 7849        &"
 7850            {(ˇ)}
 7851            [[ˇ]]
 7852            {(ˇ)}
 7853        "
 7854        .unindent(),
 7855    );
 7856
 7857    cx.update_editor(|editor, window, cx| {
 7858        editor.backspace(&Default::default(), window, cx);
 7859        editor.backspace(&Default::default(), window, cx);
 7860    });
 7861
 7862    cx.assert_editor_state(
 7863        &"
 7864            ˇ
 7865            ˇ]]
 7866            ˇ
 7867        "
 7868        .unindent(),
 7869    );
 7870
 7871    cx.update_editor(|editor, window, cx| {
 7872        editor.handle_input("{", window, cx);
 7873        editor.handle_input("{", window, cx);
 7874        editor.move_right(&MoveRight, window, cx);
 7875        editor.move_right(&MoveRight, window, cx);
 7876        editor.move_left(&MoveLeft, window, cx);
 7877        editor.move_left(&MoveLeft, window, cx);
 7878        editor.backspace(&Default::default(), window, cx);
 7879    });
 7880
 7881    cx.assert_editor_state(
 7882        &"
 7883            {ˇ}
 7884            {ˇ}]]
 7885            {ˇ}
 7886        "
 7887        .unindent(),
 7888    );
 7889
 7890    cx.update_editor(|editor, window, cx| {
 7891        editor.backspace(&Default::default(), window, cx);
 7892    });
 7893
 7894    cx.assert_editor_state(
 7895        &"
 7896            ˇ
 7897            ˇ]]
 7898            ˇ
 7899        "
 7900        .unindent(),
 7901    );
 7902}
 7903
 7904#[gpui::test]
 7905async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7906    init_test(cx, |_| {});
 7907
 7908    let language = Arc::new(Language::new(
 7909        LanguageConfig::default(),
 7910        Some(tree_sitter_rust::LANGUAGE.into()),
 7911    ));
 7912
 7913    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7914    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7915    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7916    editor
 7917        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7918        .await;
 7919
 7920    editor.update_in(cx, |editor, window, cx| {
 7921        editor.set_auto_replace_emoji_shortcode(true);
 7922
 7923        editor.handle_input("Hello ", window, cx);
 7924        editor.handle_input(":wave", window, cx);
 7925        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7926
 7927        editor.handle_input(":", window, cx);
 7928        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7929
 7930        editor.handle_input(" :smile", window, cx);
 7931        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7932
 7933        editor.handle_input(":", window, cx);
 7934        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7935
 7936        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7937        editor.handle_input(":wave", window, cx);
 7938        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7939
 7940        editor.handle_input(":", window, cx);
 7941        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7942
 7943        editor.handle_input(":1", window, cx);
 7944        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7945
 7946        editor.handle_input(":", window, cx);
 7947        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7948
 7949        // Ensure shortcode does not get replaced when it is part of a word
 7950        editor.handle_input(" Test:wave", window, cx);
 7951        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7952
 7953        editor.handle_input(":", window, cx);
 7954        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7955
 7956        editor.set_auto_replace_emoji_shortcode(false);
 7957
 7958        // Ensure shortcode does not get replaced when auto replace is off
 7959        editor.handle_input(" :wave", window, cx);
 7960        assert_eq!(
 7961            editor.text(cx),
 7962            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7963        );
 7964
 7965        editor.handle_input(":", window, cx);
 7966        assert_eq!(
 7967            editor.text(cx),
 7968            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7969        );
 7970    });
 7971}
 7972
 7973#[gpui::test]
 7974async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7975    init_test(cx, |_| {});
 7976
 7977    let (text, insertion_ranges) = marked_text_ranges(
 7978        indoc! {"
 7979            ˇ
 7980        "},
 7981        false,
 7982    );
 7983
 7984    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7985    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7986
 7987    _ = editor.update_in(cx, |editor, window, cx| {
 7988        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7989
 7990        editor
 7991            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7992            .unwrap();
 7993
 7994        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7995            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7996            assert_eq!(editor.text(cx), expected_text);
 7997            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7998        }
 7999
 8000        assert(
 8001            editor,
 8002            cx,
 8003            indoc! {"
 8004            type «» =•
 8005            "},
 8006        );
 8007
 8008        assert!(editor.context_menu_visible(), "There should be a matches");
 8009    });
 8010}
 8011
 8012#[gpui::test]
 8013async fn test_snippets(cx: &mut TestAppContext) {
 8014    init_test(cx, |_| {});
 8015
 8016    let (text, insertion_ranges) = marked_text_ranges(
 8017        indoc! {"
 8018            a.ˇ b
 8019            a.ˇ b
 8020            a.ˇ b
 8021        "},
 8022        false,
 8023    );
 8024
 8025    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8026    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8027
 8028    editor.update_in(cx, |editor, window, cx| {
 8029        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8030
 8031        editor
 8032            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8033            .unwrap();
 8034
 8035        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8036            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8037            assert_eq!(editor.text(cx), expected_text);
 8038            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8039        }
 8040
 8041        assert(
 8042            editor,
 8043            cx,
 8044            indoc! {"
 8045                a.f(«one», two, «three») b
 8046                a.f(«one», two, «three») b
 8047                a.f(«one», two, «three») b
 8048            "},
 8049        );
 8050
 8051        // Can't move earlier than the first tab stop
 8052        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8053        assert(
 8054            editor,
 8055            cx,
 8056            indoc! {"
 8057                a.f(«one», two, «three») b
 8058                a.f(«one», two, «three») b
 8059                a.f(«one», two, «three») b
 8060            "},
 8061        );
 8062
 8063        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8064        assert(
 8065            editor,
 8066            cx,
 8067            indoc! {"
 8068                a.f(one, «two», three) b
 8069                a.f(one, «two», three) b
 8070                a.f(one, «two», three) b
 8071            "},
 8072        );
 8073
 8074        editor.move_to_prev_snippet_tabstop(window, cx);
 8075        assert(
 8076            editor,
 8077            cx,
 8078            indoc! {"
 8079                a.f(«one», two, «three») b
 8080                a.f(«one», two, «three») b
 8081                a.f(«one», two, «three») b
 8082            "},
 8083        );
 8084
 8085        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8086        assert(
 8087            editor,
 8088            cx,
 8089            indoc! {"
 8090                a.f(one, «two», three) b
 8091                a.f(one, «two», three) b
 8092                a.f(one, «two», three) b
 8093            "},
 8094        );
 8095        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8096        assert(
 8097            editor,
 8098            cx,
 8099            indoc! {"
 8100                a.f(one, two, three)ˇ b
 8101                a.f(one, two, three)ˇ b
 8102                a.f(one, two, three)ˇ b
 8103            "},
 8104        );
 8105
 8106        // As soon as the last tab stop is reached, snippet state is gone
 8107        editor.move_to_prev_snippet_tabstop(window, cx);
 8108        assert(
 8109            editor,
 8110            cx,
 8111            indoc! {"
 8112                a.f(one, two, three)ˇ b
 8113                a.f(one, two, three)ˇ b
 8114                a.f(one, two, three)ˇ b
 8115            "},
 8116        );
 8117    });
 8118}
 8119
 8120#[gpui::test]
 8121async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8122    init_test(cx, |_| {});
 8123
 8124    let fs = FakeFs::new(cx.executor());
 8125    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8126
 8127    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8128
 8129    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8130    language_registry.add(rust_lang());
 8131    let mut fake_servers = language_registry.register_fake_lsp(
 8132        "Rust",
 8133        FakeLspAdapter {
 8134            capabilities: lsp::ServerCapabilities {
 8135                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8136                ..Default::default()
 8137            },
 8138            ..Default::default()
 8139        },
 8140    );
 8141
 8142    let buffer = project
 8143        .update(cx, |project, cx| {
 8144            project.open_local_buffer(path!("/file.rs"), cx)
 8145        })
 8146        .await
 8147        .unwrap();
 8148
 8149    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8150    let (editor, cx) = cx.add_window_view(|window, cx| {
 8151        build_editor_with_project(project.clone(), buffer, window, cx)
 8152    });
 8153    editor.update_in(cx, |editor, window, cx| {
 8154        editor.set_text("one\ntwo\nthree\n", window, cx)
 8155    });
 8156    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8157
 8158    cx.executor().start_waiting();
 8159    let fake_server = fake_servers.next().await.unwrap();
 8160
 8161    {
 8162        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8163            move |params, _| async move {
 8164                assert_eq!(
 8165                    params.text_document.uri,
 8166                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8167                );
 8168                assert_eq!(params.options.tab_size, 4);
 8169                Ok(Some(vec![lsp::TextEdit::new(
 8170                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8171                    ", ".to_string(),
 8172                )]))
 8173            },
 8174        );
 8175        let save = editor
 8176            .update_in(cx, |editor, window, cx| {
 8177                editor.save(true, project.clone(), window, cx)
 8178            })
 8179            .unwrap();
 8180        cx.executor().start_waiting();
 8181        save.await;
 8182
 8183        assert_eq!(
 8184            editor.update(cx, |editor, cx| editor.text(cx)),
 8185            "one, two\nthree\n"
 8186        );
 8187        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8188    }
 8189
 8190    {
 8191        editor.update_in(cx, |editor, window, cx| {
 8192            editor.set_text("one\ntwo\nthree\n", window, cx)
 8193        });
 8194        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8195
 8196        // Ensure we can still save even if formatting hangs.
 8197        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8198            move |params, _| async move {
 8199                assert_eq!(
 8200                    params.text_document.uri,
 8201                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8202                );
 8203                futures::future::pending::<()>().await;
 8204                unreachable!()
 8205            },
 8206        );
 8207        let save = editor
 8208            .update_in(cx, |editor, window, cx| {
 8209                editor.save(true, project.clone(), window, cx)
 8210            })
 8211            .unwrap();
 8212        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8213        cx.executor().start_waiting();
 8214        save.await;
 8215        assert_eq!(
 8216            editor.update(cx, |editor, cx| editor.text(cx)),
 8217            "one\ntwo\nthree\n"
 8218        );
 8219    }
 8220
 8221    // For non-dirty buffer, no formatting request should be sent
 8222    {
 8223        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8224
 8225        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8226            panic!("Should not be invoked on non-dirty buffer");
 8227        });
 8228        let save = editor
 8229            .update_in(cx, |editor, window, cx| {
 8230                editor.save(true, project.clone(), window, cx)
 8231            })
 8232            .unwrap();
 8233        cx.executor().start_waiting();
 8234        save.await;
 8235    }
 8236
 8237    // Set rust language override and assert overridden tabsize is sent to language server
 8238    update_test_language_settings(cx, |settings| {
 8239        settings.languages.insert(
 8240            "Rust".into(),
 8241            LanguageSettingsContent {
 8242                tab_size: NonZeroU32::new(8),
 8243                ..Default::default()
 8244            },
 8245        );
 8246    });
 8247
 8248    {
 8249        editor.update_in(cx, |editor, window, cx| {
 8250            editor.set_text("somehting_new\n", window, cx)
 8251        });
 8252        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8253        let _formatting_request_signal = fake_server
 8254            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8255                assert_eq!(
 8256                    params.text_document.uri,
 8257                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8258                );
 8259                assert_eq!(params.options.tab_size, 8);
 8260                Ok(Some(vec![]))
 8261            });
 8262        let save = editor
 8263            .update_in(cx, |editor, window, cx| {
 8264                editor.save(true, project.clone(), window, cx)
 8265            })
 8266            .unwrap();
 8267        cx.executor().start_waiting();
 8268        save.await;
 8269    }
 8270}
 8271
 8272#[gpui::test]
 8273async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8274    init_test(cx, |_| {});
 8275
 8276    let cols = 4;
 8277    let rows = 10;
 8278    let sample_text_1 = sample_text(rows, cols, 'a');
 8279    assert_eq!(
 8280        sample_text_1,
 8281        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8282    );
 8283    let sample_text_2 = sample_text(rows, cols, 'l');
 8284    assert_eq!(
 8285        sample_text_2,
 8286        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8287    );
 8288    let sample_text_3 = sample_text(rows, cols, 'v');
 8289    assert_eq!(
 8290        sample_text_3,
 8291        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8292    );
 8293
 8294    let fs = FakeFs::new(cx.executor());
 8295    fs.insert_tree(
 8296        path!("/a"),
 8297        json!({
 8298            "main.rs": sample_text_1,
 8299            "other.rs": sample_text_2,
 8300            "lib.rs": sample_text_3,
 8301        }),
 8302    )
 8303    .await;
 8304
 8305    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8306    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8307    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8308
 8309    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8310    language_registry.add(rust_lang());
 8311    let mut fake_servers = language_registry.register_fake_lsp(
 8312        "Rust",
 8313        FakeLspAdapter {
 8314            capabilities: lsp::ServerCapabilities {
 8315                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8316                ..Default::default()
 8317            },
 8318            ..Default::default()
 8319        },
 8320    );
 8321
 8322    let worktree = project.update(cx, |project, cx| {
 8323        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8324        assert_eq!(worktrees.len(), 1);
 8325        worktrees.pop().unwrap()
 8326    });
 8327    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8328
 8329    let buffer_1 = project
 8330        .update(cx, |project, cx| {
 8331            project.open_buffer((worktree_id, "main.rs"), cx)
 8332        })
 8333        .await
 8334        .unwrap();
 8335    let buffer_2 = project
 8336        .update(cx, |project, cx| {
 8337            project.open_buffer((worktree_id, "other.rs"), cx)
 8338        })
 8339        .await
 8340        .unwrap();
 8341    let buffer_3 = project
 8342        .update(cx, |project, cx| {
 8343            project.open_buffer((worktree_id, "lib.rs"), cx)
 8344        })
 8345        .await
 8346        .unwrap();
 8347
 8348    let multi_buffer = cx.new(|cx| {
 8349        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8350        multi_buffer.push_excerpts(
 8351            buffer_1.clone(),
 8352            [
 8353                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8354                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8355                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8356            ],
 8357            cx,
 8358        );
 8359        multi_buffer.push_excerpts(
 8360            buffer_2.clone(),
 8361            [
 8362                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8363                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8364                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8365            ],
 8366            cx,
 8367        );
 8368        multi_buffer.push_excerpts(
 8369            buffer_3.clone(),
 8370            [
 8371                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8372                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8373                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8374            ],
 8375            cx,
 8376        );
 8377        multi_buffer
 8378    });
 8379    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8380        Editor::new(
 8381            EditorMode::full(),
 8382            multi_buffer,
 8383            Some(project.clone()),
 8384            window,
 8385            cx,
 8386        )
 8387    });
 8388
 8389    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8390        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8391            s.select_ranges(Some(1..2))
 8392        });
 8393        editor.insert("|one|two|three|", window, cx);
 8394    });
 8395    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8396    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8397        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8398            s.select_ranges(Some(60..70))
 8399        });
 8400        editor.insert("|four|five|six|", window, cx);
 8401    });
 8402    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8403
 8404    // First two buffers should be edited, but not the third one.
 8405    assert_eq!(
 8406        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8407        "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}",
 8408    );
 8409    buffer_1.update(cx, |buffer, _| {
 8410        assert!(buffer.is_dirty());
 8411        assert_eq!(
 8412            buffer.text(),
 8413            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8414        )
 8415    });
 8416    buffer_2.update(cx, |buffer, _| {
 8417        assert!(buffer.is_dirty());
 8418        assert_eq!(
 8419            buffer.text(),
 8420            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8421        )
 8422    });
 8423    buffer_3.update(cx, |buffer, _| {
 8424        assert!(!buffer.is_dirty());
 8425        assert_eq!(buffer.text(), sample_text_3,)
 8426    });
 8427    cx.executor().run_until_parked();
 8428
 8429    cx.executor().start_waiting();
 8430    let save = multi_buffer_editor
 8431        .update_in(cx, |editor, window, cx| {
 8432            editor.save(true, project.clone(), window, cx)
 8433        })
 8434        .unwrap();
 8435
 8436    let fake_server = fake_servers.next().await.unwrap();
 8437    fake_server
 8438        .server
 8439        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8440            Ok(Some(vec![lsp::TextEdit::new(
 8441                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8442                format!("[{} formatted]", params.text_document.uri),
 8443            )]))
 8444        })
 8445        .detach();
 8446    save.await;
 8447
 8448    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8449    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8450    assert_eq!(
 8451        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8452        uri!(
 8453            "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}"
 8454        ),
 8455    );
 8456    buffer_1.update(cx, |buffer, _| {
 8457        assert!(!buffer.is_dirty());
 8458        assert_eq!(
 8459            buffer.text(),
 8460            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8461        )
 8462    });
 8463    buffer_2.update(cx, |buffer, _| {
 8464        assert!(!buffer.is_dirty());
 8465        assert_eq!(
 8466            buffer.text(),
 8467            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8468        )
 8469    });
 8470    buffer_3.update(cx, |buffer, _| {
 8471        assert!(!buffer.is_dirty());
 8472        assert_eq!(buffer.text(), sample_text_3,)
 8473    });
 8474}
 8475
 8476#[gpui::test]
 8477async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8478    init_test(cx, |_| {});
 8479
 8480    let fs = FakeFs::new(cx.executor());
 8481    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8482
 8483    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8484
 8485    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8486    language_registry.add(rust_lang());
 8487    let mut fake_servers = language_registry.register_fake_lsp(
 8488        "Rust",
 8489        FakeLspAdapter {
 8490            capabilities: lsp::ServerCapabilities {
 8491                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8492                ..Default::default()
 8493            },
 8494            ..Default::default()
 8495        },
 8496    );
 8497
 8498    let buffer = project
 8499        .update(cx, |project, cx| {
 8500            project.open_local_buffer(path!("/file.rs"), cx)
 8501        })
 8502        .await
 8503        .unwrap();
 8504
 8505    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8506    let (editor, cx) = cx.add_window_view(|window, cx| {
 8507        build_editor_with_project(project.clone(), buffer, window, cx)
 8508    });
 8509    editor.update_in(cx, |editor, window, cx| {
 8510        editor.set_text("one\ntwo\nthree\n", window, cx)
 8511    });
 8512    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8513
 8514    cx.executor().start_waiting();
 8515    let fake_server = fake_servers.next().await.unwrap();
 8516
 8517    let save = editor
 8518        .update_in(cx, |editor, window, cx| {
 8519            editor.save(true, project.clone(), window, cx)
 8520        })
 8521        .unwrap();
 8522    fake_server
 8523        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8524            assert_eq!(
 8525                params.text_document.uri,
 8526                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8527            );
 8528            assert_eq!(params.options.tab_size, 4);
 8529            Ok(Some(vec![lsp::TextEdit::new(
 8530                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8531                ", ".to_string(),
 8532            )]))
 8533        })
 8534        .next()
 8535        .await;
 8536    cx.executor().start_waiting();
 8537    save.await;
 8538    assert_eq!(
 8539        editor.update(cx, |editor, cx| editor.text(cx)),
 8540        "one, two\nthree\n"
 8541    );
 8542    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8543
 8544    editor.update_in(cx, |editor, window, cx| {
 8545        editor.set_text("one\ntwo\nthree\n", window, cx)
 8546    });
 8547    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8548
 8549    // Ensure we can still save even if formatting hangs.
 8550    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8551        move |params, _| async move {
 8552            assert_eq!(
 8553                params.text_document.uri,
 8554                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8555            );
 8556            futures::future::pending::<()>().await;
 8557            unreachable!()
 8558        },
 8559    );
 8560    let save = editor
 8561        .update_in(cx, |editor, window, cx| {
 8562            editor.save(true, project.clone(), window, cx)
 8563        })
 8564        .unwrap();
 8565    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8566    cx.executor().start_waiting();
 8567    save.await;
 8568    assert_eq!(
 8569        editor.update(cx, |editor, cx| editor.text(cx)),
 8570        "one\ntwo\nthree\n"
 8571    );
 8572    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8573
 8574    // For non-dirty buffer, no formatting request should be sent
 8575    let save = editor
 8576        .update_in(cx, |editor, window, cx| {
 8577            editor.save(true, project.clone(), window, cx)
 8578        })
 8579        .unwrap();
 8580    let _pending_format_request = fake_server
 8581        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8582            panic!("Should not be invoked on non-dirty buffer");
 8583        })
 8584        .next();
 8585    cx.executor().start_waiting();
 8586    save.await;
 8587
 8588    // Set Rust language override and assert overridden tabsize is sent to language server
 8589    update_test_language_settings(cx, |settings| {
 8590        settings.languages.insert(
 8591            "Rust".into(),
 8592            LanguageSettingsContent {
 8593                tab_size: NonZeroU32::new(8),
 8594                ..Default::default()
 8595            },
 8596        );
 8597    });
 8598
 8599    editor.update_in(cx, |editor, window, cx| {
 8600        editor.set_text("somehting_new\n", window, cx)
 8601    });
 8602    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8603    let save = editor
 8604        .update_in(cx, |editor, window, cx| {
 8605            editor.save(true, project.clone(), window, cx)
 8606        })
 8607        .unwrap();
 8608    fake_server
 8609        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8610            assert_eq!(
 8611                params.text_document.uri,
 8612                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8613            );
 8614            assert_eq!(params.options.tab_size, 8);
 8615            Ok(Some(vec![]))
 8616        })
 8617        .next()
 8618        .await;
 8619    cx.executor().start_waiting();
 8620    save.await;
 8621}
 8622
 8623#[gpui::test]
 8624async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8625    init_test(cx, |settings| {
 8626        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8627            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8628        ))
 8629    });
 8630
 8631    let fs = FakeFs::new(cx.executor());
 8632    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8633
 8634    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8635
 8636    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8637    language_registry.add(Arc::new(Language::new(
 8638        LanguageConfig {
 8639            name: "Rust".into(),
 8640            matcher: LanguageMatcher {
 8641                path_suffixes: vec!["rs".to_string()],
 8642                ..Default::default()
 8643            },
 8644            ..LanguageConfig::default()
 8645        },
 8646        Some(tree_sitter_rust::LANGUAGE.into()),
 8647    )));
 8648    update_test_language_settings(cx, |settings| {
 8649        // Enable Prettier formatting for the same buffer, and ensure
 8650        // LSP is called instead of Prettier.
 8651        settings.defaults.prettier = Some(PrettierSettings {
 8652            allowed: true,
 8653            ..PrettierSettings::default()
 8654        });
 8655    });
 8656    let mut fake_servers = language_registry.register_fake_lsp(
 8657        "Rust",
 8658        FakeLspAdapter {
 8659            capabilities: lsp::ServerCapabilities {
 8660                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8661                ..Default::default()
 8662            },
 8663            ..Default::default()
 8664        },
 8665    );
 8666
 8667    let buffer = project
 8668        .update(cx, |project, cx| {
 8669            project.open_local_buffer(path!("/file.rs"), cx)
 8670        })
 8671        .await
 8672        .unwrap();
 8673
 8674    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8675    let (editor, cx) = cx.add_window_view(|window, cx| {
 8676        build_editor_with_project(project.clone(), buffer, window, cx)
 8677    });
 8678    editor.update_in(cx, |editor, window, cx| {
 8679        editor.set_text("one\ntwo\nthree\n", window, cx)
 8680    });
 8681
 8682    cx.executor().start_waiting();
 8683    let fake_server = fake_servers.next().await.unwrap();
 8684
 8685    let format = editor
 8686        .update_in(cx, |editor, window, cx| {
 8687            editor.perform_format(
 8688                project.clone(),
 8689                FormatTrigger::Manual,
 8690                FormatTarget::Buffers,
 8691                window,
 8692                cx,
 8693            )
 8694        })
 8695        .unwrap();
 8696    fake_server
 8697        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8698            assert_eq!(
 8699                params.text_document.uri,
 8700                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8701            );
 8702            assert_eq!(params.options.tab_size, 4);
 8703            Ok(Some(vec![lsp::TextEdit::new(
 8704                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8705                ", ".to_string(),
 8706            )]))
 8707        })
 8708        .next()
 8709        .await;
 8710    cx.executor().start_waiting();
 8711    format.await;
 8712    assert_eq!(
 8713        editor.update(cx, |editor, cx| editor.text(cx)),
 8714        "one, two\nthree\n"
 8715    );
 8716
 8717    editor.update_in(cx, |editor, window, cx| {
 8718        editor.set_text("one\ntwo\nthree\n", window, cx)
 8719    });
 8720    // Ensure we don't lock if formatting hangs.
 8721    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8722        move |params, _| async move {
 8723            assert_eq!(
 8724                params.text_document.uri,
 8725                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8726            );
 8727            futures::future::pending::<()>().await;
 8728            unreachable!()
 8729        },
 8730    );
 8731    let format = editor
 8732        .update_in(cx, |editor, window, cx| {
 8733            editor.perform_format(
 8734                project,
 8735                FormatTrigger::Manual,
 8736                FormatTarget::Buffers,
 8737                window,
 8738                cx,
 8739            )
 8740        })
 8741        .unwrap();
 8742    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8743    cx.executor().start_waiting();
 8744    format.await;
 8745    assert_eq!(
 8746        editor.update(cx, |editor, cx| editor.text(cx)),
 8747        "one\ntwo\nthree\n"
 8748    );
 8749}
 8750
 8751#[gpui::test]
 8752async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8753    init_test(cx, |settings| {
 8754        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8755        settings.defaults.formatter =
 8756            Some(language_settings::SelectedFormatter::List(FormatterList(
 8757                vec![
 8758                    Formatter::LanguageServer { name: None },
 8759                    Formatter::CodeActions(
 8760                        [
 8761                            ("code-action-1".into(), true),
 8762                            ("code-action-2".into(), true),
 8763                        ]
 8764                        .into_iter()
 8765                        .collect(),
 8766                    ),
 8767                ]
 8768                .into(),
 8769            )))
 8770    });
 8771
 8772    let fs = FakeFs::new(cx.executor());
 8773    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8774        .await;
 8775
 8776    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8777    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8778    language_registry.add(rust_lang());
 8779
 8780    let mut fake_servers = language_registry.register_fake_lsp(
 8781        "Rust",
 8782        FakeLspAdapter {
 8783            capabilities: lsp::ServerCapabilities {
 8784                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8785                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8786                    commands: vec!["the-command-for-code-action-1".into()],
 8787                    ..Default::default()
 8788                }),
 8789                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8790                ..Default::default()
 8791            },
 8792            ..Default::default()
 8793        },
 8794    );
 8795
 8796    let buffer = project
 8797        .update(cx, |project, cx| {
 8798            project.open_local_buffer(path!("/file.rs"), cx)
 8799        })
 8800        .await
 8801        .unwrap();
 8802
 8803    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8804    let (editor, cx) = cx.add_window_view(|window, cx| {
 8805        build_editor_with_project(project.clone(), buffer, window, cx)
 8806    });
 8807
 8808    cx.executor().start_waiting();
 8809
 8810    let fake_server = fake_servers.next().await.unwrap();
 8811    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8812        move |_params, _| async move {
 8813            Ok(Some(vec![lsp::TextEdit::new(
 8814                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8815                "applied-formatting\n".to_string(),
 8816            )]))
 8817        },
 8818    );
 8819    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8820        move |params, _| async move {
 8821            assert_eq!(
 8822                params.context.only,
 8823                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8824            );
 8825            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8826            Ok(Some(vec![
 8827                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8828                    kind: Some("code-action-1".into()),
 8829                    edit: Some(lsp::WorkspaceEdit::new(
 8830                        [(
 8831                            uri.clone(),
 8832                            vec![lsp::TextEdit::new(
 8833                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8834                                "applied-code-action-1-edit\n".to_string(),
 8835                            )],
 8836                        )]
 8837                        .into_iter()
 8838                        .collect(),
 8839                    )),
 8840                    command: Some(lsp::Command {
 8841                        command: "the-command-for-code-action-1".into(),
 8842                        ..Default::default()
 8843                    }),
 8844                    ..Default::default()
 8845                }),
 8846                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8847                    kind: Some("code-action-2".into()),
 8848                    edit: Some(lsp::WorkspaceEdit::new(
 8849                        [(
 8850                            uri.clone(),
 8851                            vec![lsp::TextEdit::new(
 8852                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8853                                "applied-code-action-2-edit\n".to_string(),
 8854                            )],
 8855                        )]
 8856                        .into_iter()
 8857                        .collect(),
 8858                    )),
 8859                    ..Default::default()
 8860                }),
 8861            ]))
 8862        },
 8863    );
 8864
 8865    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8866        move |params, _| async move { Ok(params) }
 8867    });
 8868
 8869    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8870    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8871        let fake = fake_server.clone();
 8872        let lock = command_lock.clone();
 8873        move |params, _| {
 8874            assert_eq!(params.command, "the-command-for-code-action-1");
 8875            let fake = fake.clone();
 8876            let lock = lock.clone();
 8877            async move {
 8878                lock.lock().await;
 8879                fake.server
 8880                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8881                        label: None,
 8882                        edit: lsp::WorkspaceEdit {
 8883                            changes: Some(
 8884                                [(
 8885                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8886                                    vec![lsp::TextEdit {
 8887                                        range: lsp::Range::new(
 8888                                            lsp::Position::new(0, 0),
 8889                                            lsp::Position::new(0, 0),
 8890                                        ),
 8891                                        new_text: "applied-code-action-1-command\n".into(),
 8892                                    }],
 8893                                )]
 8894                                .into_iter()
 8895                                .collect(),
 8896                            ),
 8897                            ..Default::default()
 8898                        },
 8899                    })
 8900                    .await
 8901                    .unwrap();
 8902                Ok(Some(json!(null)))
 8903            }
 8904        }
 8905    });
 8906
 8907    cx.executor().start_waiting();
 8908    editor
 8909        .update_in(cx, |editor, window, cx| {
 8910            editor.perform_format(
 8911                project.clone(),
 8912                FormatTrigger::Manual,
 8913                FormatTarget::Buffers,
 8914                window,
 8915                cx,
 8916            )
 8917        })
 8918        .unwrap()
 8919        .await;
 8920    editor.update(cx, |editor, cx| {
 8921        assert_eq!(
 8922            editor.text(cx),
 8923            r#"
 8924                applied-code-action-2-edit
 8925                applied-code-action-1-command
 8926                applied-code-action-1-edit
 8927                applied-formatting
 8928                one
 8929                two
 8930                three
 8931            "#
 8932            .unindent()
 8933        );
 8934    });
 8935
 8936    editor.update_in(cx, |editor, window, cx| {
 8937        editor.undo(&Default::default(), window, cx);
 8938        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8939    });
 8940
 8941    // Perform a manual edit while waiting for an LSP command
 8942    // that's being run as part of a formatting code action.
 8943    let lock_guard = command_lock.lock().await;
 8944    let format = editor
 8945        .update_in(cx, |editor, window, cx| {
 8946            editor.perform_format(
 8947                project.clone(),
 8948                FormatTrigger::Manual,
 8949                FormatTarget::Buffers,
 8950                window,
 8951                cx,
 8952            )
 8953        })
 8954        .unwrap();
 8955    cx.run_until_parked();
 8956    editor.update(cx, |editor, cx| {
 8957        assert_eq!(
 8958            editor.text(cx),
 8959            r#"
 8960                applied-code-action-1-edit
 8961                applied-formatting
 8962                one
 8963                two
 8964                three
 8965            "#
 8966            .unindent()
 8967        );
 8968
 8969        editor.buffer.update(cx, |buffer, cx| {
 8970            let ix = buffer.len(cx);
 8971            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8972        });
 8973    });
 8974
 8975    // Allow the LSP command to proceed. Because the buffer was edited,
 8976    // the second code action will not be run.
 8977    drop(lock_guard);
 8978    format.await;
 8979    editor.update_in(cx, |editor, window, cx| {
 8980        assert_eq!(
 8981            editor.text(cx),
 8982            r#"
 8983                applied-code-action-1-command
 8984                applied-code-action-1-edit
 8985                applied-formatting
 8986                one
 8987                two
 8988                three
 8989                edited
 8990            "#
 8991            .unindent()
 8992        );
 8993
 8994        // The manual edit is undone first, because it is the last thing the user did
 8995        // (even though the command completed afterwards).
 8996        editor.undo(&Default::default(), window, cx);
 8997        assert_eq!(
 8998            editor.text(cx),
 8999            r#"
 9000                applied-code-action-1-command
 9001                applied-code-action-1-edit
 9002                applied-formatting
 9003                one
 9004                two
 9005                three
 9006            "#
 9007            .unindent()
 9008        );
 9009
 9010        // All the formatting (including the command, which completed after the manual edit)
 9011        // is undone together.
 9012        editor.undo(&Default::default(), window, cx);
 9013        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9014    });
 9015}
 9016
 9017#[gpui::test]
 9018async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9019    init_test(cx, |settings| {
 9020        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9021            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9022        ))
 9023    });
 9024
 9025    let fs = FakeFs::new(cx.executor());
 9026    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9027
 9028    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9029
 9030    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9031    language_registry.add(Arc::new(Language::new(
 9032        LanguageConfig {
 9033            name: "TypeScript".into(),
 9034            matcher: LanguageMatcher {
 9035                path_suffixes: vec!["ts".to_string()],
 9036                ..Default::default()
 9037            },
 9038            ..LanguageConfig::default()
 9039        },
 9040        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9041    )));
 9042    update_test_language_settings(cx, |settings| {
 9043        settings.defaults.prettier = Some(PrettierSettings {
 9044            allowed: true,
 9045            ..PrettierSettings::default()
 9046        });
 9047    });
 9048    let mut fake_servers = language_registry.register_fake_lsp(
 9049        "TypeScript",
 9050        FakeLspAdapter {
 9051            capabilities: lsp::ServerCapabilities {
 9052                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9053                ..Default::default()
 9054            },
 9055            ..Default::default()
 9056        },
 9057    );
 9058
 9059    let buffer = project
 9060        .update(cx, |project, cx| {
 9061            project.open_local_buffer(path!("/file.ts"), cx)
 9062        })
 9063        .await
 9064        .unwrap();
 9065
 9066    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9067    let (editor, cx) = cx.add_window_view(|window, cx| {
 9068        build_editor_with_project(project.clone(), buffer, window, cx)
 9069    });
 9070    editor.update_in(cx, |editor, window, cx| {
 9071        editor.set_text(
 9072            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9073            window,
 9074            cx,
 9075        )
 9076    });
 9077
 9078    cx.executor().start_waiting();
 9079    let fake_server = fake_servers.next().await.unwrap();
 9080
 9081    let format = editor
 9082        .update_in(cx, |editor, window, cx| {
 9083            editor.perform_code_action_kind(
 9084                project.clone(),
 9085                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9086                window,
 9087                cx,
 9088            )
 9089        })
 9090        .unwrap();
 9091    fake_server
 9092        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9093            assert_eq!(
 9094                params.text_document.uri,
 9095                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9096            );
 9097            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9098                lsp::CodeAction {
 9099                    title: "Organize Imports".to_string(),
 9100                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9101                    edit: Some(lsp::WorkspaceEdit {
 9102                        changes: Some(
 9103                            [(
 9104                                params.text_document.uri.clone(),
 9105                                vec![lsp::TextEdit::new(
 9106                                    lsp::Range::new(
 9107                                        lsp::Position::new(1, 0),
 9108                                        lsp::Position::new(2, 0),
 9109                                    ),
 9110                                    "".to_string(),
 9111                                )],
 9112                            )]
 9113                            .into_iter()
 9114                            .collect(),
 9115                        ),
 9116                        ..Default::default()
 9117                    }),
 9118                    ..Default::default()
 9119                },
 9120            )]))
 9121        })
 9122        .next()
 9123        .await;
 9124    cx.executor().start_waiting();
 9125    format.await;
 9126    assert_eq!(
 9127        editor.update(cx, |editor, cx| editor.text(cx)),
 9128        "import { a } from 'module';\n\nconst x = a;\n"
 9129    );
 9130
 9131    editor.update_in(cx, |editor, window, cx| {
 9132        editor.set_text(
 9133            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9134            window,
 9135            cx,
 9136        )
 9137    });
 9138    // Ensure we don't lock if code action hangs.
 9139    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9140        move |params, _| async move {
 9141            assert_eq!(
 9142                params.text_document.uri,
 9143                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9144            );
 9145            futures::future::pending::<()>().await;
 9146            unreachable!()
 9147        },
 9148    );
 9149    let format = editor
 9150        .update_in(cx, |editor, window, cx| {
 9151            editor.perform_code_action_kind(
 9152                project,
 9153                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9154                window,
 9155                cx,
 9156            )
 9157        })
 9158        .unwrap();
 9159    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9160    cx.executor().start_waiting();
 9161    format.await;
 9162    assert_eq!(
 9163        editor.update(cx, |editor, cx| editor.text(cx)),
 9164        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9165    );
 9166}
 9167
 9168#[gpui::test]
 9169async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9170    init_test(cx, |_| {});
 9171
 9172    let mut cx = EditorLspTestContext::new_rust(
 9173        lsp::ServerCapabilities {
 9174            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9175            ..Default::default()
 9176        },
 9177        cx,
 9178    )
 9179    .await;
 9180
 9181    cx.set_state(indoc! {"
 9182        one.twoˇ
 9183    "});
 9184
 9185    // The format request takes a long time. When it completes, it inserts
 9186    // a newline and an indent before the `.`
 9187    cx.lsp
 9188        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9189            let executor = cx.background_executor().clone();
 9190            async move {
 9191                executor.timer(Duration::from_millis(100)).await;
 9192                Ok(Some(vec![lsp::TextEdit {
 9193                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9194                    new_text: "\n    ".into(),
 9195                }]))
 9196            }
 9197        });
 9198
 9199    // Submit a format request.
 9200    let format_1 = cx
 9201        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9202        .unwrap();
 9203    cx.executor().run_until_parked();
 9204
 9205    // Submit a second format request.
 9206    let format_2 = cx
 9207        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9208        .unwrap();
 9209    cx.executor().run_until_parked();
 9210
 9211    // Wait for both format requests to complete
 9212    cx.executor().advance_clock(Duration::from_millis(200));
 9213    cx.executor().start_waiting();
 9214    format_1.await.unwrap();
 9215    cx.executor().start_waiting();
 9216    format_2.await.unwrap();
 9217
 9218    // The formatting edits only happens once.
 9219    cx.assert_editor_state(indoc! {"
 9220        one
 9221            .twoˇ
 9222    "});
 9223}
 9224
 9225#[gpui::test]
 9226async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9227    init_test(cx, |settings| {
 9228        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9229    });
 9230
 9231    let mut cx = EditorLspTestContext::new_rust(
 9232        lsp::ServerCapabilities {
 9233            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9234            ..Default::default()
 9235        },
 9236        cx,
 9237    )
 9238    .await;
 9239
 9240    // Set up a buffer white some trailing whitespace and no trailing newline.
 9241    cx.set_state(
 9242        &[
 9243            "one ",   //
 9244            "twoˇ",   //
 9245            "three ", //
 9246            "four",   //
 9247        ]
 9248        .join("\n"),
 9249    );
 9250
 9251    // Submit a format request.
 9252    let format = cx
 9253        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9254        .unwrap();
 9255
 9256    // Record which buffer changes have been sent to the language server
 9257    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9258    cx.lsp
 9259        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9260            let buffer_changes = buffer_changes.clone();
 9261            move |params, _| {
 9262                buffer_changes.lock().extend(
 9263                    params
 9264                        .content_changes
 9265                        .into_iter()
 9266                        .map(|e| (e.range.unwrap(), e.text)),
 9267                );
 9268            }
 9269        });
 9270
 9271    // Handle formatting requests to the language server.
 9272    cx.lsp
 9273        .set_request_handler::<lsp::request::Formatting, _, _>({
 9274            let buffer_changes = buffer_changes.clone();
 9275            move |_, _| {
 9276                // When formatting is requested, trailing whitespace has already been stripped,
 9277                // and the trailing newline has already been added.
 9278                assert_eq!(
 9279                    &buffer_changes.lock()[1..],
 9280                    &[
 9281                        (
 9282                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9283                            "".into()
 9284                        ),
 9285                        (
 9286                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9287                            "".into()
 9288                        ),
 9289                        (
 9290                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9291                            "\n".into()
 9292                        ),
 9293                    ]
 9294                );
 9295
 9296                // Insert blank lines between each line of the buffer.
 9297                async move {
 9298                    Ok(Some(vec![
 9299                        lsp::TextEdit {
 9300                            range: lsp::Range::new(
 9301                                lsp::Position::new(1, 0),
 9302                                lsp::Position::new(1, 0),
 9303                            ),
 9304                            new_text: "\n".into(),
 9305                        },
 9306                        lsp::TextEdit {
 9307                            range: lsp::Range::new(
 9308                                lsp::Position::new(2, 0),
 9309                                lsp::Position::new(2, 0),
 9310                            ),
 9311                            new_text: "\n".into(),
 9312                        },
 9313                    ]))
 9314                }
 9315            }
 9316        });
 9317
 9318    // After formatting the buffer, the trailing whitespace is stripped,
 9319    // a newline is appended, and the edits provided by the language server
 9320    // have been applied.
 9321    format.await.unwrap();
 9322    cx.assert_editor_state(
 9323        &[
 9324            "one",   //
 9325            "",      //
 9326            "twoˇ",  //
 9327            "",      //
 9328            "three", //
 9329            "four",  //
 9330            "",      //
 9331        ]
 9332        .join("\n"),
 9333    );
 9334
 9335    // Undoing the formatting undoes the trailing whitespace removal, the
 9336    // trailing newline, and the LSP edits.
 9337    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9338    cx.assert_editor_state(
 9339        &[
 9340            "one ",   //
 9341            "twoˇ",   //
 9342            "three ", //
 9343            "four",   //
 9344        ]
 9345        .join("\n"),
 9346    );
 9347}
 9348
 9349#[gpui::test]
 9350async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9351    cx: &mut TestAppContext,
 9352) {
 9353    init_test(cx, |_| {});
 9354
 9355    cx.update(|cx| {
 9356        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9357            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9358                settings.auto_signature_help = Some(true);
 9359            });
 9360        });
 9361    });
 9362
 9363    let mut cx = EditorLspTestContext::new_rust(
 9364        lsp::ServerCapabilities {
 9365            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9366                ..Default::default()
 9367            }),
 9368            ..Default::default()
 9369        },
 9370        cx,
 9371    )
 9372    .await;
 9373
 9374    let language = Language::new(
 9375        LanguageConfig {
 9376            name: "Rust".into(),
 9377            brackets: BracketPairConfig {
 9378                pairs: vec![
 9379                    BracketPair {
 9380                        start: "{".to_string(),
 9381                        end: "}".to_string(),
 9382                        close: true,
 9383                        surround: true,
 9384                        newline: true,
 9385                    },
 9386                    BracketPair {
 9387                        start: "(".to_string(),
 9388                        end: ")".to_string(),
 9389                        close: true,
 9390                        surround: true,
 9391                        newline: true,
 9392                    },
 9393                    BracketPair {
 9394                        start: "/*".to_string(),
 9395                        end: " */".to_string(),
 9396                        close: true,
 9397                        surround: true,
 9398                        newline: true,
 9399                    },
 9400                    BracketPair {
 9401                        start: "[".to_string(),
 9402                        end: "]".to_string(),
 9403                        close: false,
 9404                        surround: false,
 9405                        newline: true,
 9406                    },
 9407                    BracketPair {
 9408                        start: "\"".to_string(),
 9409                        end: "\"".to_string(),
 9410                        close: true,
 9411                        surround: true,
 9412                        newline: false,
 9413                    },
 9414                    BracketPair {
 9415                        start: "<".to_string(),
 9416                        end: ">".to_string(),
 9417                        close: false,
 9418                        surround: true,
 9419                        newline: true,
 9420                    },
 9421                ],
 9422                ..Default::default()
 9423            },
 9424            autoclose_before: "})]".to_string(),
 9425            ..Default::default()
 9426        },
 9427        Some(tree_sitter_rust::LANGUAGE.into()),
 9428    );
 9429    let language = Arc::new(language);
 9430
 9431    cx.language_registry().add(language.clone());
 9432    cx.update_buffer(|buffer, cx| {
 9433        buffer.set_language(Some(language), cx);
 9434    });
 9435
 9436    cx.set_state(
 9437        &r#"
 9438            fn main() {
 9439                sampleˇ
 9440            }
 9441        "#
 9442        .unindent(),
 9443    );
 9444
 9445    cx.update_editor(|editor, window, cx| {
 9446        editor.handle_input("(", window, cx);
 9447    });
 9448    cx.assert_editor_state(
 9449        &"
 9450            fn main() {
 9451                sample(ˇ)
 9452            }
 9453        "
 9454        .unindent(),
 9455    );
 9456
 9457    let mocked_response = lsp::SignatureHelp {
 9458        signatures: vec![lsp::SignatureInformation {
 9459            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9460            documentation: None,
 9461            parameters: Some(vec![
 9462                lsp::ParameterInformation {
 9463                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9464                    documentation: None,
 9465                },
 9466                lsp::ParameterInformation {
 9467                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9468                    documentation: None,
 9469                },
 9470            ]),
 9471            active_parameter: None,
 9472        }],
 9473        active_signature: Some(0),
 9474        active_parameter: Some(0),
 9475    };
 9476    handle_signature_help_request(&mut cx, mocked_response).await;
 9477
 9478    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9479        .await;
 9480
 9481    cx.editor(|editor, _, _| {
 9482        let signature_help_state = editor.signature_help_state.popover().cloned();
 9483        assert_eq!(
 9484            signature_help_state.unwrap().label,
 9485            "param1: u8, param2: u8"
 9486        );
 9487    });
 9488}
 9489
 9490#[gpui::test]
 9491async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9492    init_test(cx, |_| {});
 9493
 9494    cx.update(|cx| {
 9495        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9496            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9497                settings.auto_signature_help = Some(false);
 9498                settings.show_signature_help_after_edits = Some(false);
 9499            });
 9500        });
 9501    });
 9502
 9503    let mut cx = EditorLspTestContext::new_rust(
 9504        lsp::ServerCapabilities {
 9505            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9506                ..Default::default()
 9507            }),
 9508            ..Default::default()
 9509        },
 9510        cx,
 9511    )
 9512    .await;
 9513
 9514    let language = Language::new(
 9515        LanguageConfig {
 9516            name: "Rust".into(),
 9517            brackets: BracketPairConfig {
 9518                pairs: vec![
 9519                    BracketPair {
 9520                        start: "{".to_string(),
 9521                        end: "}".to_string(),
 9522                        close: true,
 9523                        surround: true,
 9524                        newline: true,
 9525                    },
 9526                    BracketPair {
 9527                        start: "(".to_string(),
 9528                        end: ")".to_string(),
 9529                        close: true,
 9530                        surround: true,
 9531                        newline: true,
 9532                    },
 9533                    BracketPair {
 9534                        start: "/*".to_string(),
 9535                        end: " */".to_string(),
 9536                        close: true,
 9537                        surround: true,
 9538                        newline: true,
 9539                    },
 9540                    BracketPair {
 9541                        start: "[".to_string(),
 9542                        end: "]".to_string(),
 9543                        close: false,
 9544                        surround: false,
 9545                        newline: true,
 9546                    },
 9547                    BracketPair {
 9548                        start: "\"".to_string(),
 9549                        end: "\"".to_string(),
 9550                        close: true,
 9551                        surround: true,
 9552                        newline: false,
 9553                    },
 9554                    BracketPair {
 9555                        start: "<".to_string(),
 9556                        end: ">".to_string(),
 9557                        close: false,
 9558                        surround: true,
 9559                        newline: true,
 9560                    },
 9561                ],
 9562                ..Default::default()
 9563            },
 9564            autoclose_before: "})]".to_string(),
 9565            ..Default::default()
 9566        },
 9567        Some(tree_sitter_rust::LANGUAGE.into()),
 9568    );
 9569    let language = Arc::new(language);
 9570
 9571    cx.language_registry().add(language.clone());
 9572    cx.update_buffer(|buffer, cx| {
 9573        buffer.set_language(Some(language), cx);
 9574    });
 9575
 9576    // Ensure that signature_help is not called when no signature help is enabled.
 9577    cx.set_state(
 9578        &r#"
 9579            fn main() {
 9580                sampleˇ
 9581            }
 9582        "#
 9583        .unindent(),
 9584    );
 9585    cx.update_editor(|editor, window, cx| {
 9586        editor.handle_input("(", window, cx);
 9587    });
 9588    cx.assert_editor_state(
 9589        &"
 9590            fn main() {
 9591                sample(ˇ)
 9592            }
 9593        "
 9594        .unindent(),
 9595    );
 9596    cx.editor(|editor, _, _| {
 9597        assert!(editor.signature_help_state.task().is_none());
 9598    });
 9599
 9600    let mocked_response = lsp::SignatureHelp {
 9601        signatures: vec![lsp::SignatureInformation {
 9602            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9603            documentation: None,
 9604            parameters: Some(vec![
 9605                lsp::ParameterInformation {
 9606                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9607                    documentation: None,
 9608                },
 9609                lsp::ParameterInformation {
 9610                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9611                    documentation: None,
 9612                },
 9613            ]),
 9614            active_parameter: None,
 9615        }],
 9616        active_signature: Some(0),
 9617        active_parameter: Some(0),
 9618    };
 9619
 9620    // Ensure that signature_help is called when enabled afte edits
 9621    cx.update(|_, cx| {
 9622        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9623            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9624                settings.auto_signature_help = Some(false);
 9625                settings.show_signature_help_after_edits = Some(true);
 9626            });
 9627        });
 9628    });
 9629    cx.set_state(
 9630        &r#"
 9631            fn main() {
 9632                sampleˇ
 9633            }
 9634        "#
 9635        .unindent(),
 9636    );
 9637    cx.update_editor(|editor, window, cx| {
 9638        editor.handle_input("(", window, cx);
 9639    });
 9640    cx.assert_editor_state(
 9641        &"
 9642            fn main() {
 9643                sample(ˇ)
 9644            }
 9645        "
 9646        .unindent(),
 9647    );
 9648    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9649    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9650        .await;
 9651    cx.update_editor(|editor, _, _| {
 9652        let signature_help_state = editor.signature_help_state.popover().cloned();
 9653        assert!(signature_help_state.is_some());
 9654        assert_eq!(
 9655            signature_help_state.unwrap().label,
 9656            "param1: u8, param2: u8"
 9657        );
 9658        editor.signature_help_state = SignatureHelpState::default();
 9659    });
 9660
 9661    // Ensure that signature_help is called when auto signature help override is enabled
 9662    cx.update(|_, cx| {
 9663        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9664            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9665                settings.auto_signature_help = Some(true);
 9666                settings.show_signature_help_after_edits = Some(false);
 9667            });
 9668        });
 9669    });
 9670    cx.set_state(
 9671        &r#"
 9672            fn main() {
 9673                sampleˇ
 9674            }
 9675        "#
 9676        .unindent(),
 9677    );
 9678    cx.update_editor(|editor, window, cx| {
 9679        editor.handle_input("(", window, cx);
 9680    });
 9681    cx.assert_editor_state(
 9682        &"
 9683            fn main() {
 9684                sample(ˇ)
 9685            }
 9686        "
 9687        .unindent(),
 9688    );
 9689    handle_signature_help_request(&mut cx, mocked_response).await;
 9690    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9691        .await;
 9692    cx.editor(|editor, _, _| {
 9693        let signature_help_state = editor.signature_help_state.popover().cloned();
 9694        assert!(signature_help_state.is_some());
 9695        assert_eq!(
 9696            signature_help_state.unwrap().label,
 9697            "param1: u8, param2: u8"
 9698        );
 9699    });
 9700}
 9701
 9702#[gpui::test]
 9703async fn test_signature_help(cx: &mut TestAppContext) {
 9704    init_test(cx, |_| {});
 9705    cx.update(|cx| {
 9706        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9707            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9708                settings.auto_signature_help = Some(true);
 9709            });
 9710        });
 9711    });
 9712
 9713    let mut cx = EditorLspTestContext::new_rust(
 9714        lsp::ServerCapabilities {
 9715            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9716                ..Default::default()
 9717            }),
 9718            ..Default::default()
 9719        },
 9720        cx,
 9721    )
 9722    .await;
 9723
 9724    // A test that directly calls `show_signature_help`
 9725    cx.update_editor(|editor, window, cx| {
 9726        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9727    });
 9728
 9729    let mocked_response = lsp::SignatureHelp {
 9730        signatures: vec![lsp::SignatureInformation {
 9731            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9732            documentation: None,
 9733            parameters: Some(vec![
 9734                lsp::ParameterInformation {
 9735                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9736                    documentation: None,
 9737                },
 9738                lsp::ParameterInformation {
 9739                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9740                    documentation: None,
 9741                },
 9742            ]),
 9743            active_parameter: None,
 9744        }],
 9745        active_signature: Some(0),
 9746        active_parameter: Some(0),
 9747    };
 9748    handle_signature_help_request(&mut cx, mocked_response).await;
 9749
 9750    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9751        .await;
 9752
 9753    cx.editor(|editor, _, _| {
 9754        let signature_help_state = editor.signature_help_state.popover().cloned();
 9755        assert!(signature_help_state.is_some());
 9756        assert_eq!(
 9757            signature_help_state.unwrap().label,
 9758            "param1: u8, param2: u8"
 9759        );
 9760    });
 9761
 9762    // When exiting outside from inside the brackets, `signature_help` is closed.
 9763    cx.set_state(indoc! {"
 9764        fn main() {
 9765            sample(ˇ);
 9766        }
 9767
 9768        fn sample(param1: u8, param2: u8) {}
 9769    "});
 9770
 9771    cx.update_editor(|editor, window, cx| {
 9772        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9773    });
 9774
 9775    let mocked_response = lsp::SignatureHelp {
 9776        signatures: Vec::new(),
 9777        active_signature: None,
 9778        active_parameter: None,
 9779    };
 9780    handle_signature_help_request(&mut cx, mocked_response).await;
 9781
 9782    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9783        .await;
 9784
 9785    cx.editor(|editor, _, _| {
 9786        assert!(!editor.signature_help_state.is_shown());
 9787    });
 9788
 9789    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9790    cx.set_state(indoc! {"
 9791        fn main() {
 9792            sample(ˇ);
 9793        }
 9794
 9795        fn sample(param1: u8, param2: u8) {}
 9796    "});
 9797
 9798    let mocked_response = lsp::SignatureHelp {
 9799        signatures: vec![lsp::SignatureInformation {
 9800            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9801            documentation: None,
 9802            parameters: Some(vec![
 9803                lsp::ParameterInformation {
 9804                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9805                    documentation: None,
 9806                },
 9807                lsp::ParameterInformation {
 9808                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9809                    documentation: None,
 9810                },
 9811            ]),
 9812            active_parameter: None,
 9813        }],
 9814        active_signature: Some(0),
 9815        active_parameter: Some(0),
 9816    };
 9817    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9818    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9819        .await;
 9820    cx.editor(|editor, _, _| {
 9821        assert!(editor.signature_help_state.is_shown());
 9822    });
 9823
 9824    // Restore the popover with more parameter input
 9825    cx.set_state(indoc! {"
 9826        fn main() {
 9827            sample(param1, param2ˇ);
 9828        }
 9829
 9830        fn sample(param1: u8, param2: u8) {}
 9831    "});
 9832
 9833    let mocked_response = lsp::SignatureHelp {
 9834        signatures: vec![lsp::SignatureInformation {
 9835            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9836            documentation: None,
 9837            parameters: Some(vec![
 9838                lsp::ParameterInformation {
 9839                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9840                    documentation: None,
 9841                },
 9842                lsp::ParameterInformation {
 9843                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9844                    documentation: None,
 9845                },
 9846            ]),
 9847            active_parameter: None,
 9848        }],
 9849        active_signature: Some(0),
 9850        active_parameter: Some(1),
 9851    };
 9852    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9853    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9854        .await;
 9855
 9856    // When selecting a range, the popover is gone.
 9857    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9858    cx.update_editor(|editor, window, cx| {
 9859        editor.change_selections(None, window, cx, |s| {
 9860            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9861        })
 9862    });
 9863    cx.assert_editor_state(indoc! {"
 9864        fn main() {
 9865            sample(param1, «ˇparam2»);
 9866        }
 9867
 9868        fn sample(param1: u8, param2: u8) {}
 9869    "});
 9870    cx.editor(|editor, _, _| {
 9871        assert!(!editor.signature_help_state.is_shown());
 9872    });
 9873
 9874    // When unselecting again, the popover is back if within the brackets.
 9875    cx.update_editor(|editor, window, cx| {
 9876        editor.change_selections(None, window, cx, |s| {
 9877            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9878        })
 9879    });
 9880    cx.assert_editor_state(indoc! {"
 9881        fn main() {
 9882            sample(param1, ˇparam2);
 9883        }
 9884
 9885        fn sample(param1: u8, param2: u8) {}
 9886    "});
 9887    handle_signature_help_request(&mut cx, mocked_response).await;
 9888    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9889        .await;
 9890    cx.editor(|editor, _, _| {
 9891        assert!(editor.signature_help_state.is_shown());
 9892    });
 9893
 9894    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9895    cx.update_editor(|editor, window, cx| {
 9896        editor.change_selections(None, window, cx, |s| {
 9897            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9898            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9899        })
 9900    });
 9901    cx.assert_editor_state(indoc! {"
 9902        fn main() {
 9903            sample(param1, ˇparam2);
 9904        }
 9905
 9906        fn sample(param1: u8, param2: u8) {}
 9907    "});
 9908
 9909    let mocked_response = lsp::SignatureHelp {
 9910        signatures: vec![lsp::SignatureInformation {
 9911            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9912            documentation: None,
 9913            parameters: Some(vec![
 9914                lsp::ParameterInformation {
 9915                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9916                    documentation: None,
 9917                },
 9918                lsp::ParameterInformation {
 9919                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9920                    documentation: None,
 9921                },
 9922            ]),
 9923            active_parameter: None,
 9924        }],
 9925        active_signature: Some(0),
 9926        active_parameter: Some(1),
 9927    };
 9928    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9929    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9930        .await;
 9931    cx.update_editor(|editor, _, cx| {
 9932        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9933    });
 9934    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9935        .await;
 9936    cx.update_editor(|editor, window, cx| {
 9937        editor.change_selections(None, window, cx, |s| {
 9938            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9939        })
 9940    });
 9941    cx.assert_editor_state(indoc! {"
 9942        fn main() {
 9943            sample(param1, «ˇparam2»);
 9944        }
 9945
 9946        fn sample(param1: u8, param2: u8) {}
 9947    "});
 9948    cx.update_editor(|editor, window, cx| {
 9949        editor.change_selections(None, window, cx, |s| {
 9950            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9951        })
 9952    });
 9953    cx.assert_editor_state(indoc! {"
 9954        fn main() {
 9955            sample(param1, ˇparam2);
 9956        }
 9957
 9958        fn sample(param1: u8, param2: u8) {}
 9959    "});
 9960    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9961        .await;
 9962}
 9963
 9964#[gpui::test]
 9965async fn test_completion_mode(cx: &mut TestAppContext) {
 9966    init_test(cx, |_| {});
 9967    let mut cx = EditorLspTestContext::new_rust(
 9968        lsp::ServerCapabilities {
 9969            completion_provider: Some(lsp::CompletionOptions {
 9970                resolve_provider: Some(true),
 9971                ..Default::default()
 9972            }),
 9973            ..Default::default()
 9974        },
 9975        cx,
 9976    )
 9977    .await;
 9978
 9979    struct Run {
 9980        run_description: &'static str,
 9981        initial_state: String,
 9982        buffer_marked_text: String,
 9983        completion_text: &'static str,
 9984        expected_with_insert_mode: String,
 9985        expected_with_replace_mode: String,
 9986        expected_with_replace_subsequence_mode: String,
 9987        expected_with_replace_suffix_mode: String,
 9988    }
 9989
 9990    let runs = [
 9991        Run {
 9992            run_description: "Start of word matches completion text",
 9993            initial_state: "before ediˇ after".into(),
 9994            buffer_marked_text: "before <edi|> after".into(),
 9995            completion_text: "editor",
 9996            expected_with_insert_mode: "before editorˇ after".into(),
 9997            expected_with_replace_mode: "before editorˇ after".into(),
 9998            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9999            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10000        },
10001        Run {
10002            run_description: "Accept same text at the middle of the word",
10003            initial_state: "before ediˇtor after".into(),
10004            buffer_marked_text: "before <edi|tor> after".into(),
10005            completion_text: "editor",
10006            expected_with_insert_mode: "before editorˇtor after".into(),
10007            expected_with_replace_mode: "before editorˇ after".into(),
10008            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10009            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10010        },
10011        Run {
10012            run_description: "End of word matches completion text -- cursor at end",
10013            initial_state: "before torˇ after".into(),
10014            buffer_marked_text: "before <tor|> after".into(),
10015            completion_text: "editor",
10016            expected_with_insert_mode: "before editorˇ after".into(),
10017            expected_with_replace_mode: "before editorˇ after".into(),
10018            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10019            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10020        },
10021        Run {
10022            run_description: "End of word matches completion text -- cursor at start",
10023            initial_state: "before ˇtor after".into(),
10024            buffer_marked_text: "before <|tor> after".into(),
10025            completion_text: "editor",
10026            expected_with_insert_mode: "before editorˇtor after".into(),
10027            expected_with_replace_mode: "before editorˇ after".into(),
10028            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10029            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10030        },
10031        Run {
10032            run_description: "Prepend text containing whitespace",
10033            initial_state: "pˇfield: bool".into(),
10034            buffer_marked_text: "<p|field>: bool".into(),
10035            completion_text: "pub ",
10036            expected_with_insert_mode: "pub ˇfield: bool".into(),
10037            expected_with_replace_mode: "pub ˇ: bool".into(),
10038            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10039            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10040        },
10041        Run {
10042            run_description: "Add element to start of list",
10043            initial_state: "[element_ˇelement_2]".into(),
10044            buffer_marked_text: "[<element_|element_2>]".into(),
10045            completion_text: "element_1",
10046            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10047            expected_with_replace_mode: "[element_1ˇ]".into(),
10048            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10049            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10050        },
10051        Run {
10052            run_description: "Add element to start of list -- first and second elements are equal",
10053            initial_state: "[elˇelement]".into(),
10054            buffer_marked_text: "[<el|element>]".into(),
10055            completion_text: "element",
10056            expected_with_insert_mode: "[elementˇelement]".into(),
10057            expected_with_replace_mode: "[elementˇ]".into(),
10058            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10059            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10060        },
10061        Run {
10062            run_description: "Ends with matching suffix",
10063            initial_state: "SubˇError".into(),
10064            buffer_marked_text: "<Sub|Error>".into(),
10065            completion_text: "SubscriptionError",
10066            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10067            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10068            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10069            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10070        },
10071        Run {
10072            run_description: "Suffix is a subsequence -- contiguous",
10073            initial_state: "SubˇErr".into(),
10074            buffer_marked_text: "<Sub|Err>".into(),
10075            completion_text: "SubscriptionError",
10076            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10077            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10078            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10079            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10080        },
10081        Run {
10082            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10083            initial_state: "Suˇscrirr".into(),
10084            buffer_marked_text: "<Su|scrirr>".into(),
10085            completion_text: "SubscriptionError",
10086            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10087            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10088            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10089            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10090        },
10091        Run {
10092            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10093            initial_state: "foo(indˇix)".into(),
10094            buffer_marked_text: "foo(<ind|ix>)".into(),
10095            completion_text: "node_index",
10096            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10097            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10098            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10099            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10100        },
10101    ];
10102
10103    for run in runs {
10104        let run_variations = [
10105            (LspInsertMode::Insert, run.expected_with_insert_mode),
10106            (LspInsertMode::Replace, run.expected_with_replace_mode),
10107            (
10108                LspInsertMode::ReplaceSubsequence,
10109                run.expected_with_replace_subsequence_mode,
10110            ),
10111            (
10112                LspInsertMode::ReplaceSuffix,
10113                run.expected_with_replace_suffix_mode,
10114            ),
10115        ];
10116
10117        for (lsp_insert_mode, expected_text) in run_variations {
10118            eprintln!(
10119                "run = {:?}, mode = {lsp_insert_mode:.?}",
10120                run.run_description,
10121            );
10122
10123            update_test_language_settings(&mut cx, |settings| {
10124                settings.defaults.completions = Some(CompletionSettings {
10125                    lsp_insert_mode,
10126                    words: WordsCompletionMode::Disabled,
10127                    lsp: true,
10128                    lsp_fetch_timeout_ms: 0,
10129                });
10130            });
10131
10132            cx.set_state(&run.initial_state);
10133            cx.update_editor(|editor, window, cx| {
10134                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10135            });
10136
10137            let counter = Arc::new(AtomicUsize::new(0));
10138            handle_completion_request_with_insert_and_replace(
10139                &mut cx,
10140                &run.buffer_marked_text,
10141                vec![run.completion_text],
10142                counter.clone(),
10143            )
10144            .await;
10145            cx.condition(|editor, _| editor.context_menu_visible())
10146                .await;
10147            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10148
10149            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10150                editor
10151                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10152                    .unwrap()
10153            });
10154            cx.assert_editor_state(&expected_text);
10155            handle_resolve_completion_request(&mut cx, None).await;
10156            apply_additional_edits.await.unwrap();
10157        }
10158    }
10159}
10160
10161#[gpui::test]
10162async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10163    init_test(cx, |_| {});
10164    let mut cx = EditorLspTestContext::new_rust(
10165        lsp::ServerCapabilities {
10166            completion_provider: Some(lsp::CompletionOptions {
10167                resolve_provider: Some(true),
10168                ..Default::default()
10169            }),
10170            ..Default::default()
10171        },
10172        cx,
10173    )
10174    .await;
10175
10176    let initial_state = "SubˇError";
10177    let buffer_marked_text = "<Sub|Error>";
10178    let completion_text = "SubscriptionError";
10179    let expected_with_insert_mode = "SubscriptionErrorˇError";
10180    let expected_with_replace_mode = "SubscriptionErrorˇ";
10181
10182    update_test_language_settings(&mut cx, |settings| {
10183        settings.defaults.completions = Some(CompletionSettings {
10184            words: WordsCompletionMode::Disabled,
10185            // set the opposite here to ensure that the action is overriding the default behavior
10186            lsp_insert_mode: LspInsertMode::Insert,
10187            lsp: true,
10188            lsp_fetch_timeout_ms: 0,
10189        });
10190    });
10191
10192    cx.set_state(initial_state);
10193    cx.update_editor(|editor, window, cx| {
10194        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10195    });
10196
10197    let counter = Arc::new(AtomicUsize::new(0));
10198    handle_completion_request_with_insert_and_replace(
10199        &mut cx,
10200        &buffer_marked_text,
10201        vec![completion_text],
10202        counter.clone(),
10203    )
10204    .await;
10205    cx.condition(|editor, _| editor.context_menu_visible())
10206        .await;
10207    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10208
10209    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10210        editor
10211            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10212            .unwrap()
10213    });
10214    cx.assert_editor_state(&expected_with_replace_mode);
10215    handle_resolve_completion_request(&mut cx, None).await;
10216    apply_additional_edits.await.unwrap();
10217
10218    update_test_language_settings(&mut cx, |settings| {
10219        settings.defaults.completions = Some(CompletionSettings {
10220            words: WordsCompletionMode::Disabled,
10221            // set the opposite here to ensure that the action is overriding the default behavior
10222            lsp_insert_mode: LspInsertMode::Replace,
10223            lsp: true,
10224            lsp_fetch_timeout_ms: 0,
10225        });
10226    });
10227
10228    cx.set_state(initial_state);
10229    cx.update_editor(|editor, window, cx| {
10230        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10231    });
10232    handle_completion_request_with_insert_and_replace(
10233        &mut cx,
10234        &buffer_marked_text,
10235        vec![completion_text],
10236        counter.clone(),
10237    )
10238    .await;
10239    cx.condition(|editor, _| editor.context_menu_visible())
10240        .await;
10241    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10242
10243    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10244        editor
10245            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10246            .unwrap()
10247    });
10248    cx.assert_editor_state(&expected_with_insert_mode);
10249    handle_resolve_completion_request(&mut cx, None).await;
10250    apply_additional_edits.await.unwrap();
10251}
10252
10253#[gpui::test]
10254async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10255    init_test(cx, |_| {});
10256    let mut cx = EditorLspTestContext::new_rust(
10257        lsp::ServerCapabilities {
10258            completion_provider: Some(lsp::CompletionOptions {
10259                resolve_provider: Some(true),
10260                ..Default::default()
10261            }),
10262            ..Default::default()
10263        },
10264        cx,
10265    )
10266    .await;
10267
10268    // scenario: surrounding text matches completion text
10269    let completion_text = "to_offset";
10270    let initial_state = indoc! {"
10271        1. buf.to_offˇsuffix
10272        2. buf.to_offˇsuf
10273        3. buf.to_offˇfix
10274        4. buf.to_offˇ
10275        5. into_offˇensive
10276        6. ˇsuffix
10277        7. let ˇ //
10278        8. aaˇzz
10279        9. buf.to_off«zzzzzˇ»suffix
10280        10. buf.«ˇzzzzz»suffix
10281        11. to_off«ˇzzzzz»
10282
10283        buf.to_offˇsuffix  // newest cursor
10284    "};
10285    let completion_marked_buffer = indoc! {"
10286        1. buf.to_offsuffix
10287        2. buf.to_offsuf
10288        3. buf.to_offfix
10289        4. buf.to_off
10290        5. into_offensive
10291        6. suffix
10292        7. let  //
10293        8. aazz
10294        9. buf.to_offzzzzzsuffix
10295        10. buf.zzzzzsuffix
10296        11. to_offzzzzz
10297
10298        buf.<to_off|suffix>  // newest cursor
10299    "};
10300    let expected = indoc! {"
10301        1. buf.to_offsetˇ
10302        2. buf.to_offsetˇsuf
10303        3. buf.to_offsetˇfix
10304        4. buf.to_offsetˇ
10305        5. into_offsetˇensive
10306        6. to_offsetˇsuffix
10307        7. let to_offsetˇ //
10308        8. aato_offsetˇzz
10309        9. buf.to_offsetˇ
10310        10. buf.to_offsetˇsuffix
10311        11. to_offsetˇ
10312
10313        buf.to_offsetˇ  // newest cursor
10314    "};
10315    cx.set_state(initial_state);
10316    cx.update_editor(|editor, window, cx| {
10317        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10318    });
10319    handle_completion_request_with_insert_and_replace(
10320        &mut cx,
10321        completion_marked_buffer,
10322        vec![completion_text],
10323        Arc::new(AtomicUsize::new(0)),
10324    )
10325    .await;
10326    cx.condition(|editor, _| editor.context_menu_visible())
10327        .await;
10328    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10329        editor
10330            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10331            .unwrap()
10332    });
10333    cx.assert_editor_state(expected);
10334    handle_resolve_completion_request(&mut cx, None).await;
10335    apply_additional_edits.await.unwrap();
10336
10337    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10338    let completion_text = "foo_and_bar";
10339    let initial_state = indoc! {"
10340        1. ooanbˇ
10341        2. zooanbˇ
10342        3. ooanbˇz
10343        4. zooanbˇz
10344        5. ooanˇ
10345        6. oanbˇ
10346
10347        ooanbˇ
10348    "};
10349    let completion_marked_buffer = indoc! {"
10350        1. ooanb
10351        2. zooanb
10352        3. ooanbz
10353        4. zooanbz
10354        5. ooan
10355        6. oanb
10356
10357        <ooanb|>
10358    "};
10359    let expected = indoc! {"
10360        1. foo_and_barˇ
10361        2. zfoo_and_barˇ
10362        3. foo_and_barˇz
10363        4. zfoo_and_barˇz
10364        5. ooanfoo_and_barˇ
10365        6. oanbfoo_and_barˇ
10366
10367        foo_and_barˇ
10368    "};
10369    cx.set_state(initial_state);
10370    cx.update_editor(|editor, window, cx| {
10371        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10372    });
10373    handle_completion_request_with_insert_and_replace(
10374        &mut cx,
10375        completion_marked_buffer,
10376        vec![completion_text],
10377        Arc::new(AtomicUsize::new(0)),
10378    )
10379    .await;
10380    cx.condition(|editor, _| editor.context_menu_visible())
10381        .await;
10382    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10383        editor
10384            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10385            .unwrap()
10386    });
10387    cx.assert_editor_state(expected);
10388    handle_resolve_completion_request(&mut cx, None).await;
10389    apply_additional_edits.await.unwrap();
10390
10391    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10392    // (expects the same as if it was inserted at the end)
10393    let completion_text = "foo_and_bar";
10394    let initial_state = indoc! {"
10395        1. ooˇanb
10396        2. zooˇanb
10397        3. ooˇanbz
10398        4. zooˇanbz
10399
10400        ooˇanb
10401    "};
10402    let completion_marked_buffer = indoc! {"
10403        1. ooanb
10404        2. zooanb
10405        3. ooanbz
10406        4. zooanbz
10407
10408        <oo|anb>
10409    "};
10410    let expected = indoc! {"
10411        1. foo_and_barˇ
10412        2. zfoo_and_barˇ
10413        3. foo_and_barˇz
10414        4. zfoo_and_barˇz
10415
10416        foo_and_barˇ
10417    "};
10418    cx.set_state(initial_state);
10419    cx.update_editor(|editor, window, cx| {
10420        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10421    });
10422    handle_completion_request_with_insert_and_replace(
10423        &mut cx,
10424        completion_marked_buffer,
10425        vec![completion_text],
10426        Arc::new(AtomicUsize::new(0)),
10427    )
10428    .await;
10429    cx.condition(|editor, _| editor.context_menu_visible())
10430        .await;
10431    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10432        editor
10433            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10434            .unwrap()
10435    });
10436    cx.assert_editor_state(expected);
10437    handle_resolve_completion_request(&mut cx, None).await;
10438    apply_additional_edits.await.unwrap();
10439}
10440
10441// This used to crash
10442#[gpui::test]
10443async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10444    init_test(cx, |_| {});
10445
10446    let buffer_text = indoc! {"
10447        fn main() {
10448            10.satu;
10449
10450            //
10451            // separate cursors so they open in different excerpts (manually reproducible)
10452            //
10453
10454            10.satu20;
10455        }
10456    "};
10457    let multibuffer_text_with_selections = indoc! {"
10458        fn main() {
10459            10.satuˇ;
10460
10461            //
10462
10463            //
10464
10465            10.satuˇ20;
10466        }
10467    "};
10468    let expected_multibuffer = indoc! {"
10469        fn main() {
10470            10.saturating_sub()ˇ;
10471
10472            //
10473
10474            //
10475
10476            10.saturating_sub()ˇ;
10477        }
10478    "};
10479
10480    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10481    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10482
10483    let fs = FakeFs::new(cx.executor());
10484    fs.insert_tree(
10485        path!("/a"),
10486        json!({
10487            "main.rs": buffer_text,
10488        }),
10489    )
10490    .await;
10491
10492    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10493    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10494    language_registry.add(rust_lang());
10495    let mut fake_servers = language_registry.register_fake_lsp(
10496        "Rust",
10497        FakeLspAdapter {
10498            capabilities: lsp::ServerCapabilities {
10499                completion_provider: Some(lsp::CompletionOptions {
10500                    resolve_provider: None,
10501                    ..lsp::CompletionOptions::default()
10502                }),
10503                ..lsp::ServerCapabilities::default()
10504            },
10505            ..FakeLspAdapter::default()
10506        },
10507    );
10508    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10509    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10510    let buffer = project
10511        .update(cx, |project, cx| {
10512            project.open_local_buffer(path!("/a/main.rs"), cx)
10513        })
10514        .await
10515        .unwrap();
10516
10517    let multi_buffer = cx.new(|cx| {
10518        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10519        multi_buffer.push_excerpts(
10520            buffer.clone(),
10521            [ExcerptRange::new(0..first_excerpt_end)],
10522            cx,
10523        );
10524        multi_buffer.push_excerpts(
10525            buffer.clone(),
10526            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10527            cx,
10528        );
10529        multi_buffer
10530    });
10531
10532    let editor = workspace
10533        .update(cx, |_, window, cx| {
10534            cx.new(|cx| {
10535                Editor::new(
10536                    EditorMode::Full {
10537                        scale_ui_elements_with_buffer_font_size: false,
10538                        show_active_line_background: false,
10539                        sized_by_content: false,
10540                    },
10541                    multi_buffer.clone(),
10542                    Some(project.clone()),
10543                    window,
10544                    cx,
10545                )
10546            })
10547        })
10548        .unwrap();
10549
10550    let pane = workspace
10551        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10552        .unwrap();
10553    pane.update_in(cx, |pane, window, cx| {
10554        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10555    });
10556
10557    let fake_server = fake_servers.next().await.unwrap();
10558
10559    editor.update_in(cx, |editor, window, cx| {
10560        editor.change_selections(None, window, cx, |s| {
10561            s.select_ranges([
10562                Point::new(1, 11)..Point::new(1, 11),
10563                Point::new(7, 11)..Point::new(7, 11),
10564            ])
10565        });
10566
10567        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10568    });
10569
10570    editor.update_in(cx, |editor, window, cx| {
10571        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10572    });
10573
10574    fake_server
10575        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10576            let completion_item = lsp::CompletionItem {
10577                label: "saturating_sub()".into(),
10578                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10579                    lsp::InsertReplaceEdit {
10580                        new_text: "saturating_sub()".to_owned(),
10581                        insert: lsp::Range::new(
10582                            lsp::Position::new(7, 7),
10583                            lsp::Position::new(7, 11),
10584                        ),
10585                        replace: lsp::Range::new(
10586                            lsp::Position::new(7, 7),
10587                            lsp::Position::new(7, 13),
10588                        ),
10589                    },
10590                )),
10591                ..lsp::CompletionItem::default()
10592            };
10593
10594            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10595        })
10596        .next()
10597        .await
10598        .unwrap();
10599
10600    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10601        .await;
10602
10603    editor
10604        .update_in(cx, |editor, window, cx| {
10605            editor
10606                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10607                .unwrap()
10608        })
10609        .await
10610        .unwrap();
10611
10612    editor.update(cx, |editor, cx| {
10613        assert_text_with_selections(editor, expected_multibuffer, cx);
10614    })
10615}
10616
10617#[gpui::test]
10618async fn test_completion(cx: &mut TestAppContext) {
10619    init_test(cx, |_| {});
10620
10621    let mut cx = EditorLspTestContext::new_rust(
10622        lsp::ServerCapabilities {
10623            completion_provider: Some(lsp::CompletionOptions {
10624                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10625                resolve_provider: Some(true),
10626                ..Default::default()
10627            }),
10628            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10629            ..Default::default()
10630        },
10631        cx,
10632    )
10633    .await;
10634    let counter = Arc::new(AtomicUsize::new(0));
10635
10636    cx.set_state(indoc! {"
10637        oneˇ
10638        two
10639        three
10640    "});
10641    cx.simulate_keystroke(".");
10642    handle_completion_request(
10643        &mut cx,
10644        indoc! {"
10645            one.|<>
10646            two
10647            three
10648        "},
10649        vec!["first_completion", "second_completion"],
10650        counter.clone(),
10651    )
10652    .await;
10653    cx.condition(|editor, _| editor.context_menu_visible())
10654        .await;
10655    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10656
10657    let _handler = handle_signature_help_request(
10658        &mut cx,
10659        lsp::SignatureHelp {
10660            signatures: vec![lsp::SignatureInformation {
10661                label: "test signature".to_string(),
10662                documentation: None,
10663                parameters: Some(vec![lsp::ParameterInformation {
10664                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10665                    documentation: None,
10666                }]),
10667                active_parameter: None,
10668            }],
10669            active_signature: None,
10670            active_parameter: None,
10671        },
10672    );
10673    cx.update_editor(|editor, window, cx| {
10674        assert!(
10675            !editor.signature_help_state.is_shown(),
10676            "No signature help was called for"
10677        );
10678        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10679    });
10680    cx.run_until_parked();
10681    cx.update_editor(|editor, _, _| {
10682        assert!(
10683            !editor.signature_help_state.is_shown(),
10684            "No signature help should be shown when completions menu is open"
10685        );
10686    });
10687
10688    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10689        editor.context_menu_next(&Default::default(), window, cx);
10690        editor
10691            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10692            .unwrap()
10693    });
10694    cx.assert_editor_state(indoc! {"
10695        one.second_completionˇ
10696        two
10697        three
10698    "});
10699
10700    handle_resolve_completion_request(
10701        &mut cx,
10702        Some(vec![
10703            (
10704                //This overlaps with the primary completion edit which is
10705                //misbehavior from the LSP spec, test that we filter it out
10706                indoc! {"
10707                    one.second_ˇcompletion
10708                    two
10709                    threeˇ
10710                "},
10711                "overlapping additional edit",
10712            ),
10713            (
10714                indoc! {"
10715                    one.second_completion
10716                    two
10717                    threeˇ
10718                "},
10719                "\nadditional edit",
10720            ),
10721        ]),
10722    )
10723    .await;
10724    apply_additional_edits.await.unwrap();
10725    cx.assert_editor_state(indoc! {"
10726        one.second_completionˇ
10727        two
10728        three
10729        additional edit
10730    "});
10731
10732    cx.set_state(indoc! {"
10733        one.second_completion
10734        twoˇ
10735        threeˇ
10736        additional edit
10737    "});
10738    cx.simulate_keystroke(" ");
10739    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10740    cx.simulate_keystroke("s");
10741    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10742
10743    cx.assert_editor_state(indoc! {"
10744        one.second_completion
10745        two sˇ
10746        three sˇ
10747        additional edit
10748    "});
10749    handle_completion_request(
10750        &mut cx,
10751        indoc! {"
10752            one.second_completion
10753            two s
10754            three <s|>
10755            additional edit
10756        "},
10757        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10758        counter.clone(),
10759    )
10760    .await;
10761    cx.condition(|editor, _| editor.context_menu_visible())
10762        .await;
10763    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10764
10765    cx.simulate_keystroke("i");
10766
10767    handle_completion_request(
10768        &mut cx,
10769        indoc! {"
10770            one.second_completion
10771            two si
10772            three <si|>
10773            additional edit
10774        "},
10775        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10776        counter.clone(),
10777    )
10778    .await;
10779    cx.condition(|editor, _| editor.context_menu_visible())
10780        .await;
10781    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10782
10783    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10784        editor
10785            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10786            .unwrap()
10787    });
10788    cx.assert_editor_state(indoc! {"
10789        one.second_completion
10790        two sixth_completionˇ
10791        three sixth_completionˇ
10792        additional edit
10793    "});
10794
10795    apply_additional_edits.await.unwrap();
10796
10797    update_test_language_settings(&mut cx, |settings| {
10798        settings.defaults.show_completions_on_input = Some(false);
10799    });
10800    cx.set_state("editorˇ");
10801    cx.simulate_keystroke(".");
10802    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10803    cx.simulate_keystrokes("c l o");
10804    cx.assert_editor_state("editor.cloˇ");
10805    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10806    cx.update_editor(|editor, window, cx| {
10807        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10808    });
10809    handle_completion_request(
10810        &mut cx,
10811        "editor.<clo|>",
10812        vec!["close", "clobber"],
10813        counter.clone(),
10814    )
10815    .await;
10816    cx.condition(|editor, _| editor.context_menu_visible())
10817        .await;
10818    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10819
10820    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10821        editor
10822            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10823            .unwrap()
10824    });
10825    cx.assert_editor_state("editor.closeˇ");
10826    handle_resolve_completion_request(&mut cx, None).await;
10827    apply_additional_edits.await.unwrap();
10828}
10829
10830#[gpui::test]
10831async fn test_word_completion(cx: &mut TestAppContext) {
10832    let lsp_fetch_timeout_ms = 10;
10833    init_test(cx, |language_settings| {
10834        language_settings.defaults.completions = Some(CompletionSettings {
10835            words: WordsCompletionMode::Fallback,
10836            lsp: true,
10837            lsp_fetch_timeout_ms: 10,
10838            lsp_insert_mode: LspInsertMode::Insert,
10839        });
10840    });
10841
10842    let mut cx = EditorLspTestContext::new_rust(
10843        lsp::ServerCapabilities {
10844            completion_provider: Some(lsp::CompletionOptions {
10845                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10846                ..lsp::CompletionOptions::default()
10847            }),
10848            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10849            ..lsp::ServerCapabilities::default()
10850        },
10851        cx,
10852    )
10853    .await;
10854
10855    let throttle_completions = Arc::new(AtomicBool::new(false));
10856
10857    let lsp_throttle_completions = throttle_completions.clone();
10858    let _completion_requests_handler =
10859        cx.lsp
10860            .server
10861            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10862                let lsp_throttle_completions = lsp_throttle_completions.clone();
10863                let cx = cx.clone();
10864                async move {
10865                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10866                        cx.background_executor()
10867                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10868                            .await;
10869                    }
10870                    Ok(Some(lsp::CompletionResponse::Array(vec![
10871                        lsp::CompletionItem {
10872                            label: "first".into(),
10873                            ..lsp::CompletionItem::default()
10874                        },
10875                        lsp::CompletionItem {
10876                            label: "last".into(),
10877                            ..lsp::CompletionItem::default()
10878                        },
10879                    ])))
10880                }
10881            });
10882
10883    cx.set_state(indoc! {"
10884        oneˇ
10885        two
10886        three
10887    "});
10888    cx.simulate_keystroke(".");
10889    cx.executor().run_until_parked();
10890    cx.condition(|editor, _| editor.context_menu_visible())
10891        .await;
10892    cx.update_editor(|editor, window, cx| {
10893        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10894        {
10895            assert_eq!(
10896                completion_menu_entries(&menu),
10897                &["first", "last"],
10898                "When LSP server is fast to reply, no fallback word completions are used"
10899            );
10900        } else {
10901            panic!("expected completion menu to be open");
10902        }
10903        editor.cancel(&Cancel, window, cx);
10904    });
10905    cx.executor().run_until_parked();
10906    cx.condition(|editor, _| !editor.context_menu_visible())
10907        .await;
10908
10909    throttle_completions.store(true, atomic::Ordering::Release);
10910    cx.simulate_keystroke(".");
10911    cx.executor()
10912        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10913    cx.executor().run_until_parked();
10914    cx.condition(|editor, _| editor.context_menu_visible())
10915        .await;
10916    cx.update_editor(|editor, _, _| {
10917        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10918        {
10919            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10920                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10921        } else {
10922            panic!("expected completion menu to be open");
10923        }
10924    });
10925}
10926
10927#[gpui::test]
10928async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10929    init_test(cx, |language_settings| {
10930        language_settings.defaults.completions = Some(CompletionSettings {
10931            words: WordsCompletionMode::Enabled,
10932            lsp: true,
10933            lsp_fetch_timeout_ms: 0,
10934            lsp_insert_mode: LspInsertMode::Insert,
10935        });
10936    });
10937
10938    let mut cx = EditorLspTestContext::new_rust(
10939        lsp::ServerCapabilities {
10940            completion_provider: Some(lsp::CompletionOptions {
10941                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10942                ..lsp::CompletionOptions::default()
10943            }),
10944            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10945            ..lsp::ServerCapabilities::default()
10946        },
10947        cx,
10948    )
10949    .await;
10950
10951    let _completion_requests_handler =
10952        cx.lsp
10953            .server
10954            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10955                Ok(Some(lsp::CompletionResponse::Array(vec![
10956                    lsp::CompletionItem {
10957                        label: "first".into(),
10958                        ..lsp::CompletionItem::default()
10959                    },
10960                    lsp::CompletionItem {
10961                        label: "last".into(),
10962                        ..lsp::CompletionItem::default()
10963                    },
10964                ])))
10965            });
10966
10967    cx.set_state(indoc! {"ˇ
10968        first
10969        last
10970        second
10971    "});
10972    cx.simulate_keystroke(".");
10973    cx.executor().run_until_parked();
10974    cx.condition(|editor, _| editor.context_menu_visible())
10975        .await;
10976    cx.update_editor(|editor, _, _| {
10977        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10978        {
10979            assert_eq!(
10980                completion_menu_entries(&menu),
10981                &["first", "last", "second"],
10982                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10983            );
10984        } else {
10985            panic!("expected completion menu to be open");
10986        }
10987    });
10988}
10989
10990#[gpui::test]
10991async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10992    init_test(cx, |language_settings| {
10993        language_settings.defaults.completions = Some(CompletionSettings {
10994            words: WordsCompletionMode::Disabled,
10995            lsp: true,
10996            lsp_fetch_timeout_ms: 0,
10997            lsp_insert_mode: LspInsertMode::Insert,
10998        });
10999    });
11000
11001    let mut cx = EditorLspTestContext::new_rust(
11002        lsp::ServerCapabilities {
11003            completion_provider: Some(lsp::CompletionOptions {
11004                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11005                ..lsp::CompletionOptions::default()
11006            }),
11007            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11008            ..lsp::ServerCapabilities::default()
11009        },
11010        cx,
11011    )
11012    .await;
11013
11014    let _completion_requests_handler =
11015        cx.lsp
11016            .server
11017            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11018                panic!("LSP completions should not be queried when dealing with word completions")
11019            });
11020
11021    cx.set_state(indoc! {"ˇ
11022        first
11023        last
11024        second
11025    "});
11026    cx.update_editor(|editor, window, cx| {
11027        editor.show_word_completions(&ShowWordCompletions, window, cx);
11028    });
11029    cx.executor().run_until_parked();
11030    cx.condition(|editor, _| editor.context_menu_visible())
11031        .await;
11032    cx.update_editor(|editor, _, _| {
11033        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11034        {
11035            assert_eq!(
11036                completion_menu_entries(&menu),
11037                &["first", "last", "second"],
11038                "`ShowWordCompletions` action should show word completions"
11039            );
11040        } else {
11041            panic!("expected completion menu to be open");
11042        }
11043    });
11044
11045    cx.simulate_keystroke("l");
11046    cx.executor().run_until_parked();
11047    cx.condition(|editor, _| editor.context_menu_visible())
11048        .await;
11049    cx.update_editor(|editor, _, _| {
11050        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11051        {
11052            assert_eq!(
11053                completion_menu_entries(&menu),
11054                &["last"],
11055                "After showing word completions, further editing should filter them and not query the LSP"
11056            );
11057        } else {
11058            panic!("expected completion menu to be open");
11059        }
11060    });
11061}
11062
11063#[gpui::test]
11064async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11065    init_test(cx, |language_settings| {
11066        language_settings.defaults.completions = Some(CompletionSettings {
11067            words: WordsCompletionMode::Fallback,
11068            lsp: false,
11069            lsp_fetch_timeout_ms: 0,
11070            lsp_insert_mode: LspInsertMode::Insert,
11071        });
11072    });
11073
11074    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11075
11076    cx.set_state(indoc! {"ˇ
11077        0_usize
11078        let
11079        33
11080        4.5f32
11081    "});
11082    cx.update_editor(|editor, window, cx| {
11083        editor.show_completions(&ShowCompletions::default(), window, cx);
11084    });
11085    cx.executor().run_until_parked();
11086    cx.condition(|editor, _| editor.context_menu_visible())
11087        .await;
11088    cx.update_editor(|editor, window, cx| {
11089        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11090        {
11091            assert_eq!(
11092                completion_menu_entries(&menu),
11093                &["let"],
11094                "With no digits in the completion query, no digits should be in the word completions"
11095            );
11096        } else {
11097            panic!("expected completion menu to be open");
11098        }
11099        editor.cancel(&Cancel, window, cx);
11100    });
11101
11102    cx.set_state(indoc! {"11103        0_usize
11104        let
11105        3
11106        33.35f32
11107    "});
11108    cx.update_editor(|editor, window, cx| {
11109        editor.show_completions(&ShowCompletions::default(), window, cx);
11110    });
11111    cx.executor().run_until_parked();
11112    cx.condition(|editor, _| editor.context_menu_visible())
11113        .await;
11114    cx.update_editor(|editor, _, _| {
11115        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11116        {
11117            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11118                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11119        } else {
11120            panic!("expected completion menu to be open");
11121        }
11122    });
11123}
11124
11125fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11126    let position = || lsp::Position {
11127        line: params.text_document_position.position.line,
11128        character: params.text_document_position.position.character,
11129    };
11130    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11131        range: lsp::Range {
11132            start: position(),
11133            end: position(),
11134        },
11135        new_text: text.to_string(),
11136    }))
11137}
11138
11139#[gpui::test]
11140async fn test_multiline_completion(cx: &mut TestAppContext) {
11141    init_test(cx, |_| {});
11142
11143    let fs = FakeFs::new(cx.executor());
11144    fs.insert_tree(
11145        path!("/a"),
11146        json!({
11147            "main.ts": "a",
11148        }),
11149    )
11150    .await;
11151
11152    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11153    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11154    let typescript_language = Arc::new(Language::new(
11155        LanguageConfig {
11156            name: "TypeScript".into(),
11157            matcher: LanguageMatcher {
11158                path_suffixes: vec!["ts".to_string()],
11159                ..LanguageMatcher::default()
11160            },
11161            line_comments: vec!["// ".into()],
11162            ..LanguageConfig::default()
11163        },
11164        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11165    ));
11166    language_registry.add(typescript_language.clone());
11167    let mut fake_servers = language_registry.register_fake_lsp(
11168        "TypeScript",
11169        FakeLspAdapter {
11170            capabilities: lsp::ServerCapabilities {
11171                completion_provider: Some(lsp::CompletionOptions {
11172                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11173                    ..lsp::CompletionOptions::default()
11174                }),
11175                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11176                ..lsp::ServerCapabilities::default()
11177            },
11178            // Emulate vtsls label generation
11179            label_for_completion: Some(Box::new(|item, _| {
11180                let text = if let Some(description) = item
11181                    .label_details
11182                    .as_ref()
11183                    .and_then(|label_details| label_details.description.as_ref())
11184                {
11185                    format!("{} {}", item.label, description)
11186                } else if let Some(detail) = &item.detail {
11187                    format!("{} {}", item.label, detail)
11188                } else {
11189                    item.label.clone()
11190                };
11191                let len = text.len();
11192                Some(language::CodeLabel {
11193                    text,
11194                    runs: Vec::new(),
11195                    filter_range: 0..len,
11196                })
11197            })),
11198            ..FakeLspAdapter::default()
11199        },
11200    );
11201    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11202    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11203    let worktree_id = workspace
11204        .update(cx, |workspace, _window, cx| {
11205            workspace.project().update(cx, |project, cx| {
11206                project.worktrees(cx).next().unwrap().read(cx).id()
11207            })
11208        })
11209        .unwrap();
11210    let _buffer = project
11211        .update(cx, |project, cx| {
11212            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11213        })
11214        .await
11215        .unwrap();
11216    let editor = workspace
11217        .update(cx, |workspace, window, cx| {
11218            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11219        })
11220        .unwrap()
11221        .await
11222        .unwrap()
11223        .downcast::<Editor>()
11224        .unwrap();
11225    let fake_server = fake_servers.next().await.unwrap();
11226
11227    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11228    let multiline_label_2 = "a\nb\nc\n";
11229    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11230    let multiline_description = "d\ne\nf\n";
11231    let multiline_detail_2 = "g\nh\ni\n";
11232
11233    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11234        move |params, _| async move {
11235            Ok(Some(lsp::CompletionResponse::Array(vec![
11236                lsp::CompletionItem {
11237                    label: multiline_label.to_string(),
11238                    text_edit: gen_text_edit(&params, "new_text_1"),
11239                    ..lsp::CompletionItem::default()
11240                },
11241                lsp::CompletionItem {
11242                    label: "single line label 1".to_string(),
11243                    detail: Some(multiline_detail.to_string()),
11244                    text_edit: gen_text_edit(&params, "new_text_2"),
11245                    ..lsp::CompletionItem::default()
11246                },
11247                lsp::CompletionItem {
11248                    label: "single line label 2".to_string(),
11249                    label_details: Some(lsp::CompletionItemLabelDetails {
11250                        description: Some(multiline_description.to_string()),
11251                        detail: None,
11252                    }),
11253                    text_edit: gen_text_edit(&params, "new_text_2"),
11254                    ..lsp::CompletionItem::default()
11255                },
11256                lsp::CompletionItem {
11257                    label: multiline_label_2.to_string(),
11258                    detail: Some(multiline_detail_2.to_string()),
11259                    text_edit: gen_text_edit(&params, "new_text_3"),
11260                    ..lsp::CompletionItem::default()
11261                },
11262                lsp::CompletionItem {
11263                    label: "Label with many     spaces and \t but without newlines".to_string(),
11264                    detail: Some(
11265                        "Details with many     spaces and \t but without newlines".to_string(),
11266                    ),
11267                    text_edit: gen_text_edit(&params, "new_text_4"),
11268                    ..lsp::CompletionItem::default()
11269                },
11270            ])))
11271        },
11272    );
11273
11274    editor.update_in(cx, |editor, window, cx| {
11275        cx.focus_self(window);
11276        editor.move_to_end(&MoveToEnd, window, cx);
11277        editor.handle_input(".", window, cx);
11278    });
11279    cx.run_until_parked();
11280    completion_handle.next().await.unwrap();
11281
11282    editor.update(cx, |editor, _| {
11283        assert!(editor.context_menu_visible());
11284        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11285        {
11286            let completion_labels = menu
11287                .completions
11288                .borrow()
11289                .iter()
11290                .map(|c| c.label.text.clone())
11291                .collect::<Vec<_>>();
11292            assert_eq!(
11293                completion_labels,
11294                &[
11295                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11296                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11297                    "single line label 2 d e f ",
11298                    "a b c g h i ",
11299                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11300                ],
11301                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11302            );
11303
11304            for completion in menu
11305                .completions
11306                .borrow()
11307                .iter() {
11308                    assert_eq!(
11309                        completion.label.filter_range,
11310                        0..completion.label.text.len(),
11311                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11312                    );
11313                }
11314        } else {
11315            panic!("expected completion menu to be open");
11316        }
11317    });
11318}
11319
11320#[gpui::test]
11321async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11322    init_test(cx, |_| {});
11323    let mut cx = EditorLspTestContext::new_rust(
11324        lsp::ServerCapabilities {
11325            completion_provider: Some(lsp::CompletionOptions {
11326                trigger_characters: Some(vec![".".to_string()]),
11327                ..Default::default()
11328            }),
11329            ..Default::default()
11330        },
11331        cx,
11332    )
11333    .await;
11334    cx.lsp
11335        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11336            Ok(Some(lsp::CompletionResponse::Array(vec![
11337                lsp::CompletionItem {
11338                    label: "first".into(),
11339                    ..Default::default()
11340                },
11341                lsp::CompletionItem {
11342                    label: "last".into(),
11343                    ..Default::default()
11344                },
11345            ])))
11346        });
11347    cx.set_state("variableˇ");
11348    cx.simulate_keystroke(".");
11349    cx.executor().run_until_parked();
11350
11351    cx.update_editor(|editor, _, _| {
11352        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11353        {
11354            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11355        } else {
11356            panic!("expected completion menu to be open");
11357        }
11358    });
11359
11360    cx.update_editor(|editor, window, cx| {
11361        editor.move_page_down(&MovePageDown::default(), window, cx);
11362        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11363        {
11364            assert!(
11365                menu.selected_item == 1,
11366                "expected PageDown to select the last item from the context menu"
11367            );
11368        } else {
11369            panic!("expected completion menu to stay open after PageDown");
11370        }
11371    });
11372
11373    cx.update_editor(|editor, window, cx| {
11374        editor.move_page_up(&MovePageUp::default(), window, cx);
11375        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11376        {
11377            assert!(
11378                menu.selected_item == 0,
11379                "expected PageUp to select the first item from the context menu"
11380            );
11381        } else {
11382            panic!("expected completion menu to stay open after PageUp");
11383        }
11384    });
11385}
11386
11387#[gpui::test]
11388async fn test_as_is_completions(cx: &mut TestAppContext) {
11389    init_test(cx, |_| {});
11390    let mut cx = EditorLspTestContext::new_rust(
11391        lsp::ServerCapabilities {
11392            completion_provider: Some(lsp::CompletionOptions {
11393                ..Default::default()
11394            }),
11395            ..Default::default()
11396        },
11397        cx,
11398    )
11399    .await;
11400    cx.lsp
11401        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11402            Ok(Some(lsp::CompletionResponse::Array(vec![
11403                lsp::CompletionItem {
11404                    label: "unsafe".into(),
11405                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11406                        range: lsp::Range {
11407                            start: lsp::Position {
11408                                line: 1,
11409                                character: 2,
11410                            },
11411                            end: lsp::Position {
11412                                line: 1,
11413                                character: 3,
11414                            },
11415                        },
11416                        new_text: "unsafe".to_string(),
11417                    })),
11418                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11419                    ..Default::default()
11420                },
11421            ])))
11422        });
11423    cx.set_state("fn a() {}\n");
11424    cx.executor().run_until_parked();
11425    cx.update_editor(|editor, window, cx| {
11426        editor.show_completions(
11427            &ShowCompletions {
11428                trigger: Some("\n".into()),
11429            },
11430            window,
11431            cx,
11432        );
11433    });
11434    cx.executor().run_until_parked();
11435
11436    cx.update_editor(|editor, window, cx| {
11437        editor.confirm_completion(&Default::default(), window, cx)
11438    });
11439    cx.executor().run_until_parked();
11440    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11441}
11442
11443#[gpui::test]
11444async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11445    init_test(cx, |_| {});
11446
11447    let mut cx = EditorLspTestContext::new_rust(
11448        lsp::ServerCapabilities {
11449            completion_provider: Some(lsp::CompletionOptions {
11450                trigger_characters: Some(vec![".".to_string()]),
11451                resolve_provider: Some(true),
11452                ..Default::default()
11453            }),
11454            ..Default::default()
11455        },
11456        cx,
11457    )
11458    .await;
11459
11460    cx.set_state("fn main() { let a = 2ˇ; }");
11461    cx.simulate_keystroke(".");
11462    let completion_item = lsp::CompletionItem {
11463        label: "Some".into(),
11464        kind: Some(lsp::CompletionItemKind::SNIPPET),
11465        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11466        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11467            kind: lsp::MarkupKind::Markdown,
11468            value: "```rust\nSome(2)\n```".to_string(),
11469        })),
11470        deprecated: Some(false),
11471        sort_text: Some("Some".to_string()),
11472        filter_text: Some("Some".to_string()),
11473        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11474        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11475            range: lsp::Range {
11476                start: lsp::Position {
11477                    line: 0,
11478                    character: 22,
11479                },
11480                end: lsp::Position {
11481                    line: 0,
11482                    character: 22,
11483                },
11484            },
11485            new_text: "Some(2)".to_string(),
11486        })),
11487        additional_text_edits: Some(vec![lsp::TextEdit {
11488            range: lsp::Range {
11489                start: lsp::Position {
11490                    line: 0,
11491                    character: 20,
11492                },
11493                end: lsp::Position {
11494                    line: 0,
11495                    character: 22,
11496                },
11497            },
11498            new_text: "".to_string(),
11499        }]),
11500        ..Default::default()
11501    };
11502
11503    let closure_completion_item = completion_item.clone();
11504    let counter = Arc::new(AtomicUsize::new(0));
11505    let counter_clone = counter.clone();
11506    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11507        let task_completion_item = closure_completion_item.clone();
11508        counter_clone.fetch_add(1, atomic::Ordering::Release);
11509        async move {
11510            Ok(Some(lsp::CompletionResponse::Array(vec![
11511                task_completion_item,
11512            ])))
11513        }
11514    });
11515
11516    cx.condition(|editor, _| editor.context_menu_visible())
11517        .await;
11518    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11519    assert!(request.next().await.is_some());
11520    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11521
11522    cx.simulate_keystrokes("S o m");
11523    cx.condition(|editor, _| editor.context_menu_visible())
11524        .await;
11525    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11526    assert!(request.next().await.is_some());
11527    assert!(request.next().await.is_some());
11528    assert!(request.next().await.is_some());
11529    request.close();
11530    assert!(request.next().await.is_none());
11531    assert_eq!(
11532        counter.load(atomic::Ordering::Acquire),
11533        4,
11534        "With the completions menu open, only one LSP request should happen per input"
11535    );
11536}
11537
11538#[gpui::test]
11539async fn test_toggle_comment(cx: &mut TestAppContext) {
11540    init_test(cx, |_| {});
11541    let mut cx = EditorTestContext::new(cx).await;
11542    let language = Arc::new(Language::new(
11543        LanguageConfig {
11544            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11545            ..Default::default()
11546        },
11547        Some(tree_sitter_rust::LANGUAGE.into()),
11548    ));
11549    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11550
11551    // If multiple selections intersect a line, the line is only toggled once.
11552    cx.set_state(indoc! {"
11553        fn a() {
11554            «//b();
11555            ˇ»// «c();
11556            //ˇ»  d();
11557        }
11558    "});
11559
11560    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11561
11562    cx.assert_editor_state(indoc! {"
11563        fn a() {
11564            «b();
11565            c();
11566            ˇ» d();
11567        }
11568    "});
11569
11570    // The comment prefix is inserted at the same column for every line in a
11571    // selection.
11572    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11573
11574    cx.assert_editor_state(indoc! {"
11575        fn a() {
11576            // «b();
11577            // c();
11578            ˇ»//  d();
11579        }
11580    "});
11581
11582    // If a selection ends at the beginning of a line, that line is not toggled.
11583    cx.set_selections_state(indoc! {"
11584        fn a() {
11585            // b();
11586            «// c();
11587        ˇ»    //  d();
11588        }
11589    "});
11590
11591    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11592
11593    cx.assert_editor_state(indoc! {"
11594        fn a() {
11595            // b();
11596            «c();
11597        ˇ»    //  d();
11598        }
11599    "});
11600
11601    // If a selection span a single line and is empty, the line is toggled.
11602    cx.set_state(indoc! {"
11603        fn a() {
11604            a();
11605            b();
11606        ˇ
11607        }
11608    "});
11609
11610    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11611
11612    cx.assert_editor_state(indoc! {"
11613        fn a() {
11614            a();
11615            b();
11616        //•ˇ
11617        }
11618    "});
11619
11620    // If a selection span multiple lines, empty lines are not toggled.
11621    cx.set_state(indoc! {"
11622        fn a() {
11623            «a();
11624
11625            c();ˇ»
11626        }
11627    "});
11628
11629    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11630
11631    cx.assert_editor_state(indoc! {"
11632        fn a() {
11633            // «a();
11634
11635            // c();ˇ»
11636        }
11637    "});
11638
11639    // If a selection includes multiple comment prefixes, all lines are uncommented.
11640    cx.set_state(indoc! {"
11641        fn a() {
11642            «// a();
11643            /// b();
11644            //! c();ˇ»
11645        }
11646    "});
11647
11648    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11649
11650    cx.assert_editor_state(indoc! {"
11651        fn a() {
11652            «a();
11653            b();
11654            c();ˇ»
11655        }
11656    "});
11657}
11658
11659#[gpui::test]
11660async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11661    init_test(cx, |_| {});
11662    let mut cx = EditorTestContext::new(cx).await;
11663    let language = Arc::new(Language::new(
11664        LanguageConfig {
11665            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11666            ..Default::default()
11667        },
11668        Some(tree_sitter_rust::LANGUAGE.into()),
11669    ));
11670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11671
11672    let toggle_comments = &ToggleComments {
11673        advance_downwards: false,
11674        ignore_indent: true,
11675    };
11676
11677    // If multiple selections intersect a line, the line is only toggled once.
11678    cx.set_state(indoc! {"
11679        fn a() {
11680        //    «b();
11681        //    c();
11682        //    ˇ» d();
11683        }
11684    "});
11685
11686    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11687
11688    cx.assert_editor_state(indoc! {"
11689        fn a() {
11690            «b();
11691            c();
11692            ˇ» d();
11693        }
11694    "});
11695
11696    // The comment prefix is inserted at the beginning of each line
11697    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11698
11699    cx.assert_editor_state(indoc! {"
11700        fn a() {
11701        //    «b();
11702        //    c();
11703        //    ˇ» d();
11704        }
11705    "});
11706
11707    // If a selection ends at the beginning of a line, that line is not toggled.
11708    cx.set_selections_state(indoc! {"
11709        fn a() {
11710        //    b();
11711        //    «c();
11712        ˇ»//     d();
11713        }
11714    "});
11715
11716    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11717
11718    cx.assert_editor_state(indoc! {"
11719        fn a() {
11720        //    b();
11721            «c();
11722        ˇ»//     d();
11723        }
11724    "});
11725
11726    // If a selection span a single line and is empty, the line is toggled.
11727    cx.set_state(indoc! {"
11728        fn a() {
11729            a();
11730            b();
11731        ˇ
11732        }
11733    "});
11734
11735    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11736
11737    cx.assert_editor_state(indoc! {"
11738        fn a() {
11739            a();
11740            b();
11741        //ˇ
11742        }
11743    "});
11744
11745    // If a selection span multiple lines, empty lines are not toggled.
11746    cx.set_state(indoc! {"
11747        fn a() {
11748            «a();
11749
11750            c();ˇ»
11751        }
11752    "});
11753
11754    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11755
11756    cx.assert_editor_state(indoc! {"
11757        fn a() {
11758        //    «a();
11759
11760        //    c();ˇ»
11761        }
11762    "});
11763
11764    // If a selection includes multiple comment prefixes, all lines are uncommented.
11765    cx.set_state(indoc! {"
11766        fn a() {
11767        //    «a();
11768        ///    b();
11769        //!    c();ˇ»
11770        }
11771    "});
11772
11773    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11774
11775    cx.assert_editor_state(indoc! {"
11776        fn a() {
11777            «a();
11778            b();
11779            c();ˇ»
11780        }
11781    "});
11782}
11783
11784#[gpui::test]
11785async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11786    init_test(cx, |_| {});
11787
11788    let language = Arc::new(Language::new(
11789        LanguageConfig {
11790            line_comments: vec!["// ".into()],
11791            ..Default::default()
11792        },
11793        Some(tree_sitter_rust::LANGUAGE.into()),
11794    ));
11795
11796    let mut cx = EditorTestContext::new(cx).await;
11797
11798    cx.language_registry().add(language.clone());
11799    cx.update_buffer(|buffer, cx| {
11800        buffer.set_language(Some(language), cx);
11801    });
11802
11803    let toggle_comments = &ToggleComments {
11804        advance_downwards: true,
11805        ignore_indent: false,
11806    };
11807
11808    // Single cursor on one line -> advance
11809    // Cursor moves horizontally 3 characters as well on non-blank line
11810    cx.set_state(indoc!(
11811        "fn a() {
11812             ˇdog();
11813             cat();
11814        }"
11815    ));
11816    cx.update_editor(|editor, window, cx| {
11817        editor.toggle_comments(toggle_comments, window, cx);
11818    });
11819    cx.assert_editor_state(indoc!(
11820        "fn a() {
11821             // dog();
11822             catˇ();
11823        }"
11824    ));
11825
11826    // Single selection on one line -> don't advance
11827    cx.set_state(indoc!(
11828        "fn a() {
11829             «dog()ˇ»;
11830             cat();
11831        }"
11832    ));
11833    cx.update_editor(|editor, window, cx| {
11834        editor.toggle_comments(toggle_comments, window, cx);
11835    });
11836    cx.assert_editor_state(indoc!(
11837        "fn a() {
11838             // «dog()ˇ»;
11839             cat();
11840        }"
11841    ));
11842
11843    // Multiple cursors on one line -> advance
11844    cx.set_state(indoc!(
11845        "fn a() {
11846             ˇdˇog();
11847             cat();
11848        }"
11849    ));
11850    cx.update_editor(|editor, window, cx| {
11851        editor.toggle_comments(toggle_comments, window, cx);
11852    });
11853    cx.assert_editor_state(indoc!(
11854        "fn a() {
11855             // dog();
11856             catˇ(ˇ);
11857        }"
11858    ));
11859
11860    // Multiple cursors on one line, with selection -> don't advance
11861    cx.set_state(indoc!(
11862        "fn a() {
11863             ˇdˇog«()ˇ»;
11864             cat();
11865        }"
11866    ));
11867    cx.update_editor(|editor, window, cx| {
11868        editor.toggle_comments(toggle_comments, window, cx);
11869    });
11870    cx.assert_editor_state(indoc!(
11871        "fn a() {
11872             // ˇdˇog«()ˇ»;
11873             cat();
11874        }"
11875    ));
11876
11877    // Single cursor on one line -> advance
11878    // Cursor moves to column 0 on blank line
11879    cx.set_state(indoc!(
11880        "fn a() {
11881             ˇdog();
11882
11883             cat();
11884        }"
11885    ));
11886    cx.update_editor(|editor, window, cx| {
11887        editor.toggle_comments(toggle_comments, window, cx);
11888    });
11889    cx.assert_editor_state(indoc!(
11890        "fn a() {
11891             // dog();
11892        ˇ
11893             cat();
11894        }"
11895    ));
11896
11897    // Single cursor on one line -> advance
11898    // Cursor starts and ends at column 0
11899    cx.set_state(indoc!(
11900        "fn a() {
11901         ˇ    dog();
11902             cat();
11903        }"
11904    ));
11905    cx.update_editor(|editor, window, cx| {
11906        editor.toggle_comments(toggle_comments, window, cx);
11907    });
11908    cx.assert_editor_state(indoc!(
11909        "fn a() {
11910             // dog();
11911         ˇ    cat();
11912        }"
11913    ));
11914}
11915
11916#[gpui::test]
11917async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11918    init_test(cx, |_| {});
11919
11920    let mut cx = EditorTestContext::new(cx).await;
11921
11922    let html_language = Arc::new(
11923        Language::new(
11924            LanguageConfig {
11925                name: "HTML".into(),
11926                block_comment: Some(("<!-- ".into(), " -->".into())),
11927                ..Default::default()
11928            },
11929            Some(tree_sitter_html::LANGUAGE.into()),
11930        )
11931        .with_injection_query(
11932            r#"
11933            (script_element
11934                (raw_text) @injection.content
11935                (#set! injection.language "javascript"))
11936            "#,
11937        )
11938        .unwrap(),
11939    );
11940
11941    let javascript_language = Arc::new(Language::new(
11942        LanguageConfig {
11943            name: "JavaScript".into(),
11944            line_comments: vec!["// ".into()],
11945            ..Default::default()
11946        },
11947        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11948    ));
11949
11950    cx.language_registry().add(html_language.clone());
11951    cx.language_registry().add(javascript_language.clone());
11952    cx.update_buffer(|buffer, cx| {
11953        buffer.set_language(Some(html_language), cx);
11954    });
11955
11956    // Toggle comments for empty selections
11957    cx.set_state(
11958        &r#"
11959            <p>A</p>ˇ
11960            <p>B</p>ˇ
11961            <p>C</p>ˇ
11962        "#
11963        .unindent(),
11964    );
11965    cx.update_editor(|editor, window, cx| {
11966        editor.toggle_comments(&ToggleComments::default(), window, cx)
11967    });
11968    cx.assert_editor_state(
11969        &r#"
11970            <!-- <p>A</p>ˇ -->
11971            <!-- <p>B</p>ˇ -->
11972            <!-- <p>C</p>ˇ -->
11973        "#
11974        .unindent(),
11975    );
11976    cx.update_editor(|editor, window, cx| {
11977        editor.toggle_comments(&ToggleComments::default(), window, cx)
11978    });
11979    cx.assert_editor_state(
11980        &r#"
11981            <p>A</p>ˇ
11982            <p>B</p>ˇ
11983            <p>C</p>ˇ
11984        "#
11985        .unindent(),
11986    );
11987
11988    // Toggle comments for mixture of empty and non-empty selections, where
11989    // multiple selections occupy a given line.
11990    cx.set_state(
11991        &r#"
11992            <p>A«</p>
11993            <p>ˇ»B</p>ˇ
11994            <p>C«</p>
11995            <p>ˇ»D</p>ˇ
11996        "#
11997        .unindent(),
11998    );
11999
12000    cx.update_editor(|editor, window, cx| {
12001        editor.toggle_comments(&ToggleComments::default(), window, cx)
12002    });
12003    cx.assert_editor_state(
12004        &r#"
12005            <!-- <p>A«</p>
12006            <p>ˇ»B</p>ˇ -->
12007            <!-- <p>C«</p>
12008            <p>ˇ»D</p>ˇ -->
12009        "#
12010        .unindent(),
12011    );
12012    cx.update_editor(|editor, window, cx| {
12013        editor.toggle_comments(&ToggleComments::default(), window, cx)
12014    });
12015    cx.assert_editor_state(
12016        &r#"
12017            <p>A«</p>
12018            <p>ˇ»B</p>ˇ
12019            <p>C«</p>
12020            <p>ˇ»D</p>ˇ
12021        "#
12022        .unindent(),
12023    );
12024
12025    // Toggle comments when different languages are active for different
12026    // selections.
12027    cx.set_state(
12028        &r#"
12029            ˇ<script>
12030                ˇvar x = new Y();
12031            ˇ</script>
12032        "#
12033        .unindent(),
12034    );
12035    cx.executor().run_until_parked();
12036    cx.update_editor(|editor, window, cx| {
12037        editor.toggle_comments(&ToggleComments::default(), window, cx)
12038    });
12039    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12040    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12041    cx.assert_editor_state(
12042        &r#"
12043            <!-- ˇ<script> -->
12044                // ˇvar x = new Y();
12045            <!-- ˇ</script> -->
12046        "#
12047        .unindent(),
12048    );
12049}
12050
12051#[gpui::test]
12052fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12053    init_test(cx, |_| {});
12054
12055    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12056    let multibuffer = cx.new(|cx| {
12057        let mut multibuffer = MultiBuffer::new(ReadWrite);
12058        multibuffer.push_excerpts(
12059            buffer.clone(),
12060            [
12061                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12062                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12063            ],
12064            cx,
12065        );
12066        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12067        multibuffer
12068    });
12069
12070    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12071    editor.update_in(cx, |editor, window, cx| {
12072        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12073        editor.change_selections(None, window, cx, |s| {
12074            s.select_ranges([
12075                Point::new(0, 0)..Point::new(0, 0),
12076                Point::new(1, 0)..Point::new(1, 0),
12077            ])
12078        });
12079
12080        editor.handle_input("X", window, cx);
12081        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12082        assert_eq!(
12083            editor.selections.ranges(cx),
12084            [
12085                Point::new(0, 1)..Point::new(0, 1),
12086                Point::new(1, 1)..Point::new(1, 1),
12087            ]
12088        );
12089
12090        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12091        editor.change_selections(None, window, cx, |s| {
12092            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12093        });
12094        editor.backspace(&Default::default(), window, cx);
12095        assert_eq!(editor.text(cx), "Xa\nbbb");
12096        assert_eq!(
12097            editor.selections.ranges(cx),
12098            [Point::new(1, 0)..Point::new(1, 0)]
12099        );
12100
12101        editor.change_selections(None, window, cx, |s| {
12102            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12103        });
12104        editor.backspace(&Default::default(), window, cx);
12105        assert_eq!(editor.text(cx), "X\nbb");
12106        assert_eq!(
12107            editor.selections.ranges(cx),
12108            [Point::new(0, 1)..Point::new(0, 1)]
12109        );
12110    });
12111}
12112
12113#[gpui::test]
12114fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12115    init_test(cx, |_| {});
12116
12117    let markers = vec![('[', ']').into(), ('(', ')').into()];
12118    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12119        indoc! {"
12120            [aaaa
12121            (bbbb]
12122            cccc)",
12123        },
12124        markers.clone(),
12125    );
12126    let excerpt_ranges = markers.into_iter().map(|marker| {
12127        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12128        ExcerptRange::new(context.clone())
12129    });
12130    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12131    let multibuffer = cx.new(|cx| {
12132        let mut multibuffer = MultiBuffer::new(ReadWrite);
12133        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12134        multibuffer
12135    });
12136
12137    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12138    editor.update_in(cx, |editor, window, cx| {
12139        let (expected_text, selection_ranges) = marked_text_ranges(
12140            indoc! {"
12141                aaaa
12142                bˇbbb
12143                bˇbbˇb
12144                cccc"
12145            },
12146            true,
12147        );
12148        assert_eq!(editor.text(cx), expected_text);
12149        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12150
12151        editor.handle_input("X", window, cx);
12152
12153        let (expected_text, expected_selections) = marked_text_ranges(
12154            indoc! {"
12155                aaaa
12156                bXˇbbXb
12157                bXˇbbXˇb
12158                cccc"
12159            },
12160            false,
12161        );
12162        assert_eq!(editor.text(cx), expected_text);
12163        assert_eq!(editor.selections.ranges(cx), expected_selections);
12164
12165        editor.newline(&Newline, window, cx);
12166        let (expected_text, expected_selections) = marked_text_ranges(
12167            indoc! {"
12168                aaaa
12169                bX
12170                ˇbbX
12171                b
12172                bX
12173                ˇbbX
12174                ˇb
12175                cccc"
12176            },
12177            false,
12178        );
12179        assert_eq!(editor.text(cx), expected_text);
12180        assert_eq!(editor.selections.ranges(cx), expected_selections);
12181    });
12182}
12183
12184#[gpui::test]
12185fn test_refresh_selections(cx: &mut TestAppContext) {
12186    init_test(cx, |_| {});
12187
12188    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12189    let mut excerpt1_id = None;
12190    let multibuffer = cx.new(|cx| {
12191        let mut multibuffer = MultiBuffer::new(ReadWrite);
12192        excerpt1_id = multibuffer
12193            .push_excerpts(
12194                buffer.clone(),
12195                [
12196                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12197                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12198                ],
12199                cx,
12200            )
12201            .into_iter()
12202            .next();
12203        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12204        multibuffer
12205    });
12206
12207    let editor = cx.add_window(|window, cx| {
12208        let mut editor = build_editor(multibuffer.clone(), window, cx);
12209        let snapshot = editor.snapshot(window, cx);
12210        editor.change_selections(None, window, cx, |s| {
12211            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12212        });
12213        editor.begin_selection(
12214            Point::new(2, 1).to_display_point(&snapshot),
12215            true,
12216            1,
12217            window,
12218            cx,
12219        );
12220        assert_eq!(
12221            editor.selections.ranges(cx),
12222            [
12223                Point::new(1, 3)..Point::new(1, 3),
12224                Point::new(2, 1)..Point::new(2, 1),
12225            ]
12226        );
12227        editor
12228    });
12229
12230    // Refreshing selections is a no-op when excerpts haven't changed.
12231    _ = editor.update(cx, |editor, window, cx| {
12232        editor.change_selections(None, window, cx, |s| s.refresh());
12233        assert_eq!(
12234            editor.selections.ranges(cx),
12235            [
12236                Point::new(1, 3)..Point::new(1, 3),
12237                Point::new(2, 1)..Point::new(2, 1),
12238            ]
12239        );
12240    });
12241
12242    multibuffer.update(cx, |multibuffer, cx| {
12243        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12244    });
12245    _ = editor.update(cx, |editor, window, cx| {
12246        // Removing an excerpt causes the first selection to become degenerate.
12247        assert_eq!(
12248            editor.selections.ranges(cx),
12249            [
12250                Point::new(0, 0)..Point::new(0, 0),
12251                Point::new(0, 1)..Point::new(0, 1)
12252            ]
12253        );
12254
12255        // Refreshing selections will relocate the first selection to the original buffer
12256        // location.
12257        editor.change_selections(None, window, cx, |s| s.refresh());
12258        assert_eq!(
12259            editor.selections.ranges(cx),
12260            [
12261                Point::new(0, 1)..Point::new(0, 1),
12262                Point::new(0, 3)..Point::new(0, 3)
12263            ]
12264        );
12265        assert!(editor.selections.pending_anchor().is_some());
12266    });
12267}
12268
12269#[gpui::test]
12270fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12271    init_test(cx, |_| {});
12272
12273    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12274    let mut excerpt1_id = None;
12275    let multibuffer = cx.new(|cx| {
12276        let mut multibuffer = MultiBuffer::new(ReadWrite);
12277        excerpt1_id = multibuffer
12278            .push_excerpts(
12279                buffer.clone(),
12280                [
12281                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12282                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12283                ],
12284                cx,
12285            )
12286            .into_iter()
12287            .next();
12288        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12289        multibuffer
12290    });
12291
12292    let editor = cx.add_window(|window, cx| {
12293        let mut editor = build_editor(multibuffer.clone(), window, cx);
12294        let snapshot = editor.snapshot(window, cx);
12295        editor.begin_selection(
12296            Point::new(1, 3).to_display_point(&snapshot),
12297            false,
12298            1,
12299            window,
12300            cx,
12301        );
12302        assert_eq!(
12303            editor.selections.ranges(cx),
12304            [Point::new(1, 3)..Point::new(1, 3)]
12305        );
12306        editor
12307    });
12308
12309    multibuffer.update(cx, |multibuffer, cx| {
12310        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12311    });
12312    _ = editor.update(cx, |editor, window, cx| {
12313        assert_eq!(
12314            editor.selections.ranges(cx),
12315            [Point::new(0, 0)..Point::new(0, 0)]
12316        );
12317
12318        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12319        editor.change_selections(None, window, cx, |s| s.refresh());
12320        assert_eq!(
12321            editor.selections.ranges(cx),
12322            [Point::new(0, 3)..Point::new(0, 3)]
12323        );
12324        assert!(editor.selections.pending_anchor().is_some());
12325    });
12326}
12327
12328#[gpui::test]
12329async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12330    init_test(cx, |_| {});
12331
12332    let language = Arc::new(
12333        Language::new(
12334            LanguageConfig {
12335                brackets: BracketPairConfig {
12336                    pairs: vec![
12337                        BracketPair {
12338                            start: "{".to_string(),
12339                            end: "}".to_string(),
12340                            close: true,
12341                            surround: true,
12342                            newline: true,
12343                        },
12344                        BracketPair {
12345                            start: "/* ".to_string(),
12346                            end: " */".to_string(),
12347                            close: true,
12348                            surround: true,
12349                            newline: true,
12350                        },
12351                    ],
12352                    ..Default::default()
12353                },
12354                ..Default::default()
12355            },
12356            Some(tree_sitter_rust::LANGUAGE.into()),
12357        )
12358        .with_indents_query("")
12359        .unwrap(),
12360    );
12361
12362    let text = concat!(
12363        "{   }\n",     //
12364        "  x\n",       //
12365        "  /*   */\n", //
12366        "x\n",         //
12367        "{{} }\n",     //
12368    );
12369
12370    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12371    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12372    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12373    editor
12374        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12375        .await;
12376
12377    editor.update_in(cx, |editor, window, cx| {
12378        editor.change_selections(None, window, cx, |s| {
12379            s.select_display_ranges([
12380                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12381                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12382                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12383            ])
12384        });
12385        editor.newline(&Newline, window, cx);
12386
12387        assert_eq!(
12388            editor.buffer().read(cx).read(cx).text(),
12389            concat!(
12390                "{ \n",    // Suppress rustfmt
12391                "\n",      //
12392                "}\n",     //
12393                "  x\n",   //
12394                "  /* \n", //
12395                "  \n",    //
12396                "  */\n",  //
12397                "x\n",     //
12398                "{{} \n",  //
12399                "}\n",     //
12400            )
12401        );
12402    });
12403}
12404
12405#[gpui::test]
12406fn test_highlighted_ranges(cx: &mut TestAppContext) {
12407    init_test(cx, |_| {});
12408
12409    let editor = cx.add_window(|window, cx| {
12410        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12411        build_editor(buffer.clone(), window, cx)
12412    });
12413
12414    _ = editor.update(cx, |editor, window, cx| {
12415        struct Type1;
12416        struct Type2;
12417
12418        let buffer = editor.buffer.read(cx).snapshot(cx);
12419
12420        let anchor_range =
12421            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12422
12423        editor.highlight_background::<Type1>(
12424            &[
12425                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12426                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12427                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12428                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12429            ],
12430            |_| Hsla::red(),
12431            cx,
12432        );
12433        editor.highlight_background::<Type2>(
12434            &[
12435                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12436                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12437                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12438                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12439            ],
12440            |_| Hsla::green(),
12441            cx,
12442        );
12443
12444        let snapshot = editor.snapshot(window, cx);
12445        let mut highlighted_ranges = editor.background_highlights_in_range(
12446            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12447            &snapshot,
12448            cx.theme().colors(),
12449        );
12450        // Enforce a consistent ordering based on color without relying on the ordering of the
12451        // highlight's `TypeId` which is non-executor.
12452        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12453        assert_eq!(
12454            highlighted_ranges,
12455            &[
12456                (
12457                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12458                    Hsla::red(),
12459                ),
12460                (
12461                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12462                    Hsla::red(),
12463                ),
12464                (
12465                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12466                    Hsla::green(),
12467                ),
12468                (
12469                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12470                    Hsla::green(),
12471                ),
12472            ]
12473        );
12474        assert_eq!(
12475            editor.background_highlights_in_range(
12476                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12477                &snapshot,
12478                cx.theme().colors(),
12479            ),
12480            &[(
12481                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12482                Hsla::red(),
12483            )]
12484        );
12485    });
12486}
12487
12488#[gpui::test]
12489async fn test_following(cx: &mut TestAppContext) {
12490    init_test(cx, |_| {});
12491
12492    let fs = FakeFs::new(cx.executor());
12493    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12494
12495    let buffer = project.update(cx, |project, cx| {
12496        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12497        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12498    });
12499    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12500    let follower = cx.update(|cx| {
12501        cx.open_window(
12502            WindowOptions {
12503                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12504                    gpui::Point::new(px(0.), px(0.)),
12505                    gpui::Point::new(px(10.), px(80.)),
12506                ))),
12507                ..Default::default()
12508            },
12509            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12510        )
12511        .unwrap()
12512    });
12513
12514    let is_still_following = Rc::new(RefCell::new(true));
12515    let follower_edit_event_count = Rc::new(RefCell::new(0));
12516    let pending_update = Rc::new(RefCell::new(None));
12517    let leader_entity = leader.root(cx).unwrap();
12518    let follower_entity = follower.root(cx).unwrap();
12519    _ = follower.update(cx, {
12520        let update = pending_update.clone();
12521        let is_still_following = is_still_following.clone();
12522        let follower_edit_event_count = follower_edit_event_count.clone();
12523        |_, window, cx| {
12524            cx.subscribe_in(
12525                &leader_entity,
12526                window,
12527                move |_, leader, event, window, cx| {
12528                    leader.read(cx).add_event_to_update_proto(
12529                        event,
12530                        &mut update.borrow_mut(),
12531                        window,
12532                        cx,
12533                    );
12534                },
12535            )
12536            .detach();
12537
12538            cx.subscribe_in(
12539                &follower_entity,
12540                window,
12541                move |_, _, event: &EditorEvent, _window, _cx| {
12542                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12543                        *is_still_following.borrow_mut() = false;
12544                    }
12545
12546                    if let EditorEvent::BufferEdited = event {
12547                        *follower_edit_event_count.borrow_mut() += 1;
12548                    }
12549                },
12550            )
12551            .detach();
12552        }
12553    });
12554
12555    // Update the selections only
12556    _ = leader.update(cx, |leader, window, cx| {
12557        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12558    });
12559    follower
12560        .update(cx, |follower, window, cx| {
12561            follower.apply_update_proto(
12562                &project,
12563                pending_update.borrow_mut().take().unwrap(),
12564                window,
12565                cx,
12566            )
12567        })
12568        .unwrap()
12569        .await
12570        .unwrap();
12571    _ = follower.update(cx, |follower, _, cx| {
12572        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12573    });
12574    assert!(*is_still_following.borrow());
12575    assert_eq!(*follower_edit_event_count.borrow(), 0);
12576
12577    // Update the scroll position only
12578    _ = leader.update(cx, |leader, window, cx| {
12579        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12580    });
12581    follower
12582        .update(cx, |follower, window, cx| {
12583            follower.apply_update_proto(
12584                &project,
12585                pending_update.borrow_mut().take().unwrap(),
12586                window,
12587                cx,
12588            )
12589        })
12590        .unwrap()
12591        .await
12592        .unwrap();
12593    assert_eq!(
12594        follower
12595            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12596            .unwrap(),
12597        gpui::Point::new(1.5, 3.5)
12598    );
12599    assert!(*is_still_following.borrow());
12600    assert_eq!(*follower_edit_event_count.borrow(), 0);
12601
12602    // Update the selections and scroll position. The follower's scroll position is updated
12603    // via autoscroll, not via the leader's exact scroll position.
12604    _ = leader.update(cx, |leader, window, cx| {
12605        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12606        leader.request_autoscroll(Autoscroll::newest(), cx);
12607        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12608    });
12609    follower
12610        .update(cx, |follower, window, cx| {
12611            follower.apply_update_proto(
12612                &project,
12613                pending_update.borrow_mut().take().unwrap(),
12614                window,
12615                cx,
12616            )
12617        })
12618        .unwrap()
12619        .await
12620        .unwrap();
12621    _ = follower.update(cx, |follower, _, cx| {
12622        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12623        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12624    });
12625    assert!(*is_still_following.borrow());
12626
12627    // Creating a pending selection that precedes another selection
12628    _ = leader.update(cx, |leader, window, cx| {
12629        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12630        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12631    });
12632    follower
12633        .update(cx, |follower, window, cx| {
12634            follower.apply_update_proto(
12635                &project,
12636                pending_update.borrow_mut().take().unwrap(),
12637                window,
12638                cx,
12639            )
12640        })
12641        .unwrap()
12642        .await
12643        .unwrap();
12644    _ = follower.update(cx, |follower, _, cx| {
12645        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12646    });
12647    assert!(*is_still_following.borrow());
12648
12649    // Extend the pending selection so that it surrounds another selection
12650    _ = leader.update(cx, |leader, window, cx| {
12651        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12652    });
12653    follower
12654        .update(cx, |follower, window, cx| {
12655            follower.apply_update_proto(
12656                &project,
12657                pending_update.borrow_mut().take().unwrap(),
12658                window,
12659                cx,
12660            )
12661        })
12662        .unwrap()
12663        .await
12664        .unwrap();
12665    _ = follower.update(cx, |follower, _, cx| {
12666        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12667    });
12668
12669    // Scrolling locally breaks the follow
12670    _ = follower.update(cx, |follower, window, cx| {
12671        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12672        follower.set_scroll_anchor(
12673            ScrollAnchor {
12674                anchor: top_anchor,
12675                offset: gpui::Point::new(0.0, 0.5),
12676            },
12677            window,
12678            cx,
12679        );
12680    });
12681    assert!(!(*is_still_following.borrow()));
12682}
12683
12684#[gpui::test]
12685async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12686    init_test(cx, |_| {});
12687
12688    let fs = FakeFs::new(cx.executor());
12689    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12691    let pane = workspace
12692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12693        .unwrap();
12694
12695    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12696
12697    let leader = pane.update_in(cx, |_, window, cx| {
12698        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12699        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12700    });
12701
12702    // Start following the editor when it has no excerpts.
12703    let mut state_message =
12704        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12705    let workspace_entity = workspace.root(cx).unwrap();
12706    let follower_1 = cx
12707        .update_window(*workspace.deref(), |_, window, cx| {
12708            Editor::from_state_proto(
12709                workspace_entity,
12710                ViewId {
12711                    creator: CollaboratorId::PeerId(PeerId::default()),
12712                    id: 0,
12713                },
12714                &mut state_message,
12715                window,
12716                cx,
12717            )
12718        })
12719        .unwrap()
12720        .unwrap()
12721        .await
12722        .unwrap();
12723
12724    let update_message = Rc::new(RefCell::new(None));
12725    follower_1.update_in(cx, {
12726        let update = update_message.clone();
12727        |_, window, cx| {
12728            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12729                leader.read(cx).add_event_to_update_proto(
12730                    event,
12731                    &mut update.borrow_mut(),
12732                    window,
12733                    cx,
12734                );
12735            })
12736            .detach();
12737        }
12738    });
12739
12740    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12741        (
12742            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12743            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12744        )
12745    });
12746
12747    // Insert some excerpts.
12748    leader.update(cx, |leader, cx| {
12749        leader.buffer.update(cx, |multibuffer, cx| {
12750            multibuffer.set_excerpts_for_path(
12751                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12752                buffer_1.clone(),
12753                vec![
12754                    Point::row_range(0..3),
12755                    Point::row_range(1..6),
12756                    Point::row_range(12..15),
12757                ],
12758                0,
12759                cx,
12760            );
12761            multibuffer.set_excerpts_for_path(
12762                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12763                buffer_2.clone(),
12764                vec![Point::row_range(0..6), Point::row_range(8..12)],
12765                0,
12766                cx,
12767            );
12768        });
12769    });
12770
12771    // Apply the update of adding the excerpts.
12772    follower_1
12773        .update_in(cx, |follower, window, cx| {
12774            follower.apply_update_proto(
12775                &project,
12776                update_message.borrow().clone().unwrap(),
12777                window,
12778                cx,
12779            )
12780        })
12781        .await
12782        .unwrap();
12783    assert_eq!(
12784        follower_1.update(cx, |editor, cx| editor.text(cx)),
12785        leader.update(cx, |editor, cx| editor.text(cx))
12786    );
12787    update_message.borrow_mut().take();
12788
12789    // Start following separately after it already has excerpts.
12790    let mut state_message =
12791        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12792    let workspace_entity = workspace.root(cx).unwrap();
12793    let follower_2 = cx
12794        .update_window(*workspace.deref(), |_, window, cx| {
12795            Editor::from_state_proto(
12796                workspace_entity,
12797                ViewId {
12798                    creator: CollaboratorId::PeerId(PeerId::default()),
12799                    id: 0,
12800                },
12801                &mut state_message,
12802                window,
12803                cx,
12804            )
12805        })
12806        .unwrap()
12807        .unwrap()
12808        .await
12809        .unwrap();
12810    assert_eq!(
12811        follower_2.update(cx, |editor, cx| editor.text(cx)),
12812        leader.update(cx, |editor, cx| editor.text(cx))
12813    );
12814
12815    // Remove some excerpts.
12816    leader.update(cx, |leader, cx| {
12817        leader.buffer.update(cx, |multibuffer, cx| {
12818            let excerpt_ids = multibuffer.excerpt_ids();
12819            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12820            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12821        });
12822    });
12823
12824    // Apply the update of removing the excerpts.
12825    follower_1
12826        .update_in(cx, |follower, window, cx| {
12827            follower.apply_update_proto(
12828                &project,
12829                update_message.borrow().clone().unwrap(),
12830                window,
12831                cx,
12832            )
12833        })
12834        .await
12835        .unwrap();
12836    follower_2
12837        .update_in(cx, |follower, window, cx| {
12838            follower.apply_update_proto(
12839                &project,
12840                update_message.borrow().clone().unwrap(),
12841                window,
12842                cx,
12843            )
12844        })
12845        .await
12846        .unwrap();
12847    update_message.borrow_mut().take();
12848    assert_eq!(
12849        follower_1.update(cx, |editor, cx| editor.text(cx)),
12850        leader.update(cx, |editor, cx| editor.text(cx))
12851    );
12852}
12853
12854#[gpui::test]
12855async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12856    init_test(cx, |_| {});
12857
12858    let mut cx = EditorTestContext::new(cx).await;
12859    let lsp_store =
12860        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12861
12862    cx.set_state(indoc! {"
12863        ˇfn func(abc def: i32) -> u32 {
12864        }
12865    "});
12866
12867    cx.update(|_, cx| {
12868        lsp_store.update(cx, |lsp_store, cx| {
12869            lsp_store
12870                .update_diagnostics(
12871                    LanguageServerId(0),
12872                    lsp::PublishDiagnosticsParams {
12873                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12874                        version: None,
12875                        diagnostics: vec![
12876                            lsp::Diagnostic {
12877                                range: lsp::Range::new(
12878                                    lsp::Position::new(0, 11),
12879                                    lsp::Position::new(0, 12),
12880                                ),
12881                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12882                                ..Default::default()
12883                            },
12884                            lsp::Diagnostic {
12885                                range: lsp::Range::new(
12886                                    lsp::Position::new(0, 12),
12887                                    lsp::Position::new(0, 15),
12888                                ),
12889                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12890                                ..Default::default()
12891                            },
12892                            lsp::Diagnostic {
12893                                range: lsp::Range::new(
12894                                    lsp::Position::new(0, 25),
12895                                    lsp::Position::new(0, 28),
12896                                ),
12897                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12898                                ..Default::default()
12899                            },
12900                        ],
12901                    },
12902                    &[],
12903                    cx,
12904                )
12905                .unwrap()
12906        });
12907    });
12908
12909    executor.run_until_parked();
12910
12911    cx.update_editor(|editor, window, cx| {
12912        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12913    });
12914
12915    cx.assert_editor_state(indoc! {"
12916        fn func(abc def: i32) -> ˇu32 {
12917        }
12918    "});
12919
12920    cx.update_editor(|editor, window, cx| {
12921        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12922    });
12923
12924    cx.assert_editor_state(indoc! {"
12925        fn func(abc ˇdef: i32) -> u32 {
12926        }
12927    "});
12928
12929    cx.update_editor(|editor, window, cx| {
12930        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12931    });
12932
12933    cx.assert_editor_state(indoc! {"
12934        fn func(abcˇ def: i32) -> u32 {
12935        }
12936    "});
12937
12938    cx.update_editor(|editor, window, cx| {
12939        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12940    });
12941
12942    cx.assert_editor_state(indoc! {"
12943        fn func(abc def: i32) -> ˇu32 {
12944        }
12945    "});
12946}
12947
12948#[gpui::test]
12949async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12950    init_test(cx, |_| {});
12951
12952    let mut cx = EditorTestContext::new(cx).await;
12953
12954    let diff_base = r#"
12955        use some::mod;
12956
12957        const A: u32 = 42;
12958
12959        fn main() {
12960            println!("hello");
12961
12962            println!("world");
12963        }
12964        "#
12965    .unindent();
12966
12967    // Edits are modified, removed, modified, added
12968    cx.set_state(
12969        &r#"
12970        use some::modified;
12971
12972        ˇ
12973        fn main() {
12974            println!("hello there");
12975
12976            println!("around the");
12977            println!("world");
12978        }
12979        "#
12980        .unindent(),
12981    );
12982
12983    cx.set_head_text(&diff_base);
12984    executor.run_until_parked();
12985
12986    cx.update_editor(|editor, window, cx| {
12987        //Wrap around the bottom of the buffer
12988        for _ in 0..3 {
12989            editor.go_to_next_hunk(&GoToHunk, window, cx);
12990        }
12991    });
12992
12993    cx.assert_editor_state(
12994        &r#"
12995        ˇuse some::modified;
12996
12997
12998        fn main() {
12999            println!("hello there");
13000
13001            println!("around the");
13002            println!("world");
13003        }
13004        "#
13005        .unindent(),
13006    );
13007
13008    cx.update_editor(|editor, window, cx| {
13009        //Wrap around the top of the buffer
13010        for _ in 0..2 {
13011            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13012        }
13013    });
13014
13015    cx.assert_editor_state(
13016        &r#"
13017        use some::modified;
13018
13019
13020        fn main() {
13021        ˇ    println!("hello there");
13022
13023            println!("around the");
13024            println!("world");
13025        }
13026        "#
13027        .unindent(),
13028    );
13029
13030    cx.update_editor(|editor, window, cx| {
13031        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13032    });
13033
13034    cx.assert_editor_state(
13035        &r#"
13036        use some::modified;
13037
13038        ˇ
13039        fn main() {
13040            println!("hello there");
13041
13042            println!("around the");
13043            println!("world");
13044        }
13045        "#
13046        .unindent(),
13047    );
13048
13049    cx.update_editor(|editor, window, cx| {
13050        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13051    });
13052
13053    cx.assert_editor_state(
13054        &r#"
13055        ˇuse some::modified;
13056
13057
13058        fn main() {
13059            println!("hello there");
13060
13061            println!("around the");
13062            println!("world");
13063        }
13064        "#
13065        .unindent(),
13066    );
13067
13068    cx.update_editor(|editor, window, cx| {
13069        for _ in 0..2 {
13070            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13071        }
13072    });
13073
13074    cx.assert_editor_state(
13075        &r#"
13076        use some::modified;
13077
13078
13079        fn main() {
13080        ˇ    println!("hello there");
13081
13082            println!("around the");
13083            println!("world");
13084        }
13085        "#
13086        .unindent(),
13087    );
13088
13089    cx.update_editor(|editor, window, cx| {
13090        editor.fold(&Fold, window, cx);
13091    });
13092
13093    cx.update_editor(|editor, window, cx| {
13094        editor.go_to_next_hunk(&GoToHunk, window, cx);
13095    });
13096
13097    cx.assert_editor_state(
13098        &r#"
13099        ˇuse some::modified;
13100
13101
13102        fn main() {
13103            println!("hello there");
13104
13105            println!("around the");
13106            println!("world");
13107        }
13108        "#
13109        .unindent(),
13110    );
13111}
13112
13113#[test]
13114fn test_split_words() {
13115    fn split(text: &str) -> Vec<&str> {
13116        split_words(text).collect()
13117    }
13118
13119    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13120    assert_eq!(split("hello_world"), &["hello_", "world"]);
13121    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13122    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13123    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13124    assert_eq!(split("helloworld"), &["helloworld"]);
13125
13126    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13127}
13128
13129#[gpui::test]
13130async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13131    init_test(cx, |_| {});
13132
13133    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13134    let mut assert = |before, after| {
13135        let _state_context = cx.set_state(before);
13136        cx.run_until_parked();
13137        cx.update_editor(|editor, window, cx| {
13138            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13139        });
13140        cx.run_until_parked();
13141        cx.assert_editor_state(after);
13142    };
13143
13144    // Outside bracket jumps to outside of matching bracket
13145    assert("console.logˇ(var);", "console.log(var)ˇ;");
13146    assert("console.log(var)ˇ;", "console.logˇ(var);");
13147
13148    // Inside bracket jumps to inside of matching bracket
13149    assert("console.log(ˇvar);", "console.log(varˇ);");
13150    assert("console.log(varˇ);", "console.log(ˇvar);");
13151
13152    // When outside a bracket and inside, favor jumping to the inside bracket
13153    assert(
13154        "console.log('foo', [1, 2, 3]ˇ);",
13155        "console.log(ˇ'foo', [1, 2, 3]);",
13156    );
13157    assert(
13158        "console.log(ˇ'foo', [1, 2, 3]);",
13159        "console.log('foo', [1, 2, 3]ˇ);",
13160    );
13161
13162    // Bias forward if two options are equally likely
13163    assert(
13164        "let result = curried_fun()ˇ();",
13165        "let result = curried_fun()()ˇ;",
13166    );
13167
13168    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13169    assert(
13170        indoc! {"
13171            function test() {
13172                console.log('test')ˇ
13173            }"},
13174        indoc! {"
13175            function test() {
13176                console.logˇ('test')
13177            }"},
13178    );
13179}
13180
13181#[gpui::test]
13182async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13183    init_test(cx, |_| {});
13184
13185    let fs = FakeFs::new(cx.executor());
13186    fs.insert_tree(
13187        path!("/a"),
13188        json!({
13189            "main.rs": "fn main() { let a = 5; }",
13190            "other.rs": "// Test file",
13191        }),
13192    )
13193    .await;
13194    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13195
13196    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13197    language_registry.add(Arc::new(Language::new(
13198        LanguageConfig {
13199            name: "Rust".into(),
13200            matcher: LanguageMatcher {
13201                path_suffixes: vec!["rs".to_string()],
13202                ..Default::default()
13203            },
13204            brackets: BracketPairConfig {
13205                pairs: vec![BracketPair {
13206                    start: "{".to_string(),
13207                    end: "}".to_string(),
13208                    close: true,
13209                    surround: true,
13210                    newline: true,
13211                }],
13212                disabled_scopes_by_bracket_ix: Vec::new(),
13213            },
13214            ..Default::default()
13215        },
13216        Some(tree_sitter_rust::LANGUAGE.into()),
13217    )));
13218    let mut fake_servers = language_registry.register_fake_lsp(
13219        "Rust",
13220        FakeLspAdapter {
13221            capabilities: lsp::ServerCapabilities {
13222                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13223                    first_trigger_character: "{".to_string(),
13224                    more_trigger_character: None,
13225                }),
13226                ..Default::default()
13227            },
13228            ..Default::default()
13229        },
13230    );
13231
13232    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13233
13234    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13235
13236    let worktree_id = workspace
13237        .update(cx, |workspace, _, cx| {
13238            workspace.project().update(cx, |project, cx| {
13239                project.worktrees(cx).next().unwrap().read(cx).id()
13240            })
13241        })
13242        .unwrap();
13243
13244    let buffer = project
13245        .update(cx, |project, cx| {
13246            project.open_local_buffer(path!("/a/main.rs"), cx)
13247        })
13248        .await
13249        .unwrap();
13250    let editor_handle = workspace
13251        .update(cx, |workspace, window, cx| {
13252            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13253        })
13254        .unwrap()
13255        .await
13256        .unwrap()
13257        .downcast::<Editor>()
13258        .unwrap();
13259
13260    cx.executor().start_waiting();
13261    let fake_server = fake_servers.next().await.unwrap();
13262
13263    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13264        |params, _| async move {
13265            assert_eq!(
13266                params.text_document_position.text_document.uri,
13267                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13268            );
13269            assert_eq!(
13270                params.text_document_position.position,
13271                lsp::Position::new(0, 21),
13272            );
13273
13274            Ok(Some(vec![lsp::TextEdit {
13275                new_text: "]".to_string(),
13276                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13277            }]))
13278        },
13279    );
13280
13281    editor_handle.update_in(cx, |editor, window, cx| {
13282        window.focus(&editor.focus_handle(cx));
13283        editor.change_selections(None, window, cx, |s| {
13284            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13285        });
13286        editor.handle_input("{", window, cx);
13287    });
13288
13289    cx.executor().run_until_parked();
13290
13291    buffer.update(cx, |buffer, _| {
13292        assert_eq!(
13293            buffer.text(),
13294            "fn main() { let a = {5}; }",
13295            "No extra braces from on type formatting should appear in the buffer"
13296        )
13297    });
13298}
13299
13300#[gpui::test]
13301async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13302    init_test(cx, |_| {});
13303
13304    let fs = FakeFs::new(cx.executor());
13305    fs.insert_tree(
13306        path!("/a"),
13307        json!({
13308            "main.rs": "fn main() { let a = 5; }",
13309            "other.rs": "// Test file",
13310        }),
13311    )
13312    .await;
13313
13314    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13315
13316    let server_restarts = Arc::new(AtomicUsize::new(0));
13317    let closure_restarts = Arc::clone(&server_restarts);
13318    let language_server_name = "test language server";
13319    let language_name: LanguageName = "Rust".into();
13320
13321    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13322    language_registry.add(Arc::new(Language::new(
13323        LanguageConfig {
13324            name: language_name.clone(),
13325            matcher: LanguageMatcher {
13326                path_suffixes: vec!["rs".to_string()],
13327                ..Default::default()
13328            },
13329            ..Default::default()
13330        },
13331        Some(tree_sitter_rust::LANGUAGE.into()),
13332    )));
13333    let mut fake_servers = language_registry.register_fake_lsp(
13334        "Rust",
13335        FakeLspAdapter {
13336            name: language_server_name,
13337            initialization_options: Some(json!({
13338                "testOptionValue": true
13339            })),
13340            initializer: Some(Box::new(move |fake_server| {
13341                let task_restarts = Arc::clone(&closure_restarts);
13342                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13343                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13344                    futures::future::ready(Ok(()))
13345                });
13346            })),
13347            ..Default::default()
13348        },
13349    );
13350
13351    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13352    let _buffer = project
13353        .update(cx, |project, cx| {
13354            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13355        })
13356        .await
13357        .unwrap();
13358    let _fake_server = fake_servers.next().await.unwrap();
13359    update_test_language_settings(cx, |language_settings| {
13360        language_settings.languages.insert(
13361            language_name.clone(),
13362            LanguageSettingsContent {
13363                tab_size: NonZeroU32::new(8),
13364                ..Default::default()
13365            },
13366        );
13367    });
13368    cx.executor().run_until_parked();
13369    assert_eq!(
13370        server_restarts.load(atomic::Ordering::Acquire),
13371        0,
13372        "Should not restart LSP server on an unrelated change"
13373    );
13374
13375    update_test_project_settings(cx, |project_settings| {
13376        project_settings.lsp.insert(
13377            "Some other server name".into(),
13378            LspSettings {
13379                binary: None,
13380                settings: None,
13381                initialization_options: Some(json!({
13382                    "some other init value": false
13383                })),
13384                enable_lsp_tasks: false,
13385            },
13386        );
13387    });
13388    cx.executor().run_until_parked();
13389    assert_eq!(
13390        server_restarts.load(atomic::Ordering::Acquire),
13391        0,
13392        "Should not restart LSP server on an unrelated LSP settings change"
13393    );
13394
13395    update_test_project_settings(cx, |project_settings| {
13396        project_settings.lsp.insert(
13397            language_server_name.into(),
13398            LspSettings {
13399                binary: None,
13400                settings: None,
13401                initialization_options: Some(json!({
13402                    "anotherInitValue": false
13403                })),
13404                enable_lsp_tasks: false,
13405            },
13406        );
13407    });
13408    cx.executor().run_until_parked();
13409    assert_eq!(
13410        server_restarts.load(atomic::Ordering::Acquire),
13411        1,
13412        "Should restart LSP server on a related LSP settings change"
13413    );
13414
13415    update_test_project_settings(cx, |project_settings| {
13416        project_settings.lsp.insert(
13417            language_server_name.into(),
13418            LspSettings {
13419                binary: None,
13420                settings: None,
13421                initialization_options: Some(json!({
13422                    "anotherInitValue": false
13423                })),
13424                enable_lsp_tasks: false,
13425            },
13426        );
13427    });
13428    cx.executor().run_until_parked();
13429    assert_eq!(
13430        server_restarts.load(atomic::Ordering::Acquire),
13431        1,
13432        "Should not restart LSP server on a related LSP settings change that is the same"
13433    );
13434
13435    update_test_project_settings(cx, |project_settings| {
13436        project_settings.lsp.insert(
13437            language_server_name.into(),
13438            LspSettings {
13439                binary: None,
13440                settings: None,
13441                initialization_options: None,
13442                enable_lsp_tasks: false,
13443            },
13444        );
13445    });
13446    cx.executor().run_until_parked();
13447    assert_eq!(
13448        server_restarts.load(atomic::Ordering::Acquire),
13449        2,
13450        "Should restart LSP server on another related LSP settings change"
13451    );
13452}
13453
13454#[gpui::test]
13455async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13456    init_test(cx, |_| {});
13457
13458    let mut cx = EditorLspTestContext::new_rust(
13459        lsp::ServerCapabilities {
13460            completion_provider: Some(lsp::CompletionOptions {
13461                trigger_characters: Some(vec![".".to_string()]),
13462                resolve_provider: Some(true),
13463                ..Default::default()
13464            }),
13465            ..Default::default()
13466        },
13467        cx,
13468    )
13469    .await;
13470
13471    cx.set_state("fn main() { let a = 2ˇ; }");
13472    cx.simulate_keystroke(".");
13473    let completion_item = lsp::CompletionItem {
13474        label: "some".into(),
13475        kind: Some(lsp::CompletionItemKind::SNIPPET),
13476        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13477        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13478            kind: lsp::MarkupKind::Markdown,
13479            value: "```rust\nSome(2)\n```".to_string(),
13480        })),
13481        deprecated: Some(false),
13482        sort_text: Some("fffffff2".to_string()),
13483        filter_text: Some("some".to_string()),
13484        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13485        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13486            range: lsp::Range {
13487                start: lsp::Position {
13488                    line: 0,
13489                    character: 22,
13490                },
13491                end: lsp::Position {
13492                    line: 0,
13493                    character: 22,
13494                },
13495            },
13496            new_text: "Some(2)".to_string(),
13497        })),
13498        additional_text_edits: Some(vec![lsp::TextEdit {
13499            range: lsp::Range {
13500                start: lsp::Position {
13501                    line: 0,
13502                    character: 20,
13503                },
13504                end: lsp::Position {
13505                    line: 0,
13506                    character: 22,
13507                },
13508            },
13509            new_text: "".to_string(),
13510        }]),
13511        ..Default::default()
13512    };
13513
13514    let closure_completion_item = completion_item.clone();
13515    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13516        let task_completion_item = closure_completion_item.clone();
13517        async move {
13518            Ok(Some(lsp::CompletionResponse::Array(vec![
13519                task_completion_item,
13520            ])))
13521        }
13522    });
13523
13524    request.next().await;
13525
13526    cx.condition(|editor, _| editor.context_menu_visible())
13527        .await;
13528    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13529        editor
13530            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13531            .unwrap()
13532    });
13533    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13534
13535    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13536        let task_completion_item = completion_item.clone();
13537        async move { Ok(task_completion_item) }
13538    })
13539    .next()
13540    .await
13541    .unwrap();
13542    apply_additional_edits.await.unwrap();
13543    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13544}
13545
13546#[gpui::test]
13547async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13548    init_test(cx, |_| {});
13549
13550    let mut cx = EditorLspTestContext::new_rust(
13551        lsp::ServerCapabilities {
13552            completion_provider: Some(lsp::CompletionOptions {
13553                trigger_characters: Some(vec![".".to_string()]),
13554                resolve_provider: Some(true),
13555                ..Default::default()
13556            }),
13557            ..Default::default()
13558        },
13559        cx,
13560    )
13561    .await;
13562
13563    cx.set_state("fn main() { let a = 2ˇ; }");
13564    cx.simulate_keystroke(".");
13565
13566    let item1 = lsp::CompletionItem {
13567        label: "method id()".to_string(),
13568        filter_text: Some("id".to_string()),
13569        detail: None,
13570        documentation: None,
13571        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13572            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13573            new_text: ".id".to_string(),
13574        })),
13575        ..lsp::CompletionItem::default()
13576    };
13577
13578    let item2 = lsp::CompletionItem {
13579        label: "other".to_string(),
13580        filter_text: Some("other".to_string()),
13581        detail: None,
13582        documentation: None,
13583        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13584            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13585            new_text: ".other".to_string(),
13586        })),
13587        ..lsp::CompletionItem::default()
13588    };
13589
13590    let item1 = item1.clone();
13591    cx.set_request_handler::<lsp::request::Completion, _, _>({
13592        let item1 = item1.clone();
13593        move |_, _, _| {
13594            let item1 = item1.clone();
13595            let item2 = item2.clone();
13596            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13597        }
13598    })
13599    .next()
13600    .await;
13601
13602    cx.condition(|editor, _| editor.context_menu_visible())
13603        .await;
13604    cx.update_editor(|editor, _, _| {
13605        let context_menu = editor.context_menu.borrow_mut();
13606        let context_menu = context_menu
13607            .as_ref()
13608            .expect("Should have the context menu deployed");
13609        match context_menu {
13610            CodeContextMenu::Completions(completions_menu) => {
13611                let completions = completions_menu.completions.borrow_mut();
13612                assert_eq!(
13613                    completions
13614                        .iter()
13615                        .map(|completion| &completion.label.text)
13616                        .collect::<Vec<_>>(),
13617                    vec!["method id()", "other"]
13618                )
13619            }
13620            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13621        }
13622    });
13623
13624    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13625        let item1 = item1.clone();
13626        move |_, item_to_resolve, _| {
13627            let item1 = item1.clone();
13628            async move {
13629                if item1 == item_to_resolve {
13630                    Ok(lsp::CompletionItem {
13631                        label: "method id()".to_string(),
13632                        filter_text: Some("id".to_string()),
13633                        detail: Some("Now resolved!".to_string()),
13634                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13635                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13636                            range: lsp::Range::new(
13637                                lsp::Position::new(0, 22),
13638                                lsp::Position::new(0, 22),
13639                            ),
13640                            new_text: ".id".to_string(),
13641                        })),
13642                        ..lsp::CompletionItem::default()
13643                    })
13644                } else {
13645                    Ok(item_to_resolve)
13646                }
13647            }
13648        }
13649    })
13650    .next()
13651    .await
13652    .unwrap();
13653    cx.run_until_parked();
13654
13655    cx.update_editor(|editor, window, cx| {
13656        editor.context_menu_next(&Default::default(), window, cx);
13657    });
13658
13659    cx.update_editor(|editor, _, _| {
13660        let context_menu = editor.context_menu.borrow_mut();
13661        let context_menu = context_menu
13662            .as_ref()
13663            .expect("Should have the context menu deployed");
13664        match context_menu {
13665            CodeContextMenu::Completions(completions_menu) => {
13666                let completions = completions_menu.completions.borrow_mut();
13667                assert_eq!(
13668                    completions
13669                        .iter()
13670                        .map(|completion| &completion.label.text)
13671                        .collect::<Vec<_>>(),
13672                    vec!["method id() Now resolved!", "other"],
13673                    "Should update first completion label, but not second as the filter text did not match."
13674                );
13675            }
13676            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13677        }
13678    });
13679}
13680
13681#[gpui::test]
13682async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13683    init_test(cx, |_| {});
13684
13685    let mut cx = EditorLspTestContext::new_rust(
13686        lsp::ServerCapabilities {
13687            completion_provider: Some(lsp::CompletionOptions {
13688                trigger_characters: Some(vec![".".to_string()]),
13689                resolve_provider: Some(true),
13690                ..Default::default()
13691            }),
13692            ..Default::default()
13693        },
13694        cx,
13695    )
13696    .await;
13697
13698    cx.set_state("fn main() { let a = 2ˇ; }");
13699    cx.simulate_keystroke(".");
13700
13701    let unresolved_item_1 = lsp::CompletionItem {
13702        label: "id".to_string(),
13703        filter_text: Some("id".to_string()),
13704        detail: None,
13705        documentation: None,
13706        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13707            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13708            new_text: ".id".to_string(),
13709        })),
13710        ..lsp::CompletionItem::default()
13711    };
13712    let resolved_item_1 = lsp::CompletionItem {
13713        additional_text_edits: Some(vec![lsp::TextEdit {
13714            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13715            new_text: "!!".to_string(),
13716        }]),
13717        ..unresolved_item_1.clone()
13718    };
13719    let unresolved_item_2 = lsp::CompletionItem {
13720        label: "other".to_string(),
13721        filter_text: Some("other".to_string()),
13722        detail: None,
13723        documentation: None,
13724        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13725            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13726            new_text: ".other".to_string(),
13727        })),
13728        ..lsp::CompletionItem::default()
13729    };
13730    let resolved_item_2 = lsp::CompletionItem {
13731        additional_text_edits: Some(vec![lsp::TextEdit {
13732            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13733            new_text: "??".to_string(),
13734        }]),
13735        ..unresolved_item_2.clone()
13736    };
13737
13738    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13739    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13740    cx.lsp
13741        .server
13742        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13743            let unresolved_item_1 = unresolved_item_1.clone();
13744            let resolved_item_1 = resolved_item_1.clone();
13745            let unresolved_item_2 = unresolved_item_2.clone();
13746            let resolved_item_2 = resolved_item_2.clone();
13747            let resolve_requests_1 = resolve_requests_1.clone();
13748            let resolve_requests_2 = resolve_requests_2.clone();
13749            move |unresolved_request, _| {
13750                let unresolved_item_1 = unresolved_item_1.clone();
13751                let resolved_item_1 = resolved_item_1.clone();
13752                let unresolved_item_2 = unresolved_item_2.clone();
13753                let resolved_item_2 = resolved_item_2.clone();
13754                let resolve_requests_1 = resolve_requests_1.clone();
13755                let resolve_requests_2 = resolve_requests_2.clone();
13756                async move {
13757                    if unresolved_request == unresolved_item_1 {
13758                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13759                        Ok(resolved_item_1.clone())
13760                    } else if unresolved_request == unresolved_item_2 {
13761                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13762                        Ok(resolved_item_2.clone())
13763                    } else {
13764                        panic!("Unexpected completion item {unresolved_request:?}")
13765                    }
13766                }
13767            }
13768        })
13769        .detach();
13770
13771    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13772        let unresolved_item_1 = unresolved_item_1.clone();
13773        let unresolved_item_2 = unresolved_item_2.clone();
13774        async move {
13775            Ok(Some(lsp::CompletionResponse::Array(vec![
13776                unresolved_item_1,
13777                unresolved_item_2,
13778            ])))
13779        }
13780    })
13781    .next()
13782    .await;
13783
13784    cx.condition(|editor, _| editor.context_menu_visible())
13785        .await;
13786    cx.update_editor(|editor, _, _| {
13787        let context_menu = editor.context_menu.borrow_mut();
13788        let context_menu = context_menu
13789            .as_ref()
13790            .expect("Should have the context menu deployed");
13791        match context_menu {
13792            CodeContextMenu::Completions(completions_menu) => {
13793                let completions = completions_menu.completions.borrow_mut();
13794                assert_eq!(
13795                    completions
13796                        .iter()
13797                        .map(|completion| &completion.label.text)
13798                        .collect::<Vec<_>>(),
13799                    vec!["id", "other"]
13800                )
13801            }
13802            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13803        }
13804    });
13805    cx.run_until_parked();
13806
13807    cx.update_editor(|editor, window, cx| {
13808        editor.context_menu_next(&ContextMenuNext, window, cx);
13809    });
13810    cx.run_until_parked();
13811    cx.update_editor(|editor, window, cx| {
13812        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13813    });
13814    cx.run_until_parked();
13815    cx.update_editor(|editor, window, cx| {
13816        editor.context_menu_next(&ContextMenuNext, window, cx);
13817    });
13818    cx.run_until_parked();
13819    cx.update_editor(|editor, window, cx| {
13820        editor
13821            .compose_completion(&ComposeCompletion::default(), window, cx)
13822            .expect("No task returned")
13823    })
13824    .await
13825    .expect("Completion failed");
13826    cx.run_until_parked();
13827
13828    cx.update_editor(|editor, _, cx| {
13829        assert_eq!(
13830            resolve_requests_1.load(atomic::Ordering::Acquire),
13831            1,
13832            "Should always resolve once despite multiple selections"
13833        );
13834        assert_eq!(
13835            resolve_requests_2.load(atomic::Ordering::Acquire),
13836            1,
13837            "Should always resolve once after multiple selections and applying the completion"
13838        );
13839        assert_eq!(
13840            editor.text(cx),
13841            "fn main() { let a = ??.other; }",
13842            "Should use resolved data when applying the completion"
13843        );
13844    });
13845}
13846
13847#[gpui::test]
13848async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13849    init_test(cx, |_| {});
13850
13851    let item_0 = lsp::CompletionItem {
13852        label: "abs".into(),
13853        insert_text: Some("abs".into()),
13854        data: Some(json!({ "very": "special"})),
13855        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13856        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13857            lsp::InsertReplaceEdit {
13858                new_text: "abs".to_string(),
13859                insert: lsp::Range::default(),
13860                replace: lsp::Range::default(),
13861            },
13862        )),
13863        ..lsp::CompletionItem::default()
13864    };
13865    let items = iter::once(item_0.clone())
13866        .chain((11..51).map(|i| lsp::CompletionItem {
13867            label: format!("item_{}", i),
13868            insert_text: Some(format!("item_{}", i)),
13869            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13870            ..lsp::CompletionItem::default()
13871        }))
13872        .collect::<Vec<_>>();
13873
13874    let default_commit_characters = vec!["?".to_string()];
13875    let default_data = json!({ "default": "data"});
13876    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13877    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13878    let default_edit_range = lsp::Range {
13879        start: lsp::Position {
13880            line: 0,
13881            character: 5,
13882        },
13883        end: lsp::Position {
13884            line: 0,
13885            character: 5,
13886        },
13887    };
13888
13889    let mut cx = EditorLspTestContext::new_rust(
13890        lsp::ServerCapabilities {
13891            completion_provider: Some(lsp::CompletionOptions {
13892                trigger_characters: Some(vec![".".to_string()]),
13893                resolve_provider: Some(true),
13894                ..Default::default()
13895            }),
13896            ..Default::default()
13897        },
13898        cx,
13899    )
13900    .await;
13901
13902    cx.set_state("fn main() { let a = 2ˇ; }");
13903    cx.simulate_keystroke(".");
13904
13905    let completion_data = default_data.clone();
13906    let completion_characters = default_commit_characters.clone();
13907    let completion_items = items.clone();
13908    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13909        let default_data = completion_data.clone();
13910        let default_commit_characters = completion_characters.clone();
13911        let items = completion_items.clone();
13912        async move {
13913            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13914                items,
13915                item_defaults: Some(lsp::CompletionListItemDefaults {
13916                    data: Some(default_data.clone()),
13917                    commit_characters: Some(default_commit_characters.clone()),
13918                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13919                        default_edit_range,
13920                    )),
13921                    insert_text_format: Some(default_insert_text_format),
13922                    insert_text_mode: Some(default_insert_text_mode),
13923                }),
13924                ..lsp::CompletionList::default()
13925            })))
13926        }
13927    })
13928    .next()
13929    .await;
13930
13931    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13932    cx.lsp
13933        .server
13934        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13935            let closure_resolved_items = resolved_items.clone();
13936            move |item_to_resolve, _| {
13937                let closure_resolved_items = closure_resolved_items.clone();
13938                async move {
13939                    closure_resolved_items.lock().push(item_to_resolve.clone());
13940                    Ok(item_to_resolve)
13941                }
13942            }
13943        })
13944        .detach();
13945
13946    cx.condition(|editor, _| editor.context_menu_visible())
13947        .await;
13948    cx.run_until_parked();
13949    cx.update_editor(|editor, _, _| {
13950        let menu = editor.context_menu.borrow_mut();
13951        match menu.as_ref().expect("should have the completions menu") {
13952            CodeContextMenu::Completions(completions_menu) => {
13953                assert_eq!(
13954                    completions_menu
13955                        .entries
13956                        .borrow()
13957                        .iter()
13958                        .map(|mat| mat.string.clone())
13959                        .collect::<Vec<String>>(),
13960                    items
13961                        .iter()
13962                        .map(|completion| completion.label.clone())
13963                        .collect::<Vec<String>>()
13964                );
13965            }
13966            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13967        }
13968    });
13969    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13970    // with 4 from the end.
13971    assert_eq!(
13972        *resolved_items.lock(),
13973        [&items[0..16], &items[items.len() - 4..items.len()]]
13974            .concat()
13975            .iter()
13976            .cloned()
13977            .map(|mut item| {
13978                if item.data.is_none() {
13979                    item.data = Some(default_data.clone());
13980                }
13981                item
13982            })
13983            .collect::<Vec<lsp::CompletionItem>>(),
13984        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13985    );
13986    resolved_items.lock().clear();
13987
13988    cx.update_editor(|editor, window, cx| {
13989        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13990    });
13991    cx.run_until_parked();
13992    // Completions that have already been resolved are skipped.
13993    assert_eq!(
13994        *resolved_items.lock(),
13995        items[items.len() - 16..items.len() - 4]
13996            .iter()
13997            .cloned()
13998            .map(|mut item| {
13999                if item.data.is_none() {
14000                    item.data = Some(default_data.clone());
14001                }
14002                item
14003            })
14004            .collect::<Vec<lsp::CompletionItem>>()
14005    );
14006    resolved_items.lock().clear();
14007}
14008
14009#[gpui::test]
14010async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14011    init_test(cx, |_| {});
14012
14013    let mut cx = EditorLspTestContext::new(
14014        Language::new(
14015            LanguageConfig {
14016                matcher: LanguageMatcher {
14017                    path_suffixes: vec!["jsx".into()],
14018                    ..Default::default()
14019                },
14020                overrides: [(
14021                    "element".into(),
14022                    LanguageConfigOverride {
14023                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14024                        ..Default::default()
14025                    },
14026                )]
14027                .into_iter()
14028                .collect(),
14029                ..Default::default()
14030            },
14031            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14032        )
14033        .with_override_query("(jsx_self_closing_element) @element")
14034        .unwrap(),
14035        lsp::ServerCapabilities {
14036            completion_provider: Some(lsp::CompletionOptions {
14037                trigger_characters: Some(vec![":".to_string()]),
14038                ..Default::default()
14039            }),
14040            ..Default::default()
14041        },
14042        cx,
14043    )
14044    .await;
14045
14046    cx.lsp
14047        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14048            Ok(Some(lsp::CompletionResponse::Array(vec![
14049                lsp::CompletionItem {
14050                    label: "bg-blue".into(),
14051                    ..Default::default()
14052                },
14053                lsp::CompletionItem {
14054                    label: "bg-red".into(),
14055                    ..Default::default()
14056                },
14057                lsp::CompletionItem {
14058                    label: "bg-yellow".into(),
14059                    ..Default::default()
14060                },
14061            ])))
14062        });
14063
14064    cx.set_state(r#"<p class="bgˇ" />"#);
14065
14066    // Trigger completion when typing a dash, because the dash is an extra
14067    // word character in the 'element' scope, which contains the cursor.
14068    cx.simulate_keystroke("-");
14069    cx.executor().run_until_parked();
14070    cx.update_editor(|editor, _, _| {
14071        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14072        {
14073            assert_eq!(
14074                completion_menu_entries(&menu),
14075                &["bg-red", "bg-blue", "bg-yellow"]
14076            );
14077        } else {
14078            panic!("expected completion menu to be open");
14079        }
14080    });
14081
14082    cx.simulate_keystroke("l");
14083    cx.executor().run_until_parked();
14084    cx.update_editor(|editor, _, _| {
14085        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14086        {
14087            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14088        } else {
14089            panic!("expected completion menu to be open");
14090        }
14091    });
14092
14093    // When filtering completions, consider the character after the '-' to
14094    // be the start of a subword.
14095    cx.set_state(r#"<p class="yelˇ" />"#);
14096    cx.simulate_keystroke("l");
14097    cx.executor().run_until_parked();
14098    cx.update_editor(|editor, _, _| {
14099        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14100        {
14101            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14102        } else {
14103            panic!("expected completion menu to be open");
14104        }
14105    });
14106}
14107
14108fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14109    let entries = menu.entries.borrow();
14110    entries.iter().map(|mat| mat.string.clone()).collect()
14111}
14112
14113#[gpui::test]
14114async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14115    init_test(cx, |settings| {
14116        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14117            FormatterList(vec![Formatter::Prettier].into()),
14118        ))
14119    });
14120
14121    let fs = FakeFs::new(cx.executor());
14122    fs.insert_file(path!("/file.ts"), Default::default()).await;
14123
14124    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14125    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14126
14127    language_registry.add(Arc::new(Language::new(
14128        LanguageConfig {
14129            name: "TypeScript".into(),
14130            matcher: LanguageMatcher {
14131                path_suffixes: vec!["ts".to_string()],
14132                ..Default::default()
14133            },
14134            ..Default::default()
14135        },
14136        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14137    )));
14138    update_test_language_settings(cx, |settings| {
14139        settings.defaults.prettier = Some(PrettierSettings {
14140            allowed: true,
14141            ..PrettierSettings::default()
14142        });
14143    });
14144
14145    let test_plugin = "test_plugin";
14146    let _ = language_registry.register_fake_lsp(
14147        "TypeScript",
14148        FakeLspAdapter {
14149            prettier_plugins: vec![test_plugin],
14150            ..Default::default()
14151        },
14152    );
14153
14154    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14155    let buffer = project
14156        .update(cx, |project, cx| {
14157            project.open_local_buffer(path!("/file.ts"), cx)
14158        })
14159        .await
14160        .unwrap();
14161
14162    let buffer_text = "one\ntwo\nthree\n";
14163    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14164    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14165    editor.update_in(cx, |editor, window, cx| {
14166        editor.set_text(buffer_text, window, cx)
14167    });
14168
14169    editor
14170        .update_in(cx, |editor, window, cx| {
14171            editor.perform_format(
14172                project.clone(),
14173                FormatTrigger::Manual,
14174                FormatTarget::Buffers,
14175                window,
14176                cx,
14177            )
14178        })
14179        .unwrap()
14180        .await;
14181    assert_eq!(
14182        editor.update(cx, |editor, cx| editor.text(cx)),
14183        buffer_text.to_string() + prettier_format_suffix,
14184        "Test prettier formatting was not applied to the original buffer text",
14185    );
14186
14187    update_test_language_settings(cx, |settings| {
14188        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14189    });
14190    let format = editor.update_in(cx, |editor, window, cx| {
14191        editor.perform_format(
14192            project.clone(),
14193            FormatTrigger::Manual,
14194            FormatTarget::Buffers,
14195            window,
14196            cx,
14197        )
14198    });
14199    format.await.unwrap();
14200    assert_eq!(
14201        editor.update(cx, |editor, cx| editor.text(cx)),
14202        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14203        "Autoformatting (via test prettier) was not applied to the original buffer text",
14204    );
14205}
14206
14207#[gpui::test]
14208async fn test_addition_reverts(cx: &mut TestAppContext) {
14209    init_test(cx, |_| {});
14210    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14211    let base_text = indoc! {r#"
14212        struct Row;
14213        struct Row1;
14214        struct Row2;
14215
14216        struct Row4;
14217        struct Row5;
14218        struct Row6;
14219
14220        struct Row8;
14221        struct Row9;
14222        struct Row10;"#};
14223
14224    // When addition hunks are not adjacent to carets, no hunk revert is performed
14225    assert_hunk_revert(
14226        indoc! {r#"struct Row;
14227                   struct Row1;
14228                   struct Row1.1;
14229                   struct Row1.2;
14230                   struct Row2;ˇ
14231
14232                   struct Row4;
14233                   struct Row5;
14234                   struct Row6;
14235
14236                   struct Row8;
14237                   ˇstruct Row9;
14238                   struct Row9.1;
14239                   struct Row9.2;
14240                   struct Row9.3;
14241                   struct Row10;"#},
14242        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14243        indoc! {r#"struct Row;
14244                   struct Row1;
14245                   struct Row1.1;
14246                   struct Row1.2;
14247                   struct Row2;ˇ
14248
14249                   struct Row4;
14250                   struct Row5;
14251                   struct Row6;
14252
14253                   struct Row8;
14254                   ˇstruct Row9;
14255                   struct Row9.1;
14256                   struct Row9.2;
14257                   struct Row9.3;
14258                   struct Row10;"#},
14259        base_text,
14260        &mut cx,
14261    );
14262    // Same for selections
14263    assert_hunk_revert(
14264        indoc! {r#"struct Row;
14265                   struct Row1;
14266                   struct Row2;
14267                   struct Row2.1;
14268                   struct Row2.2;
14269                   «ˇ
14270                   struct Row4;
14271                   struct» Row5;
14272                   «struct Row6;
14273                   ˇ»
14274                   struct Row9.1;
14275                   struct Row9.2;
14276                   struct Row9.3;
14277                   struct Row8;
14278                   struct Row9;
14279                   struct Row10;"#},
14280        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14281        indoc! {r#"struct Row;
14282                   struct Row1;
14283                   struct Row2;
14284                   struct Row2.1;
14285                   struct Row2.2;
14286                   «ˇ
14287                   struct Row4;
14288                   struct» Row5;
14289                   «struct Row6;
14290                   ˇ»
14291                   struct Row9.1;
14292                   struct Row9.2;
14293                   struct Row9.3;
14294                   struct Row8;
14295                   struct Row9;
14296                   struct Row10;"#},
14297        base_text,
14298        &mut cx,
14299    );
14300
14301    // When carets and selections intersect the addition hunks, those are reverted.
14302    // Adjacent carets got merged.
14303    assert_hunk_revert(
14304        indoc! {r#"struct Row;
14305                   ˇ// something on the top
14306                   struct Row1;
14307                   struct Row2;
14308                   struct Roˇw3.1;
14309                   struct Row2.2;
14310                   struct Row2.3;ˇ
14311
14312                   struct Row4;
14313                   struct ˇRow5.1;
14314                   struct Row5.2;
14315                   struct «Rowˇ»5.3;
14316                   struct Row5;
14317                   struct Row6;
14318                   ˇ
14319                   struct Row9.1;
14320                   struct «Rowˇ»9.2;
14321                   struct «ˇRow»9.3;
14322                   struct Row8;
14323                   struct Row9;
14324                   «ˇ// something on bottom»
14325                   struct Row10;"#},
14326        vec![
14327            DiffHunkStatusKind::Added,
14328            DiffHunkStatusKind::Added,
14329            DiffHunkStatusKind::Added,
14330            DiffHunkStatusKind::Added,
14331            DiffHunkStatusKind::Added,
14332        ],
14333        indoc! {r#"struct Row;
14334                   ˇstruct Row1;
14335                   struct Row2;
14336                   ˇ
14337                   struct Row4;
14338                   ˇstruct Row5;
14339                   struct Row6;
14340                   ˇ
14341                   ˇstruct Row8;
14342                   struct Row9;
14343                   ˇstruct Row10;"#},
14344        base_text,
14345        &mut cx,
14346    );
14347}
14348
14349#[gpui::test]
14350async fn test_modification_reverts(cx: &mut TestAppContext) {
14351    init_test(cx, |_| {});
14352    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14353    let base_text = indoc! {r#"
14354        struct Row;
14355        struct Row1;
14356        struct Row2;
14357
14358        struct Row4;
14359        struct Row5;
14360        struct Row6;
14361
14362        struct Row8;
14363        struct Row9;
14364        struct Row10;"#};
14365
14366    // Modification hunks behave the same as the addition ones.
14367    assert_hunk_revert(
14368        indoc! {r#"struct Row;
14369                   struct Row1;
14370                   struct Row33;
14371                   ˇ
14372                   struct Row4;
14373                   struct Row5;
14374                   struct Row6;
14375                   ˇ
14376                   struct Row99;
14377                   struct Row9;
14378                   struct Row10;"#},
14379        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14380        indoc! {r#"struct Row;
14381                   struct Row1;
14382                   struct Row33;
14383                   ˇ
14384                   struct Row4;
14385                   struct Row5;
14386                   struct Row6;
14387                   ˇ
14388                   struct Row99;
14389                   struct Row9;
14390                   struct Row10;"#},
14391        base_text,
14392        &mut cx,
14393    );
14394    assert_hunk_revert(
14395        indoc! {r#"struct Row;
14396                   struct Row1;
14397                   struct Row33;
14398                   «ˇ
14399                   struct Row4;
14400                   struct» Row5;
14401                   «struct Row6;
14402                   ˇ»
14403                   struct Row99;
14404                   struct Row9;
14405                   struct Row10;"#},
14406        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14407        indoc! {r#"struct Row;
14408                   struct Row1;
14409                   struct Row33;
14410                   «ˇ
14411                   struct Row4;
14412                   struct» Row5;
14413                   «struct Row6;
14414                   ˇ»
14415                   struct Row99;
14416                   struct Row9;
14417                   struct Row10;"#},
14418        base_text,
14419        &mut cx,
14420    );
14421
14422    assert_hunk_revert(
14423        indoc! {r#"ˇstruct Row1.1;
14424                   struct Row1;
14425                   «ˇstr»uct Row22;
14426
14427                   struct ˇRow44;
14428                   struct Row5;
14429                   struct «Rˇ»ow66;ˇ
14430
14431                   «struˇ»ct Row88;
14432                   struct Row9;
14433                   struct Row1011;ˇ"#},
14434        vec![
14435            DiffHunkStatusKind::Modified,
14436            DiffHunkStatusKind::Modified,
14437            DiffHunkStatusKind::Modified,
14438            DiffHunkStatusKind::Modified,
14439            DiffHunkStatusKind::Modified,
14440            DiffHunkStatusKind::Modified,
14441        ],
14442        indoc! {r#"struct Row;
14443                   ˇstruct Row1;
14444                   struct Row2;
14445                   ˇ
14446                   struct Row4;
14447                   ˇstruct Row5;
14448                   struct Row6;
14449                   ˇ
14450                   struct Row8;
14451                   ˇstruct Row9;
14452                   struct Row10;ˇ"#},
14453        base_text,
14454        &mut cx,
14455    );
14456}
14457
14458#[gpui::test]
14459async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14460    init_test(cx, |_| {});
14461    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14462    let base_text = indoc! {r#"
14463        one
14464
14465        two
14466        three
14467        "#};
14468
14469    cx.set_head_text(base_text);
14470    cx.set_state("\nˇ\n");
14471    cx.executor().run_until_parked();
14472    cx.update_editor(|editor, _window, cx| {
14473        editor.expand_selected_diff_hunks(cx);
14474    });
14475    cx.executor().run_until_parked();
14476    cx.update_editor(|editor, window, cx| {
14477        editor.backspace(&Default::default(), window, cx);
14478    });
14479    cx.run_until_parked();
14480    cx.assert_state_with_diff(
14481        indoc! {r#"
14482
14483        - two
14484        - threeˇ
14485        +
14486        "#}
14487        .to_string(),
14488    );
14489}
14490
14491#[gpui::test]
14492async fn test_deletion_reverts(cx: &mut TestAppContext) {
14493    init_test(cx, |_| {});
14494    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14495    let base_text = indoc! {r#"struct Row;
14496struct Row1;
14497struct Row2;
14498
14499struct Row4;
14500struct Row5;
14501struct Row6;
14502
14503struct Row8;
14504struct Row9;
14505struct Row10;"#};
14506
14507    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14508    assert_hunk_revert(
14509        indoc! {r#"struct Row;
14510                   struct Row2;
14511
14512                   ˇstruct Row4;
14513                   struct Row5;
14514                   struct Row6;
14515                   ˇ
14516                   struct Row8;
14517                   struct Row10;"#},
14518        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14519        indoc! {r#"struct Row;
14520                   struct Row2;
14521
14522                   ˇstruct Row4;
14523                   struct Row5;
14524                   struct Row6;
14525                   ˇ
14526                   struct Row8;
14527                   struct Row10;"#},
14528        base_text,
14529        &mut cx,
14530    );
14531    assert_hunk_revert(
14532        indoc! {r#"struct Row;
14533                   struct Row2;
14534
14535                   «ˇstruct Row4;
14536                   struct» Row5;
14537                   «struct Row6;
14538                   ˇ»
14539                   struct Row8;
14540                   struct Row10;"#},
14541        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14542        indoc! {r#"struct Row;
14543                   struct Row2;
14544
14545                   «ˇstruct Row4;
14546                   struct» Row5;
14547                   «struct Row6;
14548                   ˇ»
14549                   struct Row8;
14550                   struct Row10;"#},
14551        base_text,
14552        &mut cx,
14553    );
14554
14555    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14556    assert_hunk_revert(
14557        indoc! {r#"struct Row;
14558                   ˇstruct Row2;
14559
14560                   struct Row4;
14561                   struct Row5;
14562                   struct Row6;
14563
14564                   struct Row8;ˇ
14565                   struct Row10;"#},
14566        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14567        indoc! {r#"struct Row;
14568                   struct Row1;
14569                   ˇstruct Row2;
14570
14571                   struct Row4;
14572                   struct Row5;
14573                   struct Row6;
14574
14575                   struct Row8;ˇ
14576                   struct Row9;
14577                   struct Row10;"#},
14578        base_text,
14579        &mut cx,
14580    );
14581    assert_hunk_revert(
14582        indoc! {r#"struct Row;
14583                   struct Row2«ˇ;
14584                   struct Row4;
14585                   struct» Row5;
14586                   «struct Row6;
14587
14588                   struct Row8;ˇ»
14589                   struct Row10;"#},
14590        vec![
14591            DiffHunkStatusKind::Deleted,
14592            DiffHunkStatusKind::Deleted,
14593            DiffHunkStatusKind::Deleted,
14594        ],
14595        indoc! {r#"struct Row;
14596                   struct Row1;
14597                   struct Row2«ˇ;
14598
14599                   struct Row4;
14600                   struct» Row5;
14601                   «struct Row6;
14602
14603                   struct Row8;ˇ»
14604                   struct Row9;
14605                   struct Row10;"#},
14606        base_text,
14607        &mut cx,
14608    );
14609}
14610
14611#[gpui::test]
14612async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14613    init_test(cx, |_| {});
14614
14615    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14616    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14617    let base_text_3 =
14618        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14619
14620    let text_1 = edit_first_char_of_every_line(base_text_1);
14621    let text_2 = edit_first_char_of_every_line(base_text_2);
14622    let text_3 = edit_first_char_of_every_line(base_text_3);
14623
14624    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14625    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14626    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14627
14628    let multibuffer = cx.new(|cx| {
14629        let mut multibuffer = MultiBuffer::new(ReadWrite);
14630        multibuffer.push_excerpts(
14631            buffer_1.clone(),
14632            [
14633                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14634                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14635                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14636            ],
14637            cx,
14638        );
14639        multibuffer.push_excerpts(
14640            buffer_2.clone(),
14641            [
14642                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14643                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14644                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14645            ],
14646            cx,
14647        );
14648        multibuffer.push_excerpts(
14649            buffer_3.clone(),
14650            [
14651                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14652                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14653                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14654            ],
14655            cx,
14656        );
14657        multibuffer
14658    });
14659
14660    let fs = FakeFs::new(cx.executor());
14661    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14662    let (editor, cx) = cx
14663        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14664    editor.update_in(cx, |editor, _window, cx| {
14665        for (buffer, diff_base) in [
14666            (buffer_1.clone(), base_text_1),
14667            (buffer_2.clone(), base_text_2),
14668            (buffer_3.clone(), base_text_3),
14669        ] {
14670            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14671            editor
14672                .buffer
14673                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14674        }
14675    });
14676    cx.executor().run_until_parked();
14677
14678    editor.update_in(cx, |editor, window, cx| {
14679        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}");
14680        editor.select_all(&SelectAll, window, cx);
14681        editor.git_restore(&Default::default(), window, cx);
14682    });
14683    cx.executor().run_until_parked();
14684
14685    // When all ranges are selected, all buffer hunks are reverted.
14686    editor.update(cx, |editor, cx| {
14687        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");
14688    });
14689    buffer_1.update(cx, |buffer, _| {
14690        assert_eq!(buffer.text(), base_text_1);
14691    });
14692    buffer_2.update(cx, |buffer, _| {
14693        assert_eq!(buffer.text(), base_text_2);
14694    });
14695    buffer_3.update(cx, |buffer, _| {
14696        assert_eq!(buffer.text(), base_text_3);
14697    });
14698
14699    editor.update_in(cx, |editor, window, cx| {
14700        editor.undo(&Default::default(), window, cx);
14701    });
14702
14703    editor.update_in(cx, |editor, window, cx| {
14704        editor.change_selections(None, window, cx, |s| {
14705            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14706        });
14707        editor.git_restore(&Default::default(), window, cx);
14708    });
14709
14710    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14711    // but not affect buffer_2 and its related excerpts.
14712    editor.update(cx, |editor, cx| {
14713        assert_eq!(
14714            editor.text(cx),
14715            "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}"
14716        );
14717    });
14718    buffer_1.update(cx, |buffer, _| {
14719        assert_eq!(buffer.text(), base_text_1);
14720    });
14721    buffer_2.update(cx, |buffer, _| {
14722        assert_eq!(
14723            buffer.text(),
14724            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14725        );
14726    });
14727    buffer_3.update(cx, |buffer, _| {
14728        assert_eq!(
14729            buffer.text(),
14730            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14731        );
14732    });
14733
14734    fn edit_first_char_of_every_line(text: &str) -> String {
14735        text.split('\n')
14736            .map(|line| format!("X{}", &line[1..]))
14737            .collect::<Vec<_>>()
14738            .join("\n")
14739    }
14740}
14741
14742#[gpui::test]
14743async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14744    init_test(cx, |_| {});
14745
14746    let cols = 4;
14747    let rows = 10;
14748    let sample_text_1 = sample_text(rows, cols, 'a');
14749    assert_eq!(
14750        sample_text_1,
14751        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14752    );
14753    let sample_text_2 = sample_text(rows, cols, 'l');
14754    assert_eq!(
14755        sample_text_2,
14756        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14757    );
14758    let sample_text_3 = sample_text(rows, cols, 'v');
14759    assert_eq!(
14760        sample_text_3,
14761        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14762    );
14763
14764    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14765    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14766    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14767
14768    let multi_buffer = cx.new(|cx| {
14769        let mut multibuffer = MultiBuffer::new(ReadWrite);
14770        multibuffer.push_excerpts(
14771            buffer_1.clone(),
14772            [
14773                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14774                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14775                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14776            ],
14777            cx,
14778        );
14779        multibuffer.push_excerpts(
14780            buffer_2.clone(),
14781            [
14782                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14783                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14784                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14785            ],
14786            cx,
14787        );
14788        multibuffer.push_excerpts(
14789            buffer_3.clone(),
14790            [
14791                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14792                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14793                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14794            ],
14795            cx,
14796        );
14797        multibuffer
14798    });
14799
14800    let fs = FakeFs::new(cx.executor());
14801    fs.insert_tree(
14802        "/a",
14803        json!({
14804            "main.rs": sample_text_1,
14805            "other.rs": sample_text_2,
14806            "lib.rs": sample_text_3,
14807        }),
14808    )
14809    .await;
14810    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14811    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14812    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14813    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14814        Editor::new(
14815            EditorMode::full(),
14816            multi_buffer,
14817            Some(project.clone()),
14818            window,
14819            cx,
14820        )
14821    });
14822    let multibuffer_item_id = workspace
14823        .update(cx, |workspace, window, cx| {
14824            assert!(
14825                workspace.active_item(cx).is_none(),
14826                "active item should be None before the first item is added"
14827            );
14828            workspace.add_item_to_active_pane(
14829                Box::new(multi_buffer_editor.clone()),
14830                None,
14831                true,
14832                window,
14833                cx,
14834            );
14835            let active_item = workspace
14836                .active_item(cx)
14837                .expect("should have an active item after adding the multi buffer");
14838            assert!(
14839                !active_item.is_singleton(cx),
14840                "A multi buffer was expected to active after adding"
14841            );
14842            active_item.item_id()
14843        })
14844        .unwrap();
14845    cx.executor().run_until_parked();
14846
14847    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14848        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14849            s.select_ranges(Some(1..2))
14850        });
14851        editor.open_excerpts(&OpenExcerpts, window, cx);
14852    });
14853    cx.executor().run_until_parked();
14854    let first_item_id = workspace
14855        .update(cx, |workspace, window, cx| {
14856            let active_item = workspace
14857                .active_item(cx)
14858                .expect("should have an active item after navigating into the 1st buffer");
14859            let first_item_id = active_item.item_id();
14860            assert_ne!(
14861                first_item_id, multibuffer_item_id,
14862                "Should navigate into the 1st buffer and activate it"
14863            );
14864            assert!(
14865                active_item.is_singleton(cx),
14866                "New active item should be a singleton buffer"
14867            );
14868            assert_eq!(
14869                active_item
14870                    .act_as::<Editor>(cx)
14871                    .expect("should have navigated into an editor for the 1st buffer")
14872                    .read(cx)
14873                    .text(cx),
14874                sample_text_1
14875            );
14876
14877            workspace
14878                .go_back(workspace.active_pane().downgrade(), window, cx)
14879                .detach_and_log_err(cx);
14880
14881            first_item_id
14882        })
14883        .unwrap();
14884    cx.executor().run_until_parked();
14885    workspace
14886        .update(cx, |workspace, _, cx| {
14887            let active_item = workspace
14888                .active_item(cx)
14889                .expect("should have an active item after navigating back");
14890            assert_eq!(
14891                active_item.item_id(),
14892                multibuffer_item_id,
14893                "Should navigate back to the multi buffer"
14894            );
14895            assert!(!active_item.is_singleton(cx));
14896        })
14897        .unwrap();
14898
14899    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14900        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14901            s.select_ranges(Some(39..40))
14902        });
14903        editor.open_excerpts(&OpenExcerpts, window, cx);
14904    });
14905    cx.executor().run_until_parked();
14906    let second_item_id = workspace
14907        .update(cx, |workspace, window, cx| {
14908            let active_item = workspace
14909                .active_item(cx)
14910                .expect("should have an active item after navigating into the 2nd buffer");
14911            let second_item_id = active_item.item_id();
14912            assert_ne!(
14913                second_item_id, multibuffer_item_id,
14914                "Should navigate away from the multibuffer"
14915            );
14916            assert_ne!(
14917                second_item_id, first_item_id,
14918                "Should navigate into the 2nd buffer and activate it"
14919            );
14920            assert!(
14921                active_item.is_singleton(cx),
14922                "New active item should be a singleton buffer"
14923            );
14924            assert_eq!(
14925                active_item
14926                    .act_as::<Editor>(cx)
14927                    .expect("should have navigated into an editor")
14928                    .read(cx)
14929                    .text(cx),
14930                sample_text_2
14931            );
14932
14933            workspace
14934                .go_back(workspace.active_pane().downgrade(), window, cx)
14935                .detach_and_log_err(cx);
14936
14937            second_item_id
14938        })
14939        .unwrap();
14940    cx.executor().run_until_parked();
14941    workspace
14942        .update(cx, |workspace, _, cx| {
14943            let active_item = workspace
14944                .active_item(cx)
14945                .expect("should have an active item after navigating back from the 2nd buffer");
14946            assert_eq!(
14947                active_item.item_id(),
14948                multibuffer_item_id,
14949                "Should navigate back from the 2nd buffer to the multi buffer"
14950            );
14951            assert!(!active_item.is_singleton(cx));
14952        })
14953        .unwrap();
14954
14955    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14956        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14957            s.select_ranges(Some(70..70))
14958        });
14959        editor.open_excerpts(&OpenExcerpts, window, cx);
14960    });
14961    cx.executor().run_until_parked();
14962    workspace
14963        .update(cx, |workspace, window, cx| {
14964            let active_item = workspace
14965                .active_item(cx)
14966                .expect("should have an active item after navigating into the 3rd buffer");
14967            let third_item_id = active_item.item_id();
14968            assert_ne!(
14969                third_item_id, multibuffer_item_id,
14970                "Should navigate into the 3rd buffer and activate it"
14971            );
14972            assert_ne!(third_item_id, first_item_id);
14973            assert_ne!(third_item_id, second_item_id);
14974            assert!(
14975                active_item.is_singleton(cx),
14976                "New active item should be a singleton buffer"
14977            );
14978            assert_eq!(
14979                active_item
14980                    .act_as::<Editor>(cx)
14981                    .expect("should have navigated into an editor")
14982                    .read(cx)
14983                    .text(cx),
14984                sample_text_3
14985            );
14986
14987            workspace
14988                .go_back(workspace.active_pane().downgrade(), window, cx)
14989                .detach_and_log_err(cx);
14990        })
14991        .unwrap();
14992    cx.executor().run_until_parked();
14993    workspace
14994        .update(cx, |workspace, _, cx| {
14995            let active_item = workspace
14996                .active_item(cx)
14997                .expect("should have an active item after navigating back from the 3rd buffer");
14998            assert_eq!(
14999                active_item.item_id(),
15000                multibuffer_item_id,
15001                "Should navigate back from the 3rd buffer to the multi buffer"
15002            );
15003            assert!(!active_item.is_singleton(cx));
15004        })
15005        .unwrap();
15006}
15007
15008#[gpui::test]
15009async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15010    init_test(cx, |_| {});
15011
15012    let mut cx = EditorTestContext::new(cx).await;
15013
15014    let diff_base = r#"
15015        use some::mod;
15016
15017        const A: u32 = 42;
15018
15019        fn main() {
15020            println!("hello");
15021
15022            println!("world");
15023        }
15024        "#
15025    .unindent();
15026
15027    cx.set_state(
15028        &r#"
15029        use some::modified;
15030
15031        ˇ
15032        fn main() {
15033            println!("hello there");
15034
15035            println!("around the");
15036            println!("world");
15037        }
15038        "#
15039        .unindent(),
15040    );
15041
15042    cx.set_head_text(&diff_base);
15043    executor.run_until_parked();
15044
15045    cx.update_editor(|editor, window, cx| {
15046        editor.go_to_next_hunk(&GoToHunk, window, cx);
15047        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15048    });
15049    executor.run_until_parked();
15050    cx.assert_state_with_diff(
15051        r#"
15052          use some::modified;
15053
15054
15055          fn main() {
15056        -     println!("hello");
15057        + ˇ    println!("hello there");
15058
15059              println!("around the");
15060              println!("world");
15061          }
15062        "#
15063        .unindent(),
15064    );
15065
15066    cx.update_editor(|editor, window, cx| {
15067        for _ in 0..2 {
15068            editor.go_to_next_hunk(&GoToHunk, window, cx);
15069            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15070        }
15071    });
15072    executor.run_until_parked();
15073    cx.assert_state_with_diff(
15074        r#"
15075        - use some::mod;
15076        + ˇuse some::modified;
15077
15078
15079          fn main() {
15080        -     println!("hello");
15081        +     println!("hello there");
15082
15083        +     println!("around the");
15084              println!("world");
15085          }
15086        "#
15087        .unindent(),
15088    );
15089
15090    cx.update_editor(|editor, window, cx| {
15091        editor.go_to_next_hunk(&GoToHunk, window, cx);
15092        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15093    });
15094    executor.run_until_parked();
15095    cx.assert_state_with_diff(
15096        r#"
15097        - use some::mod;
15098        + use some::modified;
15099
15100        - const A: u32 = 42;
15101          ˇ
15102          fn main() {
15103        -     println!("hello");
15104        +     println!("hello there");
15105
15106        +     println!("around the");
15107              println!("world");
15108          }
15109        "#
15110        .unindent(),
15111    );
15112
15113    cx.update_editor(|editor, window, cx| {
15114        editor.cancel(&Cancel, window, cx);
15115    });
15116
15117    cx.assert_state_with_diff(
15118        r#"
15119          use some::modified;
15120
15121          ˇ
15122          fn main() {
15123              println!("hello there");
15124
15125              println!("around the");
15126              println!("world");
15127          }
15128        "#
15129        .unindent(),
15130    );
15131}
15132
15133#[gpui::test]
15134async fn test_diff_base_change_with_expanded_diff_hunks(
15135    executor: BackgroundExecutor,
15136    cx: &mut TestAppContext,
15137) {
15138    init_test(cx, |_| {});
15139
15140    let mut cx = EditorTestContext::new(cx).await;
15141
15142    let diff_base = r#"
15143        use some::mod1;
15144        use some::mod2;
15145
15146        const A: u32 = 42;
15147        const B: u32 = 42;
15148        const C: u32 = 42;
15149
15150        fn main() {
15151            println!("hello");
15152
15153            println!("world");
15154        }
15155        "#
15156    .unindent();
15157
15158    cx.set_state(
15159        &r#"
15160        use some::mod2;
15161
15162        const A: u32 = 42;
15163        const C: u32 = 42;
15164
15165        fn main(ˇ) {
15166            //println!("hello");
15167
15168            println!("world");
15169            //
15170            //
15171        }
15172        "#
15173        .unindent(),
15174    );
15175
15176    cx.set_head_text(&diff_base);
15177    executor.run_until_parked();
15178
15179    cx.update_editor(|editor, window, cx| {
15180        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15181    });
15182    executor.run_until_parked();
15183    cx.assert_state_with_diff(
15184        r#"
15185        - use some::mod1;
15186          use some::mod2;
15187
15188          const A: u32 = 42;
15189        - const B: u32 = 42;
15190          const C: u32 = 42;
15191
15192          fn main(ˇ) {
15193        -     println!("hello");
15194        +     //println!("hello");
15195
15196              println!("world");
15197        +     //
15198        +     //
15199          }
15200        "#
15201        .unindent(),
15202    );
15203
15204    cx.set_head_text("new diff base!");
15205    executor.run_until_parked();
15206    cx.assert_state_with_diff(
15207        r#"
15208        - new diff base!
15209        + use some::mod2;
15210        +
15211        + const A: u32 = 42;
15212        + const C: u32 = 42;
15213        +
15214        + fn main(ˇ) {
15215        +     //println!("hello");
15216        +
15217        +     println!("world");
15218        +     //
15219        +     //
15220        + }
15221        "#
15222        .unindent(),
15223    );
15224}
15225
15226#[gpui::test]
15227async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15228    init_test(cx, |_| {});
15229
15230    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15231    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15232    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15233    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15234    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15235    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15236
15237    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15238    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15239    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15240
15241    let multi_buffer = cx.new(|cx| {
15242        let mut multibuffer = MultiBuffer::new(ReadWrite);
15243        multibuffer.push_excerpts(
15244            buffer_1.clone(),
15245            [
15246                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15247                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15248                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15249            ],
15250            cx,
15251        );
15252        multibuffer.push_excerpts(
15253            buffer_2.clone(),
15254            [
15255                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15256                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15257                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15258            ],
15259            cx,
15260        );
15261        multibuffer.push_excerpts(
15262            buffer_3.clone(),
15263            [
15264                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15265                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15266                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15267            ],
15268            cx,
15269        );
15270        multibuffer
15271    });
15272
15273    let editor =
15274        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15275    editor
15276        .update(cx, |editor, _window, cx| {
15277            for (buffer, diff_base) in [
15278                (buffer_1.clone(), file_1_old),
15279                (buffer_2.clone(), file_2_old),
15280                (buffer_3.clone(), file_3_old),
15281            ] {
15282                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15283                editor
15284                    .buffer
15285                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15286            }
15287        })
15288        .unwrap();
15289
15290    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15291    cx.run_until_parked();
15292
15293    cx.assert_editor_state(
15294        &"
15295            ˇaaa
15296            ccc
15297            ddd
15298
15299            ggg
15300            hhh
15301
15302
15303            lll
15304            mmm
15305            NNN
15306
15307            qqq
15308            rrr
15309
15310            uuu
15311            111
15312            222
15313            333
15314
15315            666
15316            777
15317
15318            000
15319            !!!"
15320        .unindent(),
15321    );
15322
15323    cx.update_editor(|editor, window, cx| {
15324        editor.select_all(&SelectAll, window, cx);
15325        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15326    });
15327    cx.executor().run_until_parked();
15328
15329    cx.assert_state_with_diff(
15330        "
15331            «aaa
15332          - bbb
15333            ccc
15334            ddd
15335
15336            ggg
15337            hhh
15338
15339
15340            lll
15341            mmm
15342          - nnn
15343          + NNN
15344
15345            qqq
15346            rrr
15347
15348            uuu
15349            111
15350            222
15351            333
15352
15353          + 666
15354            777
15355
15356            000
15357            !!!ˇ»"
15358            .unindent(),
15359    );
15360}
15361
15362#[gpui::test]
15363async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15364    init_test(cx, |_| {});
15365
15366    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15367    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15368
15369    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15370    let multi_buffer = cx.new(|cx| {
15371        let mut multibuffer = MultiBuffer::new(ReadWrite);
15372        multibuffer.push_excerpts(
15373            buffer.clone(),
15374            [
15375                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15376                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15377                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15378            ],
15379            cx,
15380        );
15381        multibuffer
15382    });
15383
15384    let editor =
15385        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15386    editor
15387        .update(cx, |editor, _window, cx| {
15388            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15389            editor
15390                .buffer
15391                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15392        })
15393        .unwrap();
15394
15395    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15396    cx.run_until_parked();
15397
15398    cx.update_editor(|editor, window, cx| {
15399        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15400    });
15401    cx.executor().run_until_parked();
15402
15403    // When the start of a hunk coincides with the start of its excerpt,
15404    // the hunk is expanded. When the start of a a hunk is earlier than
15405    // the start of its excerpt, the hunk is not expanded.
15406    cx.assert_state_with_diff(
15407        "
15408            ˇaaa
15409          - bbb
15410          + BBB
15411
15412          - ddd
15413          - eee
15414          + DDD
15415          + EEE
15416            fff
15417
15418            iii
15419        "
15420        .unindent(),
15421    );
15422}
15423
15424#[gpui::test]
15425async fn test_edits_around_expanded_insertion_hunks(
15426    executor: BackgroundExecutor,
15427    cx: &mut TestAppContext,
15428) {
15429    init_test(cx, |_| {});
15430
15431    let mut cx = EditorTestContext::new(cx).await;
15432
15433    let diff_base = r#"
15434        use some::mod1;
15435        use some::mod2;
15436
15437        const A: u32 = 42;
15438
15439        fn main() {
15440            println!("hello");
15441
15442            println!("world");
15443        }
15444        "#
15445    .unindent();
15446    executor.run_until_parked();
15447    cx.set_state(
15448        &r#"
15449        use some::mod1;
15450        use some::mod2;
15451
15452        const A: u32 = 42;
15453        const B: u32 = 42;
15454        const C: u32 = 42;
15455        ˇ
15456
15457        fn main() {
15458            println!("hello");
15459
15460            println!("world");
15461        }
15462        "#
15463        .unindent(),
15464    );
15465
15466    cx.set_head_text(&diff_base);
15467    executor.run_until_parked();
15468
15469    cx.update_editor(|editor, window, cx| {
15470        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15471    });
15472    executor.run_until_parked();
15473
15474    cx.assert_state_with_diff(
15475        r#"
15476        use some::mod1;
15477        use some::mod2;
15478
15479        const A: u32 = 42;
15480      + const B: u32 = 42;
15481      + const C: u32 = 42;
15482      + ˇ
15483
15484        fn main() {
15485            println!("hello");
15486
15487            println!("world");
15488        }
15489      "#
15490        .unindent(),
15491    );
15492
15493    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15494    executor.run_until_parked();
15495
15496    cx.assert_state_with_diff(
15497        r#"
15498        use some::mod1;
15499        use some::mod2;
15500
15501        const A: u32 = 42;
15502      + const B: u32 = 42;
15503      + const C: u32 = 42;
15504      + const D: u32 = 42;
15505      + ˇ
15506
15507        fn main() {
15508            println!("hello");
15509
15510            println!("world");
15511        }
15512      "#
15513        .unindent(),
15514    );
15515
15516    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15517    executor.run_until_parked();
15518
15519    cx.assert_state_with_diff(
15520        r#"
15521        use some::mod1;
15522        use some::mod2;
15523
15524        const A: u32 = 42;
15525      + const B: u32 = 42;
15526      + const C: u32 = 42;
15527      + const D: u32 = 42;
15528      + const E: u32 = 42;
15529      + ˇ
15530
15531        fn main() {
15532            println!("hello");
15533
15534            println!("world");
15535        }
15536      "#
15537        .unindent(),
15538    );
15539
15540    cx.update_editor(|editor, window, cx| {
15541        editor.delete_line(&DeleteLine, window, cx);
15542    });
15543    executor.run_until_parked();
15544
15545    cx.assert_state_with_diff(
15546        r#"
15547        use some::mod1;
15548        use some::mod2;
15549
15550        const A: u32 = 42;
15551      + const B: u32 = 42;
15552      + const C: u32 = 42;
15553      + const D: u32 = 42;
15554      + const E: u32 = 42;
15555        ˇ
15556        fn main() {
15557            println!("hello");
15558
15559            println!("world");
15560        }
15561      "#
15562        .unindent(),
15563    );
15564
15565    cx.update_editor(|editor, window, cx| {
15566        editor.move_up(&MoveUp, window, cx);
15567        editor.delete_line(&DeleteLine, window, cx);
15568        editor.move_up(&MoveUp, window, cx);
15569        editor.delete_line(&DeleteLine, window, cx);
15570        editor.move_up(&MoveUp, window, cx);
15571        editor.delete_line(&DeleteLine, window, cx);
15572    });
15573    executor.run_until_parked();
15574    cx.assert_state_with_diff(
15575        r#"
15576        use some::mod1;
15577        use some::mod2;
15578
15579        const A: u32 = 42;
15580      + const B: u32 = 42;
15581        ˇ
15582        fn main() {
15583            println!("hello");
15584
15585            println!("world");
15586        }
15587      "#
15588        .unindent(),
15589    );
15590
15591    cx.update_editor(|editor, window, cx| {
15592        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15593        editor.delete_line(&DeleteLine, window, cx);
15594    });
15595    executor.run_until_parked();
15596    cx.assert_state_with_diff(
15597        r#"
15598        ˇ
15599        fn main() {
15600            println!("hello");
15601
15602            println!("world");
15603        }
15604      "#
15605        .unindent(),
15606    );
15607}
15608
15609#[gpui::test]
15610async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15611    init_test(cx, |_| {});
15612
15613    let mut cx = EditorTestContext::new(cx).await;
15614    cx.set_head_text(indoc! { "
15615        one
15616        two
15617        three
15618        four
15619        five
15620        "
15621    });
15622    cx.set_state(indoc! { "
15623        one
15624        ˇthree
15625        five
15626    "});
15627    cx.run_until_parked();
15628    cx.update_editor(|editor, window, cx| {
15629        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15630    });
15631    cx.assert_state_with_diff(
15632        indoc! { "
15633        one
15634      - two
15635        ˇthree
15636      - four
15637        five
15638    "}
15639        .to_string(),
15640    );
15641    cx.update_editor(|editor, window, cx| {
15642        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15643    });
15644
15645    cx.assert_state_with_diff(
15646        indoc! { "
15647        one
15648        ˇthree
15649        five
15650    "}
15651        .to_string(),
15652    );
15653
15654    cx.set_state(indoc! { "
15655        one
15656        ˇTWO
15657        three
15658        four
15659        five
15660    "});
15661    cx.run_until_parked();
15662    cx.update_editor(|editor, window, cx| {
15663        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15664    });
15665
15666    cx.assert_state_with_diff(
15667        indoc! { "
15668            one
15669          - two
15670          + ˇTWO
15671            three
15672            four
15673            five
15674        "}
15675        .to_string(),
15676    );
15677    cx.update_editor(|editor, window, cx| {
15678        editor.move_up(&Default::default(), window, cx);
15679        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15680    });
15681    cx.assert_state_with_diff(
15682        indoc! { "
15683            one
15684            ˇTWO
15685            three
15686            four
15687            five
15688        "}
15689        .to_string(),
15690    );
15691}
15692
15693#[gpui::test]
15694async fn test_edits_around_expanded_deletion_hunks(
15695    executor: BackgroundExecutor,
15696    cx: &mut TestAppContext,
15697) {
15698    init_test(cx, |_| {});
15699
15700    let mut cx = EditorTestContext::new(cx).await;
15701
15702    let diff_base = r#"
15703        use some::mod1;
15704        use some::mod2;
15705
15706        const A: u32 = 42;
15707        const B: u32 = 42;
15708        const C: u32 = 42;
15709
15710
15711        fn main() {
15712            println!("hello");
15713
15714            println!("world");
15715        }
15716    "#
15717    .unindent();
15718    executor.run_until_parked();
15719    cx.set_state(
15720        &r#"
15721        use some::mod1;
15722        use some::mod2;
15723
15724        ˇconst B: u32 = 42;
15725        const C: u32 = 42;
15726
15727
15728        fn main() {
15729            println!("hello");
15730
15731            println!("world");
15732        }
15733        "#
15734        .unindent(),
15735    );
15736
15737    cx.set_head_text(&diff_base);
15738    executor.run_until_parked();
15739
15740    cx.update_editor(|editor, window, cx| {
15741        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15742    });
15743    executor.run_until_parked();
15744
15745    cx.assert_state_with_diff(
15746        r#"
15747        use some::mod1;
15748        use some::mod2;
15749
15750      - const A: u32 = 42;
15751        ˇconst B: u32 = 42;
15752        const C: u32 = 42;
15753
15754
15755        fn main() {
15756            println!("hello");
15757
15758            println!("world");
15759        }
15760      "#
15761        .unindent(),
15762    );
15763
15764    cx.update_editor(|editor, window, cx| {
15765        editor.delete_line(&DeleteLine, window, cx);
15766    });
15767    executor.run_until_parked();
15768    cx.assert_state_with_diff(
15769        r#"
15770        use some::mod1;
15771        use some::mod2;
15772
15773      - const A: u32 = 42;
15774      - const B: u32 = 42;
15775        ˇconst C: u32 = 42;
15776
15777
15778        fn main() {
15779            println!("hello");
15780
15781            println!("world");
15782        }
15783      "#
15784        .unindent(),
15785    );
15786
15787    cx.update_editor(|editor, window, cx| {
15788        editor.delete_line(&DeleteLine, window, cx);
15789    });
15790    executor.run_until_parked();
15791    cx.assert_state_with_diff(
15792        r#"
15793        use some::mod1;
15794        use some::mod2;
15795
15796      - const A: u32 = 42;
15797      - const B: u32 = 42;
15798      - const C: u32 = 42;
15799        ˇ
15800
15801        fn main() {
15802            println!("hello");
15803
15804            println!("world");
15805        }
15806      "#
15807        .unindent(),
15808    );
15809
15810    cx.update_editor(|editor, window, cx| {
15811        editor.handle_input("replacement", window, cx);
15812    });
15813    executor.run_until_parked();
15814    cx.assert_state_with_diff(
15815        r#"
15816        use some::mod1;
15817        use some::mod2;
15818
15819      - const A: u32 = 42;
15820      - const B: u32 = 42;
15821      - const C: u32 = 42;
15822      -
15823      + replacementˇ
15824
15825        fn main() {
15826            println!("hello");
15827
15828            println!("world");
15829        }
15830      "#
15831        .unindent(),
15832    );
15833}
15834
15835#[gpui::test]
15836async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15837    init_test(cx, |_| {});
15838
15839    let mut cx = EditorTestContext::new(cx).await;
15840
15841    let base_text = r#"
15842        one
15843        two
15844        three
15845        four
15846        five
15847    "#
15848    .unindent();
15849    executor.run_until_parked();
15850    cx.set_state(
15851        &r#"
15852        one
15853        two
15854        fˇour
15855        five
15856        "#
15857        .unindent(),
15858    );
15859
15860    cx.set_head_text(&base_text);
15861    executor.run_until_parked();
15862
15863    cx.update_editor(|editor, window, cx| {
15864        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15865    });
15866    executor.run_until_parked();
15867
15868    cx.assert_state_with_diff(
15869        r#"
15870          one
15871          two
15872        - three
15873          fˇour
15874          five
15875        "#
15876        .unindent(),
15877    );
15878
15879    cx.update_editor(|editor, window, cx| {
15880        editor.backspace(&Backspace, window, cx);
15881        editor.backspace(&Backspace, window, cx);
15882    });
15883    executor.run_until_parked();
15884    cx.assert_state_with_diff(
15885        r#"
15886          one
15887          two
15888        - threeˇ
15889        - four
15890        + our
15891          five
15892        "#
15893        .unindent(),
15894    );
15895}
15896
15897#[gpui::test]
15898async fn test_edit_after_expanded_modification_hunk(
15899    executor: BackgroundExecutor,
15900    cx: &mut TestAppContext,
15901) {
15902    init_test(cx, |_| {});
15903
15904    let mut cx = EditorTestContext::new(cx).await;
15905
15906    let diff_base = r#"
15907        use some::mod1;
15908        use some::mod2;
15909
15910        const A: u32 = 42;
15911        const B: u32 = 42;
15912        const C: u32 = 42;
15913        const D: u32 = 42;
15914
15915
15916        fn main() {
15917            println!("hello");
15918
15919            println!("world");
15920        }"#
15921    .unindent();
15922
15923    cx.set_state(
15924        &r#"
15925        use some::mod1;
15926        use some::mod2;
15927
15928        const A: u32 = 42;
15929        const B: u32 = 42;
15930        const C: u32 = 43ˇ
15931        const D: u32 = 42;
15932
15933
15934        fn main() {
15935            println!("hello");
15936
15937            println!("world");
15938        }"#
15939        .unindent(),
15940    );
15941
15942    cx.set_head_text(&diff_base);
15943    executor.run_until_parked();
15944    cx.update_editor(|editor, window, cx| {
15945        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15946    });
15947    executor.run_until_parked();
15948
15949    cx.assert_state_with_diff(
15950        r#"
15951        use some::mod1;
15952        use some::mod2;
15953
15954        const A: u32 = 42;
15955        const B: u32 = 42;
15956      - const C: u32 = 42;
15957      + const C: u32 = 43ˇ
15958        const D: u32 = 42;
15959
15960
15961        fn main() {
15962            println!("hello");
15963
15964            println!("world");
15965        }"#
15966        .unindent(),
15967    );
15968
15969    cx.update_editor(|editor, window, cx| {
15970        editor.handle_input("\nnew_line\n", window, cx);
15971    });
15972    executor.run_until_parked();
15973
15974    cx.assert_state_with_diff(
15975        r#"
15976        use some::mod1;
15977        use some::mod2;
15978
15979        const A: u32 = 42;
15980        const B: u32 = 42;
15981      - const C: u32 = 42;
15982      + const C: u32 = 43
15983      + new_line
15984      + ˇ
15985        const D: u32 = 42;
15986
15987
15988        fn main() {
15989            println!("hello");
15990
15991            println!("world");
15992        }"#
15993        .unindent(),
15994    );
15995}
15996
15997#[gpui::test]
15998async fn test_stage_and_unstage_added_file_hunk(
15999    executor: BackgroundExecutor,
16000    cx: &mut TestAppContext,
16001) {
16002    init_test(cx, |_| {});
16003
16004    let mut cx = EditorTestContext::new(cx).await;
16005    cx.update_editor(|editor, _, cx| {
16006        editor.set_expand_all_diff_hunks(cx);
16007    });
16008
16009    let working_copy = r#"
16010            ˇfn main() {
16011                println!("hello, world!");
16012            }
16013        "#
16014    .unindent();
16015
16016    cx.set_state(&working_copy);
16017    executor.run_until_parked();
16018
16019    cx.assert_state_with_diff(
16020        r#"
16021            + ˇfn main() {
16022            +     println!("hello, world!");
16023            + }
16024        "#
16025        .unindent(),
16026    );
16027    cx.assert_index_text(None);
16028
16029    cx.update_editor(|editor, window, cx| {
16030        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16031    });
16032    executor.run_until_parked();
16033    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16034    cx.assert_state_with_diff(
16035        r#"
16036            + ˇfn main() {
16037            +     println!("hello, world!");
16038            + }
16039        "#
16040        .unindent(),
16041    );
16042
16043    cx.update_editor(|editor, window, cx| {
16044        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16045    });
16046    executor.run_until_parked();
16047    cx.assert_index_text(None);
16048}
16049
16050async fn setup_indent_guides_editor(
16051    text: &str,
16052    cx: &mut TestAppContext,
16053) -> (BufferId, EditorTestContext) {
16054    init_test(cx, |_| {});
16055
16056    let mut cx = EditorTestContext::new(cx).await;
16057
16058    let buffer_id = cx.update_editor(|editor, window, cx| {
16059        editor.set_text(text, window, cx);
16060        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16061
16062        buffer_ids[0]
16063    });
16064
16065    (buffer_id, cx)
16066}
16067
16068fn assert_indent_guides(
16069    range: Range<u32>,
16070    expected: Vec<IndentGuide>,
16071    active_indices: Option<Vec<usize>>,
16072    cx: &mut EditorTestContext,
16073) {
16074    let indent_guides = cx.update_editor(|editor, window, cx| {
16075        let snapshot = editor.snapshot(window, cx).display_snapshot;
16076        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16077            editor,
16078            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16079            true,
16080            &snapshot,
16081            cx,
16082        );
16083
16084        indent_guides.sort_by(|a, b| {
16085            a.depth.cmp(&b.depth).then(
16086                a.start_row
16087                    .cmp(&b.start_row)
16088                    .then(a.end_row.cmp(&b.end_row)),
16089            )
16090        });
16091        indent_guides
16092    });
16093
16094    if let Some(expected) = active_indices {
16095        let active_indices = cx.update_editor(|editor, window, cx| {
16096            let snapshot = editor.snapshot(window, cx).display_snapshot;
16097            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16098        });
16099
16100        assert_eq!(
16101            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16102            expected,
16103            "Active indent guide indices do not match"
16104        );
16105    }
16106
16107    assert_eq!(indent_guides, expected, "Indent guides do not match");
16108}
16109
16110fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16111    IndentGuide {
16112        buffer_id,
16113        start_row: MultiBufferRow(start_row),
16114        end_row: MultiBufferRow(end_row),
16115        depth,
16116        tab_size: 4,
16117        settings: IndentGuideSettings {
16118            enabled: true,
16119            line_width: 1,
16120            active_line_width: 1,
16121            ..Default::default()
16122        },
16123    }
16124}
16125
16126#[gpui::test]
16127async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16128    let (buffer_id, mut cx) = setup_indent_guides_editor(
16129        &"
16130    fn main() {
16131        let a = 1;
16132    }"
16133        .unindent(),
16134        cx,
16135    )
16136    .await;
16137
16138    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16139}
16140
16141#[gpui::test]
16142async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16143    let (buffer_id, mut cx) = setup_indent_guides_editor(
16144        &"
16145    fn main() {
16146        let a = 1;
16147        let b = 2;
16148    }"
16149        .unindent(),
16150        cx,
16151    )
16152    .await;
16153
16154    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16155}
16156
16157#[gpui::test]
16158async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16159    let (buffer_id, mut cx) = setup_indent_guides_editor(
16160        &"
16161    fn main() {
16162        let a = 1;
16163        if a == 3 {
16164            let b = 2;
16165        } else {
16166            let c = 3;
16167        }
16168    }"
16169        .unindent(),
16170        cx,
16171    )
16172    .await;
16173
16174    assert_indent_guides(
16175        0..8,
16176        vec![
16177            indent_guide(buffer_id, 1, 6, 0),
16178            indent_guide(buffer_id, 3, 3, 1),
16179            indent_guide(buffer_id, 5, 5, 1),
16180        ],
16181        None,
16182        &mut cx,
16183    );
16184}
16185
16186#[gpui::test]
16187async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16188    let (buffer_id, mut cx) = setup_indent_guides_editor(
16189        &"
16190    fn main() {
16191        let a = 1;
16192            let b = 2;
16193        let c = 3;
16194    }"
16195        .unindent(),
16196        cx,
16197    )
16198    .await;
16199
16200    assert_indent_guides(
16201        0..5,
16202        vec![
16203            indent_guide(buffer_id, 1, 3, 0),
16204            indent_guide(buffer_id, 2, 2, 1),
16205        ],
16206        None,
16207        &mut cx,
16208    );
16209}
16210
16211#[gpui::test]
16212async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16213    let (buffer_id, mut cx) = setup_indent_guides_editor(
16214        &"
16215        fn main() {
16216            let a = 1;
16217
16218            let c = 3;
16219        }"
16220        .unindent(),
16221        cx,
16222    )
16223    .await;
16224
16225    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16226}
16227
16228#[gpui::test]
16229async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16230    let (buffer_id, mut cx) = setup_indent_guides_editor(
16231        &"
16232        fn main() {
16233            let a = 1;
16234
16235            let c = 3;
16236
16237            if a == 3 {
16238                let b = 2;
16239            } else {
16240                let c = 3;
16241            }
16242        }"
16243        .unindent(),
16244        cx,
16245    )
16246    .await;
16247
16248    assert_indent_guides(
16249        0..11,
16250        vec![
16251            indent_guide(buffer_id, 1, 9, 0),
16252            indent_guide(buffer_id, 6, 6, 1),
16253            indent_guide(buffer_id, 8, 8, 1),
16254        ],
16255        None,
16256        &mut cx,
16257    );
16258}
16259
16260#[gpui::test]
16261async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16262    let (buffer_id, mut cx) = setup_indent_guides_editor(
16263        &"
16264        fn main() {
16265            let a = 1;
16266
16267            let c = 3;
16268
16269            if a == 3 {
16270                let b = 2;
16271            } else {
16272                let c = 3;
16273            }
16274        }"
16275        .unindent(),
16276        cx,
16277    )
16278    .await;
16279
16280    assert_indent_guides(
16281        1..11,
16282        vec![
16283            indent_guide(buffer_id, 1, 9, 0),
16284            indent_guide(buffer_id, 6, 6, 1),
16285            indent_guide(buffer_id, 8, 8, 1),
16286        ],
16287        None,
16288        &mut cx,
16289    );
16290}
16291
16292#[gpui::test]
16293async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16294    let (buffer_id, mut cx) = setup_indent_guides_editor(
16295        &"
16296        fn main() {
16297            let a = 1;
16298
16299            let c = 3;
16300
16301            if a == 3 {
16302                let b = 2;
16303            } else {
16304                let c = 3;
16305            }
16306        }"
16307        .unindent(),
16308        cx,
16309    )
16310    .await;
16311
16312    assert_indent_guides(
16313        1..10,
16314        vec![
16315            indent_guide(buffer_id, 1, 9, 0),
16316            indent_guide(buffer_id, 6, 6, 1),
16317            indent_guide(buffer_id, 8, 8, 1),
16318        ],
16319        None,
16320        &mut cx,
16321    );
16322}
16323
16324#[gpui::test]
16325async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16326    let (buffer_id, mut cx) = setup_indent_guides_editor(
16327        &"
16328        block1
16329            block2
16330                block3
16331                    block4
16332            block2
16333        block1
16334        block1"
16335            .unindent(),
16336        cx,
16337    )
16338    .await;
16339
16340    assert_indent_guides(
16341        1..10,
16342        vec![
16343            indent_guide(buffer_id, 1, 4, 0),
16344            indent_guide(buffer_id, 2, 3, 1),
16345            indent_guide(buffer_id, 3, 3, 2),
16346        ],
16347        None,
16348        &mut cx,
16349    );
16350}
16351
16352#[gpui::test]
16353async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16354    let (buffer_id, mut cx) = setup_indent_guides_editor(
16355        &"
16356        block1
16357            block2
16358                block3
16359
16360        block1
16361        block1"
16362            .unindent(),
16363        cx,
16364    )
16365    .await;
16366
16367    assert_indent_guides(
16368        0..6,
16369        vec![
16370            indent_guide(buffer_id, 1, 2, 0),
16371            indent_guide(buffer_id, 2, 2, 1),
16372        ],
16373        None,
16374        &mut cx,
16375    );
16376}
16377
16378#[gpui::test]
16379async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16380    let (buffer_id, mut cx) = setup_indent_guides_editor(
16381        &"
16382        block1
16383
16384
16385
16386            block2
16387        "
16388        .unindent(),
16389        cx,
16390    )
16391    .await;
16392
16393    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16394}
16395
16396#[gpui::test]
16397async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16398    let (buffer_id, mut cx) = setup_indent_guides_editor(
16399        &"
16400        def a:
16401        \tb = 3
16402        \tif True:
16403        \t\tc = 4
16404        \t\td = 5
16405        \tprint(b)
16406        "
16407        .unindent(),
16408        cx,
16409    )
16410    .await;
16411
16412    assert_indent_guides(
16413        0..6,
16414        vec![
16415            indent_guide(buffer_id, 1, 6, 0),
16416            indent_guide(buffer_id, 3, 4, 1),
16417        ],
16418        None,
16419        &mut cx,
16420    );
16421}
16422
16423#[gpui::test]
16424async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16425    let (buffer_id, mut cx) = setup_indent_guides_editor(
16426        &"
16427    fn main() {
16428        let a = 1;
16429    }"
16430        .unindent(),
16431        cx,
16432    )
16433    .await;
16434
16435    cx.update_editor(|editor, window, cx| {
16436        editor.change_selections(None, window, cx, |s| {
16437            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16438        });
16439    });
16440
16441    assert_indent_guides(
16442        0..3,
16443        vec![indent_guide(buffer_id, 1, 1, 0)],
16444        Some(vec![0]),
16445        &mut cx,
16446    );
16447}
16448
16449#[gpui::test]
16450async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16451    let (buffer_id, mut cx) = setup_indent_guides_editor(
16452        &"
16453    fn main() {
16454        if 1 == 2 {
16455            let a = 1;
16456        }
16457    }"
16458        .unindent(),
16459        cx,
16460    )
16461    .await;
16462
16463    cx.update_editor(|editor, window, cx| {
16464        editor.change_selections(None, window, cx, |s| {
16465            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16466        });
16467    });
16468
16469    assert_indent_guides(
16470        0..4,
16471        vec![
16472            indent_guide(buffer_id, 1, 3, 0),
16473            indent_guide(buffer_id, 2, 2, 1),
16474        ],
16475        Some(vec![1]),
16476        &mut cx,
16477    );
16478
16479    cx.update_editor(|editor, window, cx| {
16480        editor.change_selections(None, window, cx, |s| {
16481            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16482        });
16483    });
16484
16485    assert_indent_guides(
16486        0..4,
16487        vec![
16488            indent_guide(buffer_id, 1, 3, 0),
16489            indent_guide(buffer_id, 2, 2, 1),
16490        ],
16491        Some(vec![1]),
16492        &mut cx,
16493    );
16494
16495    cx.update_editor(|editor, window, cx| {
16496        editor.change_selections(None, window, cx, |s| {
16497            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16498        });
16499    });
16500
16501    assert_indent_guides(
16502        0..4,
16503        vec![
16504            indent_guide(buffer_id, 1, 3, 0),
16505            indent_guide(buffer_id, 2, 2, 1),
16506        ],
16507        Some(vec![0]),
16508        &mut cx,
16509    );
16510}
16511
16512#[gpui::test]
16513async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16514    let (buffer_id, mut cx) = setup_indent_guides_editor(
16515        &"
16516    fn main() {
16517        let a = 1;
16518
16519        let b = 2;
16520    }"
16521        .unindent(),
16522        cx,
16523    )
16524    .await;
16525
16526    cx.update_editor(|editor, window, cx| {
16527        editor.change_selections(None, window, cx, |s| {
16528            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16529        });
16530    });
16531
16532    assert_indent_guides(
16533        0..5,
16534        vec![indent_guide(buffer_id, 1, 3, 0)],
16535        Some(vec![0]),
16536        &mut cx,
16537    );
16538}
16539
16540#[gpui::test]
16541async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16542    let (buffer_id, mut cx) = setup_indent_guides_editor(
16543        &"
16544    def m:
16545        a = 1
16546        pass"
16547            .unindent(),
16548        cx,
16549    )
16550    .await;
16551
16552    cx.update_editor(|editor, window, cx| {
16553        editor.change_selections(None, window, cx, |s| {
16554            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16555        });
16556    });
16557
16558    assert_indent_guides(
16559        0..3,
16560        vec![indent_guide(buffer_id, 1, 2, 0)],
16561        Some(vec![0]),
16562        &mut cx,
16563    );
16564}
16565
16566#[gpui::test]
16567async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16568    init_test(cx, |_| {});
16569    let mut cx = EditorTestContext::new(cx).await;
16570    let text = indoc! {
16571        "
16572        impl A {
16573            fn b() {
16574                0;
16575                3;
16576                5;
16577                6;
16578                7;
16579            }
16580        }
16581        "
16582    };
16583    let base_text = indoc! {
16584        "
16585        impl A {
16586            fn b() {
16587                0;
16588                1;
16589                2;
16590                3;
16591                4;
16592            }
16593            fn c() {
16594                5;
16595                6;
16596                7;
16597            }
16598        }
16599        "
16600    };
16601
16602    cx.update_editor(|editor, window, cx| {
16603        editor.set_text(text, window, cx);
16604
16605        editor.buffer().update(cx, |multibuffer, cx| {
16606            let buffer = multibuffer.as_singleton().unwrap();
16607            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16608
16609            multibuffer.set_all_diff_hunks_expanded(cx);
16610            multibuffer.add_diff(diff, cx);
16611
16612            buffer.read(cx).remote_id()
16613        })
16614    });
16615    cx.run_until_parked();
16616
16617    cx.assert_state_with_diff(
16618        indoc! { "
16619          impl A {
16620              fn b() {
16621                  0;
16622        -         1;
16623        -         2;
16624                  3;
16625        -         4;
16626        -     }
16627        -     fn c() {
16628                  5;
16629                  6;
16630                  7;
16631              }
16632          }
16633          ˇ"
16634        }
16635        .to_string(),
16636    );
16637
16638    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16639        editor
16640            .snapshot(window, cx)
16641            .buffer_snapshot
16642            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16643            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16644            .collect::<Vec<_>>()
16645    });
16646    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16647    assert_eq!(
16648        actual_guides,
16649        vec![
16650            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16651            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16652            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16653        ]
16654    );
16655}
16656
16657#[gpui::test]
16658async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16659    init_test(cx, |_| {});
16660    let mut cx = EditorTestContext::new(cx).await;
16661
16662    let diff_base = r#"
16663        a
16664        b
16665        c
16666        "#
16667    .unindent();
16668
16669    cx.set_state(
16670        &r#"
16671        ˇA
16672        b
16673        C
16674        "#
16675        .unindent(),
16676    );
16677    cx.set_head_text(&diff_base);
16678    cx.update_editor(|editor, window, cx| {
16679        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16680    });
16681    executor.run_until_parked();
16682
16683    let both_hunks_expanded = r#"
16684        - a
16685        + ˇA
16686          b
16687        - c
16688        + C
16689        "#
16690    .unindent();
16691
16692    cx.assert_state_with_diff(both_hunks_expanded.clone());
16693
16694    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16695        let snapshot = editor.snapshot(window, cx);
16696        let hunks = editor
16697            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16698            .collect::<Vec<_>>();
16699        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16700        let buffer_id = hunks[0].buffer_id;
16701        hunks
16702            .into_iter()
16703            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16704            .collect::<Vec<_>>()
16705    });
16706    assert_eq!(hunk_ranges.len(), 2);
16707
16708    cx.update_editor(|editor, _, cx| {
16709        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16710    });
16711    executor.run_until_parked();
16712
16713    let second_hunk_expanded = r#"
16714          ˇA
16715          b
16716        - c
16717        + C
16718        "#
16719    .unindent();
16720
16721    cx.assert_state_with_diff(second_hunk_expanded);
16722
16723    cx.update_editor(|editor, _, cx| {
16724        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16725    });
16726    executor.run_until_parked();
16727
16728    cx.assert_state_with_diff(both_hunks_expanded.clone());
16729
16730    cx.update_editor(|editor, _, cx| {
16731        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16732    });
16733    executor.run_until_parked();
16734
16735    let first_hunk_expanded = r#"
16736        - a
16737        + ˇA
16738          b
16739          C
16740        "#
16741    .unindent();
16742
16743    cx.assert_state_with_diff(first_hunk_expanded);
16744
16745    cx.update_editor(|editor, _, cx| {
16746        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16747    });
16748    executor.run_until_parked();
16749
16750    cx.assert_state_with_diff(both_hunks_expanded);
16751
16752    cx.set_state(
16753        &r#"
16754        ˇA
16755        b
16756        "#
16757        .unindent(),
16758    );
16759    cx.run_until_parked();
16760
16761    // TODO this cursor position seems bad
16762    cx.assert_state_with_diff(
16763        r#"
16764        - ˇa
16765        + A
16766          b
16767        "#
16768        .unindent(),
16769    );
16770
16771    cx.update_editor(|editor, window, cx| {
16772        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16773    });
16774
16775    cx.assert_state_with_diff(
16776        r#"
16777            - ˇa
16778            + A
16779              b
16780            - c
16781            "#
16782        .unindent(),
16783    );
16784
16785    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16786        let snapshot = editor.snapshot(window, cx);
16787        let hunks = editor
16788            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16789            .collect::<Vec<_>>();
16790        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16791        let buffer_id = hunks[0].buffer_id;
16792        hunks
16793            .into_iter()
16794            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16795            .collect::<Vec<_>>()
16796    });
16797    assert_eq!(hunk_ranges.len(), 2);
16798
16799    cx.update_editor(|editor, _, cx| {
16800        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16801    });
16802    executor.run_until_parked();
16803
16804    cx.assert_state_with_diff(
16805        r#"
16806        - ˇa
16807        + A
16808          b
16809        "#
16810        .unindent(),
16811    );
16812}
16813
16814#[gpui::test]
16815async fn test_toggle_deletion_hunk_at_start_of_file(
16816    executor: BackgroundExecutor,
16817    cx: &mut TestAppContext,
16818) {
16819    init_test(cx, |_| {});
16820    let mut cx = EditorTestContext::new(cx).await;
16821
16822    let diff_base = r#"
16823        a
16824        b
16825        c
16826        "#
16827    .unindent();
16828
16829    cx.set_state(
16830        &r#"
16831        ˇb
16832        c
16833        "#
16834        .unindent(),
16835    );
16836    cx.set_head_text(&diff_base);
16837    cx.update_editor(|editor, window, cx| {
16838        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16839    });
16840    executor.run_until_parked();
16841
16842    let hunk_expanded = r#"
16843        - a
16844          ˇb
16845          c
16846        "#
16847    .unindent();
16848
16849    cx.assert_state_with_diff(hunk_expanded.clone());
16850
16851    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16852        let snapshot = editor.snapshot(window, cx);
16853        let hunks = editor
16854            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16855            .collect::<Vec<_>>();
16856        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16857        let buffer_id = hunks[0].buffer_id;
16858        hunks
16859            .into_iter()
16860            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16861            .collect::<Vec<_>>()
16862    });
16863    assert_eq!(hunk_ranges.len(), 1);
16864
16865    cx.update_editor(|editor, _, cx| {
16866        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16867    });
16868    executor.run_until_parked();
16869
16870    let hunk_collapsed = r#"
16871          ˇb
16872          c
16873        "#
16874    .unindent();
16875
16876    cx.assert_state_with_diff(hunk_collapsed);
16877
16878    cx.update_editor(|editor, _, cx| {
16879        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16880    });
16881    executor.run_until_parked();
16882
16883    cx.assert_state_with_diff(hunk_expanded.clone());
16884}
16885
16886#[gpui::test]
16887async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16888    init_test(cx, |_| {});
16889
16890    let fs = FakeFs::new(cx.executor());
16891    fs.insert_tree(
16892        path!("/test"),
16893        json!({
16894            ".git": {},
16895            "file-1": "ONE\n",
16896            "file-2": "TWO\n",
16897            "file-3": "THREE\n",
16898        }),
16899    )
16900    .await;
16901
16902    fs.set_head_for_repo(
16903        path!("/test/.git").as_ref(),
16904        &[
16905            ("file-1".into(), "one\n".into()),
16906            ("file-2".into(), "two\n".into()),
16907            ("file-3".into(), "three\n".into()),
16908        ],
16909    );
16910
16911    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16912    let mut buffers = vec![];
16913    for i in 1..=3 {
16914        let buffer = project
16915            .update(cx, |project, cx| {
16916                let path = format!(path!("/test/file-{}"), i);
16917                project.open_local_buffer(path, cx)
16918            })
16919            .await
16920            .unwrap();
16921        buffers.push(buffer);
16922    }
16923
16924    let multibuffer = cx.new(|cx| {
16925        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16926        multibuffer.set_all_diff_hunks_expanded(cx);
16927        for buffer in &buffers {
16928            let snapshot = buffer.read(cx).snapshot();
16929            multibuffer.set_excerpts_for_path(
16930                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16931                buffer.clone(),
16932                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16933                DEFAULT_MULTIBUFFER_CONTEXT,
16934                cx,
16935            );
16936        }
16937        multibuffer
16938    });
16939
16940    let editor = cx.add_window(|window, cx| {
16941        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16942    });
16943    cx.run_until_parked();
16944
16945    let snapshot = editor
16946        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16947        .unwrap();
16948    let hunks = snapshot
16949        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16950        .map(|hunk| match hunk {
16951            DisplayDiffHunk::Unfolded {
16952                display_row_range, ..
16953            } => display_row_range,
16954            DisplayDiffHunk::Folded { .. } => unreachable!(),
16955        })
16956        .collect::<Vec<_>>();
16957    assert_eq!(
16958        hunks,
16959        [
16960            DisplayRow(2)..DisplayRow(4),
16961            DisplayRow(7)..DisplayRow(9),
16962            DisplayRow(12)..DisplayRow(14),
16963        ]
16964    );
16965}
16966
16967#[gpui::test]
16968async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16969    init_test(cx, |_| {});
16970
16971    let mut cx = EditorTestContext::new(cx).await;
16972    cx.set_head_text(indoc! { "
16973        one
16974        two
16975        three
16976        four
16977        five
16978        "
16979    });
16980    cx.set_index_text(indoc! { "
16981        one
16982        two
16983        three
16984        four
16985        five
16986        "
16987    });
16988    cx.set_state(indoc! {"
16989        one
16990        TWO
16991        ˇTHREE
16992        FOUR
16993        five
16994    "});
16995    cx.run_until_parked();
16996    cx.update_editor(|editor, window, cx| {
16997        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16998    });
16999    cx.run_until_parked();
17000    cx.assert_index_text(Some(indoc! {"
17001        one
17002        TWO
17003        THREE
17004        FOUR
17005        five
17006    "}));
17007    cx.set_state(indoc! { "
17008        one
17009        TWO
17010        ˇTHREE-HUNDRED
17011        FOUR
17012        five
17013    "});
17014    cx.run_until_parked();
17015    cx.update_editor(|editor, window, cx| {
17016        let snapshot = editor.snapshot(window, cx);
17017        let hunks = editor
17018            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17019            .collect::<Vec<_>>();
17020        assert_eq!(hunks.len(), 1);
17021        assert_eq!(
17022            hunks[0].status(),
17023            DiffHunkStatus {
17024                kind: DiffHunkStatusKind::Modified,
17025                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17026            }
17027        );
17028
17029        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17030    });
17031    cx.run_until_parked();
17032    cx.assert_index_text(Some(indoc! {"
17033        one
17034        TWO
17035        THREE-HUNDRED
17036        FOUR
17037        five
17038    "}));
17039}
17040
17041#[gpui::test]
17042fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17043    init_test(cx, |_| {});
17044
17045    let editor = cx.add_window(|window, cx| {
17046        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17047        build_editor(buffer, window, cx)
17048    });
17049
17050    let render_args = Arc::new(Mutex::new(None));
17051    let snapshot = editor
17052        .update(cx, |editor, window, cx| {
17053            let snapshot = editor.buffer().read(cx).snapshot(cx);
17054            let range =
17055                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17056
17057            struct RenderArgs {
17058                row: MultiBufferRow,
17059                folded: bool,
17060                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17061            }
17062
17063            let crease = Crease::inline(
17064                range,
17065                FoldPlaceholder::test(),
17066                {
17067                    let toggle_callback = render_args.clone();
17068                    move |row, folded, callback, _window, _cx| {
17069                        *toggle_callback.lock() = Some(RenderArgs {
17070                            row,
17071                            folded,
17072                            callback,
17073                        });
17074                        div()
17075                    }
17076                },
17077                |_row, _folded, _window, _cx| div(),
17078            );
17079
17080            editor.insert_creases(Some(crease), cx);
17081            let snapshot = editor.snapshot(window, cx);
17082            let _div = snapshot.render_crease_toggle(
17083                MultiBufferRow(1),
17084                false,
17085                cx.entity().clone(),
17086                window,
17087                cx,
17088            );
17089            snapshot
17090        })
17091        .unwrap();
17092
17093    let render_args = render_args.lock().take().unwrap();
17094    assert_eq!(render_args.row, MultiBufferRow(1));
17095    assert!(!render_args.folded);
17096    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17097
17098    cx.update_window(*editor, |_, window, cx| {
17099        (render_args.callback)(true, window, cx)
17100    })
17101    .unwrap();
17102    let snapshot = editor
17103        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17104        .unwrap();
17105    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17106
17107    cx.update_window(*editor, |_, window, cx| {
17108        (render_args.callback)(false, window, cx)
17109    })
17110    .unwrap();
17111    let snapshot = editor
17112        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17113        .unwrap();
17114    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17115}
17116
17117#[gpui::test]
17118async fn test_input_text(cx: &mut TestAppContext) {
17119    init_test(cx, |_| {});
17120    let mut cx = EditorTestContext::new(cx).await;
17121
17122    cx.set_state(
17123        &r#"ˇone
17124        two
17125
17126        three
17127        fourˇ
17128        five
17129
17130        siˇx"#
17131            .unindent(),
17132    );
17133
17134    cx.dispatch_action(HandleInput(String::new()));
17135    cx.assert_editor_state(
17136        &r#"ˇone
17137        two
17138
17139        three
17140        fourˇ
17141        five
17142
17143        siˇx"#
17144            .unindent(),
17145    );
17146
17147    cx.dispatch_action(HandleInput("AAAA".to_string()));
17148    cx.assert_editor_state(
17149        &r#"AAAAˇone
17150        two
17151
17152        three
17153        fourAAAAˇ
17154        five
17155
17156        siAAAAˇx"#
17157            .unindent(),
17158    );
17159}
17160
17161#[gpui::test]
17162async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17163    init_test(cx, |_| {});
17164
17165    let mut cx = EditorTestContext::new(cx).await;
17166    cx.set_state(
17167        r#"let foo = 1;
17168let foo = 2;
17169let foo = 3;
17170let fooˇ = 4;
17171let foo = 5;
17172let foo = 6;
17173let foo = 7;
17174let foo = 8;
17175let foo = 9;
17176let foo = 10;
17177let foo = 11;
17178let foo = 12;
17179let foo = 13;
17180let foo = 14;
17181let foo = 15;"#,
17182    );
17183
17184    cx.update_editor(|e, window, cx| {
17185        assert_eq!(
17186            e.next_scroll_position,
17187            NextScrollCursorCenterTopBottom::Center,
17188            "Default next scroll direction is center",
17189        );
17190
17191        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17192        assert_eq!(
17193            e.next_scroll_position,
17194            NextScrollCursorCenterTopBottom::Top,
17195            "After center, next scroll direction should be top",
17196        );
17197
17198        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17199        assert_eq!(
17200            e.next_scroll_position,
17201            NextScrollCursorCenterTopBottom::Bottom,
17202            "After top, next scroll direction should be bottom",
17203        );
17204
17205        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17206        assert_eq!(
17207            e.next_scroll_position,
17208            NextScrollCursorCenterTopBottom::Center,
17209            "After bottom, scrolling should start over",
17210        );
17211
17212        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17213        assert_eq!(
17214            e.next_scroll_position,
17215            NextScrollCursorCenterTopBottom::Top,
17216            "Scrolling continues if retriggered fast enough"
17217        );
17218    });
17219
17220    cx.executor()
17221        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17222    cx.executor().run_until_parked();
17223    cx.update_editor(|e, _, _| {
17224        assert_eq!(
17225            e.next_scroll_position,
17226            NextScrollCursorCenterTopBottom::Center,
17227            "If scrolling is not triggered fast enough, it should reset"
17228        );
17229    });
17230}
17231
17232#[gpui::test]
17233async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17234    init_test(cx, |_| {});
17235    let mut cx = EditorLspTestContext::new_rust(
17236        lsp::ServerCapabilities {
17237            definition_provider: Some(lsp::OneOf::Left(true)),
17238            references_provider: Some(lsp::OneOf::Left(true)),
17239            ..lsp::ServerCapabilities::default()
17240        },
17241        cx,
17242    )
17243    .await;
17244
17245    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17246        let go_to_definition = cx
17247            .lsp
17248            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17249                move |params, _| async move {
17250                    if empty_go_to_definition {
17251                        Ok(None)
17252                    } else {
17253                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17254                            uri: params.text_document_position_params.text_document.uri,
17255                            range: lsp::Range::new(
17256                                lsp::Position::new(4, 3),
17257                                lsp::Position::new(4, 6),
17258                            ),
17259                        })))
17260                    }
17261                },
17262            );
17263        let references = cx
17264            .lsp
17265            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17266                Ok(Some(vec![lsp::Location {
17267                    uri: params.text_document_position.text_document.uri,
17268                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17269                }]))
17270            });
17271        (go_to_definition, references)
17272    };
17273
17274    cx.set_state(
17275        &r#"fn one() {
17276            let mut a = ˇtwo();
17277        }
17278
17279        fn two() {}"#
17280            .unindent(),
17281    );
17282    set_up_lsp_handlers(false, &mut cx);
17283    let navigated = cx
17284        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17285        .await
17286        .expect("Failed to navigate to definition");
17287    assert_eq!(
17288        navigated,
17289        Navigated::Yes,
17290        "Should have navigated to definition from the GetDefinition response"
17291    );
17292    cx.assert_editor_state(
17293        &r#"fn one() {
17294            let mut a = two();
17295        }
17296
17297        fn «twoˇ»() {}"#
17298            .unindent(),
17299    );
17300
17301    let editors = cx.update_workspace(|workspace, _, cx| {
17302        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17303    });
17304    cx.update_editor(|_, _, test_editor_cx| {
17305        assert_eq!(
17306            editors.len(),
17307            1,
17308            "Initially, only one, test, editor should be open in the workspace"
17309        );
17310        assert_eq!(
17311            test_editor_cx.entity(),
17312            editors.last().expect("Asserted len is 1").clone()
17313        );
17314    });
17315
17316    set_up_lsp_handlers(true, &mut cx);
17317    let navigated = cx
17318        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17319        .await
17320        .expect("Failed to navigate to lookup references");
17321    assert_eq!(
17322        navigated,
17323        Navigated::Yes,
17324        "Should have navigated to references as a fallback after empty GoToDefinition response"
17325    );
17326    // We should not change the selections in the existing file,
17327    // if opening another milti buffer with the references
17328    cx.assert_editor_state(
17329        &r#"fn one() {
17330            let mut a = two();
17331        }
17332
17333        fn «twoˇ»() {}"#
17334            .unindent(),
17335    );
17336    let editors = cx.update_workspace(|workspace, _, cx| {
17337        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17338    });
17339    cx.update_editor(|_, _, test_editor_cx| {
17340        assert_eq!(
17341            editors.len(),
17342            2,
17343            "After falling back to references search, we open a new editor with the results"
17344        );
17345        let references_fallback_text = editors
17346            .into_iter()
17347            .find(|new_editor| *new_editor != test_editor_cx.entity())
17348            .expect("Should have one non-test editor now")
17349            .read(test_editor_cx)
17350            .text(test_editor_cx);
17351        assert_eq!(
17352            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17353            "Should use the range from the references response and not the GoToDefinition one"
17354        );
17355    });
17356}
17357
17358#[gpui::test]
17359async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17360    init_test(cx, |_| {});
17361    cx.update(|cx| {
17362        let mut editor_settings = EditorSettings::get_global(cx).clone();
17363        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17364        EditorSettings::override_global(editor_settings, cx);
17365    });
17366    let mut cx = EditorLspTestContext::new_rust(
17367        lsp::ServerCapabilities {
17368            definition_provider: Some(lsp::OneOf::Left(true)),
17369            references_provider: Some(lsp::OneOf::Left(true)),
17370            ..lsp::ServerCapabilities::default()
17371        },
17372        cx,
17373    )
17374    .await;
17375    let original_state = r#"fn one() {
17376        let mut a = ˇtwo();
17377    }
17378
17379    fn two() {}"#
17380        .unindent();
17381    cx.set_state(&original_state);
17382
17383    let mut go_to_definition = cx
17384        .lsp
17385        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17386            move |_, _| async move { Ok(None) },
17387        );
17388    let _references = cx
17389        .lsp
17390        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17391            panic!("Should not call for references with no go to definition fallback")
17392        });
17393
17394    let navigated = cx
17395        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17396        .await
17397        .expect("Failed to navigate to lookup references");
17398    go_to_definition
17399        .next()
17400        .await
17401        .expect("Should have called the go_to_definition handler");
17402
17403    assert_eq!(
17404        navigated,
17405        Navigated::No,
17406        "Should have navigated to references as a fallback after empty GoToDefinition response"
17407    );
17408    cx.assert_editor_state(&original_state);
17409    let editors = cx.update_workspace(|workspace, _, cx| {
17410        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17411    });
17412    cx.update_editor(|_, _, _| {
17413        assert_eq!(
17414            editors.len(),
17415            1,
17416            "After unsuccessful fallback, no other editor should have been opened"
17417        );
17418    });
17419}
17420
17421#[gpui::test]
17422async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17423    init_test(cx, |_| {});
17424
17425    let language = Arc::new(Language::new(
17426        LanguageConfig::default(),
17427        Some(tree_sitter_rust::LANGUAGE.into()),
17428    ));
17429
17430    let text = r#"
17431        #[cfg(test)]
17432        mod tests() {
17433            #[test]
17434            fn runnable_1() {
17435                let a = 1;
17436            }
17437
17438            #[test]
17439            fn runnable_2() {
17440                let a = 1;
17441                let b = 2;
17442            }
17443        }
17444    "#
17445    .unindent();
17446
17447    let fs = FakeFs::new(cx.executor());
17448    fs.insert_file("/file.rs", Default::default()).await;
17449
17450    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17451    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17452    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17453    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17454    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17455
17456    let editor = cx.new_window_entity(|window, cx| {
17457        Editor::new(
17458            EditorMode::full(),
17459            multi_buffer,
17460            Some(project.clone()),
17461            window,
17462            cx,
17463        )
17464    });
17465
17466    editor.update_in(cx, |editor, window, cx| {
17467        let snapshot = editor.buffer().read(cx).snapshot(cx);
17468        editor.tasks.insert(
17469            (buffer.read(cx).remote_id(), 3),
17470            RunnableTasks {
17471                templates: vec![],
17472                offset: snapshot.anchor_before(43),
17473                column: 0,
17474                extra_variables: HashMap::default(),
17475                context_range: BufferOffset(43)..BufferOffset(85),
17476            },
17477        );
17478        editor.tasks.insert(
17479            (buffer.read(cx).remote_id(), 8),
17480            RunnableTasks {
17481                templates: vec![],
17482                offset: snapshot.anchor_before(86),
17483                column: 0,
17484                extra_variables: HashMap::default(),
17485                context_range: BufferOffset(86)..BufferOffset(191),
17486            },
17487        );
17488
17489        // Test finding task when cursor is inside function body
17490        editor.change_selections(None, window, cx, |s| {
17491            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17492        });
17493        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17494        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17495
17496        // Test finding task when cursor is on function name
17497        editor.change_selections(None, window, cx, |s| {
17498            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17499        });
17500        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17501        assert_eq!(row, 8, "Should find task when cursor is on function name");
17502    });
17503}
17504
17505#[gpui::test]
17506async fn test_folding_buffers(cx: &mut TestAppContext) {
17507    init_test(cx, |_| {});
17508
17509    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17510    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17511    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17512
17513    let fs = FakeFs::new(cx.executor());
17514    fs.insert_tree(
17515        path!("/a"),
17516        json!({
17517            "first.rs": sample_text_1,
17518            "second.rs": sample_text_2,
17519            "third.rs": sample_text_3,
17520        }),
17521    )
17522    .await;
17523    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17524    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17525    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17526    let worktree = project.update(cx, |project, cx| {
17527        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17528        assert_eq!(worktrees.len(), 1);
17529        worktrees.pop().unwrap()
17530    });
17531    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17532
17533    let buffer_1 = project
17534        .update(cx, |project, cx| {
17535            project.open_buffer((worktree_id, "first.rs"), cx)
17536        })
17537        .await
17538        .unwrap();
17539    let buffer_2 = project
17540        .update(cx, |project, cx| {
17541            project.open_buffer((worktree_id, "second.rs"), cx)
17542        })
17543        .await
17544        .unwrap();
17545    let buffer_3 = project
17546        .update(cx, |project, cx| {
17547            project.open_buffer((worktree_id, "third.rs"), cx)
17548        })
17549        .await
17550        .unwrap();
17551
17552    let multi_buffer = cx.new(|cx| {
17553        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17554        multi_buffer.push_excerpts(
17555            buffer_1.clone(),
17556            [
17557                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17558                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17559                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17560            ],
17561            cx,
17562        );
17563        multi_buffer.push_excerpts(
17564            buffer_2.clone(),
17565            [
17566                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17567                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17568                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17569            ],
17570            cx,
17571        );
17572        multi_buffer.push_excerpts(
17573            buffer_3.clone(),
17574            [
17575                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17576                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17577                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17578            ],
17579            cx,
17580        );
17581        multi_buffer
17582    });
17583    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17584        Editor::new(
17585            EditorMode::full(),
17586            multi_buffer.clone(),
17587            Some(project.clone()),
17588            window,
17589            cx,
17590        )
17591    });
17592
17593    assert_eq!(
17594        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17595        "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17596    );
17597
17598    multi_buffer_editor.update(cx, |editor, cx| {
17599        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17600    });
17601    assert_eq!(
17602        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17603        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17604        "After folding the first buffer, its text should not be displayed"
17605    );
17606
17607    multi_buffer_editor.update(cx, |editor, cx| {
17608        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17609    });
17610    assert_eq!(
17611        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17612        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17613        "After folding the second buffer, its text should not be displayed"
17614    );
17615
17616    multi_buffer_editor.update(cx, |editor, cx| {
17617        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17618    });
17619    assert_eq!(
17620        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17621        "\n\n\n\n\n",
17622        "After folding the third buffer, its text should not be displayed"
17623    );
17624
17625    // Emulate selection inside the fold logic, that should work
17626    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17627        editor
17628            .snapshot(window, cx)
17629            .next_line_boundary(Point::new(0, 4));
17630    });
17631
17632    multi_buffer_editor.update(cx, |editor, cx| {
17633        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17634    });
17635    assert_eq!(
17636        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17637        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17638        "After unfolding the second buffer, its text should be displayed"
17639    );
17640
17641    // Typing inside of buffer 1 causes that buffer to be unfolded.
17642    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17643        assert_eq!(
17644            multi_buffer
17645                .read(cx)
17646                .snapshot(cx)
17647                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17648                .collect::<String>(),
17649            "bbbb"
17650        );
17651        editor.change_selections(None, window, cx, |selections| {
17652            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17653        });
17654        editor.handle_input("B", window, cx);
17655    });
17656
17657    assert_eq!(
17658        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17659        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17660        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17661    );
17662
17663    multi_buffer_editor.update(cx, |editor, cx| {
17664        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17665    });
17666    assert_eq!(
17667        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17668        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17669        "After unfolding the all buffers, all original text should be displayed"
17670    );
17671}
17672
17673#[gpui::test]
17674async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17675    init_test(cx, |_| {});
17676
17677    let sample_text_1 = "1111\n2222\n3333".to_string();
17678    let sample_text_2 = "4444\n5555\n6666".to_string();
17679    let sample_text_3 = "7777\n8888\n9999".to_string();
17680
17681    let fs = FakeFs::new(cx.executor());
17682    fs.insert_tree(
17683        path!("/a"),
17684        json!({
17685            "first.rs": sample_text_1,
17686            "second.rs": sample_text_2,
17687            "third.rs": sample_text_3,
17688        }),
17689    )
17690    .await;
17691    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17692    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17693    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17694    let worktree = project.update(cx, |project, cx| {
17695        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17696        assert_eq!(worktrees.len(), 1);
17697        worktrees.pop().unwrap()
17698    });
17699    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17700
17701    let buffer_1 = project
17702        .update(cx, |project, cx| {
17703            project.open_buffer((worktree_id, "first.rs"), cx)
17704        })
17705        .await
17706        .unwrap();
17707    let buffer_2 = project
17708        .update(cx, |project, cx| {
17709            project.open_buffer((worktree_id, "second.rs"), cx)
17710        })
17711        .await
17712        .unwrap();
17713    let buffer_3 = project
17714        .update(cx, |project, cx| {
17715            project.open_buffer((worktree_id, "third.rs"), cx)
17716        })
17717        .await
17718        .unwrap();
17719
17720    let multi_buffer = cx.new(|cx| {
17721        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17722        multi_buffer.push_excerpts(
17723            buffer_1.clone(),
17724            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17725            cx,
17726        );
17727        multi_buffer.push_excerpts(
17728            buffer_2.clone(),
17729            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17730            cx,
17731        );
17732        multi_buffer.push_excerpts(
17733            buffer_3.clone(),
17734            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17735            cx,
17736        );
17737        multi_buffer
17738    });
17739
17740    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17741        Editor::new(
17742            EditorMode::full(),
17743            multi_buffer,
17744            Some(project.clone()),
17745            window,
17746            cx,
17747        )
17748    });
17749
17750    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17751    assert_eq!(
17752        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17753        full_text,
17754    );
17755
17756    multi_buffer_editor.update(cx, |editor, cx| {
17757        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17758    });
17759    assert_eq!(
17760        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17761        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17762        "After folding the first buffer, its text should not be displayed"
17763    );
17764
17765    multi_buffer_editor.update(cx, |editor, cx| {
17766        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17767    });
17768
17769    assert_eq!(
17770        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17771        "\n\n\n\n\n\n7777\n8888\n9999",
17772        "After folding the second buffer, its text should not be displayed"
17773    );
17774
17775    multi_buffer_editor.update(cx, |editor, cx| {
17776        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17777    });
17778    assert_eq!(
17779        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17780        "\n\n\n\n\n",
17781        "After folding the third buffer, its text should not be displayed"
17782    );
17783
17784    multi_buffer_editor.update(cx, |editor, cx| {
17785        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17786    });
17787    assert_eq!(
17788        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17789        "\n\n\n\n4444\n5555\n6666\n\n",
17790        "After unfolding the second buffer, its text should be displayed"
17791    );
17792
17793    multi_buffer_editor.update(cx, |editor, cx| {
17794        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17795    });
17796    assert_eq!(
17797        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17798        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17799        "After unfolding the first buffer, its text should be displayed"
17800    );
17801
17802    multi_buffer_editor.update(cx, |editor, cx| {
17803        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17804    });
17805    assert_eq!(
17806        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17807        full_text,
17808        "After unfolding all buffers, all original text should be displayed"
17809    );
17810}
17811
17812#[gpui::test]
17813async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17814    init_test(cx, |_| {});
17815
17816    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17817
17818    let fs = FakeFs::new(cx.executor());
17819    fs.insert_tree(
17820        path!("/a"),
17821        json!({
17822            "main.rs": sample_text,
17823        }),
17824    )
17825    .await;
17826    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17827    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17828    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17829    let worktree = project.update(cx, |project, cx| {
17830        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17831        assert_eq!(worktrees.len(), 1);
17832        worktrees.pop().unwrap()
17833    });
17834    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17835
17836    let buffer_1 = project
17837        .update(cx, |project, cx| {
17838            project.open_buffer((worktree_id, "main.rs"), cx)
17839        })
17840        .await
17841        .unwrap();
17842
17843    let multi_buffer = cx.new(|cx| {
17844        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17845        multi_buffer.push_excerpts(
17846            buffer_1.clone(),
17847            [ExcerptRange::new(
17848                Point::new(0, 0)
17849                    ..Point::new(
17850                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17851                        0,
17852                    ),
17853            )],
17854            cx,
17855        );
17856        multi_buffer
17857    });
17858    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17859        Editor::new(
17860            EditorMode::full(),
17861            multi_buffer,
17862            Some(project.clone()),
17863            window,
17864            cx,
17865        )
17866    });
17867
17868    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17869    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17870        enum TestHighlight {}
17871        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17872        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17873        editor.highlight_text::<TestHighlight>(
17874            vec![highlight_range.clone()],
17875            HighlightStyle::color(Hsla::green()),
17876            cx,
17877        );
17878        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17879    });
17880
17881    let full_text = format!("\n\n{sample_text}");
17882    assert_eq!(
17883        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17884        full_text,
17885    );
17886}
17887
17888#[gpui::test]
17889async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17890    init_test(cx, |_| {});
17891    cx.update(|cx| {
17892        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17893            "keymaps/default-linux.json",
17894            cx,
17895        )
17896        .unwrap();
17897        cx.bind_keys(default_key_bindings);
17898    });
17899
17900    let (editor, cx) = cx.add_window_view(|window, cx| {
17901        let multi_buffer = MultiBuffer::build_multi(
17902            [
17903                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17904                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17905                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17906                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17907            ],
17908            cx,
17909        );
17910        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17911
17912        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17913        // fold all but the second buffer, so that we test navigating between two
17914        // adjacent folded buffers, as well as folded buffers at the start and
17915        // end the multibuffer
17916        editor.fold_buffer(buffer_ids[0], cx);
17917        editor.fold_buffer(buffer_ids[2], cx);
17918        editor.fold_buffer(buffer_ids[3], cx);
17919
17920        editor
17921    });
17922    cx.simulate_resize(size(px(1000.), px(1000.)));
17923
17924    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17925    cx.assert_excerpts_with_selections(indoc! {"
17926        [EXCERPT]
17927        ˇ[FOLDED]
17928        [EXCERPT]
17929        a1
17930        b1
17931        [EXCERPT]
17932        [FOLDED]
17933        [EXCERPT]
17934        [FOLDED]
17935        "
17936    });
17937    cx.simulate_keystroke("down");
17938    cx.assert_excerpts_with_selections(indoc! {"
17939        [EXCERPT]
17940        [FOLDED]
17941        [EXCERPT]
17942        ˇa1
17943        b1
17944        [EXCERPT]
17945        [FOLDED]
17946        [EXCERPT]
17947        [FOLDED]
17948        "
17949    });
17950    cx.simulate_keystroke("down");
17951    cx.assert_excerpts_with_selections(indoc! {"
17952        [EXCERPT]
17953        [FOLDED]
17954        [EXCERPT]
17955        a1
17956        ˇb1
17957        [EXCERPT]
17958        [FOLDED]
17959        [EXCERPT]
17960        [FOLDED]
17961        "
17962    });
17963    cx.simulate_keystroke("down");
17964    cx.assert_excerpts_with_selections(indoc! {"
17965        [EXCERPT]
17966        [FOLDED]
17967        [EXCERPT]
17968        a1
17969        b1
17970        ˇ[EXCERPT]
17971        [FOLDED]
17972        [EXCERPT]
17973        [FOLDED]
17974        "
17975    });
17976    cx.simulate_keystroke("down");
17977    cx.assert_excerpts_with_selections(indoc! {"
17978        [EXCERPT]
17979        [FOLDED]
17980        [EXCERPT]
17981        a1
17982        b1
17983        [EXCERPT]
17984        ˇ[FOLDED]
17985        [EXCERPT]
17986        [FOLDED]
17987        "
17988    });
17989    for _ in 0..5 {
17990        cx.simulate_keystroke("down");
17991        cx.assert_excerpts_with_selections(indoc! {"
17992            [EXCERPT]
17993            [FOLDED]
17994            [EXCERPT]
17995            a1
17996            b1
17997            [EXCERPT]
17998            [FOLDED]
17999            [EXCERPT]
18000            ˇ[FOLDED]
18001            "
18002        });
18003    }
18004
18005    cx.simulate_keystroke("up");
18006    cx.assert_excerpts_with_selections(indoc! {"
18007        [EXCERPT]
18008        [FOLDED]
18009        [EXCERPT]
18010        a1
18011        b1
18012        [EXCERPT]
18013        ˇ[FOLDED]
18014        [EXCERPT]
18015        [FOLDED]
18016        "
18017    });
18018    cx.simulate_keystroke("up");
18019    cx.assert_excerpts_with_selections(indoc! {"
18020        [EXCERPT]
18021        [FOLDED]
18022        [EXCERPT]
18023        a1
18024        b1
18025        ˇ[EXCERPT]
18026        [FOLDED]
18027        [EXCERPT]
18028        [FOLDED]
18029        "
18030    });
18031    cx.simulate_keystroke("up");
18032    cx.assert_excerpts_with_selections(indoc! {"
18033        [EXCERPT]
18034        [FOLDED]
18035        [EXCERPT]
18036        a1
18037        ˇb1
18038        [EXCERPT]
18039        [FOLDED]
18040        [EXCERPT]
18041        [FOLDED]
18042        "
18043    });
18044    cx.simulate_keystroke("up");
18045    cx.assert_excerpts_with_selections(indoc! {"
18046        [EXCERPT]
18047        [FOLDED]
18048        [EXCERPT]
18049        ˇa1
18050        b1
18051        [EXCERPT]
18052        [FOLDED]
18053        [EXCERPT]
18054        [FOLDED]
18055        "
18056    });
18057    for _ in 0..5 {
18058        cx.simulate_keystroke("up");
18059        cx.assert_excerpts_with_selections(indoc! {"
18060            [EXCERPT]
18061            ˇ[FOLDED]
18062            [EXCERPT]
18063            a1
18064            b1
18065            [EXCERPT]
18066            [FOLDED]
18067            [EXCERPT]
18068            [FOLDED]
18069            "
18070        });
18071    }
18072}
18073
18074#[gpui::test]
18075async fn test_inline_completion_text(cx: &mut TestAppContext) {
18076    init_test(cx, |_| {});
18077
18078    // Simple insertion
18079    assert_highlighted_edits(
18080        "Hello, world!",
18081        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18082        true,
18083        cx,
18084        |highlighted_edits, cx| {
18085            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18086            assert_eq!(highlighted_edits.highlights.len(), 1);
18087            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18088            assert_eq!(
18089                highlighted_edits.highlights[0].1.background_color,
18090                Some(cx.theme().status().created_background)
18091            );
18092        },
18093    )
18094    .await;
18095
18096    // Replacement
18097    assert_highlighted_edits(
18098        "This is a test.",
18099        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18100        false,
18101        cx,
18102        |highlighted_edits, cx| {
18103            assert_eq!(highlighted_edits.text, "That is a test.");
18104            assert_eq!(highlighted_edits.highlights.len(), 1);
18105            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18106            assert_eq!(
18107                highlighted_edits.highlights[0].1.background_color,
18108                Some(cx.theme().status().created_background)
18109            );
18110        },
18111    )
18112    .await;
18113
18114    // Multiple edits
18115    assert_highlighted_edits(
18116        "Hello, world!",
18117        vec![
18118            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18119            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18120        ],
18121        false,
18122        cx,
18123        |highlighted_edits, cx| {
18124            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18125            assert_eq!(highlighted_edits.highlights.len(), 2);
18126            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18127            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18128            assert_eq!(
18129                highlighted_edits.highlights[0].1.background_color,
18130                Some(cx.theme().status().created_background)
18131            );
18132            assert_eq!(
18133                highlighted_edits.highlights[1].1.background_color,
18134                Some(cx.theme().status().created_background)
18135            );
18136        },
18137    )
18138    .await;
18139
18140    // Multiple lines with edits
18141    assert_highlighted_edits(
18142        "First line\nSecond line\nThird line\nFourth line",
18143        vec![
18144            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18145            (
18146                Point::new(2, 0)..Point::new(2, 10),
18147                "New third line".to_string(),
18148            ),
18149            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18150        ],
18151        false,
18152        cx,
18153        |highlighted_edits, cx| {
18154            assert_eq!(
18155                highlighted_edits.text,
18156                "Second modified\nNew third line\nFourth updated line"
18157            );
18158            assert_eq!(highlighted_edits.highlights.len(), 3);
18159            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18160            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18161            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18162            for highlight in &highlighted_edits.highlights {
18163                assert_eq!(
18164                    highlight.1.background_color,
18165                    Some(cx.theme().status().created_background)
18166                );
18167            }
18168        },
18169    )
18170    .await;
18171}
18172
18173#[gpui::test]
18174async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18175    init_test(cx, |_| {});
18176
18177    // Deletion
18178    assert_highlighted_edits(
18179        "Hello, world!",
18180        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18181        true,
18182        cx,
18183        |highlighted_edits, cx| {
18184            assert_eq!(highlighted_edits.text, "Hello, world!");
18185            assert_eq!(highlighted_edits.highlights.len(), 1);
18186            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18187            assert_eq!(
18188                highlighted_edits.highlights[0].1.background_color,
18189                Some(cx.theme().status().deleted_background)
18190            );
18191        },
18192    )
18193    .await;
18194
18195    // Insertion
18196    assert_highlighted_edits(
18197        "Hello, world!",
18198        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18199        true,
18200        cx,
18201        |highlighted_edits, cx| {
18202            assert_eq!(highlighted_edits.highlights.len(), 1);
18203            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18204            assert_eq!(
18205                highlighted_edits.highlights[0].1.background_color,
18206                Some(cx.theme().status().created_background)
18207            );
18208        },
18209    )
18210    .await;
18211}
18212
18213async fn assert_highlighted_edits(
18214    text: &str,
18215    edits: Vec<(Range<Point>, String)>,
18216    include_deletions: bool,
18217    cx: &mut TestAppContext,
18218    assertion_fn: impl Fn(HighlightedText, &App),
18219) {
18220    let window = cx.add_window(|window, cx| {
18221        let buffer = MultiBuffer::build_simple(text, cx);
18222        Editor::new(EditorMode::full(), buffer, None, window, cx)
18223    });
18224    let cx = &mut VisualTestContext::from_window(*window, cx);
18225
18226    let (buffer, snapshot) = window
18227        .update(cx, |editor, _window, cx| {
18228            (
18229                editor.buffer().clone(),
18230                editor.buffer().read(cx).snapshot(cx),
18231            )
18232        })
18233        .unwrap();
18234
18235    let edits = edits
18236        .into_iter()
18237        .map(|(range, edit)| {
18238            (
18239                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18240                edit,
18241            )
18242        })
18243        .collect::<Vec<_>>();
18244
18245    let text_anchor_edits = edits
18246        .clone()
18247        .into_iter()
18248        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18249        .collect::<Vec<_>>();
18250
18251    let edit_preview = window
18252        .update(cx, |_, _window, cx| {
18253            buffer
18254                .read(cx)
18255                .as_singleton()
18256                .unwrap()
18257                .read(cx)
18258                .preview_edits(text_anchor_edits.into(), cx)
18259        })
18260        .unwrap()
18261        .await;
18262
18263    cx.update(|_window, cx| {
18264        let highlighted_edits = inline_completion_edit_text(
18265            &snapshot.as_singleton().unwrap().2,
18266            &edits,
18267            &edit_preview,
18268            include_deletions,
18269            cx,
18270        );
18271        assertion_fn(highlighted_edits, cx)
18272    });
18273}
18274
18275#[track_caller]
18276fn assert_breakpoint(
18277    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18278    path: &Arc<Path>,
18279    expected: Vec<(u32, Breakpoint)>,
18280) {
18281    if expected.len() == 0usize {
18282        assert!(!breakpoints.contains_key(path), "{}", path.display());
18283    } else {
18284        let mut breakpoint = breakpoints
18285            .get(path)
18286            .unwrap()
18287            .into_iter()
18288            .map(|breakpoint| {
18289                (
18290                    breakpoint.row,
18291                    Breakpoint {
18292                        message: breakpoint.message.clone(),
18293                        state: breakpoint.state,
18294                        condition: breakpoint.condition.clone(),
18295                        hit_condition: breakpoint.hit_condition.clone(),
18296                    },
18297                )
18298            })
18299            .collect::<Vec<_>>();
18300
18301        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18302
18303        assert_eq!(expected, breakpoint);
18304    }
18305}
18306
18307fn add_log_breakpoint_at_cursor(
18308    editor: &mut Editor,
18309    log_message: &str,
18310    window: &mut Window,
18311    cx: &mut Context<Editor>,
18312) {
18313    let (anchor, bp) = editor
18314        .breakpoints_at_cursors(window, cx)
18315        .first()
18316        .and_then(|(anchor, bp)| {
18317            if let Some(bp) = bp {
18318                Some((*anchor, bp.clone()))
18319            } else {
18320                None
18321            }
18322        })
18323        .unwrap_or_else(|| {
18324            let cursor_position: Point = editor.selections.newest(cx).head();
18325
18326            let breakpoint_position = editor
18327                .snapshot(window, cx)
18328                .display_snapshot
18329                .buffer_snapshot
18330                .anchor_before(Point::new(cursor_position.row, 0));
18331
18332            (breakpoint_position, Breakpoint::new_log(&log_message))
18333        });
18334
18335    editor.edit_breakpoint_at_anchor(
18336        anchor,
18337        bp,
18338        BreakpointEditAction::EditLogMessage(log_message.into()),
18339        cx,
18340    );
18341}
18342
18343#[gpui::test]
18344async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18345    init_test(cx, |_| {});
18346
18347    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18348    let fs = FakeFs::new(cx.executor());
18349    fs.insert_tree(
18350        path!("/a"),
18351        json!({
18352            "main.rs": sample_text,
18353        }),
18354    )
18355    .await;
18356    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18357    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18358    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18359
18360    let fs = FakeFs::new(cx.executor());
18361    fs.insert_tree(
18362        path!("/a"),
18363        json!({
18364            "main.rs": sample_text,
18365        }),
18366    )
18367    .await;
18368    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18369    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18370    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18371    let worktree_id = workspace
18372        .update(cx, |workspace, _window, cx| {
18373            workspace.project().update(cx, |project, cx| {
18374                project.worktrees(cx).next().unwrap().read(cx).id()
18375            })
18376        })
18377        .unwrap();
18378
18379    let buffer = project
18380        .update(cx, |project, cx| {
18381            project.open_buffer((worktree_id, "main.rs"), cx)
18382        })
18383        .await
18384        .unwrap();
18385
18386    let (editor, cx) = cx.add_window_view(|window, cx| {
18387        Editor::new(
18388            EditorMode::full(),
18389            MultiBuffer::build_from_buffer(buffer, cx),
18390            Some(project.clone()),
18391            window,
18392            cx,
18393        )
18394    });
18395
18396    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18397    let abs_path = project.read_with(cx, |project, cx| {
18398        project
18399            .absolute_path(&project_path, cx)
18400            .map(|path_buf| Arc::from(path_buf.to_owned()))
18401            .unwrap()
18402    });
18403
18404    // assert we can add breakpoint on the first line
18405    editor.update_in(cx, |editor, window, cx| {
18406        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18407        editor.move_to_end(&MoveToEnd, window, cx);
18408        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18409    });
18410
18411    let breakpoints = editor.update(cx, |editor, cx| {
18412        editor
18413            .breakpoint_store()
18414            .as_ref()
18415            .unwrap()
18416            .read(cx)
18417            .all_breakpoints(cx)
18418            .clone()
18419    });
18420
18421    assert_eq!(1, breakpoints.len());
18422    assert_breakpoint(
18423        &breakpoints,
18424        &abs_path,
18425        vec![
18426            (0, Breakpoint::new_standard()),
18427            (3, Breakpoint::new_standard()),
18428        ],
18429    );
18430
18431    editor.update_in(cx, |editor, window, cx| {
18432        editor.move_to_beginning(&MoveToBeginning, window, cx);
18433        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18434    });
18435
18436    let breakpoints = editor.update(cx, |editor, cx| {
18437        editor
18438            .breakpoint_store()
18439            .as_ref()
18440            .unwrap()
18441            .read(cx)
18442            .all_breakpoints(cx)
18443            .clone()
18444    });
18445
18446    assert_eq!(1, breakpoints.len());
18447    assert_breakpoint(
18448        &breakpoints,
18449        &abs_path,
18450        vec![(3, Breakpoint::new_standard())],
18451    );
18452
18453    editor.update_in(cx, |editor, window, cx| {
18454        editor.move_to_end(&MoveToEnd, window, cx);
18455        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18456    });
18457
18458    let breakpoints = editor.update(cx, |editor, cx| {
18459        editor
18460            .breakpoint_store()
18461            .as_ref()
18462            .unwrap()
18463            .read(cx)
18464            .all_breakpoints(cx)
18465            .clone()
18466    });
18467
18468    assert_eq!(0, breakpoints.len());
18469    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18470}
18471
18472#[gpui::test]
18473async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18474    init_test(cx, |_| {});
18475
18476    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18477
18478    let fs = FakeFs::new(cx.executor());
18479    fs.insert_tree(
18480        path!("/a"),
18481        json!({
18482            "main.rs": sample_text,
18483        }),
18484    )
18485    .await;
18486    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18487    let (workspace, cx) =
18488        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18489
18490    let worktree_id = workspace.update(cx, |workspace, cx| {
18491        workspace.project().update(cx, |project, cx| {
18492            project.worktrees(cx).next().unwrap().read(cx).id()
18493        })
18494    });
18495
18496    let buffer = project
18497        .update(cx, |project, cx| {
18498            project.open_buffer((worktree_id, "main.rs"), cx)
18499        })
18500        .await
18501        .unwrap();
18502
18503    let (editor, cx) = cx.add_window_view(|window, cx| {
18504        Editor::new(
18505            EditorMode::full(),
18506            MultiBuffer::build_from_buffer(buffer, cx),
18507            Some(project.clone()),
18508            window,
18509            cx,
18510        )
18511    });
18512
18513    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18514    let abs_path = project.read_with(cx, |project, cx| {
18515        project
18516            .absolute_path(&project_path, cx)
18517            .map(|path_buf| Arc::from(path_buf.to_owned()))
18518            .unwrap()
18519    });
18520
18521    editor.update_in(cx, |editor, window, cx| {
18522        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18523    });
18524
18525    let breakpoints = editor.update(cx, |editor, cx| {
18526        editor
18527            .breakpoint_store()
18528            .as_ref()
18529            .unwrap()
18530            .read(cx)
18531            .all_breakpoints(cx)
18532            .clone()
18533    });
18534
18535    assert_breakpoint(
18536        &breakpoints,
18537        &abs_path,
18538        vec![(0, Breakpoint::new_log("hello world"))],
18539    );
18540
18541    // Removing a log message from a log breakpoint should remove it
18542    editor.update_in(cx, |editor, window, cx| {
18543        add_log_breakpoint_at_cursor(editor, "", window, cx);
18544    });
18545
18546    let breakpoints = editor.update(cx, |editor, cx| {
18547        editor
18548            .breakpoint_store()
18549            .as_ref()
18550            .unwrap()
18551            .read(cx)
18552            .all_breakpoints(cx)
18553            .clone()
18554    });
18555
18556    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18557
18558    editor.update_in(cx, |editor, window, cx| {
18559        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18560        editor.move_to_end(&MoveToEnd, window, cx);
18561        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18562        // Not adding a log message to a standard breakpoint shouldn't remove it
18563        add_log_breakpoint_at_cursor(editor, "", window, cx);
18564    });
18565
18566    let breakpoints = editor.update(cx, |editor, cx| {
18567        editor
18568            .breakpoint_store()
18569            .as_ref()
18570            .unwrap()
18571            .read(cx)
18572            .all_breakpoints(cx)
18573            .clone()
18574    });
18575
18576    assert_breakpoint(
18577        &breakpoints,
18578        &abs_path,
18579        vec![
18580            (0, Breakpoint::new_standard()),
18581            (3, Breakpoint::new_standard()),
18582        ],
18583    );
18584
18585    editor.update_in(cx, |editor, window, cx| {
18586        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18587    });
18588
18589    let breakpoints = editor.update(cx, |editor, cx| {
18590        editor
18591            .breakpoint_store()
18592            .as_ref()
18593            .unwrap()
18594            .read(cx)
18595            .all_breakpoints(cx)
18596            .clone()
18597    });
18598
18599    assert_breakpoint(
18600        &breakpoints,
18601        &abs_path,
18602        vec![
18603            (0, Breakpoint::new_standard()),
18604            (3, Breakpoint::new_log("hello world")),
18605        ],
18606    );
18607
18608    editor.update_in(cx, |editor, window, cx| {
18609        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18610    });
18611
18612    let breakpoints = editor.update(cx, |editor, cx| {
18613        editor
18614            .breakpoint_store()
18615            .as_ref()
18616            .unwrap()
18617            .read(cx)
18618            .all_breakpoints(cx)
18619            .clone()
18620    });
18621
18622    assert_breakpoint(
18623        &breakpoints,
18624        &abs_path,
18625        vec![
18626            (0, Breakpoint::new_standard()),
18627            (3, Breakpoint::new_log("hello Earth!!")),
18628        ],
18629    );
18630}
18631
18632/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18633/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18634/// or when breakpoints were placed out of order. This tests for a regression too
18635#[gpui::test]
18636async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18637    init_test(cx, |_| {});
18638
18639    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18640    let fs = FakeFs::new(cx.executor());
18641    fs.insert_tree(
18642        path!("/a"),
18643        json!({
18644            "main.rs": sample_text,
18645        }),
18646    )
18647    .await;
18648    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18649    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18650    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18651
18652    let fs = FakeFs::new(cx.executor());
18653    fs.insert_tree(
18654        path!("/a"),
18655        json!({
18656            "main.rs": sample_text,
18657        }),
18658    )
18659    .await;
18660    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18661    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18662    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18663    let worktree_id = workspace
18664        .update(cx, |workspace, _window, cx| {
18665            workspace.project().update(cx, |project, cx| {
18666                project.worktrees(cx).next().unwrap().read(cx).id()
18667            })
18668        })
18669        .unwrap();
18670
18671    let buffer = project
18672        .update(cx, |project, cx| {
18673            project.open_buffer((worktree_id, "main.rs"), cx)
18674        })
18675        .await
18676        .unwrap();
18677
18678    let (editor, cx) = cx.add_window_view(|window, cx| {
18679        Editor::new(
18680            EditorMode::full(),
18681            MultiBuffer::build_from_buffer(buffer, cx),
18682            Some(project.clone()),
18683            window,
18684            cx,
18685        )
18686    });
18687
18688    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18689    let abs_path = project.read_with(cx, |project, cx| {
18690        project
18691            .absolute_path(&project_path, cx)
18692            .map(|path_buf| Arc::from(path_buf.to_owned()))
18693            .unwrap()
18694    });
18695
18696    // assert we can add breakpoint on the first line
18697    editor.update_in(cx, |editor, window, cx| {
18698        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18699        editor.move_to_end(&MoveToEnd, window, cx);
18700        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18701        editor.move_up(&MoveUp, window, cx);
18702        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18703    });
18704
18705    let breakpoints = editor.update(cx, |editor, cx| {
18706        editor
18707            .breakpoint_store()
18708            .as_ref()
18709            .unwrap()
18710            .read(cx)
18711            .all_breakpoints(cx)
18712            .clone()
18713    });
18714
18715    assert_eq!(1, breakpoints.len());
18716    assert_breakpoint(
18717        &breakpoints,
18718        &abs_path,
18719        vec![
18720            (0, Breakpoint::new_standard()),
18721            (2, Breakpoint::new_standard()),
18722            (3, Breakpoint::new_standard()),
18723        ],
18724    );
18725
18726    editor.update_in(cx, |editor, window, cx| {
18727        editor.move_to_beginning(&MoveToBeginning, window, cx);
18728        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18729        editor.move_to_end(&MoveToEnd, window, cx);
18730        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18731        // Disabling a breakpoint that doesn't exist should do nothing
18732        editor.move_up(&MoveUp, window, cx);
18733        editor.move_up(&MoveUp, window, cx);
18734        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18735    });
18736
18737    let breakpoints = editor.update(cx, |editor, cx| {
18738        editor
18739            .breakpoint_store()
18740            .as_ref()
18741            .unwrap()
18742            .read(cx)
18743            .all_breakpoints(cx)
18744            .clone()
18745    });
18746
18747    let disable_breakpoint = {
18748        let mut bp = Breakpoint::new_standard();
18749        bp.state = BreakpointState::Disabled;
18750        bp
18751    };
18752
18753    assert_eq!(1, breakpoints.len());
18754    assert_breakpoint(
18755        &breakpoints,
18756        &abs_path,
18757        vec![
18758            (0, disable_breakpoint.clone()),
18759            (2, Breakpoint::new_standard()),
18760            (3, disable_breakpoint.clone()),
18761        ],
18762    );
18763
18764    editor.update_in(cx, |editor, window, cx| {
18765        editor.move_to_beginning(&MoveToBeginning, window, cx);
18766        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18767        editor.move_to_end(&MoveToEnd, window, cx);
18768        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18769        editor.move_up(&MoveUp, window, cx);
18770        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18771    });
18772
18773    let breakpoints = editor.update(cx, |editor, cx| {
18774        editor
18775            .breakpoint_store()
18776            .as_ref()
18777            .unwrap()
18778            .read(cx)
18779            .all_breakpoints(cx)
18780            .clone()
18781    });
18782
18783    assert_eq!(1, breakpoints.len());
18784    assert_breakpoint(
18785        &breakpoints,
18786        &abs_path,
18787        vec![
18788            (0, Breakpoint::new_standard()),
18789            (2, disable_breakpoint),
18790            (3, Breakpoint::new_standard()),
18791        ],
18792    );
18793}
18794
18795#[gpui::test]
18796async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18797    init_test(cx, |_| {});
18798    let capabilities = lsp::ServerCapabilities {
18799        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18800            prepare_provider: Some(true),
18801            work_done_progress_options: Default::default(),
18802        })),
18803        ..Default::default()
18804    };
18805    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18806
18807    cx.set_state(indoc! {"
18808        struct Fˇoo {}
18809    "});
18810
18811    cx.update_editor(|editor, _, cx| {
18812        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18813        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18814        editor.highlight_background::<DocumentHighlightRead>(
18815            &[highlight_range],
18816            |c| c.editor_document_highlight_read_background,
18817            cx,
18818        );
18819    });
18820
18821    let mut prepare_rename_handler = cx
18822        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18823            move |_, _, _| async move {
18824                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18825                    start: lsp::Position {
18826                        line: 0,
18827                        character: 7,
18828                    },
18829                    end: lsp::Position {
18830                        line: 0,
18831                        character: 10,
18832                    },
18833                })))
18834            },
18835        );
18836    let prepare_rename_task = cx
18837        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18838        .expect("Prepare rename was not started");
18839    prepare_rename_handler.next().await.unwrap();
18840    prepare_rename_task.await.expect("Prepare rename failed");
18841
18842    let mut rename_handler =
18843        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18844            let edit = lsp::TextEdit {
18845                range: lsp::Range {
18846                    start: lsp::Position {
18847                        line: 0,
18848                        character: 7,
18849                    },
18850                    end: lsp::Position {
18851                        line: 0,
18852                        character: 10,
18853                    },
18854                },
18855                new_text: "FooRenamed".to_string(),
18856            };
18857            Ok(Some(lsp::WorkspaceEdit::new(
18858                // Specify the same edit twice
18859                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18860            )))
18861        });
18862    let rename_task = cx
18863        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18864        .expect("Confirm rename was not started");
18865    rename_handler.next().await.unwrap();
18866    rename_task.await.expect("Confirm rename failed");
18867    cx.run_until_parked();
18868
18869    // Despite two edits, only one is actually applied as those are identical
18870    cx.assert_editor_state(indoc! {"
18871        struct FooRenamedˇ {}
18872    "});
18873}
18874
18875#[gpui::test]
18876async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18877    init_test(cx, |_| {});
18878    // These capabilities indicate that the server does not support prepare rename.
18879    let capabilities = lsp::ServerCapabilities {
18880        rename_provider: Some(lsp::OneOf::Left(true)),
18881        ..Default::default()
18882    };
18883    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18884
18885    cx.set_state(indoc! {"
18886        struct Fˇoo {}
18887    "});
18888
18889    cx.update_editor(|editor, _window, cx| {
18890        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18891        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18892        editor.highlight_background::<DocumentHighlightRead>(
18893            &[highlight_range],
18894            |c| c.editor_document_highlight_read_background,
18895            cx,
18896        );
18897    });
18898
18899    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18900        .expect("Prepare rename was not started")
18901        .await
18902        .expect("Prepare rename failed");
18903
18904    let mut rename_handler =
18905        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18906            let edit = lsp::TextEdit {
18907                range: lsp::Range {
18908                    start: lsp::Position {
18909                        line: 0,
18910                        character: 7,
18911                    },
18912                    end: lsp::Position {
18913                        line: 0,
18914                        character: 10,
18915                    },
18916                },
18917                new_text: "FooRenamed".to_string(),
18918            };
18919            Ok(Some(lsp::WorkspaceEdit::new(
18920                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18921            )))
18922        });
18923    let rename_task = cx
18924        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18925        .expect("Confirm rename was not started");
18926    rename_handler.next().await.unwrap();
18927    rename_task.await.expect("Confirm rename failed");
18928    cx.run_until_parked();
18929
18930    // Correct range is renamed, as `surrounding_word` is used to find it.
18931    cx.assert_editor_state(indoc! {"
18932        struct FooRenamedˇ {}
18933    "});
18934}
18935
18936#[gpui::test]
18937async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18938    init_test(cx, |_| {});
18939    let mut cx = EditorTestContext::new(cx).await;
18940
18941    let language = Arc::new(
18942        Language::new(
18943            LanguageConfig::default(),
18944            Some(tree_sitter_html::LANGUAGE.into()),
18945        )
18946        .with_brackets_query(
18947            r#"
18948            ("<" @open "/>" @close)
18949            ("</" @open ">" @close)
18950            ("<" @open ">" @close)
18951            ("\"" @open "\"" @close)
18952            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18953        "#,
18954        )
18955        .unwrap(),
18956    );
18957    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18958
18959    cx.set_state(indoc! {"
18960        <span>ˇ</span>
18961    "});
18962    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18963    cx.assert_editor_state(indoc! {"
18964        <span>
18965        ˇ
18966        </span>
18967    "});
18968
18969    cx.set_state(indoc! {"
18970        <span><span></span>ˇ</span>
18971    "});
18972    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18973    cx.assert_editor_state(indoc! {"
18974        <span><span></span>
18975        ˇ</span>
18976    "});
18977
18978    cx.set_state(indoc! {"
18979        <span>ˇ
18980        </span>
18981    "});
18982    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18983    cx.assert_editor_state(indoc! {"
18984        <span>
18985        ˇ
18986        </span>
18987    "});
18988}
18989
18990#[gpui::test(iterations = 10)]
18991async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18992    init_test(cx, |_| {});
18993
18994    let fs = FakeFs::new(cx.executor());
18995    fs.insert_tree(
18996        path!("/dir"),
18997        json!({
18998            "a.ts": "a",
18999        }),
19000    )
19001    .await;
19002
19003    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19004    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19005    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19006
19007    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19008    language_registry.add(Arc::new(Language::new(
19009        LanguageConfig {
19010            name: "TypeScript".into(),
19011            matcher: LanguageMatcher {
19012                path_suffixes: vec!["ts".to_string()],
19013                ..Default::default()
19014            },
19015            ..Default::default()
19016        },
19017        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19018    )));
19019    let mut fake_language_servers = language_registry.register_fake_lsp(
19020        "TypeScript",
19021        FakeLspAdapter {
19022            capabilities: lsp::ServerCapabilities {
19023                code_lens_provider: Some(lsp::CodeLensOptions {
19024                    resolve_provider: Some(true),
19025                }),
19026                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19027                    commands: vec!["_the/command".to_string()],
19028                    ..lsp::ExecuteCommandOptions::default()
19029                }),
19030                ..lsp::ServerCapabilities::default()
19031            },
19032            ..FakeLspAdapter::default()
19033        },
19034    );
19035
19036    let (buffer, _handle) = project
19037        .update(cx, |p, cx| {
19038            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19039        })
19040        .await
19041        .unwrap();
19042    cx.executor().run_until_parked();
19043
19044    let fake_server = fake_language_servers.next().await.unwrap();
19045
19046    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19047    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19048    drop(buffer_snapshot);
19049    let actions = cx
19050        .update_window(*workspace, |_, window, cx| {
19051            project.code_actions(&buffer, anchor..anchor, window, cx)
19052        })
19053        .unwrap();
19054
19055    fake_server
19056        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19057            Ok(Some(vec![
19058                lsp::CodeLens {
19059                    range: lsp::Range::default(),
19060                    command: Some(lsp::Command {
19061                        title: "Code lens command".to_owned(),
19062                        command: "_the/command".to_owned(),
19063                        arguments: None,
19064                    }),
19065                    data: None,
19066                },
19067                lsp::CodeLens {
19068                    range: lsp::Range::default(),
19069                    command: Some(lsp::Command {
19070                        title: "Command not in capabilities".to_owned(),
19071                        command: "not in capabilities".to_owned(),
19072                        arguments: None,
19073                    }),
19074                    data: None,
19075                },
19076                lsp::CodeLens {
19077                    range: lsp::Range {
19078                        start: lsp::Position {
19079                            line: 1,
19080                            character: 1,
19081                        },
19082                        end: lsp::Position {
19083                            line: 1,
19084                            character: 1,
19085                        },
19086                    },
19087                    command: Some(lsp::Command {
19088                        title: "Command not in range".to_owned(),
19089                        command: "_the/command".to_owned(),
19090                        arguments: None,
19091                    }),
19092                    data: None,
19093                },
19094            ]))
19095        })
19096        .next()
19097        .await;
19098
19099    let actions = actions.await.unwrap();
19100    assert_eq!(
19101        actions.len(),
19102        1,
19103        "Should have only one valid action for the 0..0 range"
19104    );
19105    let action = actions[0].clone();
19106    let apply = project.update(cx, |project, cx| {
19107        project.apply_code_action(buffer.clone(), action, true, cx)
19108    });
19109
19110    // Resolving the code action does not populate its edits. In absence of
19111    // edits, we must execute the given command.
19112    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19113        |mut lens, _| async move {
19114            let lens_command = lens.command.as_mut().expect("should have a command");
19115            assert_eq!(lens_command.title, "Code lens command");
19116            lens_command.arguments = Some(vec![json!("the-argument")]);
19117            Ok(lens)
19118        },
19119    );
19120
19121    // While executing the command, the language server sends the editor
19122    // a `workspaceEdit` request.
19123    fake_server
19124        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19125            let fake = fake_server.clone();
19126            move |params, _| {
19127                assert_eq!(params.command, "_the/command");
19128                let fake = fake.clone();
19129                async move {
19130                    fake.server
19131                        .request::<lsp::request::ApplyWorkspaceEdit>(
19132                            lsp::ApplyWorkspaceEditParams {
19133                                label: None,
19134                                edit: lsp::WorkspaceEdit {
19135                                    changes: Some(
19136                                        [(
19137                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19138                                            vec![lsp::TextEdit {
19139                                                range: lsp::Range::new(
19140                                                    lsp::Position::new(0, 0),
19141                                                    lsp::Position::new(0, 0),
19142                                                ),
19143                                                new_text: "X".into(),
19144                                            }],
19145                                        )]
19146                                        .into_iter()
19147                                        .collect(),
19148                                    ),
19149                                    ..Default::default()
19150                                },
19151                            },
19152                        )
19153                        .await
19154                        .unwrap();
19155                    Ok(Some(json!(null)))
19156                }
19157            }
19158        })
19159        .next()
19160        .await;
19161
19162    // Applying the code lens command returns a project transaction containing the edits
19163    // sent by the language server in its `workspaceEdit` request.
19164    let transaction = apply.await.unwrap();
19165    assert!(transaction.0.contains_key(&buffer));
19166    buffer.update(cx, |buffer, cx| {
19167        assert_eq!(buffer.text(), "Xa");
19168        buffer.undo(cx);
19169        assert_eq!(buffer.text(), "a");
19170    });
19171}
19172
19173#[gpui::test]
19174async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19175    init_test(cx, |_| {});
19176
19177    let fs = FakeFs::new(cx.executor());
19178    let main_text = r#"fn main() {
19179println!("1");
19180println!("2");
19181println!("3");
19182println!("4");
19183println!("5");
19184}"#;
19185    let lib_text = "mod foo {}";
19186    fs.insert_tree(
19187        path!("/a"),
19188        json!({
19189            "lib.rs": lib_text,
19190            "main.rs": main_text,
19191        }),
19192    )
19193    .await;
19194
19195    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19196    let (workspace, cx) =
19197        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19198    let worktree_id = workspace.update(cx, |workspace, cx| {
19199        workspace.project().update(cx, |project, cx| {
19200            project.worktrees(cx).next().unwrap().read(cx).id()
19201        })
19202    });
19203
19204    let expected_ranges = vec![
19205        Point::new(0, 0)..Point::new(0, 0),
19206        Point::new(1, 0)..Point::new(1, 1),
19207        Point::new(2, 0)..Point::new(2, 2),
19208        Point::new(3, 0)..Point::new(3, 3),
19209    ];
19210
19211    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19212    let editor_1 = workspace
19213        .update_in(cx, |workspace, window, cx| {
19214            workspace.open_path(
19215                (worktree_id, "main.rs"),
19216                Some(pane_1.downgrade()),
19217                true,
19218                window,
19219                cx,
19220            )
19221        })
19222        .unwrap()
19223        .await
19224        .downcast::<Editor>()
19225        .unwrap();
19226    pane_1.update(cx, |pane, cx| {
19227        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19228        open_editor.update(cx, |editor, cx| {
19229            assert_eq!(
19230                editor.display_text(cx),
19231                main_text,
19232                "Original main.rs text on initial open",
19233            );
19234            assert_eq!(
19235                editor
19236                    .selections
19237                    .all::<Point>(cx)
19238                    .into_iter()
19239                    .map(|s| s.range())
19240                    .collect::<Vec<_>>(),
19241                vec![Point::zero()..Point::zero()],
19242                "Default selections on initial open",
19243            );
19244        })
19245    });
19246    editor_1.update_in(cx, |editor, window, cx| {
19247        editor.change_selections(None, window, cx, |s| {
19248            s.select_ranges(expected_ranges.clone());
19249        });
19250    });
19251
19252    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19253        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19254    });
19255    let editor_2 = workspace
19256        .update_in(cx, |workspace, window, cx| {
19257            workspace.open_path(
19258                (worktree_id, "main.rs"),
19259                Some(pane_2.downgrade()),
19260                true,
19261                window,
19262                cx,
19263            )
19264        })
19265        .unwrap()
19266        .await
19267        .downcast::<Editor>()
19268        .unwrap();
19269    pane_2.update(cx, |pane, cx| {
19270        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19271        open_editor.update(cx, |editor, cx| {
19272            assert_eq!(
19273                editor.display_text(cx),
19274                main_text,
19275                "Original main.rs text on initial open in another panel",
19276            );
19277            assert_eq!(
19278                editor
19279                    .selections
19280                    .all::<Point>(cx)
19281                    .into_iter()
19282                    .map(|s| s.range())
19283                    .collect::<Vec<_>>(),
19284                vec![Point::zero()..Point::zero()],
19285                "Default selections on initial open in another panel",
19286            );
19287        })
19288    });
19289
19290    editor_2.update_in(cx, |editor, window, cx| {
19291        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19292    });
19293
19294    let _other_editor_1 = workspace
19295        .update_in(cx, |workspace, window, cx| {
19296            workspace.open_path(
19297                (worktree_id, "lib.rs"),
19298                Some(pane_1.downgrade()),
19299                true,
19300                window,
19301                cx,
19302            )
19303        })
19304        .unwrap()
19305        .await
19306        .downcast::<Editor>()
19307        .unwrap();
19308    pane_1
19309        .update_in(cx, |pane, window, cx| {
19310            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19311                .unwrap()
19312        })
19313        .await
19314        .unwrap();
19315    drop(editor_1);
19316    pane_1.update(cx, |pane, cx| {
19317        pane.active_item()
19318            .unwrap()
19319            .downcast::<Editor>()
19320            .unwrap()
19321            .update(cx, |editor, cx| {
19322                assert_eq!(
19323                    editor.display_text(cx),
19324                    lib_text,
19325                    "Other file should be open and active",
19326                );
19327            });
19328        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19329    });
19330
19331    let _other_editor_2 = workspace
19332        .update_in(cx, |workspace, window, cx| {
19333            workspace.open_path(
19334                (worktree_id, "lib.rs"),
19335                Some(pane_2.downgrade()),
19336                true,
19337                window,
19338                cx,
19339            )
19340        })
19341        .unwrap()
19342        .await
19343        .downcast::<Editor>()
19344        .unwrap();
19345    pane_2
19346        .update_in(cx, |pane, window, cx| {
19347            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19348                .unwrap()
19349        })
19350        .await
19351        .unwrap();
19352    drop(editor_2);
19353    pane_2.update(cx, |pane, cx| {
19354        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19355        open_editor.update(cx, |editor, cx| {
19356            assert_eq!(
19357                editor.display_text(cx),
19358                lib_text,
19359                "Other file should be open and active in another panel too",
19360            );
19361        });
19362        assert_eq!(
19363            pane.items().count(),
19364            1,
19365            "No other editors should be open in another pane",
19366        );
19367    });
19368
19369    let _editor_1_reopened = workspace
19370        .update_in(cx, |workspace, window, cx| {
19371            workspace.open_path(
19372                (worktree_id, "main.rs"),
19373                Some(pane_1.downgrade()),
19374                true,
19375                window,
19376                cx,
19377            )
19378        })
19379        .unwrap()
19380        .await
19381        .downcast::<Editor>()
19382        .unwrap();
19383    let _editor_2_reopened = workspace
19384        .update_in(cx, |workspace, window, cx| {
19385            workspace.open_path(
19386                (worktree_id, "main.rs"),
19387                Some(pane_2.downgrade()),
19388                true,
19389                window,
19390                cx,
19391            )
19392        })
19393        .unwrap()
19394        .await
19395        .downcast::<Editor>()
19396        .unwrap();
19397    pane_1.update(cx, |pane, cx| {
19398        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19399        open_editor.update(cx, |editor, cx| {
19400            assert_eq!(
19401                editor.display_text(cx),
19402                main_text,
19403                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19404            );
19405            assert_eq!(
19406                editor
19407                    .selections
19408                    .all::<Point>(cx)
19409                    .into_iter()
19410                    .map(|s| s.range())
19411                    .collect::<Vec<_>>(),
19412                expected_ranges,
19413                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19414            );
19415        })
19416    });
19417    pane_2.update(cx, |pane, cx| {
19418        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19419        open_editor.update(cx, |editor, cx| {
19420            assert_eq!(
19421                editor.display_text(cx),
19422                r#"fn main() {
19423⋯rintln!("1");
19424⋯intln!("2");
19425⋯ntln!("3");
19426println!("4");
19427println!("5");
19428}"#,
19429                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19430            );
19431            assert_eq!(
19432                editor
19433                    .selections
19434                    .all::<Point>(cx)
19435                    .into_iter()
19436                    .map(|s| s.range())
19437                    .collect::<Vec<_>>(),
19438                vec![Point::zero()..Point::zero()],
19439                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19440            );
19441        })
19442    });
19443}
19444
19445#[gpui::test]
19446async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19447    init_test(cx, |_| {});
19448
19449    let fs = FakeFs::new(cx.executor());
19450    let main_text = r#"fn main() {
19451println!("1");
19452println!("2");
19453println!("3");
19454println!("4");
19455println!("5");
19456}"#;
19457    let lib_text = "mod foo {}";
19458    fs.insert_tree(
19459        path!("/a"),
19460        json!({
19461            "lib.rs": lib_text,
19462            "main.rs": main_text,
19463        }),
19464    )
19465    .await;
19466
19467    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19468    let (workspace, cx) =
19469        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19470    let worktree_id = workspace.update(cx, |workspace, cx| {
19471        workspace.project().update(cx, |project, cx| {
19472            project.worktrees(cx).next().unwrap().read(cx).id()
19473        })
19474    });
19475
19476    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19477    let editor = workspace
19478        .update_in(cx, |workspace, window, cx| {
19479            workspace.open_path(
19480                (worktree_id, "main.rs"),
19481                Some(pane.downgrade()),
19482                true,
19483                window,
19484                cx,
19485            )
19486        })
19487        .unwrap()
19488        .await
19489        .downcast::<Editor>()
19490        .unwrap();
19491    pane.update(cx, |pane, cx| {
19492        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19493        open_editor.update(cx, |editor, cx| {
19494            assert_eq!(
19495                editor.display_text(cx),
19496                main_text,
19497                "Original main.rs text on initial open",
19498            );
19499        })
19500    });
19501    editor.update_in(cx, |editor, window, cx| {
19502        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19503    });
19504
19505    cx.update_global(|store: &mut SettingsStore, cx| {
19506        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19507            s.restore_on_file_reopen = Some(false);
19508        });
19509    });
19510    editor.update_in(cx, |editor, window, cx| {
19511        editor.fold_ranges(
19512            vec![
19513                Point::new(1, 0)..Point::new(1, 1),
19514                Point::new(2, 0)..Point::new(2, 2),
19515                Point::new(3, 0)..Point::new(3, 3),
19516            ],
19517            false,
19518            window,
19519            cx,
19520        );
19521    });
19522    pane.update_in(cx, |pane, window, cx| {
19523        pane.close_all_items(&CloseAllItems::default(), window, cx)
19524            .unwrap()
19525    })
19526    .await
19527    .unwrap();
19528    pane.update(cx, |pane, _| {
19529        assert!(pane.active_item().is_none());
19530    });
19531    cx.update_global(|store: &mut SettingsStore, cx| {
19532        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19533            s.restore_on_file_reopen = Some(true);
19534        });
19535    });
19536
19537    let _editor_reopened = workspace
19538        .update_in(cx, |workspace, window, cx| {
19539            workspace.open_path(
19540                (worktree_id, "main.rs"),
19541                Some(pane.downgrade()),
19542                true,
19543                window,
19544                cx,
19545            )
19546        })
19547        .unwrap()
19548        .await
19549        .downcast::<Editor>()
19550        .unwrap();
19551    pane.update(cx, |pane, cx| {
19552        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19553        open_editor.update(cx, |editor, cx| {
19554            assert_eq!(
19555                editor.display_text(cx),
19556                main_text,
19557                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19558            );
19559        })
19560    });
19561}
19562
19563#[gpui::test]
19564async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19565    struct EmptyModalView {
19566        focus_handle: gpui::FocusHandle,
19567    }
19568    impl EventEmitter<DismissEvent> for EmptyModalView {}
19569    impl Render for EmptyModalView {
19570        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19571            div()
19572        }
19573    }
19574    impl Focusable for EmptyModalView {
19575        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19576            self.focus_handle.clone()
19577        }
19578    }
19579    impl workspace::ModalView for EmptyModalView {}
19580    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19581        EmptyModalView {
19582            focus_handle: cx.focus_handle(),
19583        }
19584    }
19585
19586    init_test(cx, |_| {});
19587
19588    let fs = FakeFs::new(cx.executor());
19589    let project = Project::test(fs, [], cx).await;
19590    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19591    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19592    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19593    let editor = cx.new_window_entity(|window, cx| {
19594        Editor::new(
19595            EditorMode::full(),
19596            buffer,
19597            Some(project.clone()),
19598            window,
19599            cx,
19600        )
19601    });
19602    workspace
19603        .update(cx, |workspace, window, cx| {
19604            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19605        })
19606        .unwrap();
19607    editor.update_in(cx, |editor, window, cx| {
19608        editor.open_context_menu(&OpenContextMenu, window, cx);
19609        assert!(editor.mouse_context_menu.is_some());
19610    });
19611    workspace
19612        .update(cx, |workspace, window, cx| {
19613            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19614        })
19615        .unwrap();
19616    cx.read(|cx| {
19617        assert!(editor.read(cx).mouse_context_menu.is_none());
19618    });
19619}
19620
19621#[gpui::test]
19622async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19623    init_test(cx, |_| {});
19624
19625    let fs = FakeFs::new(cx.executor());
19626    fs.insert_file(path!("/file.html"), Default::default())
19627        .await;
19628
19629    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19630
19631    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19632    let html_language = Arc::new(Language::new(
19633        LanguageConfig {
19634            name: "HTML".into(),
19635            matcher: LanguageMatcher {
19636                path_suffixes: vec!["html".to_string()],
19637                ..LanguageMatcher::default()
19638            },
19639            brackets: BracketPairConfig {
19640                pairs: vec![BracketPair {
19641                    start: "<".into(),
19642                    end: ">".into(),
19643                    close: true,
19644                    ..Default::default()
19645                }],
19646                ..Default::default()
19647            },
19648            ..Default::default()
19649        },
19650        Some(tree_sitter_html::LANGUAGE.into()),
19651    ));
19652    language_registry.add(html_language);
19653    let mut fake_servers = language_registry.register_fake_lsp(
19654        "HTML",
19655        FakeLspAdapter {
19656            capabilities: lsp::ServerCapabilities {
19657                completion_provider: Some(lsp::CompletionOptions {
19658                    resolve_provider: Some(true),
19659                    ..Default::default()
19660                }),
19661                ..Default::default()
19662            },
19663            ..Default::default()
19664        },
19665    );
19666
19667    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19668    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19669
19670    let worktree_id = workspace
19671        .update(cx, |workspace, _window, cx| {
19672            workspace.project().update(cx, |project, cx| {
19673                project.worktrees(cx).next().unwrap().read(cx).id()
19674            })
19675        })
19676        .unwrap();
19677    project
19678        .update(cx, |project, cx| {
19679            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19680        })
19681        .await
19682        .unwrap();
19683    let editor = workspace
19684        .update(cx, |workspace, window, cx| {
19685            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19686        })
19687        .unwrap()
19688        .await
19689        .unwrap()
19690        .downcast::<Editor>()
19691        .unwrap();
19692
19693    let fake_server = fake_servers.next().await.unwrap();
19694    editor.update_in(cx, |editor, window, cx| {
19695        editor.set_text("<ad></ad>", window, cx);
19696        editor.change_selections(None, window, cx, |selections| {
19697            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19698        });
19699        let Some((buffer, _)) = editor
19700            .buffer
19701            .read(cx)
19702            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19703        else {
19704            panic!("Failed to get buffer for selection position");
19705        };
19706        let buffer = buffer.read(cx);
19707        let buffer_id = buffer.remote_id();
19708        let opening_range =
19709            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19710        let closing_range =
19711            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19712        let mut linked_ranges = HashMap::default();
19713        linked_ranges.insert(
19714            buffer_id,
19715            vec![(opening_range.clone(), vec![closing_range.clone()])],
19716        );
19717        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19718    });
19719    let mut completion_handle =
19720        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19721            Ok(Some(lsp::CompletionResponse::Array(vec![
19722                lsp::CompletionItem {
19723                    label: "head".to_string(),
19724                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19725                        lsp::InsertReplaceEdit {
19726                            new_text: "head".to_string(),
19727                            insert: lsp::Range::new(
19728                                lsp::Position::new(0, 1),
19729                                lsp::Position::new(0, 3),
19730                            ),
19731                            replace: lsp::Range::new(
19732                                lsp::Position::new(0, 1),
19733                                lsp::Position::new(0, 3),
19734                            ),
19735                        },
19736                    )),
19737                    ..Default::default()
19738                },
19739            ])))
19740        });
19741    editor.update_in(cx, |editor, window, cx| {
19742        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19743    });
19744    cx.run_until_parked();
19745    completion_handle.next().await.unwrap();
19746    editor.update(cx, |editor, _| {
19747        assert!(
19748            editor.context_menu_visible(),
19749            "Completion menu should be visible"
19750        );
19751    });
19752    editor.update_in(cx, |editor, window, cx| {
19753        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19754    });
19755    cx.executor().run_until_parked();
19756    editor.update(cx, |editor, cx| {
19757        assert_eq!(editor.text(cx), "<head></head>");
19758    });
19759}
19760
19761fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19762    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19763    point..point
19764}
19765
19766fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19767    let (text, ranges) = marked_text_ranges(marked_text, true);
19768    assert_eq!(editor.text(cx), text);
19769    assert_eq!(
19770        editor.selections.ranges(cx),
19771        ranges,
19772        "Assert selections are {}",
19773        marked_text
19774    );
19775}
19776
19777pub fn handle_signature_help_request(
19778    cx: &mut EditorLspTestContext,
19779    mocked_response: lsp::SignatureHelp,
19780) -> impl Future<Output = ()> + use<> {
19781    let mut request =
19782        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19783            let mocked_response = mocked_response.clone();
19784            async move { Ok(Some(mocked_response)) }
19785        });
19786
19787    async move {
19788        request.next().await;
19789    }
19790}
19791
19792/// Handle completion request passing a marked string specifying where the completion
19793/// should be triggered from using '|' character, what range should be replaced, and what completions
19794/// should be returned using '<' and '>' to delimit the range.
19795///
19796/// Also see `handle_completion_request_with_insert_and_replace`.
19797#[track_caller]
19798pub fn handle_completion_request(
19799    cx: &mut EditorLspTestContext,
19800    marked_string: &str,
19801    completions: Vec<&'static str>,
19802    counter: Arc<AtomicUsize>,
19803) -> impl Future<Output = ()> {
19804    let complete_from_marker: TextRangeMarker = '|'.into();
19805    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19806    let (_, mut marked_ranges) = marked_text_ranges_by(
19807        marked_string,
19808        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19809    );
19810
19811    let complete_from_position =
19812        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19813    let replace_range =
19814        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19815
19816    let mut request =
19817        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19818            let completions = completions.clone();
19819            counter.fetch_add(1, atomic::Ordering::Release);
19820            async move {
19821                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19822                assert_eq!(
19823                    params.text_document_position.position,
19824                    complete_from_position
19825                );
19826                Ok(Some(lsp::CompletionResponse::Array(
19827                    completions
19828                        .iter()
19829                        .map(|completion_text| lsp::CompletionItem {
19830                            label: completion_text.to_string(),
19831                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19832                                range: replace_range,
19833                                new_text: completion_text.to_string(),
19834                            })),
19835                            ..Default::default()
19836                        })
19837                        .collect(),
19838                )))
19839            }
19840        });
19841
19842    async move {
19843        request.next().await;
19844    }
19845}
19846
19847/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19848/// given instead, which also contains an `insert` range.
19849///
19850/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19851/// that is, `replace_range.start..cursor_pos`.
19852pub fn handle_completion_request_with_insert_and_replace(
19853    cx: &mut EditorLspTestContext,
19854    marked_string: &str,
19855    completions: Vec<&'static str>,
19856    counter: Arc<AtomicUsize>,
19857) -> impl Future<Output = ()> {
19858    let complete_from_marker: TextRangeMarker = '|'.into();
19859    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19860    let (_, mut marked_ranges) = marked_text_ranges_by(
19861        marked_string,
19862        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19863    );
19864
19865    let complete_from_position =
19866        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19867    let replace_range =
19868        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19869
19870    let mut request =
19871        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19872            let completions = completions.clone();
19873            counter.fetch_add(1, atomic::Ordering::Release);
19874            async move {
19875                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19876                assert_eq!(
19877                    params.text_document_position.position, complete_from_position,
19878                    "marker `|` position doesn't match",
19879                );
19880                Ok(Some(lsp::CompletionResponse::Array(
19881                    completions
19882                        .iter()
19883                        .map(|completion_text| lsp::CompletionItem {
19884                            label: completion_text.to_string(),
19885                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19886                                lsp::InsertReplaceEdit {
19887                                    insert: lsp::Range {
19888                                        start: replace_range.start,
19889                                        end: complete_from_position,
19890                                    },
19891                                    replace: replace_range,
19892                                    new_text: completion_text.to_string(),
19893                                },
19894                            )),
19895                            ..Default::default()
19896                        })
19897                        .collect(),
19898                )))
19899            }
19900        });
19901
19902    async move {
19903        request.next().await;
19904    }
19905}
19906
19907fn handle_resolve_completion_request(
19908    cx: &mut EditorLspTestContext,
19909    edits: Option<Vec<(&'static str, &'static str)>>,
19910) -> impl Future<Output = ()> {
19911    let edits = edits.map(|edits| {
19912        edits
19913            .iter()
19914            .map(|(marked_string, new_text)| {
19915                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19916                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19917                lsp::TextEdit::new(replace_range, new_text.to_string())
19918            })
19919            .collect::<Vec<_>>()
19920    });
19921
19922    let mut request =
19923        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19924            let edits = edits.clone();
19925            async move {
19926                Ok(lsp::CompletionItem {
19927                    additional_text_edits: edits,
19928                    ..Default::default()
19929                })
19930            }
19931        });
19932
19933    async move {
19934        request.next().await;
19935    }
19936}
19937
19938pub(crate) fn update_test_language_settings(
19939    cx: &mut TestAppContext,
19940    f: impl Fn(&mut AllLanguageSettingsContent),
19941) {
19942    cx.update(|cx| {
19943        SettingsStore::update_global(cx, |store, cx| {
19944            store.update_user_settings::<AllLanguageSettings>(cx, f);
19945        });
19946    });
19947}
19948
19949pub(crate) fn update_test_project_settings(
19950    cx: &mut TestAppContext,
19951    f: impl Fn(&mut ProjectSettings),
19952) {
19953    cx.update(|cx| {
19954        SettingsStore::update_global(cx, |store, cx| {
19955            store.update_user_settings::<ProjectSettings>(cx, f);
19956        });
19957    });
19958}
19959
19960pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19961    cx.update(|cx| {
19962        assets::Assets.load_test_fonts(cx);
19963        let store = SettingsStore::test(cx);
19964        cx.set_global(store);
19965        theme::init(theme::LoadThemes::JustBase, cx);
19966        release_channel::init(SemanticVersion::default(), cx);
19967        client::init_settings(cx);
19968        language::init(cx);
19969        Project::init_settings(cx);
19970        workspace::init_settings(cx);
19971        crate::init(cx);
19972    });
19973
19974    update_test_language_settings(cx, f);
19975}
19976
19977#[track_caller]
19978fn assert_hunk_revert(
19979    not_reverted_text_with_selections: &str,
19980    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19981    expected_reverted_text_with_selections: &str,
19982    base_text: &str,
19983    cx: &mut EditorLspTestContext,
19984) {
19985    cx.set_state(not_reverted_text_with_selections);
19986    cx.set_head_text(base_text);
19987    cx.executor().run_until_parked();
19988
19989    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19990        let snapshot = editor.snapshot(window, cx);
19991        let reverted_hunk_statuses = snapshot
19992            .buffer_snapshot
19993            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19994            .map(|hunk| hunk.status().kind)
19995            .collect::<Vec<_>>();
19996
19997        editor.git_restore(&Default::default(), window, cx);
19998        reverted_hunk_statuses
19999    });
20000    cx.executor().run_until_parked();
20001    cx.assert_editor_state(expected_reverted_text_with_selections);
20002    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20003}